Complex template logic with {% record %} and {% with %}
egj actionkit
ActionKit's {% record %} tag can be used to build pretty complex logic directly in your page & email code.
Here's a basic example of the pattern:
{% with "[ ]"|load_json as my_variable %} {% if user.custom_fields.favorite_color == "purple" %} {% record "purple" in my_variable %} {% elif user.custom_fields.favorite_color %} {% record "not_purple" in my_variable {% endif %} {% if my_variable|length == 0 %} What's your favorite color? You haven't told me yet. {% else %} It looks like your favorite color is {{ my_variable|nth:0 }}. {% endif %} {% endwith %}
The trick is to wrap your whole code block in one or more {% with "[ ]|load_json" as my_variable %}
statements. This lets you create arbitrary temporary variables that you can then stash computed data or state in, using {% record %}
.
This can be very useful when you're dealing with loops combined with complex conditional logic. For example, we can use it to send emails about a filtered list of events, or events from multiple categories, without needing merge queries:
{% with "[]"|load_json as matching_events %} {% for campaign_name in "first_campaign second_campaign third_campaign"|split %} {% withevents with user as nearby_user 'campaign_name' as campaign 20 as radius 10 as limit %} {% for event in events %} {% if event.custom_fields.categories|force_list|contains:"parade" %} {% record event in matching_events %} {% endif %} {% endfor %} {% endwithevents %} {% endfor %} {% if matching_events|length == 0 %} {% requires_value no_matching_events_found %} {% endif %} <ul> {% for event in matching_events|dictsort:"starts_at"|slice:":5" %} <li>{{ event.title }} {% endfor %} </ul> {% endwith %}
By stashing matching events in a temporary list instead of printing them out right away, we're able to do at least three things that would otherwise be impossible with pure backend template logic:
- Checking whether we have any matching events by the end of our loop, and suppressing the email altogether (or displaying some conditional content) if no matching events were found
- Re-sorting our matching events once we have the final list, even across multiple campaigns
- Displaying at most five matching events, regardless of how many were found to match
One thing to be careful about is that {% record %} only seems to work with simple predefined variables. If you try to chain filters in a {% record %} statement you end up doing nothing. So instead of
{% record user.custom_fields.age|add:"1" in my_variable %}
you'll need to use a {% with %} statement to set a temporary variable:
{% with user.custom_fields.age|add:"1" as computed %} {% record computed in my_variable %} {% endwith %}