Ticket #7596: 7596.diff
File 7596.diff, 26.6 KB (added by , 13 years ago) |
---|
-
django/contrib/auth/management/__init__.py
diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index f82060e..532654f 100644
a b def create_permissions(app, created_models, verbosity, **kwargs): 46 46 "content_type", "codename" 47 47 )) 48 48 49 for ctype, (codename, name) in searched_perms: 50 # If the permissions exists, move on. 51 if (ctype.pk, codename) in all_perms: 52 continue 53 p = auth_app.Permission.objects.create( 54 codename=codename, 55 name=name, 56 content_type=ctype 57 ) 58 if verbosity >= 2: 59 print "Adding permission '%s'" % p 49 objs = [ 50 auth_app.Permission(codename=codename, name=name, content_type=ctype) 51 for ctype, (codename, name) in searched_perms 52 if (ctype.pk, codename) not in all_perms 53 ] 54 auth_app.Permission.objects.bulk_create(objs) 55 if verbosity >= 2: 56 for obj in objs: 57 print "Adding permission '%s'" % obj 60 58 61 59 62 60 def create_superuser(app, created_models, verbosity, **kwargs): -
django/db/backends/__init__.py
diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 23ddedb..06ce9de 100644
a b class BaseDatabaseFeatures(object): 301 301 302 302 can_use_chunked_reads = True 303 303 can_return_id_from_insert = False 304 has_bulk_insert = False 304 305 uses_autocommit = False 305 306 uses_savepoints = False 307 can_combine_inserts_with_and_without_auto_increment_pk = False 306 308 307 309 # If True, don't use integer foreign keys referring to, e.g., positive 308 310 # integer primary keys. -
django/db/backends/mysql/base.py
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 2dd9e84..b4d497b 100644
a b class DatabaseFeatures(BaseDatabaseFeatures): 124 124 allows_group_by_pk = True 125 125 related_fields_match_type = True 126 126 allow_sliced_subqueries = False 127 has_bulk_insert = True 127 128 has_select_for_update = True 128 129 has_select_for_update_nowait = False 129 130 supports_forward_references = False … … class DatabaseOperations(BaseDatabaseOperations): 263 264 def max_name_length(self): 264 265 return 64 265 266 267 def bulk_insert_sql(self, fields, num_values): 268 items_sql = "(%s)" % ", ".join(["%s"] * len(fields)) 269 return "VALUES " + ", ".join([items_sql] * num_values) 270 271 266 272 class DatabaseWrapper(BaseDatabaseWrapper): 267 273 vendor = 'mysql' 268 274 operators = { -
django/db/backends/postgresql_psycopg2/base.py
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 7efa6e6..f0a89e5 100644
a b class DatabaseFeatures(BaseDatabaseFeatures): 74 74 can_defer_constraint_checks = True 75 75 has_select_for_update = True 76 76 has_select_for_update_nowait = True 77 has_bulk_insert = True 77 78 78 79 79 80 class DatabaseWrapper(BaseDatabaseWrapper): -
django/db/backends/postgresql_psycopg2/operations.py
diff --git a/django/db/backends/postgresql_psycopg2/operations.py b/django/db/backends/postgresql_psycopg2/operations.py index d535ee3..c3a23c2 100644
a b class DatabaseOperations(BaseDatabaseOperations): 180 180 181 181 def return_insert_id(self): 182 182 return "RETURNING %s", () 183 184 def bulk_insert_sql(self, fields, num_values): 185 items_sql = "(%s)" % ", ".join(["%s"] * len(fields)) 186 return "VALUES " + ", ".join([items_sql] * num_values) -
django/db/backends/sqlite3/base.py
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index e3c12f7..b45c0fb 100644
a b class DatabaseFeatures(BaseDatabaseFeatures): 58 58 supports_unspecified_pk = True 59 59 supports_1000_query_parameters = False 60 60 supports_mixed_date_datetime_comparisons = False 61 has_bulk_insert = True 62 can_combine_inserts_with_and_without_auto_increment_pk = True 61 63 62 64 def _supports_stddev(self): 63 65 """Confirm support for STDDEV and related stats functions … … class DatabaseOperations(BaseDatabaseOperations): 106 108 return "" 107 109 108 110 def pk_default_value(self): 109 return 'NULL'111 return "NULL" 110 112 111 113 def quote_name(self, name): 112 114 if name.startswith('"') and name.endswith('"'): … … class DatabaseOperations(BaseDatabaseOperations): 154 156 # No field, or the field isn't known to be a decimal or integer 155 157 return value 156 158 159 def bulk_insert_sql(self, fields, num_values): 160 res = [] 161 res.append("SELECT %s" % ", ".join( 162 "%%s AS %s" % self.quote_name(f.column) for f in fields 163 )) 164 res.extend(["UNION SELECT %s" % ", ".join(["%s"] * len(fields))] * (num_values - 1)) 165 return " ".join(res) 166 157 167 class DatabaseWrapper(BaseDatabaseWrapper): 158 168 vendor = 'sqlite' 159 169 # SQLite requires LIKE statements to include an ESCAPE clause if the value -
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py index 71fd1f7..4b3220b 100644
a b class Model(object): 540 540 order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count() 541 541 self._order = order_value 542 542 543 fields = meta.local_fields 543 544 if not pk_set: 544 545 if force_update: 545 546 raise ValueError("Cannot force an update in save() with no primary key.") 546 values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection)) 547 for f in meta.local_fields if not isinstance(f, AutoField)] 548 else: 549 values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection)) 550 for f in meta.local_fields] 547 fields = [f for f in fields if not isinstance(f, AutoField)] 551 548 552 549 record_exists = False 553 550 554 551 update_pk = bool(meta.has_auto_field and not pk_set) 555 if values: 556 # Create a new record. 557 result = manager._insert(values, return_id=update_pk, using=using) 558 else: 559 # Create a new record with defaults for everything. 560 result = manager._insert([(meta.pk, connection.ops.pk_default_value())], return_id=update_pk, raw_values=True, using=using) 552 result = manager._insert([self], fields=fields, return_id=update_pk, using=using, raw=raw) 561 553 562 554 if update_pk: 563 555 setattr(self, meta.pk.attname, result) -
django/db/models/fields/related.py
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index d904f56..fa7b482 100644
a b class ForeignRelatedObjectsDescriptor(object): 430 430 add.alters_data = True 431 431 432 432 def create(self, **kwargs): 433 kwargs .update({rel_field.name: instance})433 kwargs[rel_field.name] = instance 434 434 db = router.db_for_write(rel_model, instance=instance) 435 435 return super(RelatedManager, self.db_manager(db)).create(**kwargs) 436 436 create.alters_data = True … … class ForeignRelatedObjectsDescriptor(object): 438 438 def get_or_create(self, **kwargs): 439 439 # Update kwargs with the related object that this 440 440 # ForeignRelatedObjectsDescriptor knows about. 441 kwargs .update({rel_field.name: instance})441 kwargs[rel_field.name] = instance 442 442 db = router.db_for_write(rel_model, instance=instance) 443 443 return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs) 444 444 get_or_create.alters_data = True … … def create_many_related_manager(superclass, rel=False): 578 578 instance=self.instance, reverse=self.reverse, 579 579 model=self.model, pk_set=new_ids, using=db) 580 580 # Add the ones that aren't there already 581 for obj_id in new_ids:582 self.through ._default_manager.using(db).create(**{581 self.through._default_manager.using(db).bulk_create([ 582 self.through(**{ 583 583 '%s_id' % source_field_name: self._pk_val, 584 584 '%s_id' % target_field_name: obj_id, 585 585 }) 586 for obj_id in new_ids 587 ]) 586 588 if self.reverse or source_field_name == self.source_field_name: 587 589 # Don't send the signal when we are inserting the 588 590 # duplicate data row for symmetrical reverse entries. … … class ReverseManyRelatedObjectsDescriptor(object): 701 703 def __init__(self, m2m_field): 702 704 self.field = m2m_field 703 705 704 def _through(self): 706 @property 707 def through(self): 705 708 # through is provided so that you have easy access to the through 706 709 # model (Book.authors.through) for inlines, etc. This is done as 707 710 # a property to ensure that the fully resolved value is returned. 708 711 return self.field.rel.through 709 through = property(_through)710 712 711 713 def __get__(self, instance, instance_type=None): 712 714 if instance is None: -
django/db/models/manager.py
diff --git a/django/db/models/manager.py b/django/db/models/manager.py index bdd86bb..baf701f 100644
a b class Manager(object): 136 136 def create(self, **kwargs): 137 137 return self.get_query_set().create(**kwargs) 138 138 139 def bulk_create(self, *args, **kwargs): 140 return self.get_query_set().bulk_create(*args, **kwargs) 141 139 142 def filter(self, *args, **kwargs): 140 143 return self.get_query_set().filter(*args, **kwargs) 141 144 … … class Manager(object): 193 196 def exists(self, *args, **kwargs): 194 197 return self.get_query_set().exists(*args, **kwargs) 195 198 196 def _insert(self, values, **kwargs):197 return insert_query(self.model, values, **kwargs)199 def _insert(self, objs, fields, **kwargs): 200 return insert_query(self.model, objs, fields, **kwargs) 198 201 199 202 def _update(self, values, **kwargs): 200 203 return self.get_query_set()._update(values, **kwargs) -
django/db/models/query.py
diff --git a/django/db/models/query.py b/django/db/models/query.py index ff5289c..14ef144 100644
a b The main QuerySet implementation. This provides the public API for the ORM. 5 5 import copy 6 6 7 7 from django.db import connections, router, transaction, IntegrityError 8 from django.db.models.fields import AutoField 8 9 from django.db.models.query_utils import (Q, select_related_descend, 9 10 deferred_class_factory, InvalidQuery) 10 11 from django.db.models.deletion import Collector … … class QuerySet(object): 352 353 obj.save(force_insert=True, using=self.db) 353 354 return obj 354 355 356 def bulk_create(self, objs): 357 """ 358 Inserts each of the instances into the database. This does *not* call 359 save() on each of the instances, does not send any pre/post save 360 signals, and does not set the primary key attribute if it is an 361 autoincrement field. 362 """ 363 # So this case is fun. When you bulk insert you don't get the primary 364 # keys back (if it's an autoincrement), so you can't insert into the 365 # child tables which references this. There are two workarounds, 1) 366 # this could be implemented if you didn't have an autoincrement pk, 367 # and 2) you could do it by doing O(n) normal inserts into the parent 368 # tables to get the primary keys back, and then doing a single bulk 369 # insert into the childmost table. We're punting on these for now 370 # because they are relatively rare cases. 371 if self.model._meta.parents: 372 raise ValueError("Can't bulk create an inherited model") 373 if not objs: 374 return 375 self._for_write = True 376 connection = connections[self.db] 377 features = connection.features 378 fields = self.model._meta.local_fields 379 if (features.can_combine_inserts_with_and_without_auto_increment_pk 380 and self.model._meta.has_auto_field): 381 self.model._base_manager._insert( 382 objs, fields=fields, using=self.db) 383 else: 384 objs_with_pk = [o for o in objs if o.pk is not None] 385 objs_without_pk = [o for o in objs if o.pk is None] 386 if objs_with_pk: 387 self.model._base_manager._insert( 388 objs_with_pk, fields=fields, using=self.db) 389 if objs_without_pk: 390 self.model._base_manager._insert( 391 objs_without_pk, 392 fields=[f for f in fields if not isinstance(f, AutoField)], 393 using=self.db) 394 355 395 def get_or_create(self, **kwargs): 356 396 """ 357 397 Looks up an object with the given kwargs, creating one if necessary. … … class RawQuerySet(object): 1437 1477 self._model_fields[converter(column)] = field 1438 1478 return self._model_fields 1439 1479 1440 def insert_query(model, values, return_id=False, raw_values=False, using=None):1480 def insert_query(model, objs, fields, return_id=False, raw=False, using=None): 1441 1481 """ 1442 1482 Inserts a new record for the given model. This provides an interface to 1443 1483 the InsertQuery class and is how Model.save() is implemented. It is not 1444 1484 part of the public API. 1445 1485 """ 1446 1486 query = sql.InsertQuery(model) 1447 query.insert_values( values, raw_values)1487 query.insert_values(fields, objs, raw=raw) 1448 1488 return query.get_compiler(using=using).execute_sql(return_id) -
django/db/models/sql/compiler.py
diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 05c19f3..bbb851d 100644
a b 1 from itertools import izip 2 1 3 from django.core.exceptions import FieldError 2 4 from django.db import connections 3 5 from django.db import transaction … … from django.db.models.sql.query import (get_proxied_model, get_order_dir, 9 11 select_related_descend, Query) 10 12 from django.db.utils import DatabaseError 11 13 14 12 15 class SQLCompiler(object): 13 16 def __init__(self, query, connection, using): 14 17 self.query = query … … class SQLInsertCompiler(SQLCompiler): 794 797 qn = self.connection.ops.quote_name 795 798 opts = self.query.model._meta 796 799 result = ['INSERT INTO %s' % qn(opts.db_table)] 797 result.append('(%s)' % ', '.join([qn(c) for c in self.query.columns])) 798 values = [self.placeholder(*v) for v in self.query.values] 799 result.append('VALUES (%s)' % ', '.join(values)) 800 params = self.query.params 801 if self.return_id and self.connection.features.can_return_id_from_insert: 800 801 has_fields = bool(self.query.fields) 802 fields = self.query.fields if has_fields else [opts.pk] 803 result.append('(%s)' % ', '.join([qn(f.column) for f in fields])) 804 805 if has_fields: 806 params = values = [ 807 [ 808 f.get_db_prep_save( 809 getattr(obj, f.attname) if self.query.raw else 810 f.pre_save(obj, True), connection=self.connection) 811 for f in fields 812 ] 813 for obj in self.query.objs 814 ] 815 else: 816 values = [ 817 [self.connection.ops.pk_default_value()] 818 for obj in self.query.objs 819 ] 820 params = [[]] 821 fields = [None] 822 823 can_bulk = ( 824 not any(hasattr(field, "get_placeholder") for field in fields) and 825 not self.return_id) 826 827 if can_bulk: 828 placeholders = [["%s"] * len(fields)] 829 else: 830 placeholders = [ 831 [self.placeholder(field, v) for field, v in izip(fields, val)] 832 for val in values 833 ] 834 if self.return_id and \ 835 self.connection.features.can_return_id_from_insert: 836 (params,) = values 837 (placeholders,) = placeholders 802 838 col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column)) 839 result.append("VALUES (%s)" % ", ".join(placeholders)) 803 840 r_fmt, r_params = self.connection.ops.return_insert_id() 804 841 result.append(r_fmt % col) 805 params = params + r_params 806 return ' '.join(result), params 842 params += r_params 843 return [(" ".join(result), tuple(params))] 844 if can_bulk and self.connection.features.has_bulk_insert: 845 result.append( 846 self.connection.ops.bulk_insert_sql(fields, len(values))) 847 return [ 848 (" ".join(result), tuple([v for val in values for v in val]))] 849 else: 850 return [ 851 (" ".join(result + ["VALUES (%s)" % ", ".join(p)]), vals) 852 for p, vals in izip(placeholders, params) 853 ] 807 854 808 855 def execute_sql(self, return_id=False): 856 assert not (return_id and len(self.query.objs) != 1) 809 857 self.return_id = return_id 810 cursor = super(SQLInsertCompiler, self).execute_sql(None) 858 cursor = self.connection.cursor() 859 for sql, params in self.as_sql(): 860 cursor.execute(sql, params) 811 861 if not (return_id and cursor): 812 862 return 813 863 if self.connection.features.can_return_id_from_insert: 814 864 return self.connection.ops.fetch_returned_insert_id(cursor) 815 return self.connection.ops.last_insert_id(cursor, 816 self.query.model._meta.db_table, self.query.model._meta.pk.column) 865 return self.connection.ops.last_insert_id( 866 cursor, self.query.model._meta.db_table, 867 self.query.model._meta.pk.column) 817 868 818 869 819 870 class SQLDeleteCompiler(SQLCompiler): -
django/db/models/sql/query.py
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index b55de3d..101d4ac 100644
a b all about the internals of models in order to get the information it needs. 8 8 """ 9 9 10 10 import copy 11 from django.utils.tree import Node 11 12 12 from django.utils.datastructures import SortedDict 13 13 from django.utils.encoding import force_unicode 14 from django.utils.tree import Node 14 15 from django.db import connections, DEFAULT_DB_ALIAS 15 16 from django.db.models import signals 16 17 from django.db.models.fields import FieldDoesNotExist -
django/db/models/sql/subqueries.py
diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index ecde857..1c409c2 100644
a b class InsertQuery(Query): 136 136 137 137 def __init__(self, *args, **kwargs): 138 138 super(InsertQuery, self).__init__(*args, **kwargs) 139 self. columns = []140 self. values = []141 self. params = ()139 self.fields = [] 140 self.objs = [] 141 self.raw = False 142 142 143 143 def clone(self, klass=None, **kwargs): 144 144 extras = { 145 ' columns': self.columns[:],146 ' values': self.values[:],147 ' params': self.params145 'fields': self.fields[:], 146 'objs': self.objs[:], 147 'raw': self.raw, 148 148 } 149 149 extras.update(kwargs) 150 150 return super(InsertQuery, self).clone(klass, **extras) 151 151 152 def insert_values(self, insert_values, raw_values=False):152 def insert_values(self, fields, objs, raw=False): 153 153 """ 154 154 Set up the insert query from the 'insert_values' dictionary. The 155 155 dictionary gives the model field names and their target values. … … class InsertQuery(Query): 159 159 parameters. This provides a way to insert NULL and DEFAULT keywords 160 160 into the query, for example. 161 161 """ 162 placeholders, values = [], [] 163 for field, val in insert_values: 164 placeholders.append((field, val)) 165 self.columns.append(field.column) 166 values.append(val) 167 if raw_values: 168 self.values.extend([(None, v) for v in values]) 169 else: 170 self.params += tuple(values) 171 self.values.extend(placeholders) 162 self.fields = fields 163 self.objs = objs 164 self.raw = raw 172 165 173 166 class DateQuery(Query): 174 167 """ -
docs/ref/models/querysets.txt
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index b2c521e..898306d 100644
a b has a side effect on your data. For more, see `Safe methods`_ in the HTTP spec. 1158 1158 1159 1159 .. _Safe methods: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1 1160 1160 1161 bulk_create 1162 ~~~~~~~~~~~ 1163 1164 .. method:: bulk_create(objs) 1165 1166 This method inserts the provided list of objects into the database in an 1167 efficient manner (generally only 1 query, no matter how many objects there 1168 are):: 1169 1170 >>> Entry.objects.bulk_create([ 1171 ... Entry(headline="Django 1.0 Released"), 1172 ... Entry(headline="Django 1.1 Announced"), 1173 ... Entry(headline="Breaking: Django is awesome") 1174 ... ]) 1175 1176 This has a number of caveats though: 1177 1178 * The model's ``save()`` method will not be called, and the ``pre_save`` and 1179 ``post_save`` signals will not be sent. 1180 * It does not work with child models in a multi-table inheritance scenario. 1181 * If the model's primary key is an :class:`~django.db.models.AutoField` it 1182 does not retrieve and set the primary key attribute, as ``save()`` does. 1183 1161 1184 count 1162 1185 ~~~~~ 1163 1186 -
docs/topics/db/optimization.txt
diff --git a/docs/topics/db/optimization.txt b/docs/topics/db/optimization.txt index 265ef55..5093917 100644
a b instead of:: 268 268 269 269 entry.blog.id 270 270 271 Insert in bulk 272 ============== 273 274 When creating objects, where possible, use the 275 :meth:`~django.db.models.query.QuerySet.bulk_create()` method to reduce the 276 number of SQL queries. For example:: 277 278 Entry.objects.bulk_create([ 279 Entry(headline="Python 3.0 Released"), 280 Entry(headline="Python 3.1 Planned") 281 ]) 282 283 Is preferable to:: 284 285 Entry.objects.create(headline="Python 3.0 Released") 286 Entry.objects.create(headline="Python 3.1 Planned") 287 288 Note that there are a number of :meth:`caveats to this method 289 <django.db.models.query.QuerySet.bulk_create>`, make sure it is appropriate for 290 your use case. This also applies to :class:`ManyToManyFields 291 <django.db.models.ManyToManyField>`, doing:: 292 293 my_band.members.add(me, my_friend) 294 295 Is preferable to:: 296 297 my_band.members.add(me) 298 my_band.members.add(my_friend) 299 300 Where ``Bands`` and ``Artists`` have a many-to-many relationship. -
new file tests/regressiontests/bulk_create/models.py
diff --git a/tests/regressiontests/bulk_create/__init__.py b/tests/regressiontests/bulk_create/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/bulk_create/models.py b/tests/regressiontests/bulk_create/models.py new file mode 100644 index 0000000..a4c611d
- + 1 from django.db import models 2 3 4 class Country(models.Model): 5 name = models.CharField(max_length=255) 6 iso_two_letter = models.CharField(max_length=2) 7 8 class Place(models.Model): 9 name = models.CharField(max_length=100) 10 11 class Meta: 12 abstract = True 13 14 class Restaurant(Place): 15 pass 16 17 class Pizzeria(Restaurant): 18 pass 19 20 class State(models.Model): 21 two_letter_code = models.CharField(max_length=2, primary_key=True) 22 No newline at end of file -
new file tests/regressiontests/bulk_create/tests.py
diff --git a/tests/regressiontests/bulk_create/tests.py b/tests/regressiontests/bulk_create/tests.py new file mode 100644 index 0000000..64192a1
- + 1 from __future__ import with_statement 2 3 from operator import attrgetter 4 5 from django.test import TestCase, skipUnlessDBFeature 6 7 from .models import Country, Restaurant, Pizzeria, State 8 9 10 class BulkCreateTests(TestCase): 11 def setUp(self): 12 self.data = [ 13 Country(name="United States of America", iso_two_letter="US"), 14 Country(name="The Netherlands", iso_two_letter="NL"), 15 Country(name="Germany", iso_two_letter="DE"), 16 Country(name="Czech Republic", iso_two_letter="CZ") 17 ] 18 19 def test_simple(self): 20 Country.objects.bulk_create(self.data) 21 self.assertQuerysetEqual(Country.objects.order_by("-name"), [ 22 "United States of America", "The Netherlands", "Germany", 23 "Czech Republic" 24 ], attrgetter("name")) 25 26 @skipUnlessDBFeature("has_bulk_insert") 27 def test_efficiency(self): 28 with self.assertNumQueries(1): 29 Country.objects.bulk_create(self.data) 30 31 def test_inheritance(self): 32 Restaurant.objects.bulk_create([ 33 Restaurant(name="Nicholas's") 34 ]) 35 self.assertQuerysetEqual(Restaurant.objects.all(), [ 36 "Nicholas's", 37 ], attrgetter("name")) 38 with self.assertRaises(ValueError): 39 Pizzeria.objects.bulk_create([ 40 Pizzeria(name="The Art of Pizza") 41 ]) 42 self.assertQuerysetEqual(Pizzeria.objects.all(), []) 43 self.assertQuerysetEqual(Restaurant.objects.all(), [ 44 "Nicholas's", 45 ], attrgetter("name")) 46 47 def test_non_auto_increment_pk(self): 48 State.objects.bulk_create([ 49 State(two_letter_code=s) 50 for s in ["IL", "NY", "CA", "ME"] 51 ]) 52 self.assertQuerysetEqual(State.objects.order_by("two_letter_code"), [ 53 "CA", "IL", "ME", "NY", 54 ], attrgetter("two_letter_code")) -
tests/regressiontests/db_typecasts/tests.py
diff --git a/tests/regressiontests/db_typecasts/tests.py b/tests/regressiontests/db_typecasts/tests.py index 8c71c8f..1d3bbfa 100644
a b TEST_CASES = { 53 53 54 54 class DBTypeCasts(unittest.TestCase): 55 55 def test_typeCasts(self): 56 for k, v in TEST_CASES.ite ms():56 for k, v in TEST_CASES.iteritems(): 57 57 for inpt, expected in v: 58 58 got = getattr(typecasts, k)(inpt) 59 assert got == expected, "In %s: %r doesn't match %r. Got %r instead." % (k, inpt, expected, got)59 self.assertEqual(got, expected, "In %s: %r doesn't match %r. Got %r instead." % (k, inpt, expected, got)) 60 60 61 61 if __name__ == '__main__': 62 62 unittest.main()