Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #13328 -- Ensured that querysets on models with callable defaul…

…ts can be pickled. No, really this time. Thanks to Alex for his help brainstorming the solution.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13013 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 3fb57d47bd8d4f6c1d61c9fd9564ae6c1c262027 1 parent e8eac32
Russell Keith-Magee authored April 21, 2010
3  django/db/backends/creation.py
@@ -73,9 +73,6 @@ def sql_create_model(self, model, style, known_models=set()):
73 73
                 else:
74 74
                     field_output.extend(ref_output)
75 75
             table_output.append(' '.join(field_output))
76  
-        if opts.order_with_respect_to:
77  
-            table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
78  
-                style.SQL_COLTYPE(models.IntegerField().db_type(connection=self.connection)))
79 76
         for field_constraints in opts.unique_together:
80 77
             table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
81 78
                 ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
10  django/db/models/base.py
@@ -504,6 +504,13 @@ def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
504 504
                 else:
505 505
                     record_exists = False
506 506
             if not pk_set or not record_exists:
  507
+                if meta.order_with_respect_to:
  508
+                    # If this is a model with an order_with_respect_to
  509
+                    # autopopulate the _order field
  510
+                    field = meta.order_with_respect_to
  511
+                    order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count()
  512
+                    setattr(self, '_order', order_value)
  513
+
507 514
                 if not pk_set:
508 515
                     if force_update:
509 516
                         raise ValueError("Cannot force an update in save() with no primary key.")
@@ -513,9 +520,6 @@ def save_base(self, raw=False, cls=None, origin=None, force_insert=False,
513 520
                     values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection))
514 521
                         for f in meta.local_fields]
515 522
 
516  
-                if meta.order_with_respect_to:
517  
-                    field = meta.order_with_respect_to
518  
-                    values.append((meta.get_field_by_name('_order')[0], manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count()))
519 523
                 record_exists = False
520 524
 
521 525
                 update_pk = bool(meta.has_auto_field and not pk_set)
16  django/db/models/fields/__init__.py
@@ -132,22 +132,6 @@ def __deepcopy__(self, memodict):
132 132
         memodict[id(self)] = obj
133 133
         return obj
134 134
 
135  
-    def __getstate__(self):
136  
-        "Don't try to pickle a callable default value"
137  
-        obj_dict = self.__dict__.copy()
138  
-        del obj_dict['default']
139  
-        return obj_dict
140  
-
141  
-    def __setstate__(self, data):
142  
-        "When unpickling, restore the callable default"
143  
-        self.__dict__.update(data)
144  
-
145  
-        # Restore the default
146  
-        try:
147  
-            self.default = self.model._meta.get_field(self.name).default
148  
-        except FieldDoesNotExist:
149  
-            self.default = NOT_PROVIDED
150  
-
151 135
     def to_python(self, value):
152 136
         """
153 137
         Converts the input value into the expected Python data type, raising
8  django/db/models/fields/proxy.py
@@ -11,9 +11,7 @@ class OrderWrt(fields.IntegerField):
11 11
     Meta.order_with_respect_to is specified.
12 12
     """
13 13
 
14  
-    def __init__(self, model, *args, **kwargs):
  14
+    def __init__(self, *args, **kwargs):
  15
+        kwargs['name'] = '_order'
  16
+        kwargs['editable'] = False
15 17
         super(OrderWrt, self).__init__(*args, **kwargs)
16  
-        self.model = model
17  
-        self.attname = '_order'
18  
-        self.column = '_order'
19  
-        self.name = '_order'
4  django/db/models/options.py
@@ -108,7 +108,7 @@ def _prepare(self, model):
108 108
         if self.order_with_respect_to:
109 109
             self.order_with_respect_to = self.get_field(self.order_with_respect_to)
110 110
             self.ordering = ('_order',)
111  
-            self._order = OrderWrt(model)
  111
+            model.add_to_class('_order', OrderWrt())
112 112
         else:
113 113
             self.order_with_respect_to = None
114 114
 
@@ -330,8 +330,6 @@ def init_name_map(self):
330 330
             cache[f.name] = (f, model, True, True)
331 331
         for f, model in self.get_fields_with_model():
332 332
             cache[f.name] = (f, model, True, False)
