Skip to content

Instantly share code, notes, and snippets.

@pithyless
Created September 13, 2011 22:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pithyless/1215399 to your computer and use it in GitHub Desktop.
Save pithyless/1215399 to your computer and use it in GitHub Desktop.
Django Field Notes

Model + Form validations

Some of Django's choices on how to handle data integrity are wrong, or at the very least, unexpected to the uninitiated developers. I'm not here to pass judgement; like many people, I'd just like to get back to doing real work. Some notes on how to stay safe in Django:

What should I know?

  • Django now has both Form validations and Model validations
  • Django used to only have Form validations
  • Form’s is_valid() performs model validation automatically
  • full_clean() is never actually called by Django (see ticket #13100)

What should I do?

  • Stay DRY
  • Read http://schneck.cc/node/41
  • Put model validation only in clean() to avoid confusion
  • Never overwrite full_clean()
  • Use model validation whenever possible.
  • Before you save an object, apply full_clean() on it.

I'm paranoid; can Django help?

Always run model validation before saving a model instance

# http://djangosnippets.org/snippets/2319/
from django.db.models.signals import pre_save

def validate_model(sender, **kwargs):
    if 'raw' in kwargs and not kwargs['raw']:
        kwargs['instance'].full_clean()

pre_save.connect(validate_model, dispatch_uid='validate_models')

Examples

Add field-specific errors in form.clean()

# http://djangosnippets.org/snippets/337/

from django.forms.utils import ErrorList

def clean(self):
    cleaned = self.cleaned_data
    errors = False

    #
    # Validate stuff here!
    #

    if there_are_errors_for_field1:
        self._errors['field1'] = self._errors.get('field1', ErrorList())
        self._errors['field1'].append(_("Field 1 is invalid"))
        errors = True
    if there_are_errors_for_field2:
        self._errors['field2'] = self._errors.get('field2', ErrorList())
        self._errors['field2'].append(_("Field 2 is invalid"))
        errors = True

    non_field_errors = []
    if there_are_other_errors:
        non_field_errors.append(_("Non field error"))
        errors = True

    if errors:
        raise form.ValidationError(non_field_errors)
    return cleaned

Validate unique_together with one field excluded from form

# http://stackoverflow.com/questions/6892162/django-1-3-createview-modelform-unique-together-validation-with-one-field-exclud

class SubscriberForm(ModelForm):
    class Meta:
        model = Subscriber
        exclude = ('user')

class SubscriberCreateView(AuthCreateView):
    model = Subscriber
    form_class = SubscriberForm
    template_name = "forms/app.html"
    success_url = "/app/subscribers/"

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        try:
            self.object.full_clean()
        except ValidationError:
            from django.forms.util import ErrorList
            form._errors["email"] = ErrorList([u"You already have an email with that name."])
            return super(SubscriberCreateView, self).form_invalid(form)

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

Access request.user from any ModelForm, including Admin

# http://stackoverflow.com/questions/1057252/django-how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-clea

class MyModelAdmin(admin.ModelAdmin):
    form = MyCustomForm
    def get_form(self, request, obj=None, **kwargs):
        ModelForm = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
        def form_wrapper(*args, **kwargs):
            return ModelForm(request=request, *args, **kwargs)
        return form_wrapper

class MyCustomForm(forms.ModelForm):
    def __init__(self, request=None, *args, **kwargs):
        self.request = request
        super(MyCustomForm, self).__init__(*args, **kwargs)

You can then access the request object from any method of ModelForm with self.request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment