Skip to content
Advertisement

How to work around Django’s lack of composite keys in this complicated case?

I can’t seem to figure out how to work around the lack of composite keys in Django for the following case. I’m going to write the schema I’d like using SQLite3 dialect.

(The only thing below that might be unfamiliar to someone who knows SQL but not sqlite is sqlite’s “WITHOUT ROWID” clause. By default, sqlite adds a hidden integer auto-incrementing “rowid” column on tables. Using the “WITHOUT ROWID” command turns that off.)

Tables AB and BC have compound key constraints that are easily worked around using surrogate keys but table ABC has complex key constraints and cannot be implemented directly in Django.

Here’s some test data

The obvious first steps to try getting it to work Django is to use surrogate keys. The hidden “rowid” column seems like a natural choice but these columns cannot be used as a foreign key in sqlite, where foreign keys must map to declared columns. However, there is a workaround, “INTEGER PRIMARY KEY AUTOINCREMENT” is a special alias in sqlite that will cause a named column to refer to “rowid”. So that’s what we will try.

Similar questions on StackOverflow about Django and compound keys mention to use NOT NULL and UNIQUE constraints so we can do that too:

As indicated table ABC is under-constrained. Here’s the same test data to prove it (NULL is used on insert for the auto-increment columns):

Is the only option to use before insert triggers to test for values that would otherwise slip through? Using Django constraints, I’ve found no way to reference tables AB and BC in the constraint checks.

Advertisement

Answer

I’m not sure about SQL part, however. Instead of Foreign Key to A, B, and C model, I suggest you foreign key to table AB and BC instead.

so your model is something like

But the catch here is, everytime you want to create object of ABC you have to get AB and BC first:

That way, if AB doesn’t have combination of (a,b) or BC doesn’t have combination of (b,c) it will raise an Error.

Edit:

Doing it this way however, make INSERT INTO ABC VALUES (1,2,3,'should fail'); Not doable, since you need AB and BC instead of A,B,C value. If you still want to use values of A,B,C to create ABC:

I guess the other way is to override save() method.

So it checks whether AB and BC objects are present first before creating.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement