Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Django 3 Web Development Cookbook

You're reading from   Django 3 Web Development Cookbook Actionable solutions to common problems in Python web development

Arrow left icon
Product type Paperback
Published in Mar 2020
Publisher Packt
ISBN-13 9781838987428
Length 608 pages
Edition 4th Edition
Languages
Tools
Arrow right icon
Authors (2):
Arrow left icon
Jake Kronika Jake Kronika
Author Profile Icon Jake Kronika
Jake Kronika
Aidas Bendoraitis Aidas Bendoraitis
Author Profile Icon Aidas Bendoraitis
Aidas Bendoraitis
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

Preface 1. Getting Started with Django 3.0 2. Models and Database Structure FREE CHAPTER 3. Forms and Views 4. Templates and JavaScript 5. Custom Template Filters and Tags 6. Model Administration 7. Security and Performance 8. Hierarchical Structures 9. Importing and Exporting Data 10. Bells and Whistles 11. Testing 12. Deployment 13. Maintenance 14. Other Books You May Enjoy

Adding database constraints

For better database integrity, it's common to define database constraints, telling some fields to be bound to fields of other database tables, making some fields unique or not null. For advanced database constraints, such as making the fields unique with a condition or setting specific conditions for the values of some fields, Django has special classes: UniqueConstraint and CheckConstraint. In this recipe, you will see a practical example of how to use them.

Getting ready

Let's start with the ideas app and the Idea model that will have at least title and author fields.

How to do it...

Set the database constraints in the Meta class of the Idea model as follows:

# myproject/apps/ideas/models.py
from django.db import models
from django.utils.translation import gettext_lazy as _


class Idea(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_("Author"),
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name="authored_ideas",
)
title = models.CharField(
_("Title"),
max_length=200,
)

class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")
constraints = [
models.UniqueConstraint(
fields=["title"],
condition=~models.Q(author=None),
name="unique_titles_for_each_author",
),
models.CheckConstraint(
check=models.Q(
title__iregex=r"^\S.*\S$"
# starts with non-whitespace,
# ends with non-whitespace,
# anything in the middle
),
name="title_has_no_leading_and_trailing_whitespaces",
)
]

How it works...

We define two constraints in the database.

The first one, UniqueConstraint, tells the titles to be unique for each author. If the author is not set, the titles can be repeated. To check if the author is set we use the negated lookup: ~models.Q(author=None). Note that in Django, the ~ operator for lookups is equivalent to the exclude() method of a QuerySet, so these QuerySets are equivalent:

ideas_with_authors = Idea.objects.exclude(author=None)
ideas_with_authors2 = Idea.objects.filter(~models.Q(author=None))

The second constraint, CheckConstraint, checks if the title doesn't start and end with a whitespace. For that, we use a regular expression lookup.

There's more...

Database constraints don't affect form validation. They will just raise django.db.utils.IntegrityError if any data doesn't pass its conditions when saving entries to the database.

If you want to have data validated at the forms, you have to implement the validation in addition yourself, for example, in the clean() method of the model. That would look like this for the Idea model:

# myproject/apps/ideas/models.py
from django.db import models
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _


class Idea(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name=_("Author"),
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name="authored_ideas2",
)
title = models.CharField(
_("Title"),
max_length=200,
)

# other fields and attributes…

class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")
constraints = [
models.UniqueConstraint(
fields=["title"],
condition=~models.Q(author=None),
name="unique_titles_for_each_author2",
),
models.CheckConstraint(
check=models.Q(
title__iregex=r"^\S.*\S$"
# starts with non-whitespace,
# ends with non-whitespace,
# anything in the middle
),
name="title_has_no_leading_and_trailing_whitespaces2",
)
]

def clean(self):
import re
if self.author and Idea.objects.exclude(pk=self.pk).filter(
author=self.author,
title=self.title,
).exists():
raise ValidationError(
_("Each idea of the same user should have a unique title.")
)
if not re.match(r"^\S.*\S$", self.title):
raise ValidationError(
_("The title cannot start or end with a whitespace.")
)

# other properties and methods…

See also

  • Chapter 3, Forms and Views
  • The Using database query expressions recipe in Chapter 10, Bells and Whistles
You have been reading a chapter from
Django 3 Web Development Cookbook - Fourth Edition
Published in: Mar 2020
Publisher: Packt
ISBN-13: 9781838987428
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €18.99/month. Cancel anytime