Django: решение проблемы с правами доступа для прокси-моделей

В апреле вышел Django 1.11, но 8-летний баг, связанный с несогласованностью списка прав доступа для прокси-моделей, остался во фреймворке. Рассказываю, как решить проблему.

Суть проблемы заключается в том, что невозможно предоставить доступ пользователям админ-части к прокси-моделям, поскольку список доступных разрешений просто-напросто отсутствует в интерфейсе управления пользователями.

Технически подсистема типов контента рассматривает прокси-модели и их родителей как одну и ту же модель, а подсистема прав доступа считает их отдельными моделями. Это и составляет проблему, которая остается актуальной в Django 1.11.

Решение

Добавить отсутствующие разрешения возможно вручную. Чтобы упростить процесс, можно воспользоваться функционалом собственных команд django-admin. Добавим действие fix_proxy_permissions для manage.py:

"""Add permissions for proxy model.
This is needed because of the bug https://code.djangoproject.com/ticket/11154
in Django (as of 1.11, it's not fixed).
"""

import sys

from django.apps import apps
from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    help = "Fix permissions for proxy models."

    def handle(self, *args, **options):
        for model in apps.get_models():
            opts = model._meta

            ctype, created = ContentType.objects.get_or_create(
                app_label=opts.app_label,
                model=opts.object_name.lower(),
                defaults={'model': opts.object_name.lower()})

            for codename, name in _get_all_permissions(opts):
                p, created = Permission.objects.get_or_create(
                    codename=codename,
                    content_type=ctype,
                    defaults={'name': name})
                if created:
                    sys.stdout.write('Adding permission {}\n'.format(p))

Использование

  1. Создайте директорию /myproject/myapp/management/commands с файлом __init__.py.
  2. Код команды fix_proxy_permissions сохраните в файле /myproject/myapp/management/commands/fix_proxy_permissions.py.
  3. Запустите в консоли: /manage.py fix_proxy_permissions.