Code

Opened 2 years ago

Closed 2 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

Description

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:

Presave:
    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)

inheritance_save.zip (12.1 KB) - added by magon@… 2 years ago.
The test case I have used

Download all attachments as: .zip

Change History (3)

Changed 2 years ago by magon@…

The test case I have used

comment:1 Changed 2 years ago by akaariai

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

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 2 years ago by magon@…

  • Needs documentation set
  • Resolution set to duplicate
  • Status changed from new to closed

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

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.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.