Ticket #17788: ticket_17788.diff
File ticket_17788.diff, 13.1 KB (added by , 13 years ago) |
---|
-
django/db/backends/__init__.py
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index a85e7b9..8ca0fed 100644
a b class BaseDatabaseOperations(object): 469 469 This SQL is executed when a table is created. 470 470 """ 471 471 return None 472 473 def bulk_batch_size(self, fields, objs): 474 """ 475 Returns the maximum allowed batch size for the backend. The fields 476 are the fields we will be inserting in the batch, the objs contains 477 all the objects to be inserted. 478 479 The default return value of None means unlimited. This method must 480 always return a valuer greater than 0. 481 """ 482 return None 472 483 473 484 def date_extract_sql(self, lookup_type, field_name): 474 485 """ … … class BaseDatabaseOperations(object): 506 517 during a CREATE TABLE statement. 507 518 """ 508 519 return '' 520 521 def distinct_sql(self, fields): 522 """ 523 Returns an SQL DISTINCT clause which removes duplicate rows from the 524 result set. If any fields are given, only the given fields are being 525 checked for duplicates. 526 """ 527 if fields: 528 raise NotImplementedError('DISTINCT ON fields is not supported by this database backend') 529 else: 530 return 'DISTINCT' 509 531 510 532 def drop_foreignkey_sql(self): 511 533 """ … … class BaseDatabaseOperations(object): 562 584 """ 563 585 raise NotImplementedError('Full-text search is not implemented for this database backend') 564 586 565 def distinct_sql(self, fields):566 """567 Returns an SQL DISTINCT clause which removes duplicate rows from the568 result set. If any fields are given, only the given fields are being569 checked for duplicates.570 """571 if fields:572 raise NotImplementedError('DISTINCT ON fields is not supported by this database backend')573 else:574 return 'DISTINCT'575 576 587 def last_executed_query(self, cursor, sql, params): 577 588 """ 578 589 Returns a string of the query last executed by the given cursor, with -
django/db/backends/sqlite3/base.py
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 8f883e2..e4e5c52 100644
a b class DatabaseFeatures(BaseDatabaseFeatures): 83 83 supports_1000_query_parameters = False 84 84 supports_mixed_date_datetime_comparisons = False 85 85 has_bulk_insert = True 86 can_combine_inserts_with_and_without_auto_increment_pk = True86 can_combine_inserts_with_and_without_auto_increment_pk = False 87 87 88 88 def _supports_stddev(self): 89 89 """Confirm support for STDDEV and related stats functions … … class DatabaseFeatures(BaseDatabaseFeatures): 104 104 return has_support 105 105 106 106 class DatabaseOperations(BaseDatabaseOperations): 107 def bulk_batch_size(self, fields, objs): 108 """ 109 SQLite has a limit of 1000 variables per query. 110 """ 111 return max(999 / len(fields), 1) 112 107 113 def date_extract_sql(self, lookup_type, field_name): 108 114 # sqlite doesn't support extract, so we fake it with the user-defined 109 115 # function django_extract that's registered in connect(). Note that -
django/db/models/query.py
diff --git a/django/db/models/query.py b/django/db/models/query.py index 3d4fd17..5fa3fbc 100644
a b class QuerySet(object): 382 382 obj.save(force_insert=True, using=self.db) 383 383 return obj 384 384 385 def bulk_create(self, objs ):385 def bulk_create(self, objs, batch_size=None): 386 386 """ 387 387 Inserts each of the instances into the database. This does *not* call 388 388 save() on each of the instances, does not send any pre/post save … … class QuerySet(object): 395 395 # this could be implemented if you didn't have an autoincrement pk, 396 396 # and 2) you could do it by doing O(n) normal inserts into the parent 397 397 # tables to get the primary keys back, and then doing a single bulk 398 # insert into the childmost table. We're punting on these for now 399 # because they are relatively rare cases. 398 # insert into the childmost table. Some databases might allow doing 399 # this by using RETURNING clause for the insert query. We're punting 400 # on these for now because they are relatively rare cases. 401 assert batch_size is None or batch_size > 0 400 402 if self.model._meta.parents: 401 403 raise ValueError("Can't bulk create an inherited model") 402 404 if not objs: … … class QuerySet(object): 412 414 try: 413 415 if (connection.features.can_combine_inserts_with_and_without_auto_increment_pk 414 416 and self.model._meta.has_auto_field): 415 self. model._base_manager._insert(objs, fields=fields, using=self.db)417 self._batched_insert(objs, fields, batch_size) 416 418 else: 417 419 objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs) 418 420 if objs_with_pk: 419 self. model._base_manager._insert(objs_with_pk, fields=fields, using=self.db)421 self._batched_insert(objs_with_pk, fields, batch_size) 420 422 if objs_without_pk: 421 self.model._base_manager._insert(objs_without_pk, fields=[f for f in fields if not isinstance(f, AutoField)], using=self.db) 423 fields= [f for f in fields if not isinstance(f, AutoField)] 424 self._batched_insert(objs_without_pk, fields, batch_size) 422 425 if forced_managed: 423 426 transaction.commit(using=self.db) 424 427 else: … … class QuerySet(object): 854 857 ################### 855 858 # PRIVATE METHODS # 856 859 ################### 860 def _batched_insert(self, objs, fields, def_batch_size): 861 """ 862 A little helper method for bulk_insert to insert the bulk one batch 863 at a time. Inserts recursively a batch from the front of the bulk and 864 then _batched_insert() the remaining objects again. 865 """ 866 if not objs: 867 return 868 connection = connections[self.db] 869 batch_size = (def_batch_size 870 or connection.ops.bulk_batch_size(fields, objs)) 871 batch, remaining = ((objs[0:batch_size], objs[batch_size:]) 872 if batch_size else (objs, [])) 873 self.model._base_manager._insert(batch, fields=fields, 874 using=self.db) 875 self._batched_insert(remaining, fields, def_batch_size) 857 876 858 877 def _clone(self, klass=None, setup=False, **kwargs): 859 878 if klass is None: -
docs/ref/models/querysets.txt
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index b20c6e3..991e0db 100644
a b has a side effect on your data. For more, see `Safe methods`_ in the HTTP spec. 1349 1349 bulk_create 1350 1350 ~~~~~~~~~~~ 1351 1351 1352 .. method:: bulk_create(objs )1352 .. method:: bulk_create(objs, batch_size=None) 1353 1353 1354 1354 .. versionadded:: 1.4 1355 1355 … … This has a number of caveats though: 1371 1371 * If the model's primary key is an :class:`~django.db.models.AutoField` it 1372 1372 does not retrieve and set the primary key attribute, as ``save()`` does. 1373 1373 1374 .. admonition:: Limits of SQLite 1374 The ``batch_size`` parameter controls how many objects are created in single 1375 query. The default is to create all objects in one batch, except for SQLite 1376 where the default is such that at maximum 999 variables per query is used. 1375 1377 1376 SQLite sets a limit on the number of parameters per SQL statement. The 1377 maximum is defined by the SQLITE_MAX_VARIABLE_NUMBER_ compilation option, 1378 which defaults to 999. For instance, if your model has 8 fields (including 1379 the primary key), you cannot create more than 999 // 8 = 124 instances at 1380 a time. If you exceed this limit, you'll get an exception:: 1381 1382 django.db.utils.DatabaseError: too many SQL variables 1383 1384 If your application's performance requirements exceed SQLite's limits, you 1385 should switch to another database engine, such as PostgreSQL. 1386 1387 .. _SQLITE_MAX_VARIABLE_NUMBER: http://sqlite.org/limits.html#max_variable_number 1378 .. versionadded:: 1.5 1379 The ``batch_size`` parameter was added in version 1.5. 1388 1380 1389 1381 count 1390 1382 ~~~~~ -
docs/releases/1.5.txt
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 4d9572d..0466867 100644
a b Django 1.5 also includes several smaller improvements worth noting: 41 41 * The template engine now interprets ``True``, ``False`` and ``None`` as the 42 42 corresponding Python objects. 43 43 44 * :meth:`QuerySet.bulk_create() 45 <django.db.models.query.QuerySet.bulk_create>` has now a batch_size 46 argument. By default the batch_size is unlimited except for SQLite where 47 single batch is limited so that 1000 parameters per query isn't exceeded. 48 44 49 Backwards incompatible changes in 1.5 45 50 ===================================== 46 51 -
tests/regressiontests/bulk_create/models.py
diff --git a/tests/regressiontests/bulk_create/models.py b/tests/regressiontests/bulk_create/models.py index a4c611d..bc685bb 100644
a b class Pizzeria(Restaurant): 18 18 pass 19 19 20 20 class State(models.Model): 21 two_letter_code = models.CharField(max_length=2, primary_key=True) 22 No newline at end of file 21 two_letter_code = models.CharField(max_length=2, primary_key=True) 22 23 class TwoFields(models.Model): 24 f1 = models.IntegerField(unique=True) 25 f2 = models.IntegerField(unique=True) -
tests/regressiontests/bulk_create/tests.py
diff --git a/tests/regressiontests/bulk_create/tests.py b/tests/regressiontests/bulk_create/tests.py index 0b55f63..e332335 100644
a b from __future__ import absolute_import 2 2 3 3 from operator import attrgetter 4 4 5 from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature 5 from django.db import connection 6 from django.test import TestCase, skipIfDBFeature 7 from django.test.utils import override_settings 6 8 7 from .models import Country, Restaurant, Pizzeria, State 9 from .models import Country, Restaurant, Pizzeria, State, TwoFields 8 10 9 11 10 12 class BulkCreateTests(TestCase): … … class BulkCreateTests(TestCase): 27 29 self.assertEqual(created, []) 28 30 self.assertEqual(Country.objects.count(), 4) 29 31 30 @skipUnlessDBFeature("has_bulk_insert")31 32 def test_efficiency(self): 32 33 with self.assertNumQueries(1): 33 34 Country.objects.bulk_create(self.data) … … class BulkCreateTests(TestCase): 69 70 invalid_country = Country(id=0, name='Poland', iso_two_letter='PL') 70 71 with self.assertRaises(ValueError): 71 72 Country.objects.bulk_create([valid_country, invalid_country]) 73 74 def test_large_batch(self): 75 with override_settings(DEBUG=True): 76 connection.queries = [] 77 TwoFields.objects.bulk_create([ 78 TwoFields(f1=i, f2=i+1) for i in range(0, 1001) 79 ]) 80 self.assertTrue(len(connection.queries) < 10) 81 self.assertEqual(TwoFields.objects.count(), 1001) 82 self.assertEqual( 83 TwoFields.objects.filter(f1__gte=450, f1__lte=550).count(), 84 101) 85 self.assertEqual(TwoFields.objects.filter(f2__gte=901).count(), 101) 86 87 def test_large_batch_mixed(self): 88 """ 89 Test inserting a large batch with objects having primary key set 90 mixed together with objects without PK set. 91 """ 92 with override_settings(DEBUG=True): 93 connection.queries = [] 94 TwoFields.objects.bulk_create([ 95 TwoFields(id=i if i % 2 == 0 else None, f1=i, f2=i+1) 96 for i in range(100000, 101000)]) 97 self.assertTrue(len(connection.queries) < 10) 98 self.assertEqual(TwoFields.objects.count(), 1000) 99 # We can't assume much about the ID's created, except that the above 100 # created IDs must exists. 101 id_range = range(100000, 101000, 2) 102 self.assertEqual(TwoFields.objects.filter(id__in=id_range).count(), 500) 103 104 def test_explicit_batch_size(self): 105 objs = [TwoFields(f1=i, f2=i) for i in range(0, 100)] 106 with self.assertNumQueries(2): 107 TwoFields.objects.bulk_create(objs, 50) 108 TwoFields.objects.all().delete() 109 with self.assertNumQueries(1): 110 TwoFields.objects.bulk_create(objs, len(objs)) -
tests/regressiontests/queries/tests.py
diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py index ded3e8f..ed71be8 100644
a b class ConditionalTests(BaseQuerysetTest): 1807 1807 # Test that the "in" lookup works with lists of 1000 items or more. 1808 1808 Number.objects.all().delete() 1809 1809 numbers = range(2500) 1810 for num in numbers: 1811 _ = Number.objects.create(num=num) 1810 Number.objects.bulk_create(Number(num=num) for num in numbers) 1812 1811 self.assertEqual( 1813 1812 Number.objects.filter(num__in=numbers[:1000]).count(), 1814 1813 1000