16

我为我的应用扩展了 Django 管理站点,以允许非员工/超级用户访问。这工作得很好。

我为现有模型创建了一个代理模型并将其注册到我的管理站点,但是它不会出现在非员工用户中。从我阅读的文档中,我的理解是代理模型获得了自己的权限。我检查了这些并没有出现在可用权限列表中。

这是我的代码以防万一:

正常模型

class Engagement(models.Model):
    eng_type = models.CharField(max_length=5)
    environment = models.CharField(max_length=8)    
    is_scoped = models.BooleanField()    

    class Meta:
        ordering = ['eng_type', 'environment']
        app_label = 'myapp'

代理模型

class NewRequests(Engagement):
    class Meta:
        proxy = True
        app_label = 'myapp'
        verbose_name = 'New Request'
        verbose_name_plural = 'New Requests'

模型管理员

class NewRequestsAdmin(ModelAdmin):
pass

def queryset(self, request):
    return self.model.objects.filter(is_scoped=0)

自定义管理员注册

myapps_admin_site.register(NewRequests, NewRequestsAdmin)

我一直在用 South 管理我的数据库。根据这篇文章,您必须按照它指向用户的说明对其进行一些改动。这是一次失败。我的数据库中没有很多信息,所以我取消了 South 的注释并运行了一个常规的 syncdb 来排除 South。不幸的是,这仍然不起作用,我很茫然。任何帮助表示赞赏。

编辑

这是在 Django 1.4 上

4

7 回答 7

8

事实证明我没有做错任何事。我正在寻找以下权限

myapp | New Request | Can add new request

权限属于父模型。

myapp | engagement | Can add new request

于 2013-02-23T13:27:59.820 回答
8

这在 Django 2.2 中已修复,引用发行说明

现在使用代理模型的内容类型而不是具体模型的内容类型来创建代理模型的权限。运行迁移时,迁移将更新现有权限。

文档

代理模型的工作方式与具体模型完全相同。权限是使用代理模型自己的内容类型创建的。代理模型不继承其子类化的具体模型的权限。

于 2019-03-24T16:33:03.627 回答
6

有一个解决方法,你可以在这里看到它:https ://gist.github.com/magopian/7543724

它可以根据您的 django 版本而有所不同,但原理是相同的。

用 Django 1.10.1 测试

# -*- coding: utf-8 -*-

"""Add permissions for proxy model.
This is needed because of the bug https://code.djangoproject.com/ticket/11154
in Django (as of 1.6, it's not fixed).
When a permission is created for a proxy model, it actually creates if for it's
base model app_label (eg: for "article" instead of "about", for the About proxy
model).
What we need, however, is that the permission be created for the proxy model
itself, in order to have the proper entries displayed in the admin.
"""

from __future__ import unicode_literals, absolute_import, division

import sys

from django.contrib.auth.management import _get_all_permissions
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand
from django.apps import apps
from django.utils.encoding import smart_text

class Command(BaseCommand):
    help = "Fix permissions for proxy models."

    def handle(self, *args, **options):
        for model in apps.get_models():
            opts = model._meta
            ctype, created = ContentType.objects.get_or_create(
                app_label=opts.app_label,
                model=opts.object_name.lower(),
                defaults={'name': smart_text(opts.verbose_name_raw)})

            for codename, name in _get_all_permissions(opts):
                p, created = Permission.objects.get_or_create(
                    codename=codename,
                    content_type=ctype,
                    defaults={'name': name})
                if created:
                    sys.stdout.write('Adding permission {}\n'.format(p))

如何使用

  • 创建目录/myproject/myapp/management/commands
  • 创建文件/myproject/myapp/management/__init__.py
  • 创建文件/myproject/myapp/management/commands/__init__.py
  • 将上面的代码保存到/myproject/myapp/management/commands/fix_permissions.py
  • /manage.py fix_permissions
于 2016-12-15T19:45:25.057 回答
3

