Skip to main content

Django-enforced-choices

PyPi version Documentation Status PyPi license Code style: black

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 Forms, or Serializers, 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 two mixins:

  • ChoicesConstraintModelMixin which checks on the choices= parameter of the fields of that model, and based on that, creates a constraint to enforce the choices at the datbase side; and
  • RangeConstraintModelMixin, which checks the validators= parameter of the fields of that model, and based on that, creates a constraint to enforce that at the database side.

You can combine the mixins that are defined with FullChoicesConstraintModelMixin, it is probably advisable to use FullChoicesConstraintModelMixin over the mixins defined above, since as more validators are enforced , it will automatically add more constraints for these models, whereas ChoicesConstraintModelMixin for example, will only limit itself to choices.

One can exclude certain fields with the exclude_choice_check_fields and exclude_range_check_fields attributes that you can alter in the model. These need to provide a collection of strings that contain the name of the field.

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.core.validators import MaxValuevalidator, MinValueValidator 
from django_enforced_choices.models import FullChoicesConstraintModelMixin

class Movie(FullChoicesConstraintModelMixin, models.Model):
    genre = models.CharField(max_length=1, choices=[('d', 'drama'), ('h', 'horror')])
    year = models.IntegerField(validators=[MinValueValidator(1888)])

this will then add CheckConstraints to the model to enforce that genre only can contain 'd' and 'h' at the database side, and that the year is greater than or equal to 1888.

How does the package work?

For the fields defined, it will check if the choices and validators are defined. If that is the case, it will create a CheckConstraint with fieldname__in with the keys in the choices for choices, and __range, __lte or __gte for ranges depending on what values are picked.

If the field is NULLable, it will also allow NULL/None to be used.

Metadata

Release files for django-enforced-choices 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-enforced-choices 0.2.0
File Size Uploaded Provenance
django-enforced-choices-0.2.0.tar.gz 14.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-enforced-choices 0.2.0
File Interpreter ABI Platform
django_enforced_choices-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 22.9 kB

Release files / django-enforced-choices-0.2.0.tar.gz

Download URL django-enforced-choices-0.2.0.tar.gz
Size 14.9 kB
Tags Source
SHA-256 checksum
How to use checksums
4c713952ad94f4ce7c5da8c9edc8476e441316bc428c2a4e08734d7c04966c2e
BLAKE2b-256 checksum
How to use checksums
9796543ba682edae0073f137bfab5679c02bb6f7a4d7e0109029676313cc360a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.12.2

Release files / django_enforced_choices-0.2.0-py3-none-any.whl

Download URL django_enforced_choices-0.2.0-py3-none-any.whl
Size 8.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9d64721d16155b0295dbd7d56955537300c1a60e5e1ba08f5cbf866c78827fd2
BLAKE2b-256 checksum
How to use checksums
a5e16ae77c7bf3c87ab96e0b5859a4cabf2f36cd27355d23ffa62e9a8aa3f201
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.0.0 CPython/3.12.2

Security for django-enforced-choices 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page