Migrations force me to have, forever, any name/reference I use in field validators=, choices=, whatever, valid forever (even if the requirements change).

68 views
Skip to first unread message

Luis Masuelli

unread,
Feb 25, 2015, 3:25:19 PM2/25/15
to django...@googlegroups.com
I have an issue with migrations.

Suppose I declare (in my application, with name myapp) a field with a validators= declared. In such value (which is an iterable), I declare a function:

class MyModel(models.Model):
   
"""
    This one is identifiable.
    """


    identifier
= models.CharField(max_length=10, unique=True, validators=[valid_identifier])

Assume I created the migration with makemigrations:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings
import django.core.validators
import micnt.models


class Migration(migrations.Migration):

    dependencies
= [
       
...
   
]

    operations
= [
        migrations
.CreateModel(
            name
='MyModel',
            fields
=[
               
('identifier', models.CharField(max_length=10, unique=True, validators=[myapp.models.valid_identifier])),
           
],
            options
={
           
},
            bases
=(models.Model,),
       
),
   
]

Assume valid_identifier is any external callable which will take one positional argument, and eventually raise ValidationError under certain conditions.

Now the requirements vary: The identifier may be national or foreign, so the country is required as an additional field, and also a discriminator field. Additionally, the validation I must perform is cross-field, so instead of using something in validators, I define a clean() method, and make the combination of country/identifier unique, instead of just the identifier unique:

class MyModel(models.Model):
   
"""
    This one is identifiable.
    """


    identifier_type = models.CharField(max_length=10, choices=(('passport', _(u'Passport')), ('national', _(u'National'))), null=False)
    identifier
= models.CharField(max_length=10)
    country = models.ForeignKey(Country, null=False)  # assume Country model exists

    class Meta:
        unique_together = (('identifier', 'country'),)

    def clean(self):
        raise ValidationError('Implement this!')
   

And then, I don't need anymore the valid_identifier function, since my code does not use it anymore. So I delete it.

Now I will create the migration with makemigrations as usual. What will happen?

a. The command will succeed.
b. The command will explode (i. e. a normal Python exception).
c. The command will create an inconsistent migration, without exploding.
d. Jesus will come again.
e. The command will get into a kind of endless loop.

If you guesses "b", you were right. Django will report an error, since myapp.models.valid_identifier does not exist anymore (AttributeError).

So, once I set a value for validators= parameter and create a migration, I am forced to keep such reference valid for the rest of my life (e.g. by having valid_identifier=None, if I don't need the function anymore), or else the migrations will not work again since they will be traversed, imported, and will get such AttributeError.

Is this an expected, normal, behavior?

Tim Graham

unread,
Feb 26, 2015, 7:54:22 AM2/26/15
to django...@googlegroups.com
Yes, it's expected behavior. Please see the documentation on the topic:
https://docs.djangoproject.com/en/stable/topics/migrations/#historical-models

Carsten Fuchs

unread,
Feb 26, 2015, 10:42:29 AM2/26/15
to django...@googlegroups.com
Hi all,

Am 26.02.2015 um 13:54 schrieb Tim Graham:
> Yes, it's expected behavior. Please see the documentation on the topic:
> https://docs.djangoproject.com/en/stable/topics/migrations/#historical-models

I have not yet tried this, but won't squashing migrations as a side
effect also get us rid of dependencies of historical models?

Best regards,
Carsten

Carl Meyer

unread,
Feb 26, 2015, 10:50:38 AM2/26/15
to django...@googlegroups.com
Yes, squashmigrations is the right way to deal with this problem.

If you have RunPython/RunSQL migrations that can also safely go away,
you'll need to manually excise them before squashing in order to get a
complete squash, since they are opaque to squashmigrations and won't be
optimized across. See https://code.djangoproject.com/ticket/24109

Carl

signature.asc

Luis Masuelli

unread,
Feb 27, 2015, 3:17:23 PM2/27/15
to django...@googlegroups.com
Thanks :D Did not think about squashing migrations as solution for this problem! But it does the job.
OTOH the fact about historical models has nothing to do with my problem (since it is not related at all with instancing a model, but just about the definition and not getting a NameError).

Gergely Polonkai

unread,
Feb 27, 2015, 5:22:13 PM2/27/15
to django...@googlegroups.com

Hello,

another solution may be to patch your models in the migration module or class, like:

import myapp.models

myapp.models.valid_identifier = something_acceptable()

Although it seems a bit ugly, I just tested it, and it works. This way valid_identifier will live during the migration only.

Best,
Gergely

On 27 Feb 2015 21:17, "Luis Masuelli" <luisfm...@gmail.com> wrote:
Thanks :D Did not think about squashing migrations as solution for this problem! But it does the job.
OTOH the fact about historical models has nothing to do with my problem (since it is not related at all with instancing a model, but just about the definition and not getting a NameError).

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/15bb8a96-18a5-432c-afeb-7a3299ca7a4d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

marcin....@gmail.com

unread,
Apr 18, 2016, 7:42:07 PM4/18/16
to Django users


On Friday, February 27, 2015 at 11:22:13 PM UTC+1, Gergely Polonkai wrote:

Hello,

another solution may be to patch your models in the migration module or class



The best solution is not to use Django built-in migrations. Of course you can create them very quickly (via automatic "makemigrations"), but this is probably just the only advantage. Everyting else is something what I'm calling "built-in hell". 

Try to use external solutions like Liquibase. But remember - you'll need to track builtin schema changes (as a part of "builtin-hell") and converting 3rd app migrations. 
Also guys from Django Team removed very useful command - `sql`, which was a base tool for speeding up of Liquibase changesets creation. But until you know what you're doing, you can write every change manually. As a result you're getting rock-solid db schema management without python imports problem, without historical problems nor models, without strange squashing ideas, refactoring horror, and so on... 

Kind Regards,
Marcin
Reply all
Reply to author
Forward
0 new messages