3

Current situation, caused by legacy:

class Foo(models.Model)
    field = models.BooleanField()

    @property
    def renamed_field(self):
        return self.field

    @renamed_field.setter
    def renamed_field(self, value):
        self.field = value

    obsolete_field = models.BooleanField()

Desired situation:

class Foo(models.Model)
    renamed_field = models.BooleanField()

No problem yet. South can handle the migration using db.rename_column and db.deletecolumn.

Problem: our Django app runs on multiple instances with a shared MySQL instance. When we deploy our code to production, we replace old instances with new instances one by one as new instances are booted. If we want to avoid downtime, the app models needs to support the new database scheme, and here's the catch: and while instances are replaced also the old database scheme.

We would like to avoid downtime.

A naive solution would be a two-step approach where we deploy, wait until all instances have been replaced, migrate, and enable the my_feature_switch immediately after migrating:

class OldFoo(models.Model)
    field = models.BooleanField()

    @property
    def renamed_field(self):
       return self.field

    @renamed_field.setter
    def renamed_field(self, value):
        self.field = value

    obsolete_field = models.BooleanField()

    class Meta:
        abstract = True

class NewFoo(models.Model)
    renamed_field = models.BooleanField()

    class Meta:
        abstract = True

if waffle.switch_is_active('my_feature_switch'):
    foo_model = NewFoo
else:
    foo_model = OldFoo

class Foo(widget_model_model):
    pass

I hope this shows the direction of a possible solution. It needs another deploy at some point to clean this up (basically renaming NewFoo to Foo and removing everything else).

The problem with the solution above is that we would need to restart all instances to respect the new waffle switch value. Restarting all servers is problematic. Preferably it respects a condition (waffle switch) during runtime.

  • Is there another way to make the model conditional?
  • Or maybe a completely different way to solve this problem? Maybe a conditional db_column for each field, for example?

We're on Django==1.6, South==1.0.

Blaise
  • 13,139
  • 9
  • 69
  • 97
  • are you familiar with [dynamic models](https://code.djangoproject.com/wiki/DynamicModels)? They might be helpful... – yuvi Sep 16 '14 at 10:45
  • Thanks for your reply. I think this guide is outdated and I would probably enter a world of pain going down that route. It would mean I have to rewrite my entire model in a dynamic way. The example shown in my OP is a simplified version of a very fat model. – Blaise Sep 16 '14 at 10:59
  • yeah, it's outdated, but there's a link there to a [very detailed and updated SO answer](http://stackoverflow.com/questions/7933596/django-dynamic-model-fields/7934577#7934577) on the same subject. I'll try to think of another approach (btw, is vartec's answer not a helpful one?) – yuvi Sep 16 '14 at 11:49
  • Also - why not add the new model, and then add the switcher in the `views`, where you could then do `import newFoo as Foo`? Also, [this might be helpful](http://stackoverflow.com/a/9183201/2387772) – yuvi Sep 16 '14 at 11:52
  • 1
    Yes, I accepted and implemented Vartec's solution, but I'm always open to learn about other ways to solve a problem to add to my problem-solving toolbox. I'm happy I learned about dynamic models, they might come in handy some day. – Blaise Sep 16 '14 at 13:36
  • Dynamic models are just incredible – yuvi Sep 16 '14 at 14:25

1 Answers1

2

Completely different solution, rename object field, but specify old database column name using db_column parameter.

class Foo(models.Model)
    renamed_field = models.BooleanField(db_column='obsolete_field')

No migrations required.

vartec
  • 131,205
  • 36
  • 218
  • 244