Search code examples
djangodjango-templatesdjango-context

Is there a shorter way to check for a value in M2M in a Django template?


In every page (base.html), I want to check whether request.user has an administrator role from my class UserTypes and show the admin link. Currently I do something like this:

{% if user.profile.user_types.all %}
    {% for user_type in user.profile.user_types.all %}
        {% if user_type.name == "ad" %}
            <li>
                <a href="{% url admin:index %}" class="round button dark ic-settings image-left">Admin</a>
            </li>
        {% endif %}
    {% endfor %}
{% endif %}

user.profile is simply going from Django's User to my UserProfile.

But this seems a little verbose and clunky. Is there a simpler way? Maybe I should be writing my own custom context processor and pass a variable like is_admin or something, but I never wrote a custom context processor before...


Solution

  • You can add method is_admin to your UserProfile model moving business logic to models.

    Note that construction like

    {% if user.profile.user_types.all %}
        {% for user_type in user.profile.user_types.all %}
        ...
        {% endfor %}
    {% endif %}
    

    hits 2 sql query to your db. But with template tag reduces them to 1 hit.

    {% with types=user.profile.user_types.all %}
    {% if types %}
        {% for user_type in types %}
        ...
        {% endfor %}
    {% endif %}
    {% endwith %}
    

    Actually the best place for this is in models. But you should learn what django offers for your purpose (contrib.auth, permissions, user groups). Probably you reinvent the wheel.

    Then condition {% if user_type.name == "ad" %} shouldn't be hard coded in your python code (especially in templates).