| Version 6 (modified by andrew, 4 years ago) (diff) |
|---|
South tutorial
Introduction
Welcome to the South tutorial! This is a brief guide to help you understand what South is about, and how to use it; if you ever want more documentation, have a look in the docs/ directory inside South. Installation
First, go into the directory you'd like to install south in (I personally use /home/andrew/Programs/), and check out the code from subversion:
svn co https://svn.aeracode.org/svn/south/trunk south
Then, you'll need to link this directory into your python path; on OSX or Linux systems, this is usually
ln -s `pwd`/south SITE-PACKAGES-DIR/south
where SITE-PACKAGES-DIR is the directory described in the "Where are my site-packages stored?" part of the Django documentation.
Finally, go to the project you want to use South in, and add 'south' to the INSTALLED_APPS setting in settings.py. Then, run ./manage.py syncdb.
The new syncdb
When you run syncdb as I suggest above, you'll notice that it's changed looks. This is because you're now running South's syncdb rather than the normal Django one; our one examines your apps to find out which ones use migrations and which don't, and then only runs syncdb on the ones without migrations. An app is said to 'have migrations' if it has a module called 'migrations' inside it; South will create that for you when you ask to start your first migration on an app without migrations. your first migration
It's time to go ahead and start creating migrations. So let's do just that; South has a command to do all the dirty work for us. Take an app you have lying around (or create a new one, and add it to INSTALLED_APPS), and in the project directory run:
./manage.py startmigration app_name my_first_migration
That will create the migrations module inside the app 'app_name' and create a blank migration inside it for you called 0001_my_first_migration.py. Open this file, and you'll see something like the following:
from south.db import db
from public_site.models import *
class Migration:
def forwards(self):
"Write your forwards migration here"
def backwards(self):
"Write your backwards migration here"
The methods 'forwards' and 'backwards' will get called when you apply the migration and roll it back, respectively. All your migration code goes in here; for now, let's just make them print out what they're doing:
from south.db import db
from public_site.models import *
class Migration:
def forwards(self):
print "FORWARD! Hop to it."
def backwards(self):
print "RUN AWAY!!!"
Now, let's try running our migrations:
./manage.py migrate app_name
You should get some output which shows you that South has detected your app is at migration #0, that it wants to be at #1, and then it will apply the migration (and you'll see your method print 'FORWARD...' in the midst of all this).
Let's try rolling it back; you can tell South to roll forwards or backwards to any migration by specifying the migration you should end up just afterwards. Since we want to go back past the first migration, we use the special name 'zero' to roll back all changes:
./manage.py migrate app_name zero
See? It's undone the migration, and you'll have got 'RUN AWAY!!!' printed. Now it's time to do something useful with these migrations.
Automatic Migrations
At this point, we'll need a model of some kind in your app's models.py; we suggest you go and make one. Something like this will do:
class Spam(models.Model):
weight = models.FloatField()
expires = models.DateTimeField()
name = models.CharField(max_length=255)
Now, we could sit down and write a migration by hand that would create this table in forwards() and delete it in backwards(). However, we don't like lots of work, so instead South will do it for us:
./manage.py startmigration app_name create_spam --model Spam
That tells it to create a migration called create_spam, in the app app_name, and autogenerate it from the model Spam. You'll get something like this:
from south.db import db
from app_name.models import *
class Migration:
def forwards(self):
# Model 'Spam'
db.create_table("app_name_spam", (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('weight', models.FloatField()),
('expires', models.DateTimeField()),
('name', models.CharField(max_length=255))
))
db.send_create_signal('app_name', ['Spam'])
def backwards(self):
db.delete_table("aeblog_spam")
See how it's autodetected the model, and made the migration for us? Isn't that helpful?
Columns, Columns, Everywhere!
Of course, most of your work won't be adding new tables, but rather new columns. As of version 0.4/revision [109], South has an --add-field option to ./manage.py startmigration.
Like the --model example above, you call startmigration with an app and migration name:
./manage.py startmigration app_name add_foo --add-field MyModel.foo
Note that you must provide the field name as ModelName.field_name, since South won't know what model you want otherwise! You should also have already written that column's definition in models.py.
As with --model, you can use --add-field many times in a single call if you want to add a lot of columns:
./manage.py startmigration mitest some_cols --add-field user.age --add-field user.profile --add-field post.user
(also note here that you're free to write the model class names in lowercase; they're actually stored that way in Django)
All In One
However, that's not quite good enough yet. Now syncdb doesn't work, how are you going to create all the tables when you've just written your new models.py?
One answer is to use multiple --model switches on createmigration - it will create a migration that makes multiple tables at once:
./manage.py startmigration app_name create_spam --model Spam --model Eggs
However, there's a handy shortcut for creating a migration containing creation definitions for all the models:
./manage.py startmigration app_name --initial
Words of Warning
If you follow the commands above in order, exactly, you'll end up with a whole mess of migrations. If you use --initial, it must be the first migration; otherwise, two migrations will try to make the same table and you'll get an error.
Similarly, please realise createmigration is only for creating migrations for new models in your schema; if you've added a field to a model, running it won't create a migration that will upgrade your current table (you need to write that yourself); it will instead create a migration that will attempt to recreate the model's table with its new fields. This will fail.
Migrations are very simple, at their heart; they simply manipulate the database, and they must do it in a way so that running through them all from beginning to end results in a current version of the database schema. Our tools aim to make this job easier, but they're no replacement for understanding exactly what is going on.
End of the Line
This tutorial needs more work; if you feel like adding something, please do!
Some things you might want to try:
- Adding a new migration with the wrong number - e.g. another 0002 migration when you already have a 0003 (just rename a 0004 if you use createmigration and it makes one). The resulting problem, and the resolutions, are discussed in the command reference.
