Как я могу использовать разрешения Django без определения типа контента или модели?
Я хотел бы использовать систему на основе разрешений для ограничения определенных действий в моем приложении Django. Эти действия не обязательно должны быть связаны с конкретной моделью (например, доступ к разделам в приложении, поиск...), поэтому я не могу использовать Stock permissions framework, потому что Permission
модель требует Ссылки на установленный тип контента.
я мог бы написать свою собственную модель разрешений, но тогда мне пришлось бы переписать все лакомства в комплекте с Django разрешения, такие как:
- возможность назначать разрешения пользователям и группам.
- The
permission_required
оформителя. -
User.has_perm
и связанные пользовательские методы. - The
perms
переменной шаблона. - ...
Я проверил некоторые приложения, такие как Джанго-администрации и Джанго-хранитель, но они, кажется, предоставьте разрешения, еще более связанные с системой модели, разрешив разрешения для каждого объекта.
есть ли способ повторно использовать эту структуру без определения какой-либо модели (кроме User
и Group
) для проекта?
6 ответов:
Джанго
Permission
модель требуетContentType
экземпляр.Я думаю, что один из способов обойти это создание манекена
ContentType
это не связано ни с какой моделью (app_label
иmodel
поля могут быть установлены в любое строковое значение).если вы хотите, чтобы все чисто и красиво, вы можете создать
Permission
прокси-модель, который обрабатывает все уродливые детали манекенаContentType
и создает экземпляры разрешений "без модели". Вы также можете добавить пользовательский менеджер это отфильтровывает всеPermission
экземпляры, связанные с реальными моделями.
для тех из вас, кто еще в поиске:
вы можете создать вспомогательную модель без таблицы базы данных. Эта модель может привнести в ваш проект любого разрешения. Нет необходимости иметь дело с ContentType или создавать явные объекты разрешений.
from django.db import models class RightsSupport(models.Model): class Meta: managed = False # No database table creation or deletion operations \ # will be performed for this model. permissions = ( ('customer_rigths', 'Global customer rights'), ('vendor_rights', 'Global vendor rights'), ('any_rights', 'Global any rights'), )
сразу после
manage.py migrate
вы можете использовать эти разрешения, как и любые другие.# Decorator @permission_required('app.customer_rights') def my_search_view(request): … # Inside a view def my_search_view(request): request.user.has_perm('app.customer_rights') # In a template # The currently logged-in user’s permissions are stored in the template variable {{ perms }} {% if perms.app.customer_rigths %} <p>You can do any customer stuff</p> {% endif %}
после совет Гонсало, я использовал прокси-модель и custom manager для обработки моих разрешений "без модели" с фиктивным типом контента.
from django.db import models from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType class GlobalPermissionManager(models.Manager): def get_query_set(self): return super(GlobalPermissionManager, self).\ get_query_set().filter(content_type__name='global_permission') class GlobalPermission(Permission): """A global permission, not attached to a model""" objects = GlobalPermissionManager() class Meta: proxy = True def save(self, *args, **kwargs): ct, created = ContentType.objects.get_or_create( name="global_permission", app_label=self._meta.app_label ) self.content_type = ct super(GlobalPermission, self).save(*args, **kwargs)
исправить ответ Чуи в Django 1.8, который, как было предложено в нескольких комментариях.
Он говорит в выпуске:
поле " Имя " Джанго.ВНО.contenttypes.модели.Значение contentType была удаляется миграцией и заменяется свойством. Это значит, что это не так можно запросить или фильтровать ContentType по этому полю больше.
Так что это ' имя ' в ссылке в ContentType, что использует не в GlobalPermissions.
когда я это сделаю, я получаю следующее:
from django.db import models from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType class GlobalPermissionManager(models.Manager): def get_queryset(self): return super(GlobalPermissionManager, self).\ get_queryset().filter(content_type__model='global_permission') class GlobalPermission(Permission): """A global permission, not attached to a model""" objects = GlobalPermissionManager() class Meta: proxy = True verbose_name = "global_permission" def save(self, *args, **kwargs): ct, created = ContentType.objects.get_or_create( model=self._meta.verbose_name, app_label=self._meta.app_label, ) self.content_type = ct super(GlobalPermission, self).save(*args)
класс GlobalPermissionManager не изменился, но включен для полноты.
вместо того, чтобы писать и запускать этот код, который вставляет записи в базу данных, вы можете просто вставить записи в свою базу данных (очевидно, редактируя первичные и внешние ключи по мере необходимости)
insert into django_content_type(id,name,app_label,model) values (22,'app_permission','myapp','app_permission'); insert into auth_permission(id,name,content_type_id,codename) values (64,'Is Staff Member',22,'staff_member');
и тогда в вашем администраторе приложения вы сможете назначить "is Staff Member" своим пользователям или группам. Чтобы проверить это разрешение в своем классе вы должны написать
from django.contrib.auth.decorators import permission_required from django.utils.decorators import method_decorator from django.views.generic import TemplateView class MyClass(TemplateView): template_name = myapp/index.html' @method_decorator(permission_required(['myapp.staff_member'],raise_exception=True)) def dispatch(self, *args, **kwargs): return super(MyClass, self).dispatch(*args, **kwargs)
это альтернативное решение. Сначала спросите себя: почему бы не создать фиктивную модель, которая действительно существует в БД, но никогда не используется, кроме как для удержания разрешений? Это не очень хорошо, но я думаю, что это правильное и прямое решение.
from django.db import models class Permissions(models.Model): can_search_blue_flower = 'my_app.can_search_blue_flower' class Meta: permissions = [ ('can_search_blue_flower', 'Allowed to search for the blue flower'), ]
выше решение имеет то преимущество, что вы можете использовать переменную
Permissions.can_search_blue_flower
в исходном коде вместо использования литеральной строки " my_app.can_search_blue_flower". Это означает меньше опечаток и больше автозаполнения в IDE.