Подтвердить что ты не робот

Как я могу использовать разрешения Django без определения типа или модели контента?

Я хотел бы использовать систему на основе разрешений для ограничения определенных действий в моем приложении Django. Эти действия не обязательно должны быть связаны с конкретной моделью (например, доступ к разделам в приложении, поиск...), поэтому я не могу напрямую использовать рамки разрешений на акции, потому что для модели Permission требуется ссылка на установленный тип содержимого.

Я мог бы написать свою собственную модель разрешения, но потом мне пришлось бы переписать все лакомства, включенные в разрешения Django, например:

  • Возможность назначать разрешения пользователям и группам.
  • Декодер permission_required.
  • User.has_perm и соответствующие методы пользователя.
  • Шаблон perms.
  • ...

Я проверил некоторые приложения, такие как django-authority и django-guardian, но они, похоже, предоставить разрешения, даже более связанные с модельной системой, разрешив разрешения для каждого объекта.

Есть ли способ повторно использовать эту структуру без определения какой-либо модели (кроме User и Group) для проекта?

4b9b3361

Ответ 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.