diff --git a/model_utils/fields.py b/model_utils/fields.py index 4b49133..e949c23 100644 --- a/model_utils/fields.py +++ b/model_utils/fields.py @@ -100,10 +100,19 @@ class SplitDescriptor(object): obj.__dict__[self.field.name] = value class SplitField(models.TextField): + def __init__(self, *args, **kwargs): + # for South FakeORM compatibility: the frozen version of a + # SplitField can't try to add an _excerpt field, because the + # _excerpt field itself is frozen as well. See introspection + # rules below. + self.add_excerpt_field = not kwargs.pop('no_excerpt_field', False) + super(SplitField, self).__init__(*args, **kwargs) + def contribute_to_class(self, cls, name): - excerpt_field = models.TextField(editable=False) - excerpt_field.creation_counter = self.creation_counter+1 - cls.add_to_class(_excerpt_field_name(name), excerpt_field) + if self.add_excerpt_field: + excerpt_field = models.TextField(editable=False) + excerpt_field.creation_counter = self.creation_counter+1 + cls.add_to_class(_excerpt_field_name(name), excerpt_field) super(SplitField, self).contribute_to_class(cls, name) setattr(cls, self.name, SplitDescriptor(self)) @@ -127,7 +136,14 @@ class SplitField(models.TextField): # allow South to handle these fields smoothly try: from south.modelsinspector import add_introspection_rules - add_introspection_rules(patterns=['model_utils\.fields\.']) + # For a normal MarkupField, the add_excerpt_field attribute is + # always True, which means no_excerpt_field arg will always be + # True in a frozen MarkupField, which is what we want. + add_introspection_rules(rules=[((SplitField,), + [], + {'no_excerpt_field': ('add_excerpt_field', + {})})], + patterns=['model_utils\.fields\.']) except ImportError: pass diff --git a/model_utils/tests/models.py b/model_utils/tests/models.py index 9a5e674..a66d3cd 100644 --- a/model_utils/tests/models.py +++ b/model_utils/tests/models.py @@ -34,3 +34,11 @@ class Article(models.Model): def __unicode__(self): return self.title + +class NoRendered(models.Model): + """ + Test that the no_excerpt_field keyword arg works. This arg should + never be used except by the South model-freezing. + + """ + body = SplitField(no_excerpt_field=True) diff --git a/model_utils/tests/runtests.py b/model_utils/tests/runtests.py index de0f43d..b170f4d 100755 --- a/model_utils/tests/runtests.py +++ b/model_utils/tests/runtests.py @@ -13,7 +13,7 @@ from django.conf import settings def runtests(): test_runner = get_runner(settings) - failures = test_runner([], verbosity=1, interactive=True) + failures = test_runner(['tests'], verbosity=1, interactive=True) sys.exit(failures) if __name__ == '__main__': diff --git a/model_utils/tests/test_settings.py b/model_utils/tests/test_settings.py index 110e402..fd076b5 100644 --- a/model_utils/tests/test_settings.py +++ b/model_utils/tests/test_settings.py @@ -9,3 +9,10 @@ INSTALLED_APPS = ( ) DATABASE_ENGINE = 'sqlite3' + +try: + import south + INSTALLED_APPS += ('south',) +except ImportError: + pass + diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index 8e18b3f..59d81f6 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -1,5 +1,7 @@ from django.test import TestCase +from django.conf import settings from django.contrib.contenttypes.models import ContentType +from django.db.models.fields import FieldDoesNotExist from model_utils import ChoiceEnum from model_utils.fields import get_excerpt @@ -138,3 +140,17 @@ class QueryManagerTests(TestCase): def testOrdering(self): qs = Post.public_reversed.all() self.assertEquals([p.order for p in qs], [5, 4, 1, 0]) + +if 'south' in settings.INSTALLED_APPS: + class SouthFreezingTests(TestCase): + def test_introspector_adds_no_excerpt_field(self): + from south.modelsinspector import introspector + mf = Article._meta.get_field('body') + args, kwargs = introspector(mf) + self.assertEquals(kwargs['no_excerpt_field'], 'True') + + def test_no_excerpt_field_works(self): + from models import NoRendered + self.assertRaises(FieldDoesNotExist, + NoRendered._meta.get_field, + '_body_excerpt')