Fixed #10498 (again) -- Made sure the improvements done in r17641 have a smaller impact on speed. Thanks to Anssi Kääriäinen for the patch and Jonas Obrist for reviewing.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17698 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2012-03-13 03:48:20 +00:00
parent 0ee801e360
commit f7daa38a00
3 changed files with 40 additions and 10 deletions

View File

@ -20,7 +20,7 @@ from django.db.models.options import Options
from django.db.models import signals from django.db.models import signals
from django.db.models.loading import register_models, get_model from django.db.models.loading import register_models, get_model
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.functional import curry, Promise from django.utils.functional import curry
from django.utils.encoding import smart_str, force_unicode from django.utils.encoding import smart_str, force_unicode
from django.utils.text import get_text_list, capfirst from django.utils.text import get_text_list, capfirst
@ -297,14 +297,10 @@ class Model(object):
# is *not* consumed. We rely on this, so don't change the order # is *not* consumed. We rely on this, so don't change the order
# without changing the logic. # without changing the logic.
for val, field in izip(args, fields_iter): for val, field in izip(args, fields_iter):
if isinstance(val, Promise):
val = force_unicode(val)
setattr(self, field.attname, val) setattr(self, field.attname, val)
else: else:
# Slower, kwargs-ready version. # Slower, kwargs-ready version.
for val, field in izip(args, fields_iter): for val, field in izip(args, fields_iter):
if isinstance(val, Promise):
val = force_unicode(val)
setattr(self, field.attname, val) setattr(self, field.attname, val)
kwargs.pop(field.name, None) kwargs.pop(field.name, None)
# Maintain compatibility with existing calls. # Maintain compatibility with existing calls.
@ -358,8 +354,6 @@ class Model(object):
# checked) by the RelatedObjectDescriptor. # checked) by the RelatedObjectDescriptor.
setattr(self, field.name, rel_obj) setattr(self, field.name, rel_obj)
else: else:
if isinstance(val, Promise):
val = force_unicode(val)
setattr(self, field.attname, val) setattr(self, field.attname, val)
if kwargs: if kwargs:

View File

@ -8,6 +8,8 @@ from django.db.models.sql.constants import *
from django.db.models.sql.datastructures import Date from django.db.models.sql.datastructures import Date
from django.db.models.sql.query import Query from django.db.models.sql.query import Query
from django.db.models.sql.where import AND, Constraint from django.db.models.sql.where import AND, Constraint
from django.utils.functional import Promise
from django.utils.encoding import force_unicode
__all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery', __all__ = ['DeleteQuery', 'UpdateQuery', 'InsertQuery', 'DateQuery',
@ -38,7 +40,7 @@ class DeleteQuery(Query):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
where = self.where_class() where = self.where_class()
where.add((Constraint(None, field.column, field), 'in', where.add((Constraint(None, field.column, field), 'in',
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND) pk_list[offset:offset + GET_ITERATOR_CHUNK_SIZE]), AND)
self.do_query(self.model._meta.db_table, where, using=using) self.do_query(self.model._meta.db_table, where, using=using)
class UpdateQuery(Query): class UpdateQuery(Query):
@ -67,14 +69,13 @@ class UpdateQuery(Query):
return super(UpdateQuery, self).clone(klass, return super(UpdateQuery, self).clone(klass,
related_updates=self.related_updates.copy(), **kwargs) related_updates=self.related_updates.copy(), **kwargs)
def update_batch(self, pk_list, values, using): def update_batch(self, pk_list, values, using):
pk_field = self.model._meta.pk pk_field = self.model._meta.pk
self.add_update_values(values) self.add_update_values(values)
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
self.where = self.where_class() self.where = self.where_class()
self.where.add((Constraint(None, pk_field.column, pk_field), 'in', self.where.add((Constraint(None, pk_field.column, pk_field), 'in',
pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), pk_list[offset:offset + GET_ITERATOR_CHUNK_SIZE]),
AND) AND)
self.get_compiler(using).execute_sql(None) self.get_compiler(using).execute_sql(None)
@ -101,6 +102,10 @@ class UpdateQuery(Query):
Used by add_update_values() as well as the "fast" update path when Used by add_update_values() as well as the "fast" update path when
saving models. saving models.
""" """
# Check that no Promise object passes to the query. Refs #10498.
values_seq = [(value[0], value[1], force_unicode(value[2]))
if isinstance(value[2], Promise) else value
for value in values_seq]
self.values.extend(values_seq) self.values.extend(values_seq)
def add_related_update(self, model, field, value): def add_related_update(self, model, field, value):
@ -159,6 +164,12 @@ class InsertQuery(Query):
into the query, for example. into the query, for example.
""" """
self.fields = fields self.fields = fields
# Check that no Promise object reaches the DB. Refs #10498.
for field in fields:
for obj in objs:
value = getattr(obj, field.attname)
if isinstance(value, Promise):
setattr(obj, field.attname, force_unicode(value))
self.objs = objs self.objs = objs
self.raw = raw self.raw = raw

View File

@ -5,6 +5,7 @@ from datetime import datetime
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields import FieldDoesNotExist from django.db.models.fields import FieldDoesNotExist
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils.translation import ugettext_lazy
from .models import Article from .models import Article
@ -553,3 +554,27 @@ class ModelTest(TestCase):
pub_date__year=2008).extra( pub_date__year=2008).extra(
select={'dashed-value': '1', 'undashedvalue': '2'}) select={'dashed-value': '1', 'undashedvalue': '2'})
self.assertEqual(articles[0].undashedvalue, 2) self.assertEqual(articles[0].undashedvalue, 2)
def test_create_relation_with_ugettext_lazy(self):
"""
Test that ugettext_lazy objects work when saving model instances
through various methods. Refs #10498.
"""
notlazy = u'test'
lazy = ugettext_lazy(notlazy)
reporter = Article.objects.create(headline=lazy, pub_date=datetime.now())
article = Article.objects.get()
self.assertEqual(article.headline, notlazy)
# test that assign + save works with Promise objecs
article.headline = lazy
article.save()
self.assertEqual(article.headline, notlazy)
# test .update()
Article.objects.update(headline=lazy)
article = Article.objects.get()
self.assertEqual(article.headline, notlazy)
# still test bulk_create()
Article.objects.all().delete()
Article.objects.bulk_create([Article(headline=lazy, pub_date=datetime.now())])
article = Article.objects.get()
self.assertEqual(article.headline, notlazy)