在Django中查看权限

19

由于Django管理面板的用户权限只有添加、修改和删除三种,我希望在其中增加查看权限。我知道我需要自定义权限,在'auth|permission|can view permission'中添加查看权限以查看所有条目。

步骤:

[X] 1. 将“查看”添加到默认权限列表

#./contrib/auth/management/init.py
def _get_all_permissions(opts):

    "Returns (codename, name) for all permissions in the given opts."
    perms = []
    for action in ('add', 'change', 'delete', 'view'):

        perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))

    return perms + list(opts.permissions)

[X] 2. 测试所有模型是否已添加“view”权限

run manage.py syncdb

我确认在auth_permissions表中,现在已为所有表格添加了视图权限。

[X] 3. 在默认模型类中添加“get_view_permission”。

已将get_view_permission添加到模型类中。您可以在./db/models/options.py文件中找到它。这将在下一步中由管理员类使用。

def get_view_permission(self):

    return 'view_%s' % self.object_name.lower()

[X] 4. 给默认的管理类添加“has_view_permission”

为保持一致性,我将在系统中添加“has_view_permission”。似乎应该放在contrib/admin/options.py 中的某处。确保如果用户拥有更改权限,则自动包含查看权限。

# /contrib/admin/options.py
# Added has_view_permissions
def has_view_permission(self, request, obj=None):

    """
    Returns True if the given request has permission to change or view
    the given Django model instance.


    If obj is None, this should return True if the given request has
    permission to change *any* object of the given type.
    """
    opts = self.opts
    return self.has_change_permission(request, obj) or \

        request.user.has_perm(opts.app_label + '.' + opts.get_view_permission())


# modified get_model_perms to include 'view' too.
# No idea where this may be used, but trying to stay consistent
def get_model_perms(self, request):

    """
    Returns a dict of all perms for this model. This dict has the keys
    add, change, and delete and view mapping to the True/False
    for each of those actions.
    """
    return {

        'add': self.has_add_permission(request),
        'change': self.has_change_permission(request),
        'delete': self.has_delete_permission(request),
        'view': self.has_view_permission(request),

    }


# modified response_add function to return the user to the mode list
# if they added a unit and have view rights
...

    else:

        self.message_user(request, msg)

        # Figure out where to redirect. If the user has change permission,
        # redirect to the change-list page for this object. Otherwise,
        # redirect to the admin index.
        #if self.has_change_permission(request, None):
        if self.has_change_permission(request, None) or self.has_view_permission(request, None):

            post_url = '../'

        else:

            post_url = '../../../'

        return HttpResponseRedirect(post_url)

    # modified the change_view function so it becomes the details
    # for users with view permission

        #if not self.has_change_permission(request, obj):
        if not (self.has_change_permission(request, obj) or (self.has_view_permission(request, obj) and not request.POST)):

            raise PermissionDenied

        # modified the changelist_view function so it shows the list of items
        # if you have view permissions

def changelist_view(self, request, extra_context=None):

            "The 'change list' admin view for this model."
            from django.contrib.admin.views.main import ChangeList, ERROR_FLAG
            opts = self.model._meta
            app_label = opts.app_label
            #if not self.has_change_permission(request, None):
            if not (self.has_change_permission(request, None) or self.has_view_permission(request, None)):

                raise PermissionDenied

[X] 5. 如果用户具有查看权限,则更新默认模板以列出模型

我修改了contrib/admin/templates/admin/index.html中的默认模板。这也可以通过将文件复制到本地模板目录来处理。我在两个目录中都进行了更改,以便在以后的升级中,如果我的更改被覆盖,我仍然拥有一个副本。

{% for model in app.models %}

    <tr>
    {% if model.perms.change %}

        <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>

    {% else %}

        {% if model.perms.view %}

            <th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>

        {% else %}

            <th scope="row">{{ model.name }}</th>

        {% endif %}

    {% endif %}

[X] 6. 确认用户可以“查看”但不能“更改”模型

发现 contrib/admin/templatetags/admin_modify.py 看起来控制了保存/保存并继续按钮是否出现。将“save”字段从默认的始终为 True 更改为检查上下文和权限。如果用户具有更改或添加权限,他们应该能够保存。

'show_save': (change and context['has_change_permission']) or (context['add'] and context['has_add_permission'])

[X] 如果用户正在查看某个项目,则删除“保存并添加另一个”按钮。

再次修改contrib/admin/templatetags/admin_modify.py。我不知道“save_as”是什么意思,所以可能破坏了某些东西,但似乎运行正常。

#'show_save_and_add_another': context['has_add_permission'] and
# not is_popup and (not save_as or context['add']) ,
'show_save_and_add_another': not is_popup and
    (( change and context['has_change_permission']) or (context['add'] and context['has_add_permission']))
    and
    (not save_as or context['add']),

