为啥我的 Django ModelForm 不会引发 unique_together 约束的验证错误?

Posted

技术标签:

【中文标题】为啥我的 Django ModelForm 不会引发 unique_together 约束的验证错误?【英文标题】:Why my Django ModelForm doesn't raise validation error for unique_together constraint?为什么我的 Django ModelForm 不会引发 unique_together 约束的验证错误? 【发布时间】:2017-04-26 10:29:45 【问题描述】:

我在Django documentation 中读到,ModelForm 的 clean() 方法验证了我模型上的 unique_together 约束,如果未验证,则会引发 ValidationError。但是我的 ModelForm 不这样做。至少它没有在表单中给出错误消息,而是我得到一个 Django 错误和回溯:

ValueError at / The Jelentkezes 由于数据未验证而无法创建。

这不是因为我排除了 unique_together 约束的字段之一。表单的其他验证工作,它们在表单上提供错误消息。请帮忙,谢谢!

我使用 Django 1.10,Python 是 3.5。

我在 models.py 中创建了以下模型:

from django.db import models

class FoglalkozasTipus(models.Model):
    foglalkozas_tipusa = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return '%s' % (self.foglalkozas_tipusa)

class Foglalkozas(models.Model):
    kezdet = models.DateTimeField()
    veg = models.DateTimeField()
    foglalkozas_tipusa = models.ForeignKey(FoglalkozasTipus, 
        on_delete=models.CASCADE)

    class Meta:
        unique_together = (("kezdet", "veg"),)
        ordering = ['kezdet', 'veg']

    def __str__(self):
        return '%s-%s %s' % (self.kezdet.strftime("%Y.%m.%d %H:%M"), 
            self.veg.strftime("%H:%M"), self.foglalkozas_tipusa)

class Jelentkezes(models.Model):
    foglalkozas = models.ForeignKey(Foglalkozas, on_delete=models.CASCADE)
    email = models.EmailField()
    vezeteknev = models.CharField(max_length=255)
    keresztnev = models.CharField(max_length=255)

    class Meta:
        unique_together = (("foglalkozas", "email"),)

    def __str__(self):
        return '%s: %s %s (%s)' % (self.foglalkozas, self.vezeteknev, 
            self.keresztnev, self.email)

对于 Jelentkezes 模型,我在 forms.py 中创建了一个 ModelForm:

from django.utils import timezone
from django.forms import ModelForm, Radioselect
from .models import Jelentkezes, Foglalkozas

class JelentkezesForm(ModelForm):

    class Meta:
        model = Jelentkezes
        fields = ['foglalkozas', 'email', 'vezeteknev', 'keresztnev']
        widgets = 
            'foglalkozas': RadioSelect,
        

    def __init__(self, *args, **kwargs):
        super(JelentkezesForm, self).__init__(*args, **kwargs)
        self.fields['foglalkozas'].queryset = 
            Foglalkozas.objects.filter(kezdet__gt=timezone.now())
        self.fields['foglalkozas'].empty_label = None

在views.py中我使用了这个ModelForm:

from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import JelentkezesForm

def index(request):
    if request.method == 'POST':
        form = JelentkezesForm(request.POST)
        form.save()
        return HttpResponseRedirect('koszonet/')

    else:
        form = JelentkezesForm()

    return render(request, 'aviva/index.html', 'form': form)

在 index.html 中,表单看起来像这样(我使用 W3.CSS 框架和 django-widget-tweaks 进行样式设置):

% load widget_tweaks %

<form action="" method="post">
    % csrf_token %
     form.non_field_errors 
     form.foglalkozas.errors 
    <label class="w3-label" for=" form.foglalkozas.id_for_label "><b> form.foglalkozas.label :</b></label>
    % for radio in form.foglalkozas %
    <div class="w3-radio">
         radio 
    </div>
    % endfor %
     form.email.errors 
    <label class="w3-label" for=" form.email.id_for_label "><b> form.email.label :</b></label>
     form.email|add_class:"w3-input w3-border w3-round" 
     form.vezeteknev.errors 
    <label class="w3-label" for=" form.vezeteknev.id_for_label "><b> form.vezeteknev.label :</b></label>
     form.vezeteknev|add_class:"w3-input w3-border w3-round" 
     form.keresztnev.errors 
    <label class="w3-label" for=" form.keresztnev.id_for_label "><b> form.keresztnev.label :</b></label>
     form.keresztnev|add_class:"w3-input w3-border w3-round" 
    <p><input type="submit" class="w3-btn w3-blue w3-left w3-border" value="Jelentkezés elküldése" />
    <button type="reset" class="w3-btn w3-yellow w3-right w3-border" value="Reset">Űrlapadatok törlése</button></p>
</form>

【问题讨论】:

也可能是其他一些验证错误。为什么不发布完整的堆栈跟踪 【参考方案1】:

您永远不会在表单上调用验证。应该是:

if request.method == 'POST':
    form = JelentkezesForm(request.POST)
    if form.is_valid():
        form.save()
        return HttpResponseRedirect('koszonet/')

【讨论】:

谢谢你,这行得通!不知何故我误解了一些东西,我认为这没有必要。

以上是关于为啥我的 Django ModelForm 不会引发 unique_together 约束的验证错误?的主要内容,如果未能解决你的问题,请参考以下文章

Django Modelform - 它没有验证,为啥?

跟进:缺少 django Modelform 中所需的 Charfield 被保存为空字符串并且不会引发错误

Django ModelForm 浮点字段不调用 Clean_Field 方法

Django:ModelForm 自定义错误消息和占位符

如何根据在模型级别定义的枚举字段在 Django Modelform 中呈现单选按钮选择?

我的 Django ModelForm 是未绑定的吗?