I would like to change a name of specific fields in a model:
class Foo(models.Model):
name = models.CharField()
rel = models.ForeignKey(Bar)
should change to:
class Foo(models.Model):
full_name = models.CharField()
odd_relation = models.ForeignKey(Bar)
What's the easiest way to do this using South?
You can use the db.rename_column
function.
class Migration:
def forwards(self, orm):
# Rename 'name' field to 'full_name'
db.rename_column('app_foo', 'name', 'full_name')
def backwards(self, orm):
# Rename 'full_name' field to 'name'
db.rename_column('app_foo', 'full_name', 'name')
The first argument of db.rename_column
is the table name, so it's important to remember how Django creates table names:
Django automatically derives the name of the database table from the name of your model class and the app that contains it. A model's database table name is constructed by joining the model's "app label" -- the name you used in manage.py startapp -- to the model's class name, with an underscore between them.
In the case where you have a multi-worded, camel-cased model name, such as ProjectItem, the table name will be app_projectitem
(i.e., an underscore will not be inserted between project
and item
even though they are camel-cased).
Here's what I do:
Make the column name change in your model (in this example it would be myapp/models.py) Run ./manage.py schemamigration myapp renaming_column_x --auto
Note renaming_column_x
can be anything you like, it's just a way of giving a descriptive name to the migration file.
This will generate you a file called myapp/migrations/000x_renaming_column_x.py
which will delete your old column and add a new column.
Modify the code in this file to change the migration behaviour to a simple rename:
class Migration(SchemaMigration):
def forwards(self, orm):
# Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')
def backwards(self, orm):
# Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')
x
or column_x
?
--auto
migration first is a great tip. It avoids problems with the South ORM Freezer, which occur if the migration only has forwards
and backwards
methods, but does not contain the frozen model
object.
db.rename_column
does not rename the constraints associated with the column. The migration will still work but you'll have constraints named after the old column name. I had a column with a uniqueness constraint, renamed it using this method, tested that the uniqueness constraint still existed and got an error but the name of the constraint itself was still using the old column name. Perhaps an explicit db.delete_unique
and db.create_unique
would have done it but I decided to go with sjh's solution.
I didn't know about db.rename column, sounds handy, however in the past I have added the new column as one schemamigration, then created a datamigration to move values into the new field, then a second schemamigration to remove the old column
db.rename_column
won't rename constraints for you so you have to handle that manually. If you forget to do it, the migration will work except that unbeknownst to you there may be a constraint still using the old column name. It is unclear to me whether the issue is merely cosmetic or whether in a future migration where the constraint should be manipulated or dropped South won't be able to find it. At any rate, doing it here like sjh suggests is the safe way to do it: you can let South figure out what it should be figuring out.
Django 1.7 introduced Migrations so now you don't even need to install extra package to manage your migrations.
To rename your model you need to create empty migration first:
$ manage.py makemigrations <app_name> --empty
Then you need to edit your migration's code like this:
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('yourapp', 'XXXX_your_previous_migration'),
]
operations = [
migrations.RenameField(
model_name='Foo',
old_name='name',
new_name='full_name'
),
migrations.RenameField(
model_name='Foo',
old_name='rel',
new_name='odd_relation'
),
]
And after that you need to run:
$ manage.py migrate <app_name>
Just change the model and run makemigrations
in 1.9
Django automatically detects that you've deleted and created a single field, and asks:
Did you rename model.old to model.new (a IntegerField)? [y/N]
Say yes, and the right migration gets created. Magic.
Add south to your installed apps in project setting file. Comment out the added/modified field/table. $ manage.py Schemamigration
If you are using 'pycharm', then you can use 'ctrl+shift+r' instead of 'manage.py' , and 'shift ' for parameters.
Success story sharing