views:

56

answers:

2

Hi all,

I have a Django form that will add an entry to a database with a unique constraint on one of the columns. If the submission happens to attempt creation of a duplicate, I get a django.db.utls.IntegrityError stating I violated the unique constraint (thankfully).

When processing Django forms, there are a number of ways to validate:

  1. Override the form class' clean method
  2. Override the form class' clean_<field> method

If the clear_<field> method raises a django.forms.ValidationError, the error message will be appended to a list in the _errors dictionary under the field's name. When using the clean method, errors should be manually inserted into the _errors dict.

Now, the clean and clean_<field> methods don't sound like a nice place to put code that has side effects, and I fear that checking at this moment whether the value is unique does not guarantee I won't get IntegrityErrors. I would prefer to actually try and create the row in the database and signal a form error when a constraint is violated.

My main problem is that forms expose a public property errors which is read-only. Therefore, I should, according to the docs, add a method in my form that does all the work, try to insert in the database and register an error when an IntegrityError is raised. Because the action that processes the form after validation requires lots of other data, I don't want to put the code inside the form class itself.

Is there any other (documented or not) way to go about adding an entry in the errors dict from outside the class, or should I simply access the _errors "private" variable and be happy with that? I know this sounds like an OOP breach, but creating an extra method just to access this "private" variable does not seem to me like it shields anybody from anything.

Edit: Seems like someone else already raised a similar question, but it seems the answers pointed to manual insertion into _errors...

A: 

I think adding the method to the form is quite reasonable. The ModelForm class, which has a save() method which does the logic of saving the created instance to the database. If your form can inherit from ModelForm (or just add a save() method to your form), you'll still be well within "djangonian" standards.

http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method

OmerGertel
I know it's reasonable, but I can't do that in this particular case. The form is used in different places and in one case, the form's data is passed to some other entity via a polymorphic invocation. In the current design, the form *should not* know what to do with the data!
André Caron
A: 

For now, a simple solution is the following:

try:
    # Try to save model instance, which might throw `IntegrityError`.
    # ...
except django.db.utils.IntegrityError:
    # The form passed validation so it has no errors.
    # Use the `setdefault` function just in case.
    except django.db.utils.IntegrityError:
    errors = django.forms.util.ErrorList()
    errors = form._errors.setdefault(
        django.forms.forms.NON_FIELD_ERRORS, errors)
    errors.append('Sorry, this username is already in use.')
André Caron