![Show Menu](styles/mobile-menu.png)
![Page Background](./../common/page-substrates/page0206.png)
A Django Quirk: Model Save Doesn’t Run Validation
And now we discover one of Django’s little quirks.
This test should already pass
. If you
take a look at the
docs for the Django model fields ,you’ll see that
TextField
actually
defaults to
blank=False
, which means that it
should
disallow empty values.
So why is the test not failing?Well, for
slightly counterintuitive historical reasons, Djan‐
go models don’t run full validation on save. As we’ll see later, any constraints that are
actually implemented in the database will raise errors on save, but SQLite doesn’t sup‐
port enforcing emptiness constraints on text columns, and so our save method is letting
this invalid value through silently.
There’s a way of checking whether the constraint will happen at the database level or
not: if it was at the database level, we would need a migration to apply the constraint.
But, Django knows that SQLite doesn’t support this type of constraint, so if we try and
run
makemigrations
, it will report there’s nothing to do:
$
python3 manage.py makemigrations
No changes detected
Django does have a method to manually run full validation however, called
full_clean
. Let’s hack it in to see it work:
lists/tests/test_models.py.
with
self
.
assertRaises
(
ValidationError
):
item
.
save
()
item
.
full_clean
()
That gets the test to pass:
OK
That taught us a little about Django validation, and the test is there to warn us if we ever
forget our requirement and set
blank=True
on the
text
field (try it!).
Surfacing Model Validation Errors in the View
Let’s try and enforce our model validation in the views layer and bring it up through
into our templates, so the user can see them. Here’s how we can optionally display an
error in our HTML—we check whether the template has been passed an error variable,
and if so, we display it next to the form:
lists/templates/base.html (ch10l013).
<form
method=
"POST"
action=
"{% block form_action %}{% endblock %}"
>
<input
name=
"item_text"
id=
"id_new_item"
class=
"form-control input-lg"
placeholder=
"Enter a to-do item"
/>
{% csrf_token %}
{% if error %}
<div
class=
"form-group has-error"
>
178
|
Chapter 10: Input Validation and Test Organisation