Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

Handling Django choices and migrations

$
0
0

If you are using Django's choices option on a model field, you have probably come across the situation where adding / updating a choice generates a new migration. If you are using choices liberally for things that are variable over time (e.g. "where did you hear about us?") this can generate a lot of unnecessary migrations.

Take this example:

class Person(models.Model): SOURCE_ADVERT = 'advert' SOURCE_REFERRAL = 'referral' SOURCE_SEARCH = 'google' SOURCE_CHOICES = ( (SOURCE_TWITTER, 'Saw an advert'), (SOURCE_REFERRAL, 'Referred by a friend'), (SOURCE_SEARCH, 'Found on Google'), ) source = models.CharField( max_length=10, choices=SOURCE_CHOICES, help_text="How did you hear about us?" )

If you run makemigrations against this it will generate a migration that looks something like:

class Migration(migrations.Migration): operations = [ migrations.CreateModel( name='Person', fields=[ ('id', models.AutoField( verbose_name='ID', serialize=False, auto_created=True, primary_key=True) ), ('source', models.CharField( max_length=10, help_text='How did you hear about us?', # this tuple matches the state of the underlying SOURCE_FOO # and if that changes a new migration will be generated by # makemigrations choices=( (b'advert', b'Saw an advert'), (b'referral', b'Referred by a friend'), (b'google', b'Found on Google'), )) ) ] ) ]

The choices are baked into the migration in their raw text form. If you change this (even so much as adjusting a spelling mistake), and rerun makemigrations you will get a new migration.

The solution is to hack the migration code to use a callable instead, and the cleanest way of doing this is to use a lambda to convert the choices tuple into a function that returns the tuple, i.e. lambda: Person.SOURCE_CHOICES :

operations = [ migrations.CreateModel( name='Person', fields=[ ('id', models.AutoField( verbose_name='ID', serialize=False, auto_created=True, primary_key=True) ), ('source', models.CharField( max_length=10, help_text='How did you hear about us?', # this lambda will always match the current state # of SOURCE_CHOICES, and so will not generate a # new migration if the underlying values change choices=(lambda: Person.SOURCE_CHOICES)()) ) ] ) ]

If you now try amending the choices, rerunning makemigrations will not generate a new migration, but the choices will be updated.


Viewing all articles
Browse latest Browse all 9596

Trending Articles