我想使用基于权限的系统来限制我的 Django 应用程序中的某些操作。这些操作不需要与特定模型相关(例如访问应用程序中的部分、搜索...),因此我不能直接使用 stock permissions framework,因为 Permission
模型需要引用已安装的内容类型。
我可以编写自己的权限模型,但是我必须重写 Django 权限中包含的所有好东西,例如:
为用户和组分配权限的可能性。
permission_required 装饰器。
User.has_perm 和相关的用户方法。
perms 模板变量。
...
我检查了一些应用程序,例如 django-authority 和 django-guardian,但它们似乎通过允许每个对象的权限来提供与模型系统更加耦合的权限。
有没有办法在没有为项目定义任何模型(除了 User
和 Group
)的情况下重用这个框架?
对于那些仍在寻找的人:
您可以创建一个没有数据库表的辅助模型。该模型可以为您的项目带来您需要的任何权限。无需处理 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 makemigrations
和 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_rights %}
<p>You can do any customer stuff</p>
{% endif %}
Django 的 Permission
模型 requires a ContentType
instance。
我认为解决它的一种方法是创建一个与任何模型都不相关的虚拟 ContentType
(app_label
和 model
字段可以设置为任何字符串值)。
如果您希望它干净漂亮,您可以创建一个 Permission
proxy model 来处理虚拟 ContentType
的所有丑陋细节并创建“无模型”权限实例。您还可以添加自定义管理器,过滤掉与真实模型相关的所有 Permission
实例。
在 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)
FieldError: Cannot resolve keyword 'name' into field. Choices are: app_label, id, logentry, model, permission
。
修复了 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 类没有改变,但为了完整性而包含在内。
这是替代解决方案。首先问自己:为什么不创建一个真正存在于数据库中但从未被使用过的虚拟模型,除了持有权限?这不好,但我认为这是有效且直接的解决方案。
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
?
您可以将 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
模型中的 name
和 codename
权限来创建权限。
CustomPermission.objects.create(name='Can do something', codename='can_do_something')
您可以像这样仅查询和显示模板中的自定义权限。
CustomPermission.objects.filter(content_type__model='custom permission')
就我而言,对于任何更大的项目,我发现拥有一个不属于我的项目数据模型本身的通用应用程序很有用——我通常将其称为“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"),
)
除了这个,所有的答案都对我不利:
content_type = ContentType.objects.get_for_model(Permission)
Permission.objects.create(
content_type=content_type,
name='...', codename='...',
)
它在不添加新模型的情况下处理无模型权限,而是通过添加新值。
Permission
内容类型(模型)。
model-less
因为 Permission
模型已经存在,所以它是关于添加新值,而不是模型 - 这就是它满足 without defining a content type or model
的原因。
default_permissions = ()
。这将阻止 Django 自动为此模型创建默认的添加/更改/删除/查看权限,如果您使用这种方法,这些权限很可能是不必要的。dumpdata
still includes unmanaged tables, by design,因此没有附加参数的./manage.py dumpdata
将在丢失的表上失败。