#24997: Allow bulk_create with proxy inheritance
-------------------------------------+-------------------------------------
     Reporter:  wkschwartz           |      Owner:  nobody
         Type:  Bug                  |     Status:  new
    Component:  Database layer       |    Version:  1.8
  (models, ORM)                      |   Keywords:  proxy, inheritance,
     Severity:  Normal               |  bulk_create, queryset, insert
 Triage Stage:  Unreviewed           |  Has patch:  0
Easy pickings:  0                    |      UI/UX:  0
-------------------------------------+-------------------------------------
 = Overview =

 The [https://docs.djangoproject.com/en/1.8/ref/models/querysets/#bulk-
 create documentation] for the Queryset method `bulk_create` states:

  It does not work with child models in a multi-table inheritance scenario.

 `bulk_create` also does not work with proxy inheritance, which does not
 involve multiple tables. Rather than changing the documentation to reflect
 that neither type of inheritance is supported, it would be better to allow
 proxy inheritance to work.

 = Origin of the problem =

 It makes sense not to allow `bulk_create` with multiple tables. The
 
[https://github.com/django/django/blob/04e8d890aec8e996d568565555164a27a6a76057/django/db/models/query.py#L416
 comment] in `bulk_create` explains it best:

  So this case is fun. When you bulk insert you don't get the primary keys
 back (if it's an autoincrement), so you can't insert into the child tables
 which references this. There are two workarounds, 1) this could be
 implemented if you didn't have an autoincrement pk, and 2) you could do it
 by doing O(n) normal inserts into the parent tables to get the primary
 keys back, and then doing a single bulk insert into the childmost table.
 Some databases might allow doing this by using RETURNING clause for the
 insert query. We're punting on these for now because they are relatively
 rare cases.

 `bulk_create` prevents inherited models from using it with
 
[https://github.com/django/django/blob/04e8d890aec8e996d568565555164a27a6a76057/django/db/models/query.py#L426
 the following naive test]:

 {{{#!python
 if self.model._meta.parents:
     raise ValueError("Can't bulk create an inherited model")
 }}}

 This test does not discriminate between multi-table inheritance and proxy
 inheritance. However, simply modifying the code to read

 {{{#!python
 if self.model._meta.parents and not self.model._meta.proxy:
     raise ValueError("Can't bulk create an inherited model with multiple
 tables")
 }}}

 does not fix the problem because it results in more exceptions.

 = Reproducing this error =
 I did the following using Django 1.8.2 and Python 3.4.3 on Mac OS 10.9.5.

 {{{#!bash
 django-admin startproject bulk_create_proxy
 cd bulk_create_proxy
 ./manage.py startapp app
 }}}

 Add `'app'` to the `INSTALLED_APPS` list in the resultant `settings.py`.
 Save the following code in `app/models.py`.

 {{{#!python
 from django.db import models

 class Parent(models.Model):
     name = models.CharField(max_length=10)

 class Child(Parent):
     class Meta(object):
         proxy = True
 }}}

 Save the following code in `app/tests.py`.

 {{{#!python
 from django.test import TestCase

 from app.models import Parent, Child


 class TestBulkCreate(TestCase):

     def assert_can_bulk_create(self, model):
         model.objects.all().delete()
         model.objects.bulk_create([
             model(name='a'), model(name='b'), model(name='c')])
         self.assertEqual(model.objects.count(), 3)

     def test_bulk_create_parent(self):
         self.assert_can_bulk_create(Parent)

     def test_bulk_create_child(self):
         self.assert_can_bulk_create(Child)
 }}}

 Finish up with the following commands.

 {{{#!bash
 ./manage.py makemigrations app
 ./manage.py migrate
 ./manage.py test
 }}}

 You should get the following test output. (I have elided file paths; ones
 starting with `...django` come from Django.)

 {{{
 bulk_create_proxy ➜ ./manage.py test
 Creating test database for alias 'default'...
 E.
 ======================================================================
 ERROR: test_bulk_create_child (app.tests.TestBulkCreate)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "...bulk_create_proxy/app/tests.py", line 18, in
 test_bulk_create_child
     self.assert_can_bulk_create(Child)
   File "...bulk_create_proxy/app/tests.py", line 11, in
 assert_can_bulk_create
     model(name='a'), model(name='b'), model(name='c')])
   File "...django/db/models/manager.py", line 127, in manager_method
     return getattr(self.get_queryset(), name)(*args, **kwargs)
   File "...django/db/models/query.py", line 374, in bulk_create
     raise ValueError("Can't bulk create an inherited model")
 ValueError: Can't bulk create an inherited model

 ----------------------------------------------------------------------
 Ran 2 tests in 0.003s

 FAILED (errors=1)
 }}}

--
Ticket URL: <https://code.djangoproject.com/ticket/24997>
Django <https://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 unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/053.298d8ee2296b91c8f0496cfaf0ffe5f3%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to