Search code examples
djangodjango-templatesdjango-widget-tweaks

template tag 'add' requires 2 arguments, 1 provided


I have used add before but getting this strange error this time:

django.template.exceptions.TemplateSyntaxError: add requires 2 arguments, 1 provided

with the following code:

{% render_field form.full_name type="hidden" class="form-control" \
   value=user.first_name|add:"something" %}

However I don't get the error if I move add to with:

{% with fullname=user.first_name|add:"something"%}
   {% render_field form.full_name type="hidden" class="form-control" \
      value=fullname %}
{% endwith %}

Solution

  • So it looks like you are using the render_field template tag from https://github.com/jazzband/django-widget-tweaks/

    The render_field is implemented as an Advanced Custom Tag that is implemented by the package maintainer, that sadly does not support filters in it's argument list.

    A simple_tag or inclusion_tag on the other hand; which is defined by Django; does support other filters within its argument list.

    What I mean to say is that this will work if it were a simple_tag or inclusion_tag {% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}

    However, if my_tag was registered as an advanced custom tag; supporting filters within the arguments is up to the implementation.

    As I see from the implementation of the render_field code; it is an advanced custom tag and filters support appears to be broken:

    @register.tag
    def render_field(parser, token):
        """
        Render a form field using given attribute-value pairs
    
        Takes form field as first argument and list of attribute-value pairs for
        all other arguments.  Attribute-value pairs should be in the form of
        attribute=value or attribute="a value" for assignment and attribute+=value
        or attribute+="value" for appending.
        """
        error_msg = '%r tag requires a form field followed by a list of attributes and values in the form attr="value"' % token.split_contents()[0]
        try:
            bits = token.split_contents()
            tag_name = bits[0]
            form_field = bits[1]
            attr_list = bits[2:]
        except ValueError:
            raise TemplateSyntaxError(error_msg)
    
        form_field = parser.compile_filter(form_field)
    
        set_attrs = []
        append_attrs = []
        for pair in attr_list:
            match = ATTRIBUTE_RE.match(pair)
            if not match:
                raise TemplateSyntaxError(error_msg + ": %s" % pair)
            dct = match.groupdict()
            attr, sign, value = \
                dct['attr'], dct['sign'], parser.compile_filter(dct['value'])
            if sign == "=":
                set_attrs.append((attr, value))
            else:
                append_attrs.append((attr, value))
    
        return FieldAttributeNode(form_field, set_attrs, append_attrs)
    

    Specifically; the pairs of key=value are stored in attr_list in the code and run against the ATTRIBUTE_RE regex matching

    Then the filter is run using parser.compile_filter(dct['value']), however dct['value'] is 'user.first_name|add:"' -> the text "something" is lost here. My guess is that the ATTRIBUTE_RE regex needs to be improved to support this.

    >>> ATTRIBUTE_RE.match('value=user.first_name|add:"something"')
    <_sre.SRE_Match object at 0x7f8617f5a160>
    >>> match.groupdict()
    {'attr': 'value', 'value': 'user.first_name|add:"', 'sign': '='}
    

    As you can see, the value key is broken and missing the full text you supplied; which breaks the add filter downstream.

    This is exactly the use-case that the with tag is meant to solve; as it abstracts the filtering process to a level above; and you can pass the new variable to your custom render_field tag nicely.

    References:

    How to use a template filter on a custom template tag?

    https://docs.djangoproject.com/en/2.0/howto/custom-template-tags/#advanced-custom-template-tags