Django: Model clean method called before form clea

2019-06-01 16:26发布

问题:

I don't understand, why is my model clean method called before full form validation.

I have required fields in my form. If I don't fill them, I don't get form errors, instead of, the model clean method is called (so I suppose it's because save is called).

It crashes in models clean() method:

if self.date_from > self.date_to

can't compare datetime.date to NoneType

Because I didn't filled date_to field. I think that form should have handle it and raise ValidationError and models save() shouldn't be even called.

Create view should inherit form_invalid method.

class TripCreationForm(forms.ModelForm):
    date_from = forms.DateField(required=True)
    date_to = forms.DateField(required=True)
    place_id = forms.CharField(widget=forms.HiddenInput(),required=True)


    class Meta:
        model = Trip
        fields = ['date_from','date_to','detail','participants','place_id']

    def __init__(self, *args,**kwargs):
        user = kwargs.pop('user')
        super(TripCreationForm, self).__init__(*args,**kwargs)
        fs_helpers.add_widget_attribute('class','datepicker',self.fields['date_from'])
        fs_helpers.add_widget_attribute('class','datepicker',self.fields['date_to'])
        self.instance.user = user

    def clean(self):

        cleaned_data = super(TripCreationForm,self).clean()
        city, created = City.objects.get_or_create(place_id=self.cleaned_data['place_id'])
        self.instance.city = city
        date_from = self.cleaned_data.get('date_from')
        date_to = self.cleaned_data.get('date_to')
        if date_from and date_to and date_from>=date_to:
            raise ValidationError(_('Date from can\'t be higher that date to'))

        return cleaned_data

This is my view:

class TripCreationView(SuccessMessageMixin,CreateView):
    form_class = TripCreationForm
    template_name = 'trips/add_new_trip.html'
    success_message = _('Congratulations! You\'ve added a new trip!')
    context_object_name = 'trip_creation_form'

    def post(self, request, *args, **kwargs):  # TODO Switch to get_success_url
        return super(TripCreationView, self).post(self, request, *args, **kwargs)

    def get_form_kwargs(self):
        kwargs = super(TripCreationView, self).get_form_kwargs()
        kwargs['user'] = self.request.user
        return kwargs

    def get_context_data(self, **kwargs):
        context=  super(TripCreationView,self).get_context_data(**kwargs)
        context['trip_creation_form'] = context['form']
        return context

    def get_success_url(self):
        return self.request.POST.get('success_url') or reverse('frontend:homepage'

)

And this is part of the model:

def save(self, *args, **kwargs):
    created = not (bool(self.pk))
    self.full_clean()
    with transaction.atomic():
        Trip.objects.stretch_trips(self)
        super(Trip, self).save(*args, **kwargs)

def clean(self):
    if self.date_from > self.date_to: # HERE IT CRASHES
        raise ValidationError(_("Date to can't be lower than date from"))

Where is the problem? Why there is no form clean error raised?

回答1:

When a model form is validated, it runs the clean method for the model. If you look at the full traceback, which you haven't included, then I think you'll see that the error occurs when the view calls form.is_valid(), not when the form is saved.

In your model's clean method, you should check that self.date_from and self.date_to are set, similar to your form's clean method.

class Trip(models.Model):
    def clean(self):
        if self.date_from and self.date_to and self.date_from > self.date_to:                 raise ValidationError(_("Date to can't be lower than date from"))

You could remove the check from the form's clean method, to prevent duplication.