Ticket #1 (closed enhancement: fixed)
Documentation on doing Django DB API operations on a table as part of a migration.
| Reported by: | scanner@… | Owned by: | andrew |
|---|---|---|---|
| Priority: | minor | Milestone: | |
| Component: | migrations | Version: | 0.6-pre |
| Keywords: | datamanipulation while modifying a table.. | Cc: |
Description
At first I was going to file a bug report and then I figured out how to solve the issue.
I have a migration where I add some columns and delete a column. The column I am deleting is replaced by a column I am adding so I wrote code to generate data for the new column from the old column before deleting it.
This was giving failures in psycopg2 (postgres backend, obviously) about 'cannot ALTER TABLE ... because it has pending trigger events.'
I found that if I wrapped the code that generated the new data in a transaction, I could then do the 'db.delete_columns()' without it throwing that exception. So file this one under documentation:
class Migration:
def forwards(self):
"""
Add content, keyframe, container, length, and synopsis columns.
Take the 'filename' column and use it to populate the 'content'
column. After we are done doing that, drop the 'filename' column.
"""
db.add_column('videos_video',
'content',
models.FileField(storage = fs, max_length = 1024,
upload_to = "%Y/%M", null = True,
blank = True))
db.add_column('videos_video',
'keyframe',
models.ImageField(null = True, blank = True,
max_length = 1024,
upload_to = "img/video_keyframes"))
db.add_column('videos_video',
'container',
models.CharField(max_length = 32, default = 'unknown'))
db.add_column('videos_video',
'length',
models.FloatField(null = True, blank = True,
help_text = "Length in seconds"))
db.add_column('videos_video',
'synopsis',
models.TextField(null = True, blank = True,
max_length = 4096))
print "Migrating video filenames to File objects."
db.start_transaction()
for video in Video.objects.all():
video.content = video.filename
video.save()
print "Done migration of filename to File objects."
db.commit_transaction()
db.delete_column('videos_video', 'filename')
def backwards(self):
"""
Add back the 'filename' column. Populate it from the 'path' of the
content field. After that delete the columns we had added above.
"""
db.add_column('videos_video', 'filename',
models.CharField(max_length = 2048, db_index = True,
null = True))
db.start_transaction()
for video in Video.objects.all():
video.filename = video.content.name
video.save()
db.commit_transaction()
db.delete_column('videos_video', 'content')
db.delete_column('videos_video', 'keyframe')
db.delete_column('videos_video', 'container')
db.delete_column('videos_video', 'length')
db.delete_column('videos_video', 'synopsis')

You're right, this should probably be documented. You can also just do commit_transaction; start_transaction since the whole migration is transactioned but either will work!
I'll go add this to the wiki now, so closing this ticket.