在Django中查看权限

19 投票
2 回答
47440 浏览
提问于 2025-04-18 02:56

Django的管理后台有三种权限:添加、修改和删除!我想在管理面板中添加查看权限。我知道我需要自定义权限,把查看权限加到'auth|permission|can view permission'中,这样才能查看所有条目!

步骤:

[X] 1. 在默认权限列表中添加'view'

#./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"字段的默认值从总是为真改为根据上下文和权限来检查。如果用户有修改或添加权限,他们应该能够保存。

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

[X] 7. 如果用户正在查看某个项目,则移除"保存并添加另一个"按钮

再次修改了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. 修改"view"权限使表单只读

如果用户有"view"权限和"change"权限,那么什么都不做。修改权限会覆盖查看权限。

如果用户只有"view"权限而没有"change"权限,那么就修改默认表单,为表单元素添加DISABLED或READONLY属性。并不是所有浏览器都支持这个,但为了我的目的,我可以要求用户使用合适的浏览器。[Disabled / Readonly示例][1]

发现并不是所有浏览器都尊重"readonly",所以有些控件设置为只读,其他的设置为禁用。这允许用户在需要时从文本控件中复制数据。

#/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以创建"view"权限?

问题:按照上述步骤操作后,我已经完成并可以看到这个127.0.0.1:8000/en-us/admin/页面是只读的**但用户在用户列表中不可见127.0.0.1:8000/en-us/admin/user**。需要帮助!**

2 个回答

22

给默认权限列表添加“查看”权限

你的解决方案可以用,但最好还是尽量避免直接修改源代码。其实在框架内有几种方法可以做到这一点:

1. 在 post_syncdb()时添加权限:

在你的应用的管理文件夹下创建一个文件:

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”命令时,系统会检查所有内容类型是否有“查看”权限,如果没有,就会创建一个。

2. 将权限添加到 Meta权限选项中:

在每个模型下,你可以在它的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选项。在每个模型下,你可以在default_permissions选项中添加“查看”权限:

class Pizza(models.Model):
    cheesiness = models.IntegerField()

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

Django 2.1 在默认权限中增加了一个查看权限。下面的解决方案可能适用于早期版本的 Django。

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

[X] 1. 将 'view' 添加到默认权限列表中: 成功
[X] 2. 测试 'view' 权限是否已添加到所有模型中: 成功

[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. 修改 "view" 权限使表单只读

@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. 修改 "view" 权限使表单只读: 成功,但我有其他解决方案,见上文。

撰写回答