[X] 8. 修改“查看”权限以使表单只读

如果用户具有“查看”和“更改”权限,则不进行任何操作。更改会覆盖查看。

如果用户没有“更改”权限而只有“查看”权限,则更改默认表单并将DISABLED或READONLY属性添加到表单元素中。并非所有浏览器都支持此功能,但出于我的目的,我可以要求用户使用正确的浏览器。[禁用/只读示例][1]

发现并非所有浏览器都支持“readonly”,因此它会将某些控件设置为readonly,其他控件设置为disabled。这允许用户在需要时从文本控件中复制数据。

#/django/contrib/admin/templates/admin/change_form.html


{# JavaScript for prepopulated fields #}
{% prepopulated_fields_js %}


</div>
</form></div>
{% if has_view_permission and not has_change_permission %}

    <script type="text/javascript">
    jQuery('input:text').attr('readonly', 'readonly');
    jQuery('textarea').attr('readonly', 'readonly');
    jQuery('input:checkbox').attr('disabled', true);
    jQuery('select').attr('disabled', true);
    jQuery('.add-another').hide();
    </script>

{% endif %}

回答来源: 如何修改Django以创建“查看”权限?

问题: 我按照上面的答案进行了操作,现在可以在127.0.0.1:8000/en-us/admin/页面上看到只读内容,但是在用户中的用户不可见127.0.0.1:8000/en-us/admin/user。需要帮助!


127.0.0.1:8000/en-us/admin/user,这不应该是 127.0.0.1:8000/en-us/admin/auth/user 吗? - elssar
如下所述:Django 2.1引入了默认的视图权限ModelAdmin.has_view_permission - djvg
2个回答

22

将“查看”权限添加到默认权限列表中

您的解决方案有效,但如果可能的话,请避免编辑源代码。有几种方法可以在框架内完成此操作:

1. 程序中创建权限期间添加该权限:post_syncdb()

在your_app/management/目录下的文件中

from django.db.models.signals import post_syncdb
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Permission

def add_view_permissions(sender, **kwargs):
    """
    This syncdb hooks takes care of adding a view permission too all our 
    content types.
    """
    # for each of our content types
    for content_type in ContentType.objects.all():
        # build our permission slug
        codename = "view_%s" % content_type.model

        # if it doesn't exist..
        if not Permission.objects.filter(content_type=content_type, codename=codename):
            # add it
            Permission.objects.create(content_type=content_type,
                                      codename=codename,
                                      name="Can view %s" % content_type.name)
            print "Added view permission for %s" % content_type.name

# check for all our view permissions after a syncdb
post_syncdb.connect(add_view_permissions)

每当您执行“syncdb”命令时,可以检查所有内容类型以查看它们是否具有“view”权限,如果没有,则创建一个权限。
来源:The Nyaruka Blog
2. 将权限添加到Meta permissions option
在每个模型下,您需要将以下内容添加到其Meta选项中:
class Pizza(models.Model):
    cheesiness = models.IntegerField()

    class Meta:
        permissions = (
            ('view_pizza', 'Can view pizza'),
        )

这将与1相同,只是您需要手动将其添加到每个类中。 3. Django 1.7中新增,在Meta default_permissions选项中添加权限: 在Django 1.7中,他们添加了default_permissions Meta选项。在每个模型下,您需要将“view”添加到default_permissions选项中:
class Pizza(models.Model):
    cheesiness = models.IntegerField()

    class Meta:
        default_permissions = ('add', 'change', 'delete', 'view')

1
我将这个答案发布在用户的相同(但更清晰)的问题 Add Permission to Django Admin 下。 - pcoronel
1
如果您还没有使用1.7版本,我建议现在只做#1或#2 - 它们与#3完成的事情相同,但适用于Django的早期版本。不过,如果升级到1.7对您的系统/程序来说不太困难,那就去做吧。最重要的是,不要修改源代码,而是使用上述方法之一,然后只需使用user.has_perm('my_app.view_mymodel')来测试用户是否具有权限。 - pcoronel

20

Django 2.1在默认权限中添加了一个视图权限。以下解决方案可能适用于Django的早期版本。 https://docs.djangoproject.com/en/2.1/releases/2.1/#model-view-permission


这是在Django 1.6.2中测试过的有效解决方案。

[X] 1. 将“view”添加到默认权限列表:OK
[X] 2. 测试所有模型是否已添加“view”权限:OK

[X] 3. 添加“get_view_permission”到默认模型类不再有用:

def get_add_permission(self):
    """
    This method has been deprecated in favor of
    `django.contrib.auth.get_permission_codename`. refs #20642
    """
    warnings.warn(
        "`Options.get_add_permission` has been deprecated in favor "
        "of `django.contrib.auth.get_permission_codename`.",
        PendingDeprecationWarning, stacklevel=2)
    return 'add_%s' % self.model_name

对于所有这些方法而言,都是如此 get_foo_permission

[X] 4. 在默认的管理员类中添加"has_view_permission" 应该改为:

def has_view_permission(self, request, obj=None):
    """
    Returns True if the given request has permission to change or view
    the given Django model instance.


    If obj is None, this should return True if the given request has
    permission to change *any* object of the given type.
    """
    opts = self.opts
    codename = get_permission_codename('view', opts)
    return self.has_change_permission(request, obj) or \
        request.user.has_perm("%s.%s" % (opts.app_label, codename))

如果模型是内联的,请检查它的右边,因此需要注意右侧视图

def get_inline_instances(self, request, obj=None):

    ...

    if not (inline.has_add_permission(request) or
            inline.has_change_permission(request, obj) or
            inline.has_delete_permission(request, obj) or
            inline.has_view_permission(request, obj)):  # add the view right
        continue

    ...

get_model_perms函数修改为包括'view',同样的想法再对这个函数进行修改:

def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):

    ...

    context.update({

        ...

        'has_view_permission': self.has_view_permission(request, obj), # add the view right

        ...

    })

    ....

