rocky experience adding a foreign key

5 views
Skip to first unread message

ptone

unread,
Dec 3, 2009, 5:20:24 PM12/3/09
to South Users
So I'm relatively new to django and have been really happy to have
South integrated into my dev workflow from early on. But I hit and
worked through a snag that I'm not sure whether is a bug or just a
place to have a suggestive error.

In one migration I added a new model (B), and in another model(A), a
foreign key to model B.

run migrate

raise ValueError("You cannot add a null=False column without a
default value.")

makes sense - can't add a non-null column (FK) to existing rows
without a default.

Its early here, so I just nuke the DB, and syncdb and migrate from
scratch.

Same error - seems like a blank DB shouldn't have that issue, don't
know if its possible to check for rows and add the column if there are
none.

I read the docs about keep_default, but all the examples use
model.fields (pre orm freezing?)

So I figure I need to modify the frozen ORM to add a default (I used
0)

try to migrate.

Now I get an error that the table already exists - so first suggestion
from this experience. If a migration hits an error anywhere in its
forward method - it should try to run its backwards method to make a
given migration as all or nothing as possible.

so I drop the table in dbshell and run migrate and it all seems to
work

So for such a small change to models.py - south didn't save me time in
this case ;-) (however the overall time saved by south is still waaaay
in positive territory)

how to improve?

update docs on keep_default to mention frozen ORM
start an FAQ and have an item about adding FK fields
be less strict about non-null columns when there are no rows
??

OK, back to the actual project....

-Preston


Andrew Godwin

unread,
Dec 4, 2009, 4:49:37 AM12/4/09
to south...@googlegroups.com
So, defaults have always been an issue. While PostgreSQL, for example,
lets you add a NOT NULL column with no defaults to an empty table, the
same is not true of some other DBMSen - I think SQLite is among them.

Modifying the frozen ORM is also the wrong way; what you're supposed to
do (and what is entirely non-obvious) is copy the field definition over
the orm['app.model:field'] call and add default=foo there.

This is obviously awful, so the current plan for 0.7 (some of which is
already done, some of which is coming) is:

- Startmigration once again uses proper field definitions (e.g.
models.CharField(max_length=500)) in the migrations files it creates
- More importantly, startmigration will detect when you're adding a NOT
NULL field and prompt you for what default you wish to give it (if none
is defined on the model) - I make this same mistake all the time, and
it's annoying me.

This should solve this issue in general, hopefully, and make it more
obvious where to proceed. I'll hopefully have a working beta of 0.7 in
the new year.

Andrew
> --
>
> You received this message because you are subscribed to the Google Groups "South Users" group.
> To post to this group, send email to south...@googlegroups.com.
> To unsubscribe from this group, send email to south-users...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/south-users?hl=en.
>
>

ptone

unread,
Dec 7, 2009, 3:17:45 AM12/7/09
to South Users
What do you think of the idea of running the backwards migration if an
error is encountered in the forwards? I could easily be missing
something, but I can't think of why that would be bad and it would
prevent errors from existing tables when a forward gets partially
completed.

Glad to hear of the planned progress on defaults.

-Preston

Andrew Godwin

unread,
Dec 7, 2009, 5:52:46 AM12/7/09
to south...@googlegroups.com
Because if the forward failed halfway through, the backwards one won't
have the schema it thinks it's going to start off with - for example, if
forwards() was supposed to create two tables but only created one and
then errored, backwards() will try to delete the non-existent table, and
then error.

If you want failed migrations to roll back when they fail, use
PostgreSQL; it's only MySQL that has this problem, since it has rubbish
transaction support.

Andrew
Reply all
Reply to author
Forward
0 new messages