ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

为用户和组分配权限的可能性。

permission_required 装饰器。

User.has_perm 和相关的用户方法。

perms 模板变量。

...

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

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


J
Jarett Millard

对于那些仍在寻找的人:

您可以创建一个没有数据库表的辅助模型。该模型可以为您的项目带来您需要的任何权限。无需处理 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 自动为此模型创建默认的添加/更改/删除/查看权限,如果您使用这种方法,这些权限很可能是不必要的。
这种方法的一个小问题是 dumpdata still includes unmanaged tables, by design,因此没有附加参数的 ./manage.py dumpdata 将在丢失的表上失败。
G
Gonzalo

Django 的 Permission 模型 requires a ContentType instance

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

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


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

Gonzalo's advice 之后,我使用了 proxy modelcustom 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”之间缺少 _)。
u
user2390182

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


这仍然不能为 django 1.8 修复它,因为在 syncdb django 断言“名称”字段不能为空时。
它对我有用,但我没有使用迁移,因为我的项目中仍然存在非 django 遗留的东西。您是否从以前的 django 升级,因为 1.8 中不应该有名称字段
g
guettli

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

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 是的,你可能是对的。我想我上次只是尝试了“抽象”。
a
arjun

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

d
damon

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

S
Sławomir Lenart

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

content_type = ContentType.objects.get_for_model(Permission)

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

它在不添加新模型的情况下处理无模型权限,而是通过添加新值。


这不是无模型的,它只是使用 Permission 内容类型(模型)。
@soxwithMonica model-less 因为 Permission 模型已经存在,所以它是关于添加新值,而不是模型 - 这就是它满足 without defining a content type or model 的原因。
我是这么想的,但无论如何最好明确说明。

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