允许“正确视图”呈现页面(一个对象的页面),并禁用“正确视图”以保存对页面所做的修改,避免 [X] 8. 修改“查看”权限以使表单只读

@csrf_protect_m
@transaction.atomic
def change_view(self, request, object_id, form_url='', extra_context=None):
    "The 'change' admin view for this model."
    model = self.model
    opts = model._meta

    obj = self.get_object(request, unquote(object_id))

    # addthe view right
    if not (self.has_view_permission(request, obj) or
            self.has_change_permission(request, obj)):
        raise PermissionDenied

    ...

    inline_instances = self.get_inline_instances(request, obj)
    # do not save the change if I'm not allowed to:
    if request.method == 'POST' and self.has_change_permission(request, obj):
        form = ModelForm(request.POST, request.FILES, instance=obj)

    ...

允许“正确的视图”渲染页面(所有对象的列表)

@csrf_protect_m
def changelist_view(self, request, extra_context=None):
    """
    The 'change list' admin view for this model.
    """
    from django.contrib.admin.views.main import ERROR_FLAG
    opts = self.model._meta
    app_label = opts.app_label
    # allow user with the view right to see the page
    if not (self.has_view_permission(request, None) or
            self.has_change_permission(request, None)):
        raise PermissionDenied

    ....

[X] 5. 如果用户具有查看权限,请更新默认模板以列出模型:好的,但为了避免修改HTML模板,请编辑此文件:contrib/admin/site.py

class AdminSite(object):

    @never_cache
    def index(self, request, extra_context=None):

        ...

        # add the view right
        if perms.get('view', False) or perms.get('change', False):
        try:
            model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
        except NoReverseMatch:
            pass

        ...

    def app_index(self, request, app_label, extra_context=None):

        ...

        # add the view right
        if perms.get('view', False) or perms.get('change', False):
            try:
                model_dict['admin_url'] = reverse('admin:%s_%s_changelist' % info, current_app=self.name)
            except NoReverseMatch:
                pass

        ...

[X] 6. 确认用户可以“查看”但不可以“更改”模型,以及 [X] 7. 如果用户正在查看特定项目,则删除“保存并添加另一个”按钮:应该没问题,但我已经这么做了:

'show_save_as_new': context['has_add_permission'] and not is_popup and change and save_as,
'show_save': context['has_change_permission'],

[X] 8. 修改“查看”权限以使表单只读:好的,但我有另一种解决方案,请参见上面。


谢谢,但我不理解第三步 - AND。第四步是针对文件contrib/admin/options.py吗?@MoiTux - Tameen Malik
这会生成错误: 在/admin/auth/user/处发生AttributeError异常类型: AttributeError 异常值: 'UserAdmin'对象没有属性'has_view_permission' 异常位置: \django\contrib\admin\options.py中的changelist_view,第1295行 - Tameen Malik
@TameenMalik,contrib/admin/options.py 的想法是在每次有 has_change_permission 时添加 has_view_permission。我没有收到任何错误消息,但我只是在基本管理员中进行了测试。 - MoiTux
@TameenMalik 我可以毫无问题地进入用户的管理界面。 - MoiTux
@TameenMalik 对不起,我在复制/粘贴代码时出错了。现在应该没问题了。 - MoiTux
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接