这是 Django 中的一个已知错误:https ://code.djangoproject.com/ticket/11154 (查看一些补丁的评论)

于 2013-12-24T22:05:01.163 回答
1

对于 Django 1.11,此问题与auth_permission表中的错误content_type_id有关。默认情况下,它添加基本模型的内容类型而不是代理模型内容类型。

于 2018-04-11T11:44:03.887 回答
1

截至 2021 年和 Django 3+,代理模型缺少权限的解决方案很简单,只需生成迁移makemigrations

app@e31a3ffef22c:~/app$ python manage.py makemigrations my_app
Migrations for 'main':
  main/migrations/0193_myproxymodel.py
    - Create proxy model MyProxyModel

我来到这里并不确定,这个问题的正确原因/解决方案是什么。

于 2021-12-15T13:10:24.237 回答
0

我意识到这个问题不久前就关闭了,但我正在分享对我有用的东西,以防它可能对其他人有所帮助。

事实证明,即使我创建的代理模型的权限列在父应用程序下(如@chirinosky所提到的那样),即使我授予我的非超级用户所有权限,它仍然被拒绝访问我的代理模型通过管理员。

我必须做的是解决已知的 Django 错误(https://code.djangoproject.com/ticket/11154)并连接到post_syncdb信号以正确创建代理模型的权限。下面的代码是根据该线程上的一些评论从https://djangosnippets.org/snippets/2677/修改而来的。

我把它放在保存我的代理模型的 myapp/models.py 中。从理论上讲,这可以存在于您的任何INSTALLED_APPS之后,django.contrib.contenttypes因为它需要在update_contenttypes为信号注册处理程序之后加载,post_syncdb以便我们可以断开它。

def create_proxy_permissions(app, created_models, verbosity, **kwargs):
    """
    Creates permissions for proxy models which are not created automatically
    by 'django.contrib.auth.management.create_permissions'.
    See https://code.djangoproject.com/ticket/11154
    Source: https://djangosnippets.org/snippets/2677/

    Since we can't rely on 'get_for_model' we must fallback to
    'get_by_natural_key'. However, this method doesn't automatically create
    missing 'ContentType' so we must ensure all the models' 'ContentType's are
    created before running this method. We do so by un-registering the
    'update_contenttypes' 'post_syncdb' signal and calling it in here just
    before doing everything.
    """
    update_contenttypes(app, created_models, verbosity, **kwargs)
    app_models = models.get_models(app)
    # The permissions we're looking for as (content_type, (codename, name))
    searched_perms = list()
    # The codenames and ctypes that should exist.
    ctypes = set()
    for model in app_models:
        opts = model._meta
        if opts.proxy:
            # Can't use 'get_for_model' here since it doesn't return
            # the correct 'ContentType' for proxy models.
            # See https://code.djangoproject.com/ticket/17648
            app_label, model = opts.app_label, opts.object_name.lower()
            ctype = ContentType.objects.get_by_natural_key(app_label, model)
            ctypes.add(ctype)
            for perm in _get_all_permissions(opts, ctype):
                searched_perms.append((ctype, perm))

    # Find all the Permissions that have a content_type for a model we're
    # looking for. We don't need to check for codenames since we already have
    # a list of the ones we're going to create.
    all_perms = set(Permission.objects.filter(
        content_type__in=ctypes,
    ).values_list(
        "content_type", "codename"
    ))

    objs = [
        Permission(codename=codename, name=name, content_type=ctype)
        for ctype, (codename, name) in searched_perms
        if (ctype.pk, codename) not in all_perms
    ]
    Permission.objects.bulk_create(objs)
    if verbosity >= 2:
        for obj in objs:
            sys.stdout.write("Adding permission '%s'" % obj)


models.signals.post_syncdb.connect(create_proxy_permissions)
# See 'create_proxy_permissions' docstring to understand why we un-register
# this signal handler.
models.signals.post_syncdb.disconnect(update_contenttypes)
于 2014-10-15T21:40:02.173 回答