333  
-        if self.order_with_respect_to:
334  
-            cache['_order'] = self._order, None, True, False
335 333
         if app_cache_ready():
336 334
             self._name_map = cache
337 335
         return cache
26  django/db/models/sql/where.py
@@ -258,7 +258,7 @@ class ExtraWhere(object):
258 258
     def __init__(self, sqls, params):
259 259
         self.sqls = sqls
260 260
         self.params = params
261  
-    
  261
+
262 262
     def as_sql(self, qn=None, connection=None):
263 263
         return " AND ".join(self.sqls), tuple(self.params or ())
264 264
 
@@ -270,6 +270,30 @@ class Constraint(object):
270 270
     def __init__(self, alias, col, field):
271 271
         self.alias, self.col, self.field = alias, col, field
272 272
 
  273
+    def __getstate__(self):
  274
+        """Save the state of the Constraint for pickling.
  275
+
  276
+        Fields aren't necessarily pickleable, because they can have
  277
+        callable default values. So, instead of pickling the field
  278
+        store a reference so we can restore it manually
  279
+        """
  280
+        obj_dict = self.__dict__.copy()
  281
+        if self.field:
  282
+            obj_dict['model'] = self.field.model
  283
+            obj_dict['field_name'] = self.field.name
  284
+        del obj_dict['field']
  285
+        return obj_dict
  286
+
  287
+    def __setstate__(self, data):
  288
+        """Restore the constraint """
  289
+        model = data.pop('model', None)
  290
+        field_name = data.pop('field_name', None)
  291
+        self.__dict__.update(data)
  292
+        if model is not None:
  293
+            self.field = model._meta.get_field(field_name)
  294
+        else:
  295
+            self.field = None
  296
+
273 297
     def prepare(self, lookup_type, value):
274 298
         if self.field:
275 299
             return self.field.get_prep_lookup(lookup_type, value)
20  tests/regressiontests/queryset_pickle/models.py
@@ -2,11 +2,22 @@
2 2
 from django.db import models
3 3
 from django.utils.translation import ugettext_lazy as _
4 4
 
  5
+def standalone_number(self):
  6
+    return 1
  7
+
5 8
 class Numbers(object):
  9
+    @staticmethod
  10
+    def get_static_number(self):
  11
+        return 2
6 12
 
7 13
     @classmethod
8  
-    def get_number(self):
9  
-        return 2
  14
+    def get_class_number(self):
  15
+        return 3
  16
+
  17
+    def get_member_number(self):
  18
+        return 4
  19
+
  20
+nn = Numbers()
10 21
 
11 22
 class Group(models.Model):
12 23
     name = models.CharField(_('name'), max_length=100)
@@ -17,4 +28,7 @@ class Event(models.Model):
17 28
 class Happening(models.Model):
18 29
     when = models.DateTimeField(blank=True, default=datetime.datetime.now)
19 30
     name = models.CharField(blank=True, max_length=100, default=lambda:"test")
20  
-    number = models.IntegerField(blank=True, default=Numbers.get_number)
  31
+    number1 = models.IntegerField(blank=True, default=standalone_number)
  32
+    number2 = models.IntegerField(blank=True, default=Numbers.get_static_number)
  33
+    number3 = models.IntegerField(blank=True, default=Numbers.get_class_number)
  34
+    number4 = models.IntegerField(blank=True, default=nn.get_member_number)
13  tests/regressiontests/queryset_pickle/tests.py
@@ -23,5 +23,14 @@ def test_datetime_callable_default_filter(self):
23 23
     def test_lambda_as_default(self):
24 24
         self.assert_pickles(Happening.objects.filter(name="test"))
25 25
 
26  
-    def test_callable_as_default(self):
27  
-        self.assert_pickles(Happening.objects.filter(number=1))
  26
+    def test_standalone_method_as_default(self):
  27
+        self.assert_pickles(Happening.objects.filter(number1=1))
  28
+
  29
+    def test_staticmethod_as_default(self):
  30
+        self.assert_pickles(Happening.objects.filter(number2=1))
  31
+
  32
+    def test_classmethod_as_default(self):
  33
+        self.assert_pickles(Happening.objects.filter(number2=1))
  34
+
  35
+    def test_membermethod_as_default(self):
  36
+        self.assert_pickles(Happening.objects.filter(number2=1))

0 notes on commit 3fb57d4

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