Skip to content

Commit 5b26a01

Browse files
alasdairnicoltimgraham
authored andcommitted
Fixed #23865 -- documented how to assign errors to a field in Model.clean()
Also added a unit test wit the simpler syntax which we have documented, where the dictionary values are strings.
1 parent a3aeba0 commit 5b26a01

File tree

3 files changed

+26
-3
lines changed

3 files changed

+26
-3
lines changed

docs/ref/models/instances.txt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,10 @@ access to more than a single field::
200200
Note, however, that like :meth:`Model.full_clean()`, a model's ``clean()``
201201
method is not invoked when you call your model's :meth:`~Model.save()` method.
202202

203-
Any :exc:`~django.core.exceptions.ValidationError` exceptions raised by
204-
``Model.clean()`` will be stored in a special key error dictionary key,
205-
:data:`~django.core.exceptions.NON_FIELD_ERRORS`, that is used for errors
203+
In the above example, the :exc:`~django.core.exceptions.ValidationError`
204+
exception raised by ``Model.clean()`` was instantiated with a string, so it
205+
will be stored in a special error dictionary key,
206+
:data:`~django.core.exceptions.NON_FIELD_ERRORS`. This key is used for errors
206207
that are tied to the entire model instead of to a specific field::
207208

208209
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
@@ -211,6 +212,19 @@ that are tied to the entire model instead of to a specific field::
211212
except ValidationError as e:
212213
non_field_errors = e.message_dict[NON_FIELD_ERRORS]
213214

215+
To assign exceptions to a specific field, instantiate the
216+
:exc:`~django.core.exceptions.ValidationError` with a dictionary, where the
217+
keys are the field names. We could update the previous example to assign the
218+
error to the ``pub_date`` field::
219+
220+
class Article(models.Model):
221+
...
222+
def clean(self):
223+
# Don't allow draft entries to have a pub_date.
224+
if self.status == 'draft' and self.pub_date is not None:
225+
raise ValidationError({'pub_date': 'Draft entries may not have a publication date.'})
226+
...
227+
214228
Finally, ``full_clean()`` will check any unique constraints on your model.
215229

216230
.. method:: Model.validate_unique(exclude=None)

tests/model_forms/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ class CustomErrorMessage(models.Model):
381381
def clean(self):
382382
if self.name1 == 'FORBIDDEN_VALUE':
383383
raise ValidationError({'name1': [ValidationError('Model.clean() error messages.')]})
384+
elif self.name1 == 'FORBIDDEN_VALUE2':
385+
raise ValidationError({'name1': 'Model.clean() error messages (simpler syntax).'})
384386
elif self.name1 == 'GLOBAL_ERROR':
385387
raise ValidationError("Global error message.")
386388

tests/model_forms/tests.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,6 +2269,13 @@ def test_model_clean_error_messages(self):
22692269
str(form.errors['name1']),
22702270
'<ul class="errorlist"><li>Model.clean() error messages.</li></ul>'
22712271
)
2272+
data = {'name1': 'FORBIDDEN_VALUE2', 'name2': 'ABC'}
2273+
form = CustomErrorMessageForm(data)
2274+
self.assertFalse(form.is_valid())
2275+
self.assertHTMLEqual(
2276+
str(form.errors['name1']),
2277+
'<ul class="errorlist"><li>Model.clean() error messages (simpler syntax).</li></ul>'
2278+
)
22722279
data = {'name1': 'GLOBAL_ERROR', 'name2': 'ABC'}
22732280
form = CustomErrorMessageForm(data)
22742281
self.assertFalse(form.is_valid())

0 commit comments

Comments
 (0)