Opened 5 years ago

Closed 5 years ago

#12311 closed (invalid)

Fixtures and multi-table inheritance : loading data from one fixture to related tables doesn't work

Reported by: zimnyx 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

As an example I created sample application called "animal":

mypro/animal/models.py

from django.db import models
class Animal(models.Model):
    name = models.CharField(max_length=30, primary_key=True)
class Cat(Animal):
    tail_length = models.DecimalField(max_digits=4, decimal_places=2)

mypro/animal/fixtures/initial_data.yaml

- model: animal.Cat
  pk: 1
  fields:
    name: Sonny
    tail_length: 10

Lets try to load the fixture:

python manage.py syncdb
Creating table animal_animal
Creating table animal_cat
Installing yaml fixture 'initial_data' from '/tmp/mypro/../mypro/animal/fixtures'.
Installed 1 object(s) from 1 fixture(s)

And these are queries run in MySQL:

091204 11:07:02	   80 Connect	root@localhost on animal
		   80 Query	SET NAMES utf8
		   80 Query	set autocommit=0
		   80 Query	SHOW TABLES
		   80 Query	CREATE TABLE `animal_animal` (
    `name` varchar(30) NOT NULL PRIMARY KEY
)
		   80 Query	CREATE TABLE `animal_cat` (
    `animal_ptr_id` varchar(30) NOT NULL PRIMARY KEY,
    `tail_length` numeric(4, 2) NOT NULL
)
		   80 Query	ALTER TABLE `animal_cat` ADD CONSTRAINT `animal_ptr_id_refs_name_372b1522` FOREIGN KEY (`animal_ptr_id`) REFERENCES `animal_animal` (`name`)
		   80 Query	commit
		   80 Query	commit
		   80 Query	SELECT (1) AS `a` FROM `animal_cat` WHERE `animal_cat`.`animal_ptr_id` = 1
		   80 Query	INSERT INTO `animal_cat` (`animal_ptr_id`, `tail_length`) VALUES (1, '10.00')
		   80 Query	commit
		   80 Quit	

As you see Django hasn't done INSERT into parent table: animal_animal!

And why such fixture does not work, while specifying model data in following way works:

python manage.py shell
In [1]: from animal import models
In [2]: c = models.Cat()
In [3]: c.name = 'Sonny'
In [4]: c.tail_length = 10
In [5]: c.save()
In [6]:

Database queries were like this:

091204 10:48:21	  772 Connect	root@localhost on animal
		  772 Query	SET storage_engine=INNODB
		  772 Query	SET NAMES utf8
		  772 Query	SET SESSION sql_mode='TRADITIONAL'
		  772 Query	set autocommit=0
		  772 Query	SELECT (1) AS `a` FROM `animal_animal` WHERE `animal_animal`.`name` = 'Kittie'
		  772 Query	INSERT INTO `animal_animal` (`name`) VALUES ('Kittie')
		  772 Query	commit
		  772 Query	SELECT (1) AS `a` FROM `animal_cat` WHERE `animal_cat`.`animal_ptr_id` = 'Kittie'
		  772 Query	INSERT INTO `animal_cat` (`animal_ptr_id`, `tail_length`) VALUES ('Kittie', '31.00')
		  772 Query	commit

As you see, Django now knows that it should do insert to parent table first, and then to child table.

If django figured out how to insert data for model created via
manage.py shell, it should easily does the same when loading fixtures
but now it just fails.
Support for such fixtures would save our time, and simplify writing fixtures for such multi-table inheritance models.

Change History (1)

comment:1 Changed 5 years ago by russellm

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

That's not how fixtures work. While I accept that it would be convenient to be able to deserialize a Cat and get the underlying Animal instance, it opens up a nest of vipers around determining whether the parent instance already exists. This problem doesn't exist when you're saving a single instance by itself - it only becomes a problem with complex fixtures.

The right way to serialize a Cat is to include both the Cat and the Animal in the fixture - that is, to literally mirror what is in the database table.

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