python - 在 ModelForm 中手动设置模型字段
问题描述
我有一个带有外键和唯一约束的模型,如下所示:
class Menu(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
name = models.CharField(max_length=128)
date_menu = models.DateField()
class Meta:
constraints = [
models.UniqueConstraint(fields=['tournament', 'name', 'date_menu'], name="unique_name_menu")
]
我想创建一个表单来添加菜单实例。然而,锦标赛的价值是由页面的 URL 设置的。我不希望用户能够设置它。
为此,我使用了一个 modelForm,不包括锦标赛字段:
class MenuForm(forms.ModelForm):
date_menu = forms.DateField(initial=datetime.datetime.now())
class Meta:
model = Menu
exclude = ['tournament']
这是我的观点:
def add_menu(request, tournament_slug):
tournament = get_object_or_404(Tournament, slug=tournament_slug)
form = MenuForm(request.POST or None)
if form.is_valid():
menu_id = form.save(commit=False)
menu_id.tournament = Tournament.objects.get(pk=1)
menu_id.save() # I get the integrity error only here
return HttpResponseRedirect(reverse('admin'))
return render(request, "view.html", {'form': form, 'formset': formset, "tournament": tournament})
我的问题是,当我.is_valid()
在此表单上调用该函数时,由于未设置锦标赛字段,因此无法检查唯一性条件。结果,在视图中调用保存函数时出现完整性错误。
问题是:在检查它是否有效之前,如何链接表单创建的 Menu 实例以添加锦标赛字段?如果这不是正确的做法,我该如何检查模型实例的唯一性并在需要时将相应的错误返回给模板?
我尝试将锦标赛字段作为隐藏字段包含在视图中,它可以工作,但我不知道这是否是最好的方法......
解决方案
您应该使用未保存的实例简单地实例化表单,Menu
因此您的视图应该如下所示:
def add_menu(request, tournament_slug):
tournament = get_object_or_404(Tournament, slug=tournament_slug)
if request.method == 'POST':
form = MenuForm(request.POST, instance=Menu(tournament=tournament))
if form.is_valid():
menu_id = form.save()
return HttpResponseRedirect(reverse('admin'))
else:
form = MenuForm(instance=Menu(tournament=tournament))
return render(request, "view.html", {'form': form, "tournament": tournament})
表单还调用_get_validation_exclusions()
并排除表单中不存在的字段进行验证。您可以尝试覆盖validate_unique
以克服此问题:
class MenuForm(forms.ModelForm):
date_menu = forms.DateField(initial=datetime.datetime.now())
class Meta:
model = Menu
exclude = ['tournament']
def validate_unique(self):
exclude = self._get_validation_exclusions()
if 'tournament' in exclude:
exclude.remove('tournament') # Make sure `tournament` gets validated
try:
self.instance.validate_unique(exclude=exclude)
except ValidationError as e:
self._update_errors(e)
注意:我更改了您的视图结构以避免使用
MenuForm(request.POST or None)
which is an antipattern。(即使 POST 数据中没有发送任何内容,表单也可能有效,而您编写此类表单的方式将被视为无效)。
编辑validate_unique
:正如评论中所讨论的,隐藏和禁用字段的选项可能比覆盖表单方法要好得多:
class MenuForm(forms.ModelForm):
tournament = forms.ModelChoiceField(
queryset=Tournament.objects.all(),
widget=forms.HiddenInput(),
disabled=True
)
date_menu = forms.DateField(initial=datetime.datetime.now())
class Meta:
model = Menu
fields = ['tournament', 'name', 'date_menu']
推荐阅读
- python - 在 Pandas 中按增量时间移动行
- android - 如何从格鲁吉亚国家/地区上传到谷歌商店(不在列表中)
- xml - 在 pl/sql 上创建大 xml
- android-fileprovider - androidx FILE_PROVIDER_PATHS
- python-3.x - 将参数放在列表中并在定义中返回它们并带有索引
- ios - 如何在 Arkit 中提供持久性?
- sql - 如何计算具有特定列的所有表中值的出现次数?
- c++ - 为什么 GCC 在这里优化分配?
- python - Jupyter,ipywidgets,如何使用下拉菜单刷新绘图
- python - 多级引号包裹 csv 文件 pandas