For the most part, the forms class works pretty well. Unless you're trying to specify exactly where an error should show up in the clean() function...
If you're using fieldname_clean() methods, then this isn't a problem.
However, during your clean() method you find that it makes more sense to target an error towards a certain field, you can't do it by simply raising a forms.ValidationError.
Luckily, it's still relatively simple to do.
def clean(self):
data = self.cleaned_data
# This error shows up under form.non_field_errors
try:
shirt = Shirt.objects.get(title__iexact = data['title'])
raise forms.ValidationError("A shirt with a similar name already exists.")
except Shirt.DoesNotExist:
pass
# Show an error under the "description" field
self._errors["description"] = self.error_class(forms.ValidationError("error message of your choice).messages)
return data
Now you're able to specify error messages to any field in the form.