Skip to content

Improve Choices for migrations #51

Open
@jarekwg

Description

@jarekwg

Code of Conduct

  • I agree to follow Django's Code of Conduct

Feature Description

When using Django's Choices in model field choices, there should be a mechanism to avoid migrations updating each time the Choices enum changes.

Problem

Given:

class Customer(models.Model):
    class TaxCode(models.TextChoices):
        TAX = 'TAX', 'Tax'
        EXEMPT = 'EXEMPT', 'Exempt'

    taxcode = models.CharField(max_length=6, choices=TaxCode.choices)

Each time i change my enum, django will generate a fresh migration:

        migrations.AlterField(
            model_name='customer',
            name='taxcode',
            field=models.CharField(choices=[('TAX', 'Tax'), ('EXEMPT', 'Exempt'), ('BLAH', ...)], max_length=6),
        ),

There should be a way to indicate to django that migrations don't need updating.

Request or proposal

proposal

Additional Details

Original discussion here.

The current way around this issue, which is only possible since dj5, is to define a separate function for each Choices enum, and pass that to model choices..

def get_taxcode_choices():
    return Customer.TaxCode.choices
class Customer(models.Model):
    class TaxCode(models.TextChoices):
        TAX = 'TAX', 'Tax'
        EXEMPT = 'EXEMPT', 'Exempt'

    taxcode = models.CharField(max_length=6, choices=get_taxcode_choices)

This gets repetitive and noisy very quickly.

Implementation Suggestions

There were some early thoughts to have migration generator handle this magically (ie not unfurl .choices prematurely if a django Choices is detected), however to avoid magic or cause backwards compatibility issues, the more sensible approach is probs to just introduce an explicit callable classmethod on the Choices model.

I propose we add a model_choices method:

class Choices(enum.Enum, metaclass=ChoicesType):
    @classmethod
    def model_choices(cls):
        return cls.choices

    ...

Users can then do this:

class Customer(models.Model):
    class TaxCode(models.TextChoices):
        TAX = 'TAX', 'Tax'
        EXEMPT = 'EXEMPT', 'Exempt'

    taxcode = models.CharField(max_length=6, choices=TaxCode.model_choices)

which results in migrations generating out like this initially (and not generating new operations from thereon):

        migrations.CreateModel(
            name='Customer',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('taxcode', models.CharField(choices=Customer.TaxCode.model_choices, max_length=6)),
            ],
        ),

Credits to @bayangan1991 for initial idea.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Idea

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions