Correct Way to Validate Django Model Objects?

django, django-models

Solution

I had a similar misunderstanding of the ORM when I first started with Django.

- No, don't put `self.full_clean()` inside of `save`. Either

A) use a `ModelForm` (which will cause all the same validation to occur - note: `ModelForm.is_valid()` won't call `Model.full_clean` explicitly, but will perform the exact same checks as `Model.full_clean`). Example:

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

def add_person(request):
    if request.method == 'POST':
        form = PersonForm(request.POST, request.FILES)
        if form.is_valid():  # Performs your validation, including ``validate_gender``
            person = form.save()
            return redirect('some-other-view')
    else:
        form = PersonForm()
        # ... return response with ``form`` in the context for rendering in a template

Also note, forms aren't for use only in views that render them in templates - they're great for any sort of use, including an API, etc. After running `form.is_valid()` and getting errors, you'll have `form.errors` which is a dictionary containing all the errors in the form, including a key called `'__all__'` which will contain non-field errors.

B) Simply use `model_instance.full_clean()` in your view (or other logical application layer), instead of using a form, but forms are a nice abstraction for this.

- I don't really have a solution to, but I've never run into such a problem, even in large projects (the current project I work with my company on has 146 tables) and I don't suspect it'll be a concern in your case either.

Problem

I'm still trying to understand the correct way to validate a Django model object using a custom validator at the model level. I know that validation is usually done within a form or model form. However, I want to ensure the integrity of my data at the model level if I'm interacting with it via the ORM in the Python shell. Here's my current approach: ``` from django.db import models from django.core import validators from django.core exceptions import ValidationError def validate_gender(value): """ Custom validator """ if not value in ('m', 'f', 'M', 'F'): raise ValidationError(u'%s is not a valid value for gender.' % value) class Person(models.Model): name = models.CharField(max_length=128) age = models.IntegerField() gender = models.CharField(maxlength=1, validators=[validate_gender]) def save(self, *args, **kwargs): """ Override Person's save """ self.full_clean(exclude=None) super(Person, self).save(*args, **kwargs) ``` Here are my questions: Should I create a custom validation function, designate it as a validator, and then override the Person's save() function as I've done above? (By the way, I know I could validate my gender choices using the 'choices' field option but I created 'validate_gender' for the purpose of illustration). If I really want to ensure the integrity of my data, should I not only write Django unit tests for testing at the model layer but also equivalent database-level unit tests using Python/Psycopg? I've noticed that Django unit tests, which raise ValidationErrors, only test the model's understanding of the database schema using a copy of the database. Even if I were to use South for migrations, any database-level constraints are limited to what Django can understand and translate into a Postgres constraint. If I need a custom constraint that Django can't replicate, I could potentially enter data into my database that violates that constraint if I'm interacting with the database directly via the psql terminal. Thanks!

Original source