diff --git a/django/db/models/base.py b/django/db/models/base.py index 53b62df135c..fc38224345f 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -20,7 +20,7 @@ from django.db.models.options import Options from django.db.models import signals from django.db.models.loading import register_models, get_model 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.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 # without changing the logic. for val, field in izip(args, fields_iter): - if isinstance(val, Promise): - val = force_unicode(val) setattr(self, field.attname, val) else: # Slower, kwargs-ready version. for val, field in izip(args, fields_iter): - if isinstance(val, Promise): - val = force_unicode(val) setattr(self, field.attname, val) kwargs.pop(field.name, None) # Maintain compatibility with existing calls. @@ -358,8 +354,6 @@ class Model(object): # checked) by the RelatedObjectDescriptor. setattr(self, field.name, rel_obj) else: - if isinstance(val, Promise): - val = force_unicode(val) setattr(self, field.attname, val) if kwargs: diff --git a/django/db/models/sql/subqueries.py b/django/db/models/sql/subqueries.py index 1b03647595b..5cfc9847707 100644 --- a/django/db/models/sql/subqueries.py +++ b/django/db/models/sql/subqueries.py @@ -8,6 +8,8 @@ from django.db.models.sql.constants import * from django.db.models.sql.datastructures import Date from django.db.models.sql.query import Query 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', @@ -38,7 +40,7 @@ class DeleteQuery(Query): for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): where = self.where_class() 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) class UpdateQuery(Query): @@ -67,14 +69,13 @@ class UpdateQuery(Query): return super(UpdateQuery, self).clone(klass, related_updates=self.related_updates.copy(), **kwargs) - def update_batch(self, pk_list, values, using): pk_field = self.model._meta.pk self.add_update_values(values) for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): self.where = self.where_class() 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) 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 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) def add_related_update(self, model, field, value): @@ -159,6 +164,12 @@ class InsertQuery(Query): into the query, for example. """ 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.raw = raw diff --git a/tests/modeltests/basic/tests.py b/tests/modeltests/basic/tests.py index 30ed3de564c..f9141dc6928 100644 --- a/tests/modeltests/basic/tests.py +++ b/tests/modeltests/basic/tests.py @@ -5,6 +5,7 @@ from datetime import datetime from django.core.exceptions import ObjectDoesNotExist from django.db.models.fields import FieldDoesNotExist from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature +from django.utils.translation import ugettext_lazy from .models import Article @@ -553,3 +554,27 @@ class ModelTest(TestCase): pub_date__year=2008).extra( select={'dashed-value': '1', '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)