NEFEGAGO
NEFEGAGO

Reputation: 281

Optimize code in Django - view ManyToMany as matrix

I'm trying to show user group permissions in Django and show them in a "Drupal" style like a matrix. It works, but it takes too long to make the query and paint it in the template. Is there some way to improve my code? view img up(accomplished),down(views and template.html)

views :

def GroupPermissionsView(request):

    title = "Groups Permissions"
    groups = Group.objects.all()
    permissions = Permission.objects.all()

    context = Context({
        'title': title,
        'groups': groups,
        'permissions': permissions,
    })
    return render(
        request,
        'forms_permissions.html',
        context
    )

template:

<table class="table table-striped table-inverse table-responsive table-bordered">

  <thead>
        <tr>
            <th>Permission</th>
            {% for group in groups %}
                <th>{{ group.name }}</th>
            {% endfor %}
        </tr>
  </thead>

  <tbody>
        {% for permission in permissions %}
        <tr>
        <td><b>{{permission.name}}<b></td>
        {% for group in groups %}
             {% if permission in group.permissions.all %}
                        <td><input type="checkbox" name="" checked="checked"></input></td>
            {% else %}
                        <td><input type="checkbox" ></input></td>
            {% endif %}
        {% endfor %}

        </tr>
        {% endfor %}
  </tbody>
</table>

Upvotes: 3

Views: 261

Answers (1)

hynekcer
hynekcer

Reputation: 15568

Your problem was that you run more than 4 * 200 queries, one query for every combination or rows and columns (permissions and groups). It is useful to get them all by one query. It is however not easy because the intermediate model of ManyToMany relationship between Permission and Group models is not explicit in django.contrib.auth.models. You can get that model by Model._meta API:

>>> GroupPermissions = Permission._meta.get_field('group').through
>>> GroupPermissions
<class 'django.contrib.auth.models.Group_permissions'>
>>> GroupPermissions._meta.db_table  # the table that you use in the raw query
'auth_group_permissions'  

Put it all together. Prefer a longer view and simple template:

update the view:

from collections import OrderedDict

GroupPermissions = Permission._meta.get_field('group').through
groups = Group.objects.all()
permissions = Permission.objects.all()
permission_group_set = set()
for x in GroupPermissions.objects.all():
    permission_group_set.add((x.permission_id, x.group_id))
# row title and cells for every permission
permission_group_table = OrderedDict([
    (permission, [(permission.id, group.id) in permission_group_set for group in groups])
    for permission in permissions
])

context = Context({
    'title': title,
    'groups': groups,
    'permission_group_table': permission_group_table,
})

update the template

{% for permission, cells in permission_group_table.items %}
    <tr><td><b>{{permission.name}}<b></td>
    {% for cell in cells %}
        {% if cell %}
            <td><input type="checkbox" name="" checked="checked"></input></td>
        {% else %}
            <td><input type="checkbox" ></input></td>
        {%  endif %}
    {% endfor %}
    </tr>
{% endfor %}

Upvotes: 2

Related Questions