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.
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.
{% %} 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.
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With