I was wondering if the following migration is possible with Django south and still retain data.
Before:
I currently have two apps, one called tv, one called movies, each with a VideoFile model (simplified here):
tv/models.py:
class VideoFile(models.Model):
show = models.ForeignKey(Show, blank=True, null=True)
name = models.CharField(max_length=1024, blank=True)
size = models.IntegerField(blank=True, null=True)
ctime = models.DateTimeField(blank=True, null=True)
movies/models.py:
class VideoFile(models.Model):
movie = models.ForeignKey(Movie, blank=True, null=True)
name = models.CharField(max_length=1024, blank=True)
size = models.IntegerField(blank=True, null=True)
ctime = models.DateTimeField(blank=True, null=True)
After:
Because the two videofile objects are so similar I want to get rid of duplication and create a new model in a separate app called media that contains a generic VideoFile class and use inheritance to extend it:
media/models.py:
class VideoFile(models.Model):
name = models.CharField(max_length=1024, blank=True)
size = models.IntegerField(blank=True, null=True)
ctime = models.DateTimeField(blank=True, null=True)
tv/models.py:
class VideoFile(media.models.VideoFile):
show = models.ForeignKey(Show, blank=True, null=True)
movies/models.py:
class VideoFile(media.models.VideoFile):
movie = models.ForeignKey(Movie, blank=True, null=True)
So my question is, how can I accomplish this with django-south and still maintain existing data?
All three these apps are already managed by south migrations and according to the south documentation it is bad practice to combine a schema and data migration and they recommend it should be done in a few steps.
I think it could be done using separate migrations like this (assuming media.VideoFile is already created)
- Schema migration to rename all fields in tv.VideoFile and movies.VideoFile that will move to the new media.VideoFile model, maybe to something like old_name, old_size, etc
- Schema migration to tv.VideoFile and movies.VideoFile to inherit from media.VideoFile
- Data migration to copy old_name to name, old_size to size, etc
- Scheme migration to remove old_ fields
Before I go through all that work, do you think that will work? Is there a better way?
If you're interested, the project is hosted here: http://code.google.com/p/medianav/
Best Answer
Check out response below by Paul for some notes on compatibility with newer versions of Django/South.
This seemed like an interesting problem, and I'm becoming a big fan of South, so I decided to look into this a bit. I built a test project on the abstract of what you've described above, and have successfully used South to perform the migration you are asking about. Here's a couple of notes before we get to the code:
The South documentation recommends doing schema migrations and data migrations separate. I've followed suit in this.
On the backend, Django represents an inherited table by automatically creating a OneToOne field on the inheriting model
Understanding this, our South migration needs to properly handle the OneToOne field manually, however, in experimenting with this it seems that South (or perhaps Django itself) cannot create a OneToOne filed on multiple inherited tables with the same name. Because of this, I renamed each child-table in the movies/tv app to be respective to it's own app (ie. MovieVideoFile/ShowVideoFile).
In playing with the actual data migration code, it seems South prefers to create the OneToOne field first, and then assign data to it. Assigning data to the OneToOne field during creation cause South to choke. (A fair compromise for all the coolness that is South).
So having said all that, I tried to keep a log of the console commands being issued. I'll interject commentary where necessary. The final code is at the bottom.
Command History
For space sake, and since the models invariably look the same in the end, I'm only going to demonstrate with 'movies' app.
movies/models.py
movies/migrations/0002_unified-videofile.py (schema migration)
movies/migration/0003_videofile-to-movievideofile-data.py (data migration)
South is awesome!
Ok standard disclaimer: You're dealing with live data. I've given you working code here, but please use the
--db-dry-run
to test your schema. Always make a backup before trying anything, and generally be careful.COMPATIBILITY NOTICE
I'm going to keep my original message intact, but South has since changed the command
manage.py startmigration
intomanage.py schemamigration
.