From 3fc07acb348b212f7300c75ce31c00770ff4e32f Mon Sep 17 00:00:00 2001 From: Nicolas Delaby Date: Wed, 24 Feb 2016 13:37:38 +0100 Subject: [PATCH] For ConditionalVersionField get_fields() returns too many fields That needs to be filtered out. --- src/concurrency/fields.py | 23 +++++++++++++--- .../migrations/0003_auto_20160224_0637.py | 27 +++++++++++++++++++ tests/demoapp/demo/models.py | 11 ++++++++ 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 tests/demoapp/demo/migrations/0003_auto_20160224_0637.py diff --git a/src/concurrency/fields.py b/src/concurrency/fields.py index c6b1157..bf7e911 100755 --- a/src/concurrency/fields.py +++ b/src/concurrency/fields.py @@ -1,6 +1,7 @@ from __future__ import absolute_import, unicode_literals import copy +import functools import hashlib import logging import time @@ -44,8 +45,8 @@ def class_prepared_concurrency_handler(sender, **kwargs): if hasattr(sender, 'ConcurrencyMeta'): sender._concurrencymeta.enabled = getattr(sender.ConcurrencyMeta, 'enabled', True) - check_fields = getattr(sender.ConcurrencyMeta, 'check_fields', []) - ignore_fields = getattr(sender.ConcurrencyMeta, 'ignore_fields', []) + check_fields = getattr(sender.ConcurrencyMeta, 'check_fields', None) + ignore_fields = getattr(sender.ConcurrencyMeta, 'ignore_fields', None) if check_fields and ignore_fields: raise ValueError("Cannot set both 'check_fields' and 'ignore_fields'") @@ -310,6 +311,19 @@ def inner(self, force_insert=False, force_update=False, using=None, **kwargs): return update_wrapper(inner, func) +def filter_fields(instance, field): + if not field.concrete: + # reverse relation + return False + if field.is_relation and field.related_model is None: + # generic foreignkeys + return False + if field.many_to_many and instance.pk is None: + # can't load remote object yet + return False + return True + + class ConditionalVersionField(AutoIncVersionField): def contribute_to_class(self, cls, name, virtual_only=False): super(ConditionalVersionField, self).contribute_to_class(cls, name, virtual_only) @@ -333,10 +347,11 @@ def _get_hash(self, instance): check_fields = instance._concurrencymeta.check_fields ignore_fields = instance._concurrencymeta.ignore_fields + filter_ = functools.partial(filter_fields, instance) if check_fields is None and ignore_fields is None: - fields = sorted([f.name for f in instance._meta.get_fields()]) + fields = sorted([f.name for f in filter(filter_, instance._meta.get_fields())]) elif check_fields is None: - fields = sorted([f.name for f in instance._meta.get_fields() + fields = sorted([f.name for f in filter(filter_, instance._meta.get_fields()) if f.name not in ignore_fields]) else: fields = instance._concurrencymeta.check_fields diff --git a/tests/demoapp/demo/migrations/0003_auto_20160224_0637.py b/tests/demoapp/demo/migrations/0003_auto_20160224_0637.py new file mode 100644 index 0000000..d7d3b9e --- /dev/null +++ b/tests/demoapp/demo/migrations/0003_auto_20160224_0637.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('demo', '0002_conditionalversionmodelwithoutmeta'), + ] + + operations = [ + migrations.CreateModel( + name='Anything', + fields=[ + ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)), + ('name', models.CharField(max_length=10)), + ('a_relation', models.ForeignKey(to='demo.ConditionalVersionModelWithoutMeta')), + ], + ), + migrations.AddField( + model_name='conditionalversionmodelwithoutmeta', + name='anythings', + field=models.ManyToManyField(to='demo.Anything'), + ), + ] diff --git a/tests/demoapp/demo/models.py b/tests/demoapp/demo/models.py index 69093e0..a91b315 100644 --- a/tests/demoapp/demo/models.py +++ b/tests/demoapp/demo/models.py @@ -12,6 +12,7 @@ 'ProxyModel', 'InheritedModel', 'CustomSaveModel', 'ConcreteModel', 'TriggerConcurrentModel', 'ConditionalVersionModelWithoutMeta', + 'Anything', ] @@ -213,6 +214,15 @@ class ConcurrencyMeta: check_fields = ['field1', 'field2', 'user'] +class Anything(models.Model): + """ + Will create a ManyToOneRel automatic field on + ConditionalVersionModelWithoutMeta instances. + """ + name = models.CharField(max_length=10) + a_relation = models.ForeignKey('demo.ConditionalVersionModelWithoutMeta') + + class ConditionalVersionModelWithoutMeta(models.Model): """ This model doesn't have ConcurrencyMeta defined. @@ -222,6 +232,7 @@ class ConditionalVersionModelWithoutMeta(models.Model): field2 = models.CharField(max_length=30, blank=True, null=True, unique=True) field3 = models.CharField(max_length=30, blank=True, null=True, unique=True) user = models.ForeignKey(User, null=True) + anythings = models.ManyToManyField(Anything) class Meta: app_label = 'demo'