I have a model which I want to rename but also change its data. The model field, sayfoo
contains char data 1
... 6
and I want to map this data in following ways:
1
, 2
-> 1
&& 3
, 4
-> 2
&& 5
-> 3
&& 6
-> 4
I did convert_to_south
on my app, which created 0001_initial
and then changed new new field in models.py
and did schemamigration
which made 0002_initial.py
. Now how do I modify the existing data? Here are my migrations:
0001_initial
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Applicant'
db.create_table(u'registrar_applicant', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('registered_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('workshop', self.gf('django.db.models.fields.CharField')(max_length=20)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('semester', self.gf('django.db.models.fields.CharField')(max_length=1)),
('python_experience', self.gf('django.db.models.fields.CharField')(max_length=1)),
('phone_number', self.gf('django.db.models.fields.CharField')(max_length=10, blank=True)),
('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
('solved_puzzle', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal(u'registrar', ['Applicant'])
def backwards(self, orm):
# Deleting model 'Applicant'
db.delete_table(u'registrar_applicant')
models = {
u'registrar.applicant': {
'Meta': {'ordering': "['-solved_puzzle', 'registered_at', 'semester', 'name']", 'object_name': 'Applicant'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'phone_number': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}),
'python_experience': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'registered_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'semester': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'solved_puzzle': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'workshop': ('django.db.models.fields.CharField', [], {'max_length': '20'})
}
}
complete_apps = ['registrar']
0002_initial
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Applicant'
db.create_table(u'registrar_applicant', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('registered_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('workshop', self.gf('django.db.models.fields.CharField')(max_length=20)),
('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
('year', self.gf('django.db.models.fields.CharField')(max_length=1)),
('python_experience', self.gf('django.db.models.fields.CharField')(max_length=1)),
('phone_number', self.gf('django.db.models.fields.CharField')(max_length=10, blank=True)),
('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
('solved_puzzle', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal(u'registrar', ['Applicant'])
def backwards(self, orm):
# Deleting model 'Applicant'
db.delete_table(u'registrar_applicant')
models = {
u'registrar.applicant': {
'Meta': {'ordering': "['-solved_puzzle', 'registered_at', 'year', 'name']", 'object_name': 'Applicant'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'phone_number': ('django.db.models.fields.CharField', [], {'max_length': '10', 'blank': 'True'}),
'python_experience': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'registered_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'solved_puzzle': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'workshop': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'year': ('django.db.models.fields.CharField', [], {'max_length': '1'})
}
}
complete_apps = ['registrar']
For this, you'll have to create a data migration
# Run this command from the shell
python manage.py datamigration <app_name> change_foo_values
This is will create a migration file named 0003_change_foo_values.py
. You will have to manually edit the file.
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
# Note: Don't use "from appname.models import ModelName".
# Use orm.ModelName to refer to models in this application,
# and orm['appname.ModelName'] for models in other applications.
orm.Applicant.objects.filter(foo__in=['1', '2']).update(foo='1')
orm.Applicant.objects.filter(foo__in=['3', '4']).update(foo='2')
orm.Applicant.objects.filter(foo__in=['5', '6']).update(foo='3')
def backwards(self, orm):
"Write your backwards methods here."
# This migration cannot be reversed
pass # don't do anything when running reverse migration
# or
raise RuntimeError("Cannot reverse this migration") # stop south from reverting beyond this migration
Note: You won't be able to go revert back to the previous values of the column. If that is not a problem then this is sufficient.
However, if you do want to be able to reverse the migration, I suggest creating a new field, that will hold the old value of the column for each row.
PS: Your migration files seem wrong. You should not have two initial
migrations for one app. Did you run convert_to_south
after renaming your model(which doesn't seem to have been renamed) again? You only need to run convert_to_south
once, when initially converting an existing model to south. After that you need to create schema migrations. The simplest way would be to run the schemamigration
management command with the appname, and --auto
as arguments.
python manage.py schemamigration <app_name> --auto
PPS: If you want to rename a model, please create an empty migration and write the migration code yourself, just use db.rename_table
. IIRC south will delete your old table before creating a new one if you try to create auto schemamigrations after renaming the model.
python manage.py schemamigration <app_name> --empty rename_model_x_to_y
And in your migration file
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
"Write your forwards methods here."
# Note: Don't use "from appname.models import ModelName".
# Use orm.ModelName to refer to models in this application,
# and orm['appname.ModelName'] for models in other applications.
db.rename_table('old', 'new')
def backwards(self, orm):
"Write your backwards methods here."
db.rename_table('new', 'old')