Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

[1.1.X] Fixed #12734. Deferred fields will now be properly converted …

…to python when accessed. Backport of r12579 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12692 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 55bcc8ac3013efb852d71f937f12132bc3371780 1 parent daec734
Joseph Kocherhans authored March 06, 2010
19  django/db/models/query_utils.py
@@ -182,11 +182,28 @@ def __get__(self, instance, owner):
182 182
         Retrieves and caches the value from the datastore on the first lookup.
183 183
         Returns the cached value.
184 184
         """
  185
+        from django.db.models.fields import FieldDoesNotExist
  186
+
185 187
         assert instance is not None
186 188
         cls = self.model_ref()
187 189
         data = instance.__dict__
188 190
         if data.get(self.field_name, self) is self:
189  
-            data[self.field_name] = cls._base_manager.filter(pk=instance.pk).values_list(self.field_name, flat=True).get()
  191
+            # self.field_name is the attname of the field, but only() takes the
  192
+            # actual name, so we need to translate it here.
  193
+            try:
  194
+                cls._meta.get_field_by_name(self.field_name)
  195
+                name = self.field_name
  196
+            except FieldDoesNotExist:
  197
+                name = [f.name for f in cls._meta.fields
  198
+                    if f.attname == self.field_name][0]
  199
+            # We use only() instead of values() here because we want the
  200
+            # various data coersion methods (to_python(), etc.) to be called
  201
+            # here.
  202
+            val = getattr(
  203
+                cls._base_manager.filter(pk=instance.pk).only(name).get(),
  204
+                self.field_name
  205
+            )
  206
+            data[self.field_name] = val
190 207
         return data[self.field_name]
191 208
 
192 209
     def __set__(self, instance, value):
71  tests/modeltests/field_subclassing/fields.py
... ...
@@ -0,0 +1,71 @@
  1
+from django.core.exceptions import FieldError
  2
+from django.db import models
  3
+from django.utils import simplejson as json
  4
+from django.utils.encoding import force_unicode
  5
+
  6
+
  7
+class Small(object):
  8
+    """
  9
+    A simple class to show that non-trivial Python objects can be used as
  10
+    attributes.
  11
+    """
  12
+    def __init__(self, first, second):
  13
+        self.first, self.second = first, second
  14
+
  15
+    def __unicode__(self):
  16
+        return u'%s%s' % (force_unicode(self.first), force_unicode(self.second))
  17
+
  18
+    def __str__(self):
  19
+        return unicode(self).encode('utf-8')
  20
+
  21
+class SmallField(models.Field):
  22
+    """
  23
+    Turns the "Small" class into a Django field. Because of the similarities
  24
+    with normal character fields and the fact that Small.__unicode__ does
  25
+    something sensible, we don't need to implement a lot here.
  26
+    """
  27
+    __metaclass__ = models.SubfieldBase
  28
+
  29
+    def __init__(self, *args, **kwargs):
  30
+        kwargs['max_length'] = 2
  31
+        super(SmallField, self).__init__(*args, **kwargs)
  32
+
  33
+    def get_internal_type(self):
  34
+        return 'CharField'
  35
+
  36
+    def to_python(self, value):
  37
+        if isinstance(value, Small):
  38
+            return value
  39
+        return Small(value[0], value[1])
  40
+
  41
+    def get_db_prep_save(self, value):
  42
+        return unicode(value)
  43
+
  44
+    def get_db_prep_lookup(self, lookup_type, value):
  45
+        if lookup_type == 'exact':
  46
+            return force_unicode(value)
  47
+        if lookup_type == 'in':
  48
+            return [force_unicode(v) for v in value]
  49
+        if lookup_type == 'isnull':
  50
+            return []
  51
+        raise FieldError('Invalid lookup type: %r' % lookup_type)
  52
+
  53
+
  54
+class JSONField(models.TextField):
  55
+    __metaclass__ = models.SubfieldBase
  56
+    
  57
+    description = ("JSONField automatically serializes and desializes values to "
  58
+        "and from JSON.")
  59
+    
  60
+    def to_python(self, value):
  61
+        if not value:
  62
+            return None
  63
+        
  64
+        if isinstance(value, basestring):
  65
+            value = json.loads(value)
  66
+        return value
  67
+    
  68
+    def get_db_prep_save(self, value):
  69
+        if value is None:
  70
+            return None
  71
+        return json.dumps(value)
51  tests/modeltests/field_subclassing/models.py
@@ -2,56 +2,12 @@
2 2
 Tests for field subclassing.
3 3
 """
4 4
 
  5
+from django.core import serializers
5 6
 from django.db import models
