98

我想使用基于权限的系统来限制我的 Django 应用程序中的某些操作。这些操作不需要与特定模型相关(例如访问应用程序中的部分、搜索...),因此我不能直接使用库存权限框架,因为Permission模型需要引用已安装的内容类型。

我可以编写自己的权限模型,但是我必须重写 Django 权限中包含的所有好东西,例如:

我检查了一些应用程序,例如django-authoritydjango-guardian,但它们似乎通过允许每个对象的权限来提供与模型系统更加耦合的权限。

有没有办法在没有为项目定义任何模型(除了UserGroup)的情况下重用这个框架?

4

8 回答 8

195

对于那些仍在寻找的人:

您可以创建一个没有数据库表的辅助模型。该模型可以为您的项目带来您需要的任何权限。无需处理 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. 
                
        default_permissions = () # disable "add", "change", "delete"
                                 # and "view" default permissions

        permissions = ( 
            ('customer_rights', 'Global customer rights'),  
            ('vendor_rights', 'Global vendor rights'), 
            ('any_rights', 'Global any rights'), 
        )

之后manage.py makemigrationsmanage.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_rights %}
    <p>You can do any customer stuff</p>
{% endif %}
于 2016-06-23T10:07:35.973 回答
61

Django 的Permission模型需要一个ContentType实例

我认为解决它的一种方法是创建一个ContentType与任何模型都不相关的虚拟对象(app_labelandmodel字段可以设置为任何字符串值)。

如果您希望一切都干净漂亮,您可以创建一个Permission 代理模型来处理假人的所有丑陋细节ContentType并创建“无模型”权限实例。您还可以添加自定义管理器,过滤掉Permission与真实模型相关的所有实例。

于 2012-12-18T12:27:31.007 回答
54

按照Gonzalo 的建议,我使用代理模型自定义管理器来处理具有虚拟内容类型的“无模型”权限。

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)
于 2012-12-19T12:09:05.840 回答
10

修复了 Chewie 在 Django 1.8 中的回答,这是一些评论中要求的。

它在发行说明中说:

django.contrib.contenttypes.models.ContentType 的 name 字段已被迁移删除并被属性替换。这意味着不能再通过该字段查询或过滤 ContentType。

因此,在 GlobalPermissions 中没有使用的是 ContentType 中引用的“名称”。

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

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 类没有改​​变,但为了完整性而包含在内。

于 2015-06-24T15:50:36.737 回答
5

这是替代解决方案。首先问自己:为什么不创建一个真正存在于数据库中但从未被使用过的虚拟模型,除了持有权限?这不好,但我认为这是有效且直接的解决方案。

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 中的拼写错误更少,自动完成功能更多。

于 2017-12-21T12:18:47.060 回答
2

您可以proxy model将此用于虚拟内容类型。

from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType


class CustomPermission(Permission):

    class Meta:
        proxy = True

    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(CustomPermission, self).save(*args)

现在您可以使用模型的权限name创建codename权限CustomPermission

 CustomPermission.objects.create(name='Can do something', codename='can_do_something')

您可以像这样仅查询和显示模板中的自定义权限。

 CustomPermission.objects.filter(content_type__model='custom permission')
于 2020-05-27T14:47:29.630 回答
1

就我而言,对于任何更大的项目,我发现拥有一个不属于我的项目数据模型本身的通用应用程序很有用——我通常将其称为“projectlibs”。这是一个简单的 django 应用程序,我在其中放置了诸如用于导入的固定装置、可用于多个应用程序的模板标签等内容。其中一些是我发现自己经常重复使用的模板内容,因此拥有此类内容的额外好处一个应用程序是它可以重复用于其他项目。

所以在里面projectlibs/models.py,你可以:

本质上,您可以创建那个“元应用程序”,并将其分配content_type给一些虚拟类:

class UserRightsSupport(models.Model):
    class Meta:
        default_permissions = ()  # disable defaults add, delete, view, change perms
        permissions = (
            ("perm_name", "Verbose description"),
        )
于 2021-04-01T13:20:42.407 回答
0

除了这个,所有的答案都对我不利:

content_type = ContentType.objects.get_for_model(Permission)

Permission.objects.create(
    content_type=content_type,
    name='...', codename='...',
)

它处理无模型权限而不添加任何额外内容。

于 2021-10-01T16:30:47.963 回答