Python Django Admin Clean() 方法未覆盖值

10 投票
5 回答
18666 浏览
提问于 2025-04-17 13:27

我可能漏掉了什么,但根据Django的文档,我应该可以在clean()方法中覆盖从管理员表单发送的值。

def clean(self):
    from django.core.exceptions import ValidationError
    # Don't allow draft entries to have a pub_date.
    if self.status == 'draft' and self.pub_date is not None:
        raise ValidationError('Draft entries may not have a publication date.')
    # Set the pub_date for published items if it hasn't been set already.
    if self.status == 'published' and self.pub_date is None:
        self.pub_date = datetime.date.today()

我把代码简化了,现在只是想在管理员界面试一个基本的例子。

Models.py

class Test(models.Model):
name = models.CharField(max_length=255,)

def clean(self):
    self.name = 'Robin Hood'
    return self

所以当我尝试添加一个新的测试记录时,如果我把名字字段留空,它应该能从clean方法中获取值并保存。

但实际上,表单没有通过验证,字段还是空的。

我是不是漏掉了什么明显的东西?

5 个回答

4

解决这个问题只需要两个简单的步骤 :)

1) 在调用 clean() 之前会先调用 full_clean(),所以在 models.py 文件中把 pub_date 设置成:

pub_date = models.DateField(null=True)

2) 只需从 clean 函数返回 cleaned_data 字典,以便修改 pub_date 这个模型字段的值。试试这个:

def clean(self):
    from django.core.exceptions import ValidationError
    # Don't allow draft entries to have a pub_date.
    if self.cleaned_data['status'] == 'draft' and self.cleaned_data['pub_date'] is not None:
        raise ValidationError('Draft entries may not have a publication date.')
    # Set the pub_date for published items if it hasn't been set already.
    if self.cleaned_data['status'] == 'published' and self.cleaned_data['pub_date'] is None:
       self.cleaned_data['pub_date'] = datetime.date.today()
    return self.cleaned_data
  • 为了其他开发者的方便,建议在 forms.py 文件的顶部导入 ValidationError,而不是在每个 clean 函数里时不时地导入它 :)
5

在Django的管理后台创建对象时,模型的 clean() 方法不会被调用。其实,clean() 这个方法只有在调用 full_clean() 后才会执行,但在Django的管理后台保存对象时,并不会自动调用这个方法。

正如你在 官方文档中关于 Model.clean() 的说明 所看到的,full_clean() 是用来捕捉额外的验证错误的。

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError, e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

为了让这个方法能够被调用,你需要重写 save() 方法。

8

你甚至连运行模型的清理方法都不会执行。Django会先运行表单的验证代码,因为你的字段没有设置为 blank=True,所以表单会首先强制执行这个限制。

你应该做的是重写这个表单,把 name 字段的 required 设置为 False,然后写一个 form 的清理方法,这个方法会设置值并返回 self.cleaned_data

class TestForm(forms.ModelForm):
    name = forms.CharField(required=False)

    class Meta:
        model = Test

    def clean(self):
        self.cleaned_data['name'] = 'Robin Hood'
        return self.cleaned_data

然后在你的管理类中引用这个表单:

class TestAdmin(admin.ModelAdmin):
    form = TestForm

撰写回答