Opened 6 years ago

Closed 5 years ago

#14262 closed New feature (fixed)

Helper for "get_something as varname" template tag pattern

Reported by: Carl Meyer Owned by: nobody
Component: Template system Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX:

Description

It's a common pattern to write a template tag that fetches some data and sticks it in a context variable. Currently writing these tags requires writing a full-blown parsing function and Node class, which is mostly boilerplate except for the render() method. It would be good to have a template tag helper for writing tags that follow this pattern. (Similar to simple_tag, except allowing for updating the context).

Attachments (3)

14262.assignment_tag.diff (13.6 KB) - added by Julien Phalip 5 years ago.
14262.assignment_tag.2.diff (13.3 KB) - added by Julien Phalip 5 years ago.
Updated patch to current trunk
14262.assignment_tag.3.diff (12.3 KB) - added by Julien Phalip 5 years ago.
Small doc fixes

Download all attachments as: .zip

Change History (14)

comment:1 Changed 6 years ago by Carl Meyer

Needs documentation: unset
Needs tests: unset
Patch needs improvement: unset
Triage Stage: UnreviewedAccepted

Per design conversation with Jannis, Malcolm, and Russell at sprint, marking as Accepted and superseding #1105.

comment:2 Changed 6 years ago by Julien Phalip

I'm keen to write a patch for this, but first I'd like to be sure of the intent with this decorator. The name "assignment_tag" has been suggested in (1). Now which API do you anticipate? Is it one of the following:

{% my_tag arg1 arg2 %}

@register.assignment_tag
def my_tag(arg1, arg2, ...):
    ...
    return {
            'key1': value1,
            'key2': value2,
        } # These keys/values will be added to the context

or:

{% my_tag arg1 arg2 as blah %}

@register.assignment_tag
def my_tag(arg1, arg2, ...):
    ...
    return value # This will be set to the variable 'blah' in the context

or else?

Also, it might be useful to have access to the context itself (at least in read-only mode) from within that tag. If you agree, then what would the API be?

(1) http://groups.google.com/group/django-developers/browse_thread/thread/fbf7d9fd8f256b17/

comment:3 in reply to:  2 Changed 6 years ago by Carl Meyer

Replying to julien:

Now which API do you anticipate?

Definitely the latter ({% mytag arg1 arg2 as blah %}), with the varname explicitly specified in the template.

The other encourages a pattern where the keys to update in the context are fixed in the tag, which makes the tag less flexible and the template less readable. Of course it's possible to have those keys passed in, but it makes it more work to do things right and easier to do them wrong.

Also, it might be useful to have access to the context itself (at least in read-only mode) from within that tag. If you agree, then what would the API be?

Now that both inclusion_tag and simple_tag have takes_context, I see no reason not to carry that consistency to this shortcut as well, and have a takes_context arg that works the same way. I know this leaves room for people to do wierd and confusing things where they update the context themselves in the tag code, and then also return something for the automatic assignment_tag update; but on the whole I think the value of consistency with the other shortcuts outweighs that minor concern.

I'd also note that with the #14908 fix, it's now possible to write a simple_tag like this:

@register.simple_tag(takes_context=True)
def get_foo(context, _as, varname):
    # ... fetch some data ...
    context[varname] = ...
    return ''

Which achieves the same thing as assignment_tag, although using it in a template requires unsightly quoting of both "as" and the target varname: {% get_foo "as" "bar" %}.

I'm still in favor of adding assignment_tag, to make the syntax nicer in the template and make the technique more explicit.

comment:4 Changed 6 years ago by Julien Phalip

I totally agree there should be an explicit and designer-friendly way of assigning values so I too am in favour of introducing this new decorator. My only concern now is that some times you may want to assign multiple values after executing some expensive calculations (e.g. heavy database queries), so it would be great if something like the following could be achieved:

{% get_values arg1 arg2 as var1 var2 %}

@register.assignment_tag
def get_values(arg1, arg2):
    ... expensive calculations ...
    return (foo, bar) # foo would be assigned to 'var1' and foo to 'var2'.

If multiple variables were specified after the 'as' then the tag would be expected to return a list or tuple. If only one variable were specified, then whatever is returned by the tag would be assigned to that variable (a single value or a list or whatever).

If this idea is rejected, then at least one will still be able to do the following:

{% get_values arg1 arg2 as var %} {# Tag returns a list #}

{{ var.0 }} and {{ var.1 }}

... less explicit but no big deal.

What do you think?

comment:5 Changed 6 years ago by Carl Meyer

The multiple-assignment version is a poor tradeoff of adding complexity to support an edge case, when just assigning a list (or a dict for better template readability) works just fine. And if you have complex needs, you write a Node. The simple shortcuts are for simple cases.

comment:6 Changed 6 years ago by Julien Phalip

Also check out #2016 which was in fact the first ticket to ask for this new functionality and has patches.

comment:7 Changed 5 years ago by Julien Phalip

Severity: Normal
Type: Uncategorized

See #6378 for a related feature request (capturing template output).

comment:8 Changed 5 years ago by Chris Beaven

Type: UncategorizedNew feature

Changed 5 years ago by Julien Phalip

Attachment: 14262.assignment_tag.diff added

comment:9 Changed 5 years ago by Julien Phalip

Easy pickings: unset
Has patch: set

I've attached a patch which implements this new feature and includes tests and doc. Any feedback welcome ;)

Changed 5 years ago by Julien Phalip

Attachment: 14262.assignment_tag.2.diff added

Updated patch to current trunk

Changed 5 years ago by Julien Phalip

Attachment: 14262.assignment_tag.3.diff added

Small doc fixes

comment:10 Changed 5 years ago by Jannis Leidel

Triage Stage: AcceptedReady for checkin

comment:11 Changed 5 years ago by Jannis Leidel

Resolution: fixed
Status: newclosed

In [16149]:

Fixed #14262 -- Added new assignment_tag as a simple way to assign the result of a template tag to a context variable. Thanks, Julien Phalip.

Note: See TracTickets for help on using tickets.
Back to Top