如何在不定义内容类型或模型的情况下使用Django权限?

时间:2022-02-26 17:07:48

I'd like to use a permissions based system to restrict certain actions within my Django application. These actions need not be related to a particular model (e.g. access to sections in the application, searching...), so I can't use the stock permissions framework directly, because the Permission model requires a reference to an installed content type.

我希望使用基于权限的系统来限制我的Django应用程序中的某些操作。这些操作不需要与特定的模型相关(例如访问应用程序中的节、搜索……),所以我不能直接使用stock permissions框架,因为权限模型需要对已安装的内容类型的引用。

I could write my own permission model but then I'd have to rewrite all the goodies included with the Django permissions, such as:

我可以编写自己的权限模型,但是我必须重写Django权限中包含的所有优点,比如:

I've checked some apps like django-authority and django-guardian, but they seem to provide permissions even more coupled to the model system, by allowing per-object permissions.

我已经检查了一些应用程序,比如django-authority和django-guardian,但是它们似乎提供了更多的权限,通过允许每个对象的权限,来提供更多的连接到模型系统的权限。

Is there a way to reuse this framework without having defined any model (besides User and Group) for the project?

是否有一种方法可以在没有为项目定义任何模型(除了用户和组)的情况下重用这个框架?

6 个解决方案

#1


37  

Django's Permission model requires a ContentType instance.

Django的权限模型需要一个ContentType实例。

I think one way around it is creating a dummy ContentType that isn't related to any model (the app_label and model fields can be set to any string value).

我认为解决这个问题的一种方法是创建一个与任何模型无关的虚拟内容类型(app_label和模型字段可以设置为任何字符串值)。

If you want it all clean and nice, you can create a Permission proxy model that handles all the ugly details of the dummy ContentType and creates "modelless" permission instances. You can also add a custom manager that filters out all Permission instances related to real models.

如果您希望它干净整洁,您可以创建一个权限代理模型来处理虚拟ContentType的所有丑陋细节,并创建“模式”权限实例。您还可以添加一个自定义管理器来过滤与真实模型相关的所有权限实例。

#2


59  

For those of you, who are still searching:

对于你们中仍在寻找的人:

You can create an auxiliary model with no database table. That model can bring to your project any permission you need. There is no need to deal with ContentType or create Permission objects explicit.

您可以创建一个没有数据库表的辅助模型。该模型可以为您的项目带来任何您需要的许可。不需要处理内容类型或创建显式的权限对象。

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'), 
        )

Right after manage.py migrate you can use these permissions like any other.

后管理。您可以像使用其他权限一样使用这些权限。

# 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 %}

#3


45  

Following Gonzalo's advice, I used a proxy model and a custom manager to handle my "modelless" permissions with a dummy content type.

按照冈萨洛的建议,我使用了一个代理模型和一个自定义管理器,用一个虚拟内容类型来处理我的“无模式”权限。

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


8  

Fix for Chewie's answer in Django 1.8, which as been requested in a few comments.

在Django 1.8中修正了Chewie的答案,这是在一些评论中被要求的。

It says in the release notes:

它在发布说明中写道:

The name field of django.contrib.contenttypes.models.ContentType has been removed by a migration and replaced by a property. That means it’s not possible to query or filter a ContentType by this field any longer.

django.programb .contenttypes.models的名称字段。内容类型已被迁移删除,并由属性替换。这意味着不可能再通过这个字段查询或过滤一个内容类型。

So it's the 'name' in reference in ContentType that the uses not in GlobalPermissions.

所以在ContentType中引用的“name”在GlobalPermissions中没有使用。

When I fix it I get the following:

当我修复它时,我得到以下信息:

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)

The GlobalPermissionManager class is unchanged but included for completeness.

GlobalPermissionManager类没有改变,但为了完整性而包含在内。

#5


1  

Instead of writing and running this code which inserts records into the database you could just insert the records into your database (obviously editing the primary and foreign keys as needed)

而不是编写并运行将记录插入数据库的代码,您只需将记录插入到数据库中(显然需要根据需要编辑主键和外键)

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');

And then in your application admin you would have the ability to assign 'Is Staff Member' to your users or groups. To check this permission in your class you would write

然后在你的应用管理中你可以将“Is人员”分配给你的用户或组。要在类中检查此权限,您需要编写

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


1  

