Opened 7 years ago

Closed 7 years ago

#7367 closed (invalid)

Cannot have non-primary-key OneToOneField to parent class

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

Description

I am using multi-table inheritance in the latest version from SVN (r7573), and I want a child class to have two OneToOneFields: one to the parent class as the primary-key parent link (the default created one with a primary key), and another to the parent class representing a different relation (which is not a primary key).

For example, a Restaurant is a Place, so it has a parent link to Place, but it also has one kitchen (which could be a place):

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_pizza = models.BooleanField()
    kitchen = models.OneToOneField(Place, related_name='restaurant_i_am_the_kitchen_of')

This shouldn't make kitchen_id a primary key, but it does (even if I set primary_key=False explicitly). It generates the following SQL:

BEGIN;
CREATE TABLE `cms_place` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `name` varchar(50) NOT NULL,
    `address` varchar(80) NOT NULL
)
;
CREATE TABLE `cms_restaurant` (
    `serves_pizza` bool NOT NULL,
    `kitchen_id` integer NOT NULL UNIQUE PRIMARY KEY
)
;
ALTER TABLE `cms_restaurant` ADD CONSTRAINT kitchen_id_refs_id_c2a98e0 FOREIGN KEY (`kitchen_id`) REFERENCES `cms_place` (`id`);
COMMIT;

This behavior of making every OneToOneField that goes to a parent class a primary key happens in django/db/models/base.py on line 98-101:

if base in o2o_map:
    field = o2o_map[base]
    field.primary_key = True
    new_class._meta.setup_pk(field)

If I disable this and force it go to to the else clause (by making it 'if False'), then I get the desired SQL:

BEGIN;
CREATE TABLE `cms_place` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `name` varchar(50) NOT NULL,
    `address` varchar(80) NOT NULL
)
;
CREATE TABLE `cms_restaurant` (
    `place_ptr_id` integer NOT NULL UNIQUE PRIMARY KEY,
    `serves_pizza` bool NOT NULL,
    `kitchen_id` integer NOT NULL UNIQUE
)
;
ALTER TABLE `cms_restaurant` ADD CONSTRAINT place_ptr_id_refs_id_c2a98e0 FOREIGN KEY (`place_ptr_id`) REFERENCES `cms_place` (`id`);
ALTER TABLE `cms_restaurant` ADD CONSTRAINT kitchen_id_refs_id_c2a98e0 FOREIGN KEY (`kitchen_id`) REFERENCES `cms_place` (`id`);
COMMIT;

It seems that lines 98-101 shouldn't just check that the base class is in the o2o_map, but it should also check that the OneToOneField has parent_link set to True.

Change History (4)

comment:1 Changed 7 years ago by jbronn

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to invalid
  • Status changed from new to closed

Invalid: By definition multiple one-to-one fields no longer makes it a one-to-one relationship.

comment:2 Changed 7 years ago by jacob

That is, OneToOneField really just represents a "is-a" relationship. For other kinds of relationships you should use the more generic ForeignKey. In this case, ForeignKey(unique=True).

comment:3 Changed 7 years ago by pkenjora@…

  • Resolution invalid deleted
  • Status changed from closed to reopened

What about a mutable object that is sometimes A and sometimes B, maybe even sometimes both.

For example:

Tip (table describing tipping rules) applies to a Person (1-1).
Tip (same rules) applies to Restaurant (1-1).

I want to describe the tipping rules for a Person or a specific Restaurant (or a combination of the two). Each case is a (1-1). A foreign key would complicate things here and not reflect the true meaning of Tip. Tip is always 1-1 with at least one other table, that table can vary though.

This is a very common approach for adding properties to objects. Please provide an alternate or consider allowing non-primary key 1-1 tables.

comment:4 Changed 7 years ago by emulbreh

  • Resolution set to invalid
  • Status changed from reopened to closed
Note: See TracTickets for help on using tickets.
Back to Top