6 7
 from django.utils.encoding import force_unicode
7  
-from django.core import serializers
8  
-from django.core.exceptions import FieldError
9  
-
10  
-class Small(object):
11  
-    """
12  
-    A simple class to show that non-trivial Python objects can be used as
13  
-    attributes.
14  
-    """
15  
-    def __init__(self, first, second):
16  
-        self.first, self.second = first, second
17  
-
18  
-    def __unicode__(self):
19  
-        return u'%s%s' % (force_unicode(self.first), force_unicode(self.second))
20  
-
21  
-    def __str__(self):
22  
-        return unicode(self).encode('utf-8')
23 8
 
24  
-class SmallField(models.Field):
25  
-    """
26  
-    Turns the "Small" class into a Django field. Because of the similarities
27  
-    with normal character fields and the fact that Small.__unicode__ does
28  
-    something sensible, we don't need to implement a lot here.
29  
-    """
30  
-    __metaclass__ = models.SubfieldBase
  9
+from fields import Small, SmallField, JSONField
31 10
 
32  
-    def __init__(self, *args, **kwargs):
33  
-        kwargs['max_length'] = 2
34  
-        super(SmallField, self).__init__(*args, **kwargs)
35  
-
36  
-    def get_internal_type(self):
37  
-        return 'CharField'
38  
-
39  
-    def to_python(self, value):
40  
-        if isinstance(value, Small):
41  
-            return value
42  
-        return Small(value[0], value[1])
43  
-
44  
-    def get_db_prep_save(self, value):
45  
-        return unicode(value)
46  
-
47  
-    def get_db_prep_lookup(self, lookup_type, value):
48  
-        if lookup_type == 'exact':
49  
-            return force_unicode(value)
50  
-        if lookup_type == 'in':
51  
-            return [force_unicode(v) for v in value]
52  
-        if lookup_type == 'isnull':
53  
-            return []
54  
-        raise FieldError('Invalid lookup type: %r' % lookup_type)
55 11
 
56 12
 class MyModel(models.Model):
57 13
     name = models.CharField(max_length=10)
@@ -60,6 +16,9 @@ class MyModel(models.Model):
60 16
     def __unicode__(self):
61 17
         return force_unicode(self.name)
62 18
 
  19
+class DataModel(models.Model):
  20
+    data = JSONField()
  21
+
63 22
 __test__ = {'API_TESTS': ur"""
64 23
 # Creating a model with custom fields is done as per normal.
65 24
 >>> s = Small(1, 2)
21  tests/modeltests/field_subclassing/tests.py
... ...
@@ -0,0 +1,21 @@
  1
+from django.test import TestCase
  2
+
  3
+from models import DataModel
  4
+
  5
+
  6
+class CustomField(TestCase):
  7
+    def test_defer(self):
  8
+        d = DataModel.objects.create(data=[1, 2, 3])
  9
+        
  10
+        self.assertTrue(isinstance(d.data, list))
  11
+        
  12
+        d = DataModel.objects.get(pk=d.pk)
  13
+        self.assertTrue(isinstance(d.data, list))
  14
+        self.assertEqual(d.data, [1, 2, 3])
  15
+        
  16
+        d = DataModel.objects.defer("data").get(pk=d.pk)
  17
+        d.save()
  18
+        
  19
+        d = DataModel.objects.get(pk=d.pk)
  20
+        self.assertTrue(isinstance(d.data, list))
  21
+        self.assertEqual(d.data, [1, 2, 3])
5  tests/regressiontests/defer_regress/models.py
@@ -142,9 +142,8 @@ def __unicode__(self):
142 142
 >>> sorted(get_models(models.get_app('defer_regress')), key=lambda obj: obj.__class__.__name__)
143 143
 [<class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Leaf'>]
144 144
 
145  
->>> sorted(get_models(models.get_app('defer_regress'), include_deferred=True), key=lambda obj: obj.__class__.__name__)
146  
-[<class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Leaf'>, <class 'regressiontests.defer_regress.models.Item_Deferred_text_value'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name_other_value_text'>, <class 'regressiontests.defer_regress.models.RelatedItem_Deferred_item_id'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_second_child_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_name_value'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name'>, <class 'regressiontests.defer_regress.models.Item_Deferred_other_value_text_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_value'>]
147  
-
  145
+>>> sorted(get_models(models.get_app('defer_regress'), include_deferred=True), key=lambda obj: obj._meta.object_name)
  146
+[<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'>]
148 147
 """
149 148
 }
150 149
 

0 notes on commit 55bcc8a

Please sign in to comment.
Something went wrong with that request. Please try again.