django admin 进行不同用户验证

背景

django version :2.0.2

需求:

管理后台部分 user 能查看其他用户数据,但是不能修改其他用户的数据。

解决方案

直接看代码,models.py 代码:

from django.db import models
from django.contrib.auth.models import User

# Create your models here.


class Blog(models.Model):
    id = models.AutoField(unique=True, primary_key=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=32, verbose_name="名称")
    create_time = models.DateTimeField(verbose_name='添加时间', auto_now_add=True, blank=True)

admin.py代码:

from django.contrib import admin
from django import forms

from .models import Blog

class BlogConfigAdminForm(forms.ModelForm):
    ''' 成员界面的 Form 渲染实现。 '''
    def clean(self):
        user = self.cleaned_data.get('user', None)
        if self.loged_user and user == self.loged_user.username:
            raise forms.ValidationError('只能修改本帐号数据!')

    class Meta:
        model = Blog
        fields = '__all__'

@admin.register(Blog)
class BlogConfigAdmin(admin.ModelAdmin):
    form = BlogConfigAdminForm
    list_display = ('id', 'name', 'create_time')
    list_per_page = 50
    search_fields = ['name']
    exclude = ['user']
    list_display_links = ('id', 'name',)

    def get_form(self, request, obj=None, **kwargs):
        form = super(BlogConfigAdmin, self).get_form(request, obj=obj, **kwargs)
        form.loged_user = request.user # 关键位置
        return form

各种尝试

在找到上面代码之前做了如下尝试:

  1. 直接使用 clean_user 函数,失败原因见后面。

  2. 通过 save_model 方法中判断,失败原因见后面。

  3. 通过 save_form 方法中判断,失败原因见后面。

  4. 在 modelAdmin 中通过__init__传递值到 form

    按:Pass initial value to a modelform in django, 测试没有效果。

  5. 把 userid 通过带 hide 属性的 html 标签传递给浏览器

    参考代码:

    class PersonAdmin(admin.ModelAdmin):
       def get_form(self, request, obj=None, **kwargs):
           form = super(PersonAdmin, self).get_form(request, obj=obj, **kwargs)
           form.base_fields['log_user'].initial = get_current_user()
           return form

    此方法会报 KeyError:

    [05/Feb/2018 14:54:53] "GET /favicon.ico HTTP/1.1" 404 2579
    Internal Server Error: /admin/app/blog/add/
    Traceback (most recent call last):
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
       response = get_response(request)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\core\handlers\base.py", line 249, in _legacy_get_response
       response = self._get_response(request)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
       response = self.process_exception_by_middleware(e, request)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
       response = wrapped_callback(request, *callback_args, **callback_kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 551, in wrapper
       return self.admin_site.admin_view(view)(*args, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\utils\decorators.py", line 149, in _wrapped_view
       response = view_func(request, *args, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\views\decorators\cache.py", line 57, in _wrapped_view_func
       response = view_func(request, *args, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\sites.py", line 224, in inner
       return view(request, *args, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 1508, in add_view
       return self.changeform_view(request, None, form_url, extra_context)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\utils\decorators.py", line 67, in _wrapper
       return bound_func(*args, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\utils\decorators.py", line 149, in _wrapped_view
       response = view_func(request, *args, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\utils\decorators.py", line 63, in bound_func
       return func.__get__(self, type(self))(*args2, **kwargs2)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 1408, in changeform_view
       return self._changeform_view(request, object_id, form_url, extra_context)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 1437, in _changeform_view
       ModelForm = self.get_form(request, obj)
     File "E:\repos\DjangoWebProject1\app\admin.py", line 26, in get_form
       form = super(BlogConfigAdmin, self).get_form(request, obj=obj, **kwargs)
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 615, in get_form
       fields = flatten_fieldsets(self.get_fieldsets(request, obj))
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 304, in get_fieldsets
       return [(None, {'fields': self.get_fields(request, obj)})]
     File "E:\repos\DjangoWebProject1\env\lib\site-packages\django\contrib\admin\options.py", line 604, in get_fields
       form = self.get_form(request, obj, fields=None)
     File "E:\repos\DjangoWebProject1\app\admin.py", line 28, in get_form
       form.base_fields['log_user'].initial = 1
    KeyError: 'log_user'

深入分析

前面的所有尝试都是失败的,官方核心处理流程源码(/python3.6/site-packages/django/contrib/admin/options.py)如下:

        ModelForm = self.get_form(request, obj)
        if request.method == 'POST':
            form = ModelForm(request.POST, request.FILES, instance=obj)
            if form.is_valid():
                form_validated = True
                new_object = self.save_form(request, form, change=not add)
            else:
                form_validated = False
                new_object = form.instance
            formsets, inline_instances = self._create_formsets(request, new_object, change=not add)
            if all_valid(formsets) and form_validated:
                self.save_model(request, new_object, form, not add)
                self.save_related(request, form, formsets, not add)
                change_message = self.construct_change_message(request, form, formsets, add)
                if add:
                    self.log_addition(request, new_object, change_message)
                    return self.response_add(request, new_object)
                else:
                    self.log_change(request, new_object, change_message)
                    return self.response_change(request, new_object)
            else:
                form_validated = False
        else:
            if add:
                initial = self.get_changeform_initial_data(request)
                form = ModelForm(initial=initial)
                formsets, inline_instances = self._create_formsets(request, form.instance, change=False)
            else:
                form = ModelForm(instance=obj)
                formsets, inline_instances = self._create_formsets(request, obj, change=True)

基本流程如下:

Created with Raphaël 2.1.2 get_form() form.is_valid() Yes or No? all_valid() check validated save_model() save_related() End form_validated = False yes no yes

从流程来看,表单验证必须在 is_valid() 函数全部完成

            if form.is_valid():
                form_validated = True
                new_object = self.save_form(request, form, change=not add)
            else:
                form_validated = False
                new_object = form.instance

所以尝试2、3都失败了。而 is_valid() 函数调用如下:

   # python3.6/site-packages/django/forms/forms.py
    @property
    def errors(self):
        """Return an ErrorDict for the data provided for the form."""
        if self._errors is None:
            self.full_clean()
        return self._errors

    def is_valid(self):
        """Return True if the form has no errors, or False otherwise."""
        return self.is_bound and not self.errors

相同文件下的 full_clean()函数:

    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and self.cleaned_data.
        """
        self._errors = ErrorDict()
        if not self.is_bound:  # Stop further processing.
            return
        self.cleaned_data = {}
        # If the form is permitted to be empty, and none of the form data has
        # changed from the initial data, short circuit any validation.
        if self.empty_permitted and not self.has_changed():
            return

        self._clean_fields()
        self._clean_form()
        self._post_clean()

    def _clean_fields(self):
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)

    def _clean_form(self):
        try:
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
        else:
            if cleaned_data is not None:
                self.cleaned_data = cleaned_data

    def _post_clean(self):
        """
        An internal hook for performing additional cleaning after form cleaning
        is complete. Used for model validation in model forms.
        """
        pass

因为 user 在 exclude 中,self.fields 就不包含 user 字段,所以尝试1就失败了。

你可能感兴趣的:(Python/Django)