# I’m writing a custom model field and would like it to come with a database CHECK

**URL:** <https://forum.djangoproject.com/t/i-m-writing-a-custom-model-field-and-would-like-it-to-come-with-a-database-check/35101>\
**Category:** Using the ORM\
**Created:** [September 25, 2024, 1:40pm UTC](https://forum.djangoproject.com/t/i-m-writing-a-custom-model-field-and-would-like-it-to-come-with-a-database-check/35101 "2024-09-25T13:40:15Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![jeremyshurtleff54](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jeremyshurtleff54/32/24131_2.png) [@jeremyshurtleff54](https://forum.djangoproject.com/u/jeremyshurtleff54)\
**Post date:** [September 25, 2024, 1:40pm UTC](https://forum.djangoproject.com/t/i-m-writing-a-custom-model-field-and-would-like-it-to-come-with-a-database-check/35101/1 "2024-09-25T13:40:15Z")

</div>

I’m writing a custom model field and would like it to come with a database CHECK constraint. The actual use case is DB validation of Django’s `choices` , to avoid invalid values (since the data can be edited from outside of Django).

---

<div class="post-metadata">

**Author:** ![anefta](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/anefta/32/10649_2.png) [@anefta](https://forum.djangoproject.com/u/anefta)\
**Post date:** [September 27, 2024, 8:00am UTC](https://forum.djangoproject.com/t/i-m-writing-a-custom-model-field-and-would-like-it-to-come-with-a-database-check/35101/2 "2024-09-27T08:00:41Z")

</div>

What is your question? Post your code

---

<div class="post-metadata">

**Author:** ![covracer](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/covracer/32/26417_2.png) [@covracer](https://forum.djangoproject.com/u/covracer)\
**Post date:** [March 9, 2025, 5:47pm UTC](https://forum.djangoproject.com/t/i-m-writing-a-custom-model-field-and-would-like-it-to-come-with-a-database-check/35101/3 "2025-03-09T17:47:38Z")

</div>

In my experience, the simplest way to define a database constraint is at the model level, rather than at the (custom) field level.

```python
class Format(Model):
    right_column = PositiveSmallIntegerField(default=12)
    class Meta(TypedModelMeta):
        constraints = [
            CheckConstraint(
                condition=Q(right_column__lte=24),
                name='right_column <= 24'
            )
        ]

```

If you’re very interested in keeping the constraint(s) close to the custom field definition, the `contribute_to_class` has some gotchas but can be made to work:

> **[stupid-django-tricks/column\_check\_constraints at master ·...](https://github.com/shangxiao/stupid-django-tricks/tree/master/column_check_constraints#adding-a-checkconstraint-to-the-model-from-the-field)**
>
> Contribute to shangxiao/stupid-django-tricks development by creating an account on GitHub.

To avoid weird duplication during migrations I had to bail on `cls. __module__ == ' __fake__'`:

```python
    def contribute_to_class(self, cls: type[Model], name: str, private_only: bool = False) -> None: # noqa: FBT001, FBT002
        super().contribute_to_class(cls, name, private_only=private_only)

        if cls. __module__ == ' __fake__':
            return # Avoid duplicate constraints when migrating

        # Ensure ModelState.from_model() considers constraints
        cls._meta.original_attrs['constraints'] = cls._meta.original_attrs.get('constraints', [])

        cls._meta.constraints.append(
            CheckConstraint(
                condition=Q(f'{name}__lte'=24),
                name=f'{cls._meta.db_table}.{name} <= 24',
            )
        )

```

In general, database constraints can be column-specific or for the whole table. A constraint behaves identically no matter where it’s attached, so the choice probably only affects raw SQL schema readability. If you’re very interested in a column constraint, the upper section of the above link explains how to implement the constraint in a way ends up looking like the constraint of a PositiveIntegerField. But it’s implemented in a more database-independent way than the backend-specific `data_type_check_constaints` dictionary, by overriding the `db_check` method.
