A Django package to enforce choices as database constraints.
Project description
Django-enforced-choices
Django does not enforce choices at the model level: yes a ModelForm
can validate these, but unfortunately people oftend define form fields themselves or even worse don't work with any Form
or Serializer
at all. So this means that if we define a model:
class Movie(models.Model):
genre = models.CharField(max_length=1, choices=[('d', 'drama'), ('h', 'horror')])
The database will not enforce that one can only pick 'd'
or 'h'
as genre. A simple solution might be to encapsulate the item in a separate model, and then the FOREIGN KEY
constraint will take care of this:
class Genre(models.Model):
id = models.CharField(max_length=1, primary_key=True)
name = models.CharField(max_length=128)
class Movie(models.Model):
genre = models.ForeignKey(Genre, on_delete=models.PROTECT)
we can then fill the table with a data migration for example. This also makes some sense: a quote I once heared is that there should only be three constants in programming: zero, one and infinity, meaning that typically one should not restrict the number of genres.
But regardless, people often use the former model. This is good if we work with Form
s, or Serializer
s, but models don't (eagerly) validate data, and even if they did, the database does not know about the choices, and thus will happily accept 's'
as value (for example for science fiction).
What does this package provide?
This package provides a ChoicesConstraintModelMixin
mixin to mix into models. This will look for the model fields, and if there are choices it will add CheckConstraint
s for these fields. One can exclude certain fields that have choices by adding the corresponding names in a class attribute named exclude_choice_check_fields
, which is by default empty.
Another option is to import the correspond field from the django_enforced_choices.fields
module, or django_enforced_choices.fields.postgres
for PostgreSQL-specific fields. This will, by default, also check if the fields have choices, but we do not recommend to use these, since this means the field has for example as type ChoiceCharField
, and certain Django functionalities (and packages) sometimes check full type equality to determine a widget, not through an instanceof
. This thus means that certain functionalities might no longer work as intended.
Usage
One can import the ChoicesConstraintModelMixin
and mix it into a model, like:
from django_enforced_choices.models import ChoicesConstraintModelMixin
class Movie(ChoicesConstraintModelMixin, models.Model):
genre = models.CharField(max_length=1, choices=[('d', 'drama'), ('h', 'horror')])
this will then add CheckConstraint
s to the model to enforce that genre
only can contain 'd'
and 'h'
at the database side.
How does the package work?
For the fields defined, it will check if the choices
are defined. If that is the case, it will create a CheckConstraint
with fieldname__in
with the keys in the choices. If the field is NULLable, it will also allow NULL
/None
to be used.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for django-enforced-choices-0.1.2.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | ebd51299423b46fde1fb0a1e5d3c81253b1219a6bee318630279d5f2ec950853 |
|
MD5 | cec23fbc51fdf2913239c38f7b17d840 |
|
BLAKE2b-256 | 80b5b448e80ad3e40045c23edb9b5a1bb377e2aeac8eda536c2aa6d5b0fc3873 |
Hashes for django_enforced_choices-0.1.2-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 98aa444e9ded0088d9a12088bf2188780882cee8c467c88625072d8b5a819652 |
|
MD5 | ce756dab044101eccd650b141ef9d92a |
|
BLAKE2b-256 | 4a6f10b3faf0db1a3d9ed24609e57cbce8b85a402344b2cf395cc229a03366af |