Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use method parameters in a Django template?

Tags:

I know there is a post here: django template system, calling a function inside a model describing how you can make a custom template filter to achieve this, but from a programmer's standpoint this is a failure because that's hacking something that isn't meant for that. It seems almost ridiculous that you can't call a method with parameters in Django's template system.

like image 837
orokusaki Avatar asked Oct 07 '09 04:10

orokusaki


People also ask

How do you pass variables from Django view to a template?

And this is rather simple, because Django has built-in template modules that makes a transfer easy. Basically you just take the variable from views.py and enclose it within curly braces {{ }} in the template file. We won't go into a database example for this one.

What does {% %} mean in Django?

{% %} and {{ }} are part of Django templating language. They are used to pass the variables from views to template. {% %} is basically used when you have an expression and are called tags while {{ }} is used to simply access the variable.


2 Answers

The Django team has decided, as a matter of philosophy, not to allow passing method parameters in a view. Personally, I agree with them; it forces a separation of logic and presentation that I find helpful. It prevents the kind of spaghetti code that PHP is notorious for.

The right thing to do in the case you linked is to pass the result of that call from the view into the template via the context. It's just more maintainable that way. If later you need to change my_related_deltas(3) to my_related_deltas(4), you go to the view, which should be fairly concise, instead of searching through templates to figure out exactly where it is defined.

like image 138
tghw Avatar answered Oct 11 '22 21:10

tghw


Despite django authors suggest not to feed our methods with arguments you can still do that using this 'little' template tag I wrote.

In my example I'm just showing that this is possible. For security reasons I strongly recommend you to write templatetags instead of trying to pass arguments to model methods.

!WARNING! this is for testing purpose only! By using this you might be able to hack into NASA as well as you may got killed.

class CallNode(template.Node):     def __init__(self,object, method, args=None, kwargs=None, context_name=None):         self.object = template.Variable(object)         self.method = method         if args:             self.args = []             for arg in args:                 self.args.append(template.Variable(arg))         else:             self.args = None          if kwargs:             self.kwargs = {}             for key in kwargs:                 self.kwargs[key] = template.Variable(kwargs[key])         else:             self.kwargs = None          self.context_name = context_name      def render(self, context):         object = self.object.resolve(context)         if isinstance(object, str):             raise template.TemplateSyntaxError('Given object is string ("%s") of length %d'                                                 % (object, len(object)))          args = []         kwargs = {}         if self.args:             for arg in self.args:                 args.append(arg.resolve(context))         if self.kwargs:             for key in self.kwargs:                 kwargs[key] = self.kwargs[key].resolve(context)          method = getattr(object, self.method, None)          if method:             if hasattr(method, '__call__'):                  result = method(*args, **kwargs)             else:                 result = method             if self.context_name:                 context[self.context_name] = result                 return ''             else:                 if not result == None:                      return result                 else:                     return ''         else:             raise template.TemplateSyntaxError('Model %s doesn\'t have method "%s"'                                                 % (object._meta.object_name, self.method))   @register.tag def call(parser, token):     """     Passes given arguments to given method and returns result      Syntax::          {% call <object>[.<foreignobject>].<method or attribute> [with <*args> <**kwargs>] [as <context_name>] %}      Example usage::          {% call article.__unicode__ %}         {% call article.get_absolute_url as article_url %}         {% call article.is_visible with user %}         {% call article.get_related with tag 5 as related_articles %}          {% call object.foreign_object.test with other_object "some text" 123 article=article text="some text" number=123 as test %}      """      bits = token.split_contents()     syntax_message = ("%(tag_name)s expects a syntax of %(tag_name)s "                        "<object>.<method or attribute> [with <*args> <**kwargs>] [as <context_name>]" %                        dict(tag_name=bits[0]))      temp = bits[1].split('.')     method = temp[-1]     object = '.'.join(temp[:-1])      # Must have at least 2 bits in the tag     if len(bits) > 2:         try:             as_pos = bits.index('as')         except ValueError:             as_pos = None         try:             with_pos = bits.index('with')         except ValueError:             with_pos = None          if as_pos:             context_name = bits[as_pos+1]         else:             context_name = None          if with_pos:             if as_pos:                 bargs = bits[with_pos+1:as_pos]             else:                 bargs = bits[with_pos+1:]         else:             bargs = []          args = []         kwargs = {}          if bargs:             for barg in bargs:                 t = barg.split('=')                 if len(t) > 1:                     kwargs[t[0]] = t[1]                 else:                     args.append(t[0])          return CallNode(object, method, args=args, kwargs=kwargs, context_name=context_name)     elif len(bits) == 2:         return CallNode(object, method)     else:         raise template.TemplateSyntaxError(syntax_message) 
like image 27
seler Avatar answered Oct 11 '22 22:10

seler