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
在找到上面代码之前做了如下尝试:
直接使用 clean_user 函数,失败原因见后面。
通过 save_model 方法中判断,失败原因见后面。
通过 save_form 方法中判断,失败原因见后面。
在 modelAdmin 中通过__init__
传递值到 form
按:Pass initial value to a modelform in django, 测试没有效果。
把 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)
基本流程如下:
从流程来看,表单验证必须在 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就失败了。