Django inlineformset_factory 验证器不工作

问题描述

代码片段

Django 版本:3.1.7

models.py

# custom validator
is_numeric = RegexValidator(r"^[0-9]*$","Only numbers are allowed")

class Person(models.Model):
    # all the boring fields


class PersonAddress(models.Model):
    # all the boring fields
    person = models.ForeignKey(
        Person,null=True,on_delete=models.SET_NULL,related_name="person_address",)
    postcode = models.CharField(
        blank=True,help_text="Include leading zero if exists",max_length=10,validators=[is_numeric],)

PersonAddress 将是 Person 的内联表单集,因为一个人可以有多个地址

forms.py

class PersonForm(forms.ModelForm):
    class Meta:
        model = Person
        fields = "__all__"

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.helper = FormHelper()


class PersonAddressForm(forms.ModelForm):
    class Meta:
        model = PersonAddress
        fields = "__all__"

    def __init__(self,**kwargs)
        self.helper = FormHelper()
        self.helper.form_method = "post"


PersonAddressFormSet = forms.models.inlineformset_factory(
    Person,PersonAddress,form=PersonAddressForm,extra=1,can_delete=True,)

# min_num,validate_min don't help even though I read from other stackoverflow solution
# PersonAddressFormSet = forms.models.inlineformset_factory(
#     Person,min_num=1,validate_min=True,# )

views.py

# Person CreateView
class PersonCreateView(SuccessMessageMixin,LoginrequiredMixin,CreateView):
    model = Person
    form_class = PersonForm

    def get_context_data(self,**kwargs):
        context = super(PersonCreateView,self).get_context_data(**kwargs)
        if self.request.POST:
            context["addresses"] = PersonAddressFormSet(self.request.POST,self.request.FILES)
        else:
            context["addresses"] = PersonAddressFormSet()
        return context

    # I think the problem is here,not 100% sure
    def form_valid(self,form):
        context = self.get_context_data()
        addresses = context["addresses"]
        self.object = form.save()
        if addresses.is_valid():
            addresses.instance = self.object
            addresses.save()

        # https://stackoverflow.com/questions/47476941/django-inlineformset-factory-error-messages-not-working
        # my attempt,got this error
        # TypeError: join() argument must be str,bytes,or os.pathLike object,not 'nonetype'
        # else:
        #    return render(self.request,self.template_name,context)

        return super(PersonCreateView,self).form_valid(form)


# PersonAddress CreateView
class PersonAddressCreateView(SuccessMessageMixin,CreateView):
    model = PersonAddress
    form_class = PersonAddressForm

问题

如果我使用非数字字符串 PersonAddress 创建一个 postcode 对象,PersonAddressCreateView HTML 表单将返回验证错误

但是如果我在 PersonCreateView HTML 表单中做同样的事情,就不会有任何验证错误Person 对象将被创建,PersonAddress 不会被创建,因为 postcode 无效。我期望 PersonCreateView HTML 表单向我显示验证错误而不是保存表单。

我认为解决方案应该与 PersonCreateViewform_valid 有关,但我不确定。

提前致谢!

解决方法

确实,问题出在您的 form_valid 实现中。确切地说,当 PersonAddressFormSet 无效时,您什么都不做。要解决该问题,请尝试:

    def form_valid(self,form):
        context = self.get_context_data()
        addresses = context["addresses"]
        self.object = form.save()
        if addresses.is_valid():
            addresses.instance = self.object
            addresses.save()
        else:
            return self.form_invalid(form)
        return super(PersonCreateView,self).form_valid(form)