Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django south migration, doesnt set default

I use south to migrate my django models. There is however a nasty bug in south. It doesn't set default values in Postgres Databases. Example:

created_at = models.DateTimeField(default = datetime.now)
tag_id = models.PositiveIntegerField(default = 0)

South will add these 2 fields to database, but fail to set their default values, which needs to be done manually.

Is there any patch for this bug?

UPDATE I had already tried setting default date with auto_now_add=True, but that is also not setting defaults. Adding null=True in field adds a db.alter_column in migration script produced by south. But that only removes NOT NULL constraint, doesnt add a default. Same for integer field

like image 669
jerrymouse Avatar asked Apr 03 '12 10:04

jerrymouse


3 Answers

If you are auto-generating your migrations using:

./manage.py schemamigration app_name --auto

Then you need to make a small edit to the migration before you actually apply it. Go into the generated migration (should be called something like app_name/migrations/000X__auto_add_field_foo.py) and look for the argument:

keep_default=False

in the db.add_column call. Simply change this to:

keep_default=True

And Django will now apply your default value to the actual schema, in addition to any existing rows. Would be great if South had some kind of setting to generate this parameter as True by default, but no such luck. You will need to make this edit every time.

like image 67
galarant Avatar answered Nov 07 '22 14:11

galarant


This is not a bug, in South or elsewhere.

I think you are confused about how default values work in Django generally. Django does not set default values in the database schema. It applies them directly in Python, when a new instance is created. You can verify this by doing manage.py sqlall and see that the generated SQL does not contain default attributes.

like image 21
Daniel Roseman Avatar answered Nov 07 '22 15:11

Daniel Roseman


As mentioned in earlier answers, the default mechanism in django is implemented in the model class, and is not relevant to south migrations.

Also, since south 0.8, the keep_default flag is deprecated, and won't add the default value to your model.

What I do to solve this is writing a custom migration to add the default value. You can do that by creating a separate data migration:

./manage.py datamigration your_app_name migration_name

and add the following line to the forwards function:

orm.YourModel.objects.update(field_name = DEFAULT_VALUE)

Alternatively, instead of creating a new migration, you can modify your original migration:

  1. add no_dry_run = True to the class itself (so you will have access to the ORM).
  2. add orm.YourModel.objects.update(field_name = DEFAULT_VALUE) to the end of the forwards function.

This way you don't have to write a backwards migration, because you already have the original delete-column one.

like image 2
Tzach Avatar answered Nov 07 '22 13:11

Tzach