保存 ModelForm 时验证多个外键
Validate multiple foreign keys while saving a ModelForm
解释:
我有一个有两个外键的模型:
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=CASCADE)
created_by = models.ForeignKey(User, blank=True, null=True, on_delete=CASCADE)
我有一个模型:
class BookForm(modelForm):
class Meta:
model = Book
exclude = ('author', 'created_by',)
在我的业务逻辑中,我想在同一个视图中保存一位新作者和一本书,并且 created_by
在某些情况下可能为空。因此,在保存 BookForm 的实例之前,我将首先创建一个作者。所以,假设我已经创建了一个作者,那么我将执行以下操作来保存一本书:
f = BookForm(post_data)
if form.is_valid():
instance = f.save(commit=False)
instance.author = author
instance.created_by = user
instance.save()
到目前为止我所做的工作正常,但外键字段不是验证的一部分。从模型 class 可以看出,author
字段是必填项,但 created_by
字段是可选的。所以,如果我想为它们添加一些验证,我必须通过自定义 is_valid()
方法来显式添加它们,或者在 instance.author = author
.
之前添加一些 if
条件。
问题:
更大的图景是我有一个带有更多外键的模型;其中一些是强制性的,而另一些则不是。因此,使用我在上面所做的相同方法,我必须明确地为每个 FK 添加自定义验证,我认为这是重复和不干净的,因为那些 FK 字段验证是在我的模型 class.
中定义的
那么,通过直接检查模型验证而不一一提及每个 FK,在保存时自动验证那些多个外键的最佳做法是什么?
由于外键字段未包含在您的表单中,因此不会对它们进行任何表单验证。您需要的是模型验证。
Django 提供了 model.full_clean()
方法,该方法执行所有模型验证步骤。
您将需要手动调用该方法,因为 save() 方法不会调用任何模型清理方法。
参见文档 here。
例如,
f = BookForm(post_data)
if form.is_valid():
instance = f.save(commit=False)
instance.author = author
instance.created_by = user
try:
instance.full_clean()
except ValidationError as e:
# Do something based on the errors contained in e.message_dict.
# Display them to a user, or handle them programmatically.
pass
instance.save()
如果您想向用户显示错误消息,只需将 e 传递给上下文并循环遍历模板中的消息,在您想要的任何位置显示它们。这似乎比向表单添加错误更简洁,因为它们是模型验证错误而不是表单错误。
在您看来:
except ValidationError as e:
context = {'e':e} # add other relevant data to context
return render(request, 'your_template', context)
在您的模板中:
{% for key, values in e.message_dict %}
{{key}}: {{value}}
{% endfor %}
如果要将错误添加到表单中,请使用 form.add_error
:
except ValidationError as e:
for key, value in e.items():
# this adds none-field errors to the form for each model error
form.add_error(None, f'{key}: {value}')
解释:
我有一个有两个外键的模型:
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=CASCADE)
created_by = models.ForeignKey(User, blank=True, null=True, on_delete=CASCADE)
我有一个模型:
class BookForm(modelForm):
class Meta:
model = Book
exclude = ('author', 'created_by',)
在我的业务逻辑中,我想在同一个视图中保存一位新作者和一本书,并且 created_by
在某些情况下可能为空。因此,在保存 BookForm 的实例之前,我将首先创建一个作者。所以,假设我已经创建了一个作者,那么我将执行以下操作来保存一本书:
f = BookForm(post_data)
if form.is_valid():
instance = f.save(commit=False)
instance.author = author
instance.created_by = user
instance.save()
到目前为止我所做的工作正常,但外键字段不是验证的一部分。从模型 class 可以看出,author
字段是必填项,但 created_by
字段是可选的。所以,如果我想为它们添加一些验证,我必须通过自定义 is_valid()
方法来显式添加它们,或者在 instance.author = author
.
if
条件。
问题:
更大的图景是我有一个带有更多外键的模型;其中一些是强制性的,而另一些则不是。因此,使用我在上面所做的相同方法,我必须明确地为每个 FK 添加自定义验证,我认为这是重复和不干净的,因为那些 FK 字段验证是在我的模型 class.
中定义的那么,通过直接检查模型验证而不一一提及每个 FK,在保存时自动验证那些多个外键的最佳做法是什么?
由于外键字段未包含在您的表单中,因此不会对它们进行任何表单验证。您需要的是模型验证。
Django 提供了 model.full_clean()
方法,该方法执行所有模型验证步骤。
您将需要手动调用该方法,因为 save() 方法不会调用任何模型清理方法。
参见文档 here。
例如,
f = BookForm(post_data)
if form.is_valid():
instance = f.save(commit=False)
instance.author = author
instance.created_by = user
try:
instance.full_clean()
except ValidationError as e:
# Do something based on the errors contained in e.message_dict.
# Display them to a user, or handle them programmatically.
pass
instance.save()
如果您想向用户显示错误消息,只需将 e 传递给上下文并循环遍历模板中的消息,在您想要的任何位置显示它们。这似乎比向表单添加错误更简洁,因为它们是模型验证错误而不是表单错误。
在您看来:
except ValidationError as e:
context = {'e':e} # add other relevant data to context
return render(request, 'your_template', context)
在您的模板中:
{% for key, values in e.message_dict %}
{{key}}: {{value}}
{% endfor %}
如果要将错误添加到表单中,请使用 form.add_error
:
except ValidationError as e:
for key, value in e.items():
# this adds none-field errors to the form for each model error
form.add_error(None, f'{key}: {value}')