Background Image
Table of Contents Table of Contents
Previous Page  206 / 478 Next Page
Information
Show Menu
Previous Page 206 / 478 Next Page
Page Background

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