42

I have a view, which creates models from CSV file. I've added clean method to the model class definition, but it isn't called when model is created.

Here is example of models.py:

class Run(models.Model):
    name = models.CharField(max_length=120)
    tested_build = models.ForeignKey('apps.Build')
    timestamp_start = models.DateTimeField()
    timestamp_end = models.DateTimeField()

class CommonMeasurement(models.Model):
    timestamp = models.DateTimeField()
    value = models.FloatField()
    run = models.ForeignKey(Run)

    def clean(self):
        super(CommonMeasurement, self).clean()
        print 'here we go'
        if self.timestamp < self.run.timestamp_start or self.timestamp > self.run.timestamp_end:
            raise django_excetions.ValidationError('Measurement is outside the run')


class ClientMeasurement(CommonMeasurement):
    metric = models.ForeignKey(ClientMetric)
    account = models.CharField(max_length=120, blank=True)

Here is my Form View code examples:

class BaseMeasurementsUpload(generic_views.FormView):
    template_name = 'upload.html'
    models_to_upload = None   

    def get_success_url(self):
        return self.request.get_full_path()

    def form_valid(self, form):
        uploader = getattr(importers, form.cleaned_data['uploader'])
        try:
            the_uploader = uploader(form.cleaned_data, self.models_to_upload)
            upload_results = the_uploader.get_result_info()
        except django_exceptions.ValidationError as e:
            custom_errors = e
        return render_to_response(self.template_name,
                                  {'upload_results': upload_results,
                                   'custom_errors': custom_errors},
                                  context_instance=RequestContext(self.request))


class ClientMeasurementsUploadView(BaseMeasurementsUpload):
    form_class = forms.ClientMeasurementsUploadForm
    models_to_upload = models.ClientMeasurement

    def get_form(self, form_class):
        uploaders = (('MeasurementsSimpleCsv', importers.MeasurementsSimpleCsv.__doc__),
                     ('ClientMeasurementsBulkCsv', importers.ClientMeasurementsBulkCsv.__doc__,))
        if self.request.POST:
            # get bound form
            return self.form_class(uploaders,
                                   self.request.POST,
                                   self.request.FILES)
        else:
            return forms.ClientMeasurementsUploadForm(uploaders)

importers perform actual validation and call create method for each model.

3
  • Can you post your view code? Perhaps you are not calling is_valid on your ModelForm? Commented Sep 14, 2013 at 15:35
  • 1
    clean is for ModelForm or Form objects. Not for models. this would never get called automatically. Commented Sep 14, 2013 at 15:58
  • 1
    @karthikr -- while you're correct that this clean() will not be called automatically, you're incorrect that it's "not for models" -- see the documentation on this point Commented Dec 21, 2018 at 3:08

4 Answers 4

55

To call the model clean method we will override save method. Check the link: https://docs.djangoproject.com/en/2.0/ref/models/instances/#django.db.models.Model.clean

class CommonMeasurement(models.Model):
    timestamp = models.DateTimeField()
    value = models.FloatField()
    run = models.ForeignKey(Run)

    def clean(self):
        if self.timestamp < self.run.timestamp_start or self.timestamp > self.run.timestamp_end:
            raise django_excetions.ValidationError('Measurement is outside the run')

    def save(self, *args, **kwargs):
        self.full_clean()
        return super(CommonMeasurement, self).save(*args, **kwargs)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. In djangorestframework, the clean method not calling which using this method my problem solved.
@SirSaleh did you manage to get an error sent back to the REST api or did it just showed a debugger error?
15

I've found a solution to override method:

class CommonMeasurement(models.Model):
    timestamp = models.DateTimeField()
    value = models.FloatField()
    run = models.ForeignKey(Run)

    objects = models.Manager()
    analyzes = managers.MeasureStatManager()

    def save(self, **kwargs):
        self.clean()
        return super(CommonMeasurement, self).save(**kwargs)

    def clean(self):
        super(CommonMeasurement, self).clean()
        print 'here we go'
        if self.timestamp < self.run.timestamp_start or self.timestamp > self.run.timestamp_end:
            raise django_excetions.ValidationError('Measurement is outside the run')

But I'm not sure that it can be a good decision.

6 Comments

I believe calling clean from save will prevent your ValidationError from reaching your view/template, so the user would not see the error message but a debug message w/traceback if debug=True or a server error if debug is False.
Actually, validation error is successfully passed to upper view layer.
This works however Django doesn't like ValidationError during save() and return a (horrible) 500 Server Error instead of 400 Bad Request. I'm still looking for solution on this.
I'm using Django==2.2.5 and djangorestframework==3.10.3 and this annoying 500 error is still happening! Any solutions?
if there are people with problem of 500 debug message instead of with normal admin error message - in django==3.2 its better call ValidationError from django.core.exceptions.ValidationError
|
8

Apparently model.clean() is never called to ensure backward compatibility. For more information on this: https://code.djangoproject.com/ticket/13100

2 Comments

Is it bad practice to use my solution? Which compatibility I break with auto-calling clean() method from save?
1. My solution was to move all the code I would have put in clean() in save(). In that respect I think your solution is cleaner. 2. I have no idea what it is supposed to break.
3

Apparently in newer versions of Django the ModelForm.full_clean does call it's instance's full_clean method:

3 Comments

Yes, but save doesn't call full_clean either.
github.com/django/django/blob/… for a link that won't become confusingly invalid in future.
Thanks @AdamBarnes, I've updated the link in the answer ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.