Ticket #12734: django-defer-save.diff

File django-defer-save.diff, 9.9 KB (added by Alex Gaynor, 14 years ago)
  • django/db/models/query_utils.py

    diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
    index 8e804ec..9129d95 100644
    a b class DeferredAttribute(object):  
    183183        Retrieves and caches the value from the datastore on the first lookup.
    184184        Returns the cached value.
    185185        """
     186        from django.db.models.fields import FieldDoesNotExist
     187
    186188        assert instance is not None
    187189        cls = self.model_ref()
    188190        data = instance.__dict__
    189191        if data.get(self.field_name, self) is self:
    190             data[self.field_name] = cls._base_manager.filter(pk=instance.pk).values_list(self.field_name, flat=True).using(instance._state.db).get()
     192            # self.field_name is the attname of the field, but only() takes the
     193            # actual name, so we need to translate it here.
     194            try:
     195                cls._meta.get_field_by_name(self.field_name)
     196                name = self.field_name
     197            except FieldDoesNotExist:
     198                name = [f.name for f in cls._meta.fields
     199                    if f.attname == self.field_name][0]
     200            # We use only() instead of values() here because we want the
     201            # various data coersion methods (to_python(), etc.) to be called
     202            # here.
     203            val = getattr(
     204                cls._base_manager.filter(pk=instance.pk).only(name).using(
     205                    instance._state.db).get(),
     206                self.field_name
     207            )
     208            data[self.field_name] = val
    191209        return data[self.field_name]
    192210
    193211    def __set__(self, instance, value):
  • new file tests/modeltests/field_subclassing/fields.py

    diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py
    new file mode 100644
    index 0000000..d545b19
    - +  
     1from django.core.exceptions import FieldError
     2from django.db import models
     3from django.utils import simplejson as json
     4from django.utils.encoding import force_unicode
     5
     6
     7class 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
     21class 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
     54class 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)
  • tests/modeltests/field_subclassing/models.py

    diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py
    index c776146..93b30c2 100644
    a b  
    22Tests for field subclassing.
    33"""
    44
     5from django.core import serializers
    56from django.db import models
    67from 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')
    238
    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
     9from fields import Small, SmallField, JSONField
    3110
    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)
    5511
    5612class MyModel(models.Model):
    5713    name = models.CharField(max_length=10)
    class MyModel(models.Model):  
    6016    def __unicode__(self):
    6117        return force_unicode(self.name)
    6218
     19class DataModel(models.Model):
     20    data = JSONField()
     21
    6322__test__ = {'API_TESTS': ur"""
    6423# Creating a model with custom fields is done as per normal.
    6524>>> s = Small(1, 2)
  • new file tests/modeltests/field_subclassing/tests.py

    diff --git a/tests/modeltests/field_subclassing/tests.py b/tests/modeltests/field_subclassing/tests.py
    new file mode 100644
    index 0000000..731ab51
    - +  
     1from django.test import TestCase
     2
     3from models import DataModel
     4
     5
     6class 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])
  • tests/regressiontests/defer_regress/models.py

    diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py
    index c32f5bb..08c4d62 100644
    a b False  
    142142[<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'>]
    143143
    144144>>> sorted(get_models(models.get_app('defer_regress'), include_deferred=True), key=lambda obj: obj._meta.object_name)
    145 [<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'>]
    146 
     145[<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'>]
    147146"""
    148147}
    149148
Back to Top