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


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

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

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

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

6 59

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.