diff --git a/django/db/backends/creation.py b/django/db/backends/creation.py index 6bda89de10..aea00ea7f4 100644 --- a/django/db/backends/creation.py +++ b/django/db/backends/creation.py @@ -73,9 +73,6 @@ class BaseDatabaseCreation(object): else: field_output.extend(ref_output) table_output.append(' '.join(field_output)) - if opts.order_with_respect_to: - table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \ - style.SQL_COLTYPE(models.IntegerField().db_type(connection=self.connection))) for field_constraints in opts.unique_together: table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints])) diff --git a/django/db/models/base.py b/django/db/models/base.py index 27d571ca13..d8c4a7efd1 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -504,6 +504,13 @@ class Model(object): else: record_exists = False if not pk_set or not record_exists: + if meta.order_with_respect_to: + # If this is a model with an order_with_respect_to + # autopopulate the _order field + field = meta.order_with_respect_to + order_value = manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count() + setattr(self, '_order', order_value) + if not pk_set: if force_update: raise ValueError("Cannot force an update in save() with no primary key.") @@ -513,9 +520,6 @@ class Model(object): values = [(f, f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True), connection=connection)) for f in meta.local_fields] - if meta.order_with_respect_to: - field = meta.order_with_respect_to - values.append((meta.get_field_by_name('_order')[0], manager.using(using).filter(**{field.name: getattr(self, field.attname)}).count())) record_exists = False update_pk = bool(meta.has_auto_field and not pk_set) diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 298c29b83a..fe47ac8a0b 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -132,22 +132,6 @@ class Field(object): memodict[id(self)] = obj return obj - def __getstate__(self): - "Don't try to pickle a callable default value" - obj_dict = self.__dict__.copy() - del obj_dict['default'] - return obj_dict - - def __setstate__(self, data): - "When unpickling, restore the callable default" - self.__dict__.update(data) - - # Restore the default - try: - self.default = self.model._meta.get_field(self.name).default - except FieldDoesNotExist: - self.default = NOT_PROVIDED - def to_python(self, value): """ Converts the input value into the expected Python data type, raising diff --git a/django/db/models/fields/proxy.py b/django/db/models/fields/proxy.py index 10da0dd431..c0cc873f4c 100644 --- a/django/db/models/fields/proxy.py +++ b/django/db/models/fields/proxy.py @@ -11,9 +11,7 @@ class OrderWrt(fields.IntegerField): Meta.order_with_respect_to is specified. """ - def __init__(self, model, *args, **kwargs): + def __init__(self, *args, **kwargs): + kwargs['name'] = '_order' + kwargs['editable'] = False super(OrderWrt, self).__init__(*args, **kwargs) - self.model = model - self.attname = '_order' - self.column = '_order' - self.name = '_order' diff --git a/django/db/models/options.py b/django/db/models/options.py index f9cfeb0f04..250d505267 100644 --- a/django/db/models/options.py +++ b/django/db/models/options.py @@ -108,7 +108,7 @@ class Options(object): if self.order_with_respect_to: self.order_with_respect_to = self.get_field(self.order_with_respect_to) self.ordering = ('_order',) - self._order = OrderWrt(model) + model.add_to_class('_order', OrderWrt()) else: self.order_with_respect_to = None @@ -330,8 +330,6 @@ class Options(object): cache[f.name] = (f, model, True, True) for f, model in self.get_fields_with_model(): cache[f.name] = (f, model, True, False) - if self.order_with_respect_to: - cache['_order'] = self._order, None, True, False if app_cache_ready(): self._name_map = cache return cache diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py index cf147c6ad9..4e5a647259 100644 --- a/django/db/models/sql/where.py +++ b/django/db/models/sql/where.py @@ -258,7 +258,7 @@ class ExtraWhere(object): def __init__(self, sqls, params): self.sqls = sqls self.params = params - + def as_sql(self, qn=None, connection=None): return " AND ".join(self.sqls), tuple(self.params or ()) @@ -270,6 +270,30 @@ class Constraint(object): def __init__(self, alias, col, field): self.alias, self.col, self.field = alias, col, field + def __getstate__(self): + """Save the state of the Constraint for pickling. + + Fields aren't necessarily pickleable, because they can have + callable default values. So, instead of pickling the field + store a reference so we can restore it manually + """ + obj_dict = self.__dict__.copy() + if self.field: + obj_dict['model'] = self.field.model + obj_dict['field_name'] = self.field.name + del obj_dict['field'] + return obj_dict + + def __setstate__(self, data): + """Restore the constraint """ + model = data.pop('model', None) + field_name = data.pop('field_name', None) + self.__dict__.update(data) + if model is not None: + self.field = model._meta.get_field(field_name) + else: + self.field = None + def prepare(self, lookup_type, value): if self.field: return self.field.get_prep_lookup(lookup_type, value) diff --git a/tests/regressiontests/queryset_pickle/models.py b/tests/regressiontests/queryset_pickle/models.py index b0cae0a41f..df0a6e648d 100644 --- a/tests/regressiontests/queryset_pickle/models.py +++ b/tests/regressiontests/queryset_pickle/models.py @@ -2,11 +2,22 @@ import datetime from django.db import models from django.utils.translation import ugettext_lazy as _ +def standalone_number(self): + return 1 + class Numbers(object): + @staticmethod + def get_static_number(self): + return 2 @classmethod - def get_number(self): - return 2 + def get_class_number(self): + return 3 + + def get_member_number(self): + return 4 + +nn = Numbers() class Group(models.Model): name = models.CharField(_('name'), max_length=100) @@ -17,4 +28,7 @@ class Event(models.Model): class Happening(models.Model): when = models.DateTimeField(blank=True, default=datetime.datetime.now) name = models.CharField(blank=True, max_length=100, default=lambda:"test") - number = models.IntegerField(blank=True, default=Numbers.get_number) + number1 = models.IntegerField(blank=True, default=standalone_number) + number2 = models.IntegerField(blank=True, default=Numbers.get_static_number) + number3 = models.IntegerField(blank=True, default=Numbers.get_class_number) + number4 = models.IntegerField(blank=True, default=nn.get_member_number) diff --git a/tests/regressiontests/queryset_pickle/tests.py b/tests/regressiontests/queryset_pickle/tests.py index 8384df5647..f77b4f8cd0 100644 --- a/tests/regressiontests/queryset_pickle/tests.py +++ b/tests/regressiontests/queryset_pickle/tests.py @@ -23,5 +23,14 @@ class PickleabilityTestCase(TestCase): def test_lambda_as_default(self): self.assert_pickles(Happening.objects.filter(name="test")) - def test_callable_as_default(self): - self.assert_pickles(Happening.objects.filter(number=1)) + def test_standalone_method_as_default(self): + self.assert_pickles(Happening.objects.filter(number1=1)) + + def test_staticmethod_as_default(self): + self.assert_pickles(Happening.objects.filter(number2=1)) + + def test_classmethod_as_default(self): + self.assert_pickles(Happening.objects.filter(number2=1)) + + def test_membermethod_as_default(self): + self.assert_pickles(Happening.objects.filter(number2=1))