Opened 5 years ago

Closed 5 years ago

#18252 closed Bug (duplicate)

Multi-table inheritance + ForeignKey to parent => corrupted save

Reported by: magon@… Owned by: nobody
Component: Database layer (models, ORM) Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: yes
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


I have a models like in this minimal test case:

class A(models.Model):
    title = models.CharField(max_length=100)

    def __str__(self):
        return "Title(%s)" % self.title

class B(models.Model):
    more = models.CharField(max_length=100)

    def __str__(self):
        return "More(%s)" % (self.more)

#class C(B, A):
class C(A, B):
    def __str__(self):
        return "%s %s %s" % (self.problem, self.title, self.more)
    problem = models.ForeignKey(B, related_name='problem')

Saving "C" is corrupted in this case.
It is best ilustrated here:

    More(more 1) title more 2
Retrieved afted save:
    More(more 2) title more 2

It should output the same two lines, but the the second is different.
Here are the queries performed:

{'time': '0.000', 'sql': u'INSERT INTO "save_b" ("more") VALUES (more 1)'}
{'time': '0.000', 'sql': u'INSERT INTO "save_a" ("title") VALUES (title)'}
{'time': '0.000', 'sql': u'SELECT (1) AS "a" FROM "save_b" WHERE "save_b"."id" = 1  LIMIT 1'}
{'time': '0.000', 'sql': u'UPDATE "save_b" SET "more" = more 2 WHERE "save_b"."id" = 1 '}
{'time': '0.000', 'sql': u'SELECT (1) AS "a" FROM "save_c" WHERE "save_c"."a_ptr_id" = 1  LIMIT 1'}
{'time': '0.000', 'sql': u'INSERT INTO "save_c" ("b_ptr_id", "a_ptr_id", "problem_id") SELECT 1 AS "b_ptr_id", 1 AS "a_ptr_id", 1 AS "problem_id"'}
{'time': '0.000', 'sql': u'SELECT "save_a"."id", "save_a"."title", "save_b"."id", "save_b"."more", "save_c"."b_ptr_id", "save_c"."a_ptr_id", "save_c"."problem_id" FROM "save_c" INNER JOIN "save_b" ON ("save_c"."b_ptr_id" = "save_b"."id") INNER JOIN "save_a" ON ("save_c"."a_ptr_id" = "save_a"."id") WHERE "save_c"."b_ptr_id" = 1 '}
{'time': '0.000', 'sql': u'SELECT "save_b"."id", "save_b"."more" FROM "save_b" WHERE "save_b"."id" = 1 '}

The problem is in the "UPDATE" there. It should be an insert. It seems like it have set "pk" after the first line, but it should be unset.

Interchanging the commented line with the one below solves the problem. But for another reasons I need them this way, not the other way around.

Attachments (1) (12.1 KB) - added by magon@… 5 years ago.
The test case I have used

Download all attachments as: .zip

Change History (3)

Changed 5 years ago by magon@…

Attachment: added

The test case I have used

comment:1 Changed 5 years ago by Anssi Kääriäinen

You are going to face problems with your model definition. You have two "id" fields in your C model, one from A, and one from B. The fix is to forbid this behavior - it doesn't work currently, and there is no even remotely easy way to make it working (#12002, #16733, #17673). Can you check if adding

    a_id = models.AutoField(db_column='id')

for model A, and similar 'b_id' field for model B solves your problem.

In general you might face interesting problems with your model definition. The C model has effectively two different primary key fields and this is going to cause problems.

comment:2 Changed 5 years ago by magon@…

Needs documentation: set
Resolution: duplicate
Status: newclosed

Thank for pointing me to the right tickets. I was unable to find something similar.

a_id = models.AutoField(db_column='id', primary_key=True)

in either class "A" or class "B" (or both) solves the problem.

As suggested in the other tickets, I vote for not validating this models (without the autoFields). It seems that fixing it inside core is troublesome.

Making a note in multi-table inheritance documentation would be great as it really causes data loss.

Marking duplicate for #12002.

Note: See TracTickets for help on using tickets.
Back to Top