How to use method parameters in a Django template?

前端 未结 2 1305
北海茫月
北海茫月 2020-12-10 11:05

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 progr

相关标签:
2条回答
  • 2020-12-10 11:17

    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.

    0 讨论(0)
  • 2020-12-10 11:39

    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)
    
    0 讨论(0)
提交回复
热议问题