15

I want to assign a variable do different values depending on if a variable exists, is this possible? My non working example might make it clearer:

{% if username %}
    {% with menu_user=username %}
{% elif recent_users %}
    {% with sorted_users=recent_users|dictsortreversed:"timestamp" %}
    {% with menu_user=sorted_users.0.username %}
{% endif %}
{% if menu_user %}
    <div id="menu">
        <ul>
            <li><a href="/user/{{ menu_user|urlencode }}">Profile</a></li>
            <li><a href="/user/{{ menu_user|urlencode }}/products/">Products</a></li>
        </ul>
    </div>
{% endif %}
{% if recent_users %}
    {% endwith %}
{% endif %}
    {% endwith %}

Pseudocode of what I try to do:

if username:
    menu_user = username
elif recent_users:
    menu_user = sorted(recent_users)[0]['username']

if menu_user:
    <div id="menu">
        <ul>
            <li><a href="/user/{{ menu_user|urlencode }}">Profile</a></li>
            <li><a href="/user/{{ menu_user|urlencode }}/products/">Products</a></li>
        </ul>
    </div>
olofom
  • 6,233
  • 11
  • 37
  • 50
  • You should place that kind of logic in *view* layer. Try to keep templates as simple as possible. – seler May 28 '12 at 15:27
  • 1
    @seler the thing is that every page should render this and there will be a lot of boilerplate code, that is why I want it in only one place in the base template. – olofom May 28 '12 at 20:44
  • related: https://stackoverflow.com/q/30933754 – djvg Mar 26 '21 at 08:23

3 Answers3

8

update Then its better to customize a template tag like

@register.inclusion_tag('menu_snippet.html')  # or you could use takes_context=True and fetch values from the context
def render_menu(username, recent_users):
    if username:
        menu_user = username
    elif recent_users:
        # sorted here could be replaced by min or QuerySet method, it depends
        # for example: 
        # menu_user = min(recent_users, key=lambda u:u.timestamp).username
        menu_user = sorted(recent_users)[0]['username']
    return {'menu_user':menu_user}

# in template, it looks like
{% render_menu username recent_users %}

Putting the code in the view is much better. Just as your pseudocode, clean and readable.

If you still want to write template, I prefer something like

{% if username %}
    <div id="menu">
        <ul>
            <li><a href="/user/{{ username|urlencode }}">Profile</a></li>
            <li><a href="/user/{{ username|urlencode }}/products/">Products</a></li>
        </ul>
    </div>
{% else %}
    {% if recent_users %}
    {% with sorted_users=recent_users|dictsortreversed:"timestamp" %}
    {% with menu_user=sorted_users.0.username %}
    <div id="menu">
        <ul>
            <li><a href="/user/{{ menu_user|urlencode }}">Profile</a></li>
            <li><a href="/user/{{ menu_user|urlencode }}/products/">Products</a></li>
        </ul>
    </div>
    {% endwith %}{% endwith %}
    {% endif %}
{% endif %}

Depends on your actual usage, customized template tag or the include tag are also possibly useful.

okm
  • 23,575
  • 5
  • 83
  • 90
  • That's the way I have it now but the menu is quite large and I'd prefer not having it multiple times. This page is the base template so it's displayed on every page and that's why I don't want to do it in the view, because there's so many views that should display this (all of them). – olofom May 28 '12 at 14:41
  • @olofom updated the answer, you could use some customized tag then. – okm May 28 '12 at 14:54
  • That sounds better, I'll have a look at it later. Not sure how to call it in the template though, I've only used filter with |. – olofom May 28 '12 at 15:18
  • @olofom just call `{% render_menu username recent_users %}`, check [the doc](https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#howto-custom-template-tags-inclusion-tags) – okm May 28 '12 at 15:24
  • I ended up using the context instead so I just call it with {% render_menu %}. Works great, thanks! – olofom May 29 '12 at 08:02
  • 1
    small optimisation, you can assign both sorted_user and menu_user within the same "with" tag – Iker Jimenez Oct 21 '13 at 21:32
4

Template tag:

@register.assignment_tag
def alias(obj):
    """
    Alias Tag
    """
    return obj

Template:

{% alias sorted_users.0.username as menu_user %}
laffuste
  • 16,287
  • 8
  • 84
  • 91
  • What is the difference with the `with` tag? – merwok Aug 27 '18 at 17:19
  • 2
    I might be wrong, this is from some time ago, but I think the `with` tag encloses the code, while this can be used anywhere akin to declaring a variable in your regular python code. You could declare it as empty and then reassign it inside an `if`: its value would be preserved outside the `if`. Not that is a good practice but it could be useful at some point. – laffuste Sep 12 '18 at 15:15
  • 2
    Note, [as of Django 1.9](https://docs.djangoproject.com/en/3.1/releases/1.9/#assignment-tag), we need to use `simple_tag` instead of `assignment_tag` – djvg Mar 26 '21 at 08:31
0

Create a template tag that takes username and recent_users as arguments which then outputs the menu. That way you will keep your template clean from that kind of logic.

Mikael
  • 3,148
  • 22
  • 20