Opened 15 years ago
Closed 15 years ago
#12311 closed (invalid)
Fixtures and multi-table inheritance : loading data from one fixture to related tables doesn't work
Reported by: | Piotr Czachur | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Keywords: | ||
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
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.
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.