This is alternative solution. First ask yourself: Why not create a Dummy-Model which really exists in DB but never ever gets used, except for holding permissions? That's not nice, but I think it is valid and straight forward solution.

这是替代解决方案。首先问问自己:为什么不创建一个真正存在于DB中但从未被使用过的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'),
        ]

Above solution has the benefit, that you can use the variable Permissions.can_search_blue_flower in your source code instead of using the literal string "my_app.can_search_blue_flower". This means less typos and more autocomplete in IDE.

上面的解决方案有好处,您可以使用变量权限。在源代码中使用can_search_blue_flower而不是文字字符串“my_app.can_search_blue_flower”。这意味着在IDE中输入错误更少,自动完成更多。

#1


37  

Django's Permission model requires a ContentType instance.

Django的权限模型需要一个ContentType实例。

I think one way around it is creating a dummy ContentType that isn't related to any model (the app_label and model fields can be set to any string value).

我认为解决这个问题的一种方法是创建一个与任何模型无关的虚拟内容类型(app_label和模型字段可以设置为任何字符串值)。

If you want it all clean and nice, you can create a Permission proxy model that handles all the ugly details of the dummy ContentType and creates "modelless" permission instances. You can also add a custom manager that filters out all Permission instances related to real models.

如果您希望它干净整洁,您可以创建一个权限代理模型来处理虚拟ContentType的所有丑陋细节,并创建“模式”权限实例。您还可以添加一个自定义管理器来过滤与真实模型相关的所有权限实例。

#2


59  

For those of you, who are still searching:

对于你们中仍在寻找的人:

You can create an auxiliary model with no database table. That model can bring to your project any permission you need. There is no need to deal with ContentType or create Permission objects explicit.

您可以创建一个没有数据库表的辅助模型。该模型可以为您的项目带来任何您需要的许可。不需要处理内容类型或创建显式的权限对象。

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'), 
        )

Right after manage.py migrate you can use these permissions like any other.

后管理。您可以像使用其他权限一样使用这些权限。

# 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 %}

#3


45  

Following Gonzalo's advice, I used a proxy model and a custom manager to handle my "modelless" permissions with a dummy content type.

按照冈萨洛的建议,我使用了一个代理模型和一个自定义管理器,用一个虚拟内容类型来处理我的“无模式”权限。

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


8  

Fix for Chewie's answer in Django 1.8, which as been requested in a few comments.

在Django 1.8中修正了Chewie的答案,这是在一些评论中被要求的。

It says in the release notes:

它在发布说明中写道:

The name field of django.contrib.contenttypes.models.ContentType has been removed by a migration and replaced by a property. That means it’s not possible to query or filter a ContentType by this field any longer.

django.programb .contenttypes.models的名称字段。内容类型已被迁移删除,并由属性替换。这意味着不可能再通过这个字段查询或过滤一个内容类型。

So it's the 'name' in reference in ContentType that the uses not in GlobalPermissions.

所以在ContentType中引用的“name”在GlobalPermissions中没有使用。

When I fix it I get the following:

当我修复它时,我得到以下信息:

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)

The GlobalPermissionManager class is unchanged but included for completeness.

GlobalPermissionManager类没有改变,但为了完整性而包含在内。

#5


1  

Instead of writing and running this code which inserts records into the database you could just insert the records into your database (obviously editing the primary and foreign keys as needed)

而不是编写并运行将记录插入数据库的代码,您只需将记录插入到数据库中(显然需要根据需要编辑主键和外键)

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');

And then in your application admin you would have the ability to assign 'Is Staff Member' to your users or groups. To check this permission in your class you would write

然后在你的应用管理中你可以将“Is人员”分配给你的用户或组。要在类中检查此权限,您需要编写

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


1  

This is alternative solution. First ask yourself: Why not create a Dummy-Model which really exists in DB but never ever gets used, except for holding permissions? That's not nice, but I think it is valid and straight forward solution.

这是替代解决方案。首先问问自己:为什么不创建一个真正存在于DB中但从未被使用过的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'),
        ]

Above solution has the benefit, that you can use the variable Permissions.can_search_blue_flower in your source code instead of using the literal string "my_app.can_search_blue_flower". This means less typos and more autocomplete in IDE.

上面的解决方案有好处,您可以使用变量权限。在源代码中使用can_search_blue_flower而不是文字字符串“my_app.can_search_blue_flower”。这意味着在IDE中输入错误更少,自动完成更多。