Как я могу использовать разрешения Django без определения типа или модели контента?
Я хотел бы использовать систему на основе разрешений для ограничения определенных действий в моем приложении Django. Эти действия не обязательно должны быть связаны с конкретной моделью (например, доступ к разделам в приложении, поиск...), поэтому я не могу напрямую использовать рамки разрешений на акции, потому что для модели Permission
требуется ссылка на установленный тип содержимого.
Я мог бы написать свою собственную модель разрешения, но потом мне пришлось бы переписать все лакомства, включенные в разрешения Django, например:
Я проверил некоторые приложения, такие как django-authority и django-guardian, но они, похоже, предоставить разрешения, даже более связанные с модельной системой, разрешив разрешения для каждого объекта.
Есть ли способ повторно использовать эту структуру без определения какой-либо модели (кроме User
и Group
) для проекта?
Ответы
Ответ 1
Django Permission
model требует экземпляр ContentType
.
Я думаю, что один путь вокруг него создает манекен ContentType
, который не связан ни с одной моделью (поля app_label
и model
могут быть установлены на любое строковое значение).
Если вы хотите, чтобы все было чисто и красиво, вы можете создать прокси-модель Permission
, которая обрабатывает все уродливые детали манекена ContentType
и создает экземпляры разрешений "modelless". Вы также можете добавить настраиваемый менеджер, который отфильтровывает все экземпляры Permission
, связанные с реальными моделями.
Ответ 2
Для тех из вас, кто все еще ищет:
Вы можете создать вспомогательную модель без таблицы базы данных. Эта модель может принести вашему проекту любое разрешение, которое вам нужно. Нет необходимости иметь дело с ContentType или явно создавать объекты Permission.
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_rights', '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 users permissions are stored in the template variable {{ perms }}
{% if perms.app.customer_rigths %}
<p>You can do any customer stuff</p>
{% endif %}
Ответ 3
Следуя совету Гонсало, я использовал прокси-модель и собственный менеджер для обработки своих "немодельных" разрешений с фиктивным типом контента.
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)
Ответ 4
Исправить для ответа Chewie в Django 1.8, который был запрошен в нескольких комментариях.
В примечаниях к выпуску говорится:
Поле имени django.contrib.contenttypes.models.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 не изменяется, но включен для полноты.
Ответ 5
Вместо того, чтобы писать и запускать этот код, который вставляет записи в базу данных, вы можете просто вставить записи в свою базу данных (очевидно, редактируя первичный и внешний ключи по мере необходимости)
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');
И тогда в вашем приложении администратора вы сможете назначить "Пользовательский член" своим пользователям или группам. Чтобы проверить это разрешение в своем классе, вы должны написать
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)
Ответ 6
Это альтернативное решение. Сначала спросите себя: почему бы не создать Dummy-Model, которая действительно существует в БД, но никогда не используется, кроме разрешения на доступ? Это нехорошо, но я думаю, что это правильное и прямое решение.
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.