#13987: Primary key not set correctly for concrete->abstract->concrete model inheritance. ------------------------------------------------+--------------------------- Reporter: Aramgutang | Owner: nobody Status: new | Milestone: 1.3 Component: Database layer (models, ORM) | Version: 1.2 Keywords: inheritance, abstract, primary key | Stage: Unreviewed Has_patch: 1 | ------------------------------------------------+--------------------------- When a concrete model inherits from an abstract model which in turn inherits from a concrete model, the "primary_key" attribute on the {{{OneToOneField}}} in the child model is not set, and the SQL definition for that model does not define a primary key. However, any subsequent children inheriting from the abstract class will have a correctly set primary key. For example:
{{{ class ConcreteParent(models.Model): concrete_field = models.TextField() class AbstractParent(ConcreteParent): abstract_field = models.TextField() class Meta: abstract = True class FirstChild(AbstractParent): child_field = models.TextField() class SecondChild(AbstractParent): child_field = models.TextField() }}} The output of "sqlall" for these definitions is: {{{ CREATE TABLE "testapp_concreteparent" ( "id" serial NOT NULL PRIMARY KEY, "concrete_field" text NOT NULL ); CREATE TABLE "testapp_firstchild" ( "concreteparent_ptr_id" integer NOT NULL UNIQUE REFERENCES "testapp_concreteparent" ("id") DEFERRABLE INITIALLY DEFERRED, "abstract_field" text NOT NULL, "child_field" text NOT NULL ); CREATE TABLE "testapp_secondchild" ( "concreteparent_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "testapp_concreteparent" ("id") DEFERRABLE INITIALLY DEFERRED, "abstract_field" text NOT NULL, "child_field" text NOT NULL ); }}} Note the absence of "PRIMARY KEY" in the first child and its presence in the second child. We can also go in the shell to see what's happening: {{{ >>> FirstChild._meta.get_field_by_name('concreteparent_ptr')[0].primary_key False >>> SecondChild._meta.get_field_by_name('concreteparent_ptr')[0].primary_key True >>> FirstChild._meta.get_field_by_name('concreteparent_ptr')[0] == FirstChild._meta.pk True >>> FirstChild._meta.get_field_by_name('concreteparent_ptr')[0] is FirstChild._meta.pk False >>> SecondChild._meta.get_field_by_name('concreteparent_ptr')[0] == SecondChild._meta.pk True >>> SecondChild._meta.get_field_by_name('concreteparent_ptr')[0] is SecondChild._meta.pk True }}} I believe what is happening is that in {{{db.models.options.py}}}, when initialising a Child object in {{{Options._prepare()}}}, the first parent link is selected to be the primary key, its primary key attribute is set to True, and it is then passed to {{{setup_pk()}}}. However, the field fetched through the parent link is the field on the {{{AbstractParent}}} model, and is thus not properly registered with the Child model. But in this process, its primary_key attribute is set to True, which allows the second Child to set it as its PK on the initial {{{add_field()}}} call. The simplest fix for this is to initialise the {{{OneToOneField}}} in {{{ModelBase.__new__()}}} with {{{primary_key=True}}}. Alternately, and perhaps preferably, {{{Options._prepare()}}} can be altered to fetch the {{{OneToOneField}}} for the Child model, rather than the {{{AbstractParent}}} model. I've attached two patches, one for each solution, and perhaps someone more qualified than me can decide which one is the better one. -- Ticket URL: <http://code.djangoproject.com/ticket/13987> Django <http://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-upda...@googlegroups.com. To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-updates?hl=en.