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

Posted

技术标签:

【中文标题】如何在不定义内容类型或模型的情况下使用 Django 权限?【英文标题】:How can I use Django permissions without defining a content type or model? 【发布时间】:2012-12-05 15:32:35 【问题描述】:

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

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

可以为用户和组分配权限。 permission_required decorator。 User.has_perm 和相关的用户方法。 perms template variable。 ...

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

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

【问题讨论】:

【参考方案1】:

对于那些仍在寻找的人:

您可以创建一个没有数据库表的辅助模型。该模型可以为您的项目带来您需要的任何权限。无需处理 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 %

【讨论】:

那是天才,拯救我的一天! 在我运行 manage.py migrate 后没有任何改变...我没有看到任何新权限:( 您是否将应用添加到您的项目中(INSTALLED_APPS)? 这个答案很完美。我还 []ed default_permissions,在模型的 save() 上引发 NotImplementedError,如果非托管模型真的只是为了这个权限,我可能会考虑让 has_*_permission() 返回 False。 我建议在 Meta 类中添加以下内容:default_permissions = ()。这将阻止 Django 自动为此模型创建默认的添加/更改/删除/查看权限,如果您使用这种方法,这些权限很可能是不必要的。【参考方案2】:

Django 的Permission 模型requires a ContentType instance。

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

如果你想要它干净漂亮,你可以创建一个Permission proxy model 来处理虚拟ContentType 的所有丑陋细节并创建“无模型”权限实例。您还可以添加自定义管理器,过滤掉与真实模型相关的所有Permission 实例。

【讨论】:

如果你不介意,我会用我的实现来完成你的答案。 很遗憾,我无法批准,因为我没有足够的声誉来审核您的编辑(它要求我 +2k)。其他用户拒绝您的编辑,因此我建议您将其添加为另一个答案(您有我的支持!)再次感谢。 这很奇怪。这确实是您答案的完成,因此对其进行编辑是有意义的。无论如何,我把它放在另一个答案中。【参考方案3】:

在Gonzalo's advice 之后,我使用了proxy model 和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)

【讨论】:

感谢您提供的代码,如果还可以展示一个有关如何使用此代码的示例,那就太好了。 该模型权限应该放在哪里? 创建一个 GlobalPermission: from app.models import GlobalPermission gp = GlobalPermission.objects.create(codename='can_do_it', name='Can do it') 运行后,您可以添加该权限像任何其他权限一样对用户/组。 @JulienGrenier Django 1.8 中的代码中断:FieldError: Cannot resolve keyword 'name' into field. Choices are: app_label, id, logentry, model, permission. 警告:较新版本的 Django(至少 1.10)需要覆盖方法“get_queryset”(注意“query”和“set”之间缺少 _)。【参考方案4】:

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

它在发行说明中说:

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 类未更改,但为了完整性而包含在内。

【讨论】:

这仍然没有为 django 1.8 修复它,因为在 syncdb django 断言“名称”字段不能为空时。 它对我有用,但我没有使用迁移,因为我的项目中仍然存在非 django 遗留的东西。你是从以前的 django 升级的吗,因为 1.8 中不应该有 name 字段【参考方案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 中的拼写错误更少,自动完成功能更多。

【讨论】:

由于某种原因,使用managed=False 是否不允许您使用Permissions.can_search_blue_flower @SamBobel 是的,你可能是对的。我想我上次只是尝试了“抽象”。【参考方案6】:

您可以将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)

现在您可以只使用来自CustomPermission 模型的权限的namecodename 创建权限。

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

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

 CustomPermission.objects.filter(content_type__model='custom permission')

【讨论】:

【参考方案7】:

就我而言,对于任何较大的项目,我发现拥有一个不属于我的项目数据模型的通用应用程序非常有用本身 - 我通常将其称为“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"),
        )

【讨论】:

【参考方案8】:

除此之外,所有答案都对我不利:

content_type = ContentType.objects.get_for_model(Permission)

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

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

【讨论】:

以上是关于如何在不定义内容类型或模型的情况下使用 Django 权限?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不欠拟合或过拟合的情况下实现回归模型

我们如何在不使用泛型类型进行自定义的情况下处理身份核心用户管理和用户存储的多个用户使用情况?

如何在不使用 cat -b 或 cat -n 选项的情况下打印文件内容以及行号[关闭]

如何在不捐赠意图或使用快捷方式的情况下利用 Siri 创建自定义对象?

MailCore2 - 在不下载图像或附件的情况下获取邮件内容?

如何在不刷新页面的情况下使用 django-crispy-forms 实现引导模式表单