# Dynamically populate constraints in Field.contribute\_to\_class

**URL:** <https://forum.djangoproject.com/t/dynamically-populate-constraints-in-field-contribute-to-class/22934>\
**Category:** ORM\
**Created:** [August 9, 2023, 10:28pm UTC](https://forum.djangoproject.com/t/dynamically-populate-constraints-in-field-contribute-to-class/22934 "2023-08-09T22:28:52Z")\
**Posts on this page:** 1\
**Showing post:** 10

<div class="post-metadata">

**Author:** ![nessita](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/nessita/32/12194_2.png) [@nessita](https://forum.djangoproject.com/u/nessita)\
**Post date:** [August 16, 2023, 1:48am UTC](https://forum.djangoproject.com/t/dynamically-populate-constraints-in-field-contribute-to-class/22934/10 "2023-08-16T01:48:55Z")

</div>

I’ve been doing some R&D to understand the original issue presented in this post. I think there are two main things to discuss, perhaps separately:

1. Why/how the constraints added by a field to its model’s constraint list (via `contribute_to_class`) is not picked up in the generated migration but it’s present in the DB (the original forum post).

2. A new feature that we could summarize using @Lily-Foote’s words as _[Let’s define] a clear, clean api for defining custom fields that set constraints._

Regarding item 1, the core of the issue is that `makemigrations` would not pick up the field-defined constraint but `migrate` will apply it. Specifically, for a simple model like (taken from the linked repo):

```python
from django.db import models

class ConstraintField(models.CharField):
    def contribute_to_class(self, cls, name, private_only=False):
        super().contribute_to_class(cls, name, private_only)
        cls._meta.constraints.append(
            models.CheckConstraint(
                check=models.Q(**{f"{name}__lte": 10}),
                name=f"test_constraint_{cls. __name__.lower()}_lte_value",
            )
        )

class DynamicModelWithConstraintField(models.Model):

    constrained_field = ConstraintField(max_length=10)

```

The generated migration is:

```python
    operations = [
        migrations.CreateModel(
            name='DynamicModelWithConstraintField',
            fields=[
                ('id', models.BigAutoField(...)), # reduced for brevity
                ('constrained_field', ticket_dynamic_constraints.models.ConstraintField(max_length=10)),
            ],
        ),
    ]

```

But `sqlmigrate` shows:

```sql
BEGIN;
--
-- Create model DynamicModelWithConstraintField
--
CREATE TABLE "ticket_dynamic_constraints_dynamicmodelwithconstraintfield" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "constrained_field" varchar(10) NOT NULL, CONSTRAINT "test_constraint_dynamicmodelwithconstraintfield_lte_value" CHECK ("constrained_field" <= '10'));
COMMIT;

```

So clearly the constraint is (to be) added to the DB, but the migration machinery is not aware of it so further changes to the constraint result in all kinds of errors on future operations over those constraints.  
I realized that the code in the migrations’ state module, when calling `ModelState.from_model`, the `model._meta.original_attrs` is used to build the `ModelState` instance that represents the model, but this `original_attrs` is populated in `Options.contribute_to_class` and that’s not accounting/including what the Field has added to the model’s constraints (more debugging on this is needed, but for starter, there is a [if self.meta](https://github.com/django/django/blob/main/django/db/models/options.py#L191C9-L191C22) that does not do any magic if Meta is not defined.

So for this item, the main question is do we consider this a bug? From #28888 likely not since there is no documented support for adding indexes (or constraints) in `Field.contribute_to_class`, but it’s worth asking the question.

What about item 2? We could discuss whether a `get_constraints` would make sense as a new public method in the `Field` class. This could be a way to provide an API to solve the use case but I’m not sure if there is a similar precedent that would guide this decision making.

---

_[View the full topic](https://forum.djangoproject.com/t/dynamically-populate-constraints-in-field-contribute-to-class/22934)._
