#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.

Reply via email to