diff options
author | Joseph Kocherhans <joseph@jkocherhans.com> | 2010-02-24 19:06:59 +0000 |
---|---|---|
committer | Joseph Kocherhans <joseph@jkocherhans.com> | 2010-02-24 19:06:59 +0000 |
commit | ff963358d7d3eb31b51f3d74581b8e17025e3859 (patch) | |
tree | c881cc555e24f5c0db12d1ac7a0804071e87ec11 | |
parent | 16fe73d918c8a35f7122aed858312bd3163ac454 (diff) | |
download | django-ff963358d7d3eb31b51f3d74581b8e17025e3859.tar.gz |
Fixed #12734. Deferred fields will now be properly converted to python when accessed. Thanks, Alex Gaynor.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12579 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r-- | django/db/models/query_utils.py | 20 | ||||
-rw-r--r-- | tests/modeltests/field_subclassing/fields.py | 71 | ||||
-rw-r--r-- | tests/modeltests/field_subclassing/models.py | 51 | ||||
-rw-r--r-- | tests/modeltests/field_subclassing/tests.py | 21 | ||||
-rw-r--r-- | tests/regressiontests/defer_regress/models.py | 3 |
5 files changed, 117 insertions, 49 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py index 8e804ec3ef..9129d95a0b 100644 --- a/django/db/models/query_utils.py +++ b/django/db/models/query_utils.py @@ -183,11 +183,29 @@ class DeferredAttribute(object): Retrieves and caches the value from the datastore on the first lookup. Returns the cached value. """ + from django.db.models.fields import FieldDoesNotExist + assert instance is not None cls = self.model_ref() data = instance.__dict__ if data.get(self.field_name, self) is self: - data[self.field_name] = cls._base_manager.filter(pk=instance.pk).values_list(self.field_name, flat=True).using(instance._state.db).get() + # self.field_name is the attname of the field, but only() takes the + # actual name, so we need to translate it here. + try: + cls._meta.get_field_by_name(self.field_name) + name = self.field_name + except FieldDoesNotExist: + name = [f.name for f in cls._meta.fields + if f.attname == self.field_name][0] + # We use only() instead of values() here because we want the + # various data coersion methods (to_python(), etc.) to be called + # here. + val = getattr( + cls._base_manager.filter(pk=instance.pk).only(name).using( + instance._state.db).get(), + self.field_name + ) + data[self.field_name] = val return data[self.field_name] def __set__(self, instance, value): diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py new file mode 100644 index 0000000000..d545b1968d --- /dev/null +++ b/tests/modeltests/field_subclassing/fields.py @@ -0,0 +1,71 @@ +from django.core.exceptions import FieldError +from django.db import models +from django.utils import simplejson as json +from django.utils.encoding import force_unicode + + +class Small(object): + """ + A simple class to show that non-trivial Python objects can be used as + attributes. + """ + def __init__(self, first, second): + self.first, self.second = first, second + + def __unicode__(self): + return u'%s%s' % (force_unicode(self.first), force_unicode(self.second)) + + def __str__(self): + return unicode(self).encode('utf-8') + +class SmallField(models.Field): + """ + Turns the "Small" class into a Django field. Because of the similarities + with normal character fields and the fact that Small.__unicode__ does + something sensible, we don't need to implement a lot here. + """ + __metaclass__ = models.SubfieldBase + + def __init__(self, *args, **kwargs): + kwargs['max_length'] = 2 + super(SmallField, self).__init__(*args, **kwargs) + + def get_internal_type(self): + return 'CharField' + + def to_python(self, value): + if isinstance(value, Small): + return value + return Small(value[0], value[1]) + + def get_db_prep_save(self, value): + return unicode(value) + + def get_db_prep_lookup(self, lookup_type, value): + if lookup_type == 'exact': + return force_unicode(value) + if lookup_type == 'in': + return [force_unicode(v) for v in value] + if lookup_type == 'isnull': + return [] + raise FieldError('Invalid lookup type: %r' % lookup_type) + + +class JSONField(models.TextField): + __metaclass__ = models.SubfieldBase + + description = ("JSONField automatically serializes and desializes values to " + "and from JSON.") + + def to_python(self, value): + if not value: + return None + + if isinstance(value, basestring): + value = json.loads(value) + return value + + def get_db_prep_save(self, value): + if value is None: + return None + return json.dumps(value) diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py index c776146de5..93b30c2ec2 100644 --- a/tests/modeltests/field_subclassing/models.py +++ b/tests/modeltests/field_subclassing/models.py @@ -2,56 +2,12 @@ Tests for field subclassing. """ +from django.core import serializers from django.db import models from django.utils.encoding import force_unicode -from django.core import serializers -from django.core.exceptions import FieldError - -class Small(object): - """ - A simple class to show that non-trivial Python objects can be used as - attributes. - """ - def __init__(self, first, second): - self.first, self.second = first, second - - def __unicode__(self): - return u'%s%s' % (force_unicode(self.first), force_unicode(self.second)) - - def __str__(self): - return unicode(self).encode('utf-8') -class SmallField(models.Field): - """ - Turns the "Small" class into a Django field. Because of the similarities - with normal character fields and the fact that Small.__unicode__ does - something sensible, we don't need to implement a lot here. - """ - __metaclass__ = models.SubfieldBase +from fields import Small, SmallField, JSONField - def __init__(self, *args, **kwargs): - kwargs['max_length'] = 2 - super(SmallField, self).__init__(*args, **kwargs) - - def get_internal_type(self): - return 'CharField' - - def to_python(self, value): - if isinstance(value, Small): - return value - return Small(value[0], value[1]) - - def get_db_prep_save(self, value): - return unicode(value) - - def get_db_prep_lookup(self, lookup_type, value): - if lookup_type == 'exact': - return force_unicode(value) - if lookup_type == 'in': - return [force_unicode(v) for v in value] - if lookup_type == 'isnull': - return [] - raise FieldError('Invalid lookup type: %r' % lookup_type) class MyModel(models.Model): name = models.CharField(max_length=10) @@ -60,6 +16,9 @@ class MyModel(models.Model): def __unicode__(self): return force_unicode(self.name) +class DataModel(models.Model): + data = JSONField() + __test__ = {'API_TESTS': ur""" # Creating a model with custom fields is done as per normal. >>> s = Small(1, 2) diff --git a/tests/modeltests/field_subclassing/tests.py b/tests/modeltests/field_subclassing/tests.py new file mode 100644 index 0000000000..731ab51d24 --- /dev/null +++ b/tests/modeltests/field_subclassing/tests.py @@ -0,0 +1,21 @@ +from django.test import TestCase + +from models import DataModel + + +class CustomField(TestCase): + def test_defer(self): + d = DataModel.objects.create(data=[1, 2, 3]) + + self.assertTrue(isinstance(d.data, list)) + + d = DataModel.objects.get(pk=d.pk) + self.assertTrue(isinstance(d.data, list)) + self.assertEqual(d.data, [1, 2, 3]) + + d = DataModel.objects.defer("data").get(pk=d.pk) + d.save() + + d = DataModel.objects.get(pk=d.pk) + self.assertTrue(isinstance(d.data, list)) + self.assertEqual(d.data, [1, 2, 3]) diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index c32f5bb493..08c4d621b6 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -142,8 +142,7 @@ False [<class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.Leaf'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.ResolveThis'>] >>> sorted(get_models(models.get_app('defer_regress'), include_deferred=True), key=lambda obj: obj._meta.object_name) -[<class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name_other_value_text'>, <class 'regressiontests.defer_regress.models.Item_Deferred_other_value_text_value'>, <class 'regressiontests.defer_regress.models.Item_Deferred_text_value'>, <class 'regressiontests.defer_regress.models.Leaf'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_name_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_second_child_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_value'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.RelatedItem_Deferred_item_id'>, <class 'regressiontests.defer_regress.models.ResolveThis'>, <class 'regressiontests.defer_regress.models.ResolveThis_Deferred_num'>] - +[<class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name_other_value_text'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name_other_value_value'>, <class 'regressiontests.defer_regress.models.Item_Deferred_other_value_text_value'>, <class 'regressiontests.defer_regress.models.Item_Deferred_text_value'>, <class 'regressiontests.defer_regress.models.Leaf'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_child_id_second_child_id_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_name_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_second_child_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_value'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.RelatedItem_Deferred_'>, <class 'regressiontests.defer_regress.models.RelatedItem_Deferred_item_id'>, <class 'regressiontests.defer_regress.models.ResolveThis'>, <class 'regressiontests.defer_regress.models.ResolveThis_Deferred_num'>] """ } |