How can I maintain old and new database schema at the same time in Django?
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()
Required situation:
class Foo(models.Model)
renamed_field = models.BooleanField()
No problems. South can handle migration with db.rename_column
and db.deletecolumn
.
Problem: Our Django application runs in multiple instances with a shared MySQL instance. When we deploy our code to production, we replace the old instances with new instances one by one as new instances are loaded. If we want to avoid downtime, the application models must support the new database schema, and here's the catch: and while the instances are being replaced by the old database schema as well.
We would like to avoid downtime.
A naive solution would be a two-step approach where we deploy, wait for all instances to be swapped out, migrate and enable my_feature_switch
right after migration:
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
Hopefully this will show the direction of a possible solution. It will need a different deployment at some point (basically renaming NewFoo
to Foo
and removing everything else).
The problem with the above solution is that we will need to restart all instances to evaluate the new value of the waffle switch. Restarting all servers is problematic. Preferably it meets a condition (waffle switch) at runtime.
- Is there any other 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 are on Django == 1.6, South == 1.0.
source to share