Skip to content

Commit

Permalink
Multi-level inheritance support; make spoke tests more flexible
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivo van der Wijk committed Jan 9, 2014
1 parent 2ca005e commit dc8e928
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 9 deletions.
22 changes: 20 additions & 2 deletions wheelcms_axle/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class ContentBase(models.Model):
## one could argue that this can be a property on a node
navigation = models.BooleanField(default=False)

meta_type = models.CharField(max_length=20)
meta_type = models.CharField(max_length=100)

## can be null for now, should move to null=False eventually
owner = models.ForeignKey(User, null=True)
Expand All @@ -87,7 +87,21 @@ class Meta:
abstract = True

def save(self, update_lm=True, *a, **b):
"""
Store the meta type of this class. The meta_type is used to
resolve a derived class from the base content type.
Since there may be multiple levels of inheritance, some magic
is required to store a "path" to the final derived class
"""
mytype = self.__class__.__name__.lower()
for base in self.__class__.__bases__:
if issubclass(base, Content):
if base is Content:
break
elif not base._meta.abstract:
mytype = base.__name__.lower() + '/' + mytype

self.meta_type = mytype

## default does not seem to work as expected?
Expand Down Expand Up @@ -137,7 +151,11 @@ def copy(self, node=None):

def content(self):
if self.meta_type:
return getattr(self, self.meta_type)
base = self
for part in self.meta_type.split('/'):
base = getattr(base, part)
return base
# return getattr(self, self.meta_type)

def spoke(self):
""" return the spoke for this model """
Expand Down
134 changes: 134 additions & 0 deletions wheelcms_axle/migrations/0004_auto__chg_field_content_meta_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):

# Changing field 'Content.meta_type'
db.alter_column('wheelcms_axle_content', 'meta_type', self.gf('django.db.models.fields.CharField')(max_length=100))

def backwards(self, orm):

# Changing field 'Content.meta_type'
db.alter_column('wheelcms_axle_content', 'meta_type', self.gf('django.db.models.fields.CharField')(max_length=20))

models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'taggit.tag': {
'Meta': {'object_name': 'Tag'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'})
},
'taggit.taggeditem': {
'Meta': {'object_name': 'TaggedItem'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
},
'wheelcms_axle.configuration': {
'Meta': {'object_name': 'Configuration'},
'analytics': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'head': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mailto': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'sender': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'sendermail': ('django.db.models.fields.EmailField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'theme': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '256', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'})
},
'wheelcms_axle.content': {
'Meta': {'object_name': 'Content'},
'allowed': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'classes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'content'", 'blank': 'True', 'to': "orm['wheelcms_axle.ContentClass']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'discussable': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'expire': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2034, 1, 12, 0, 0)', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'meta_type': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'navigation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentbase'", 'null': 'True', 'to': "orm['wheelcms_axle.Node']"}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
'publication': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'template': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '256'})
},
'wheelcms_axle.contentclass': {
'Meta': {'object_name': 'ContentClass'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '256'})
},
'wheelcms_axle.node': {
'Meta': {'object_name': 'Node'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'position': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'tree_path': ('django.db.models.fields.CharField', [], {'default': "'0x493a2c71fa168a901L'", 'unique': 'True', 'max_length': '255'})
},
'wheelcms_axle.paths': {
'Meta': {'unique_together': "(('language', 'path'),)", 'object_name': 'Paths'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'paths'", 'to': "orm['wheelcms_axle.Node']"}),
'path': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'wheelcms_axle.wheelprofile': {
'Meta': {'object_name': 'WheelProfile'},
'google': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inform': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'language': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '5'}),
'linkedin': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'mugshot': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'privacy': ('django.db.models.fields.CharField', [], {'default': "'registered'", 'max_length': '15'}),
'twitter': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'my_profile'", 'unique': 'True', 'to': "orm['auth.User']"})
}
}

complete_apps = ['wheelcms_axle']
2 changes: 2 additions & 0 deletions wheelcms_axle/tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ def localtyperegistry(request):
registry.register(request.cls.type)
for type in getattr(request.cls, 'types', []):
registry.register(type)
for type in getattr(request.cls, 'extra_types', []):
registry.register(type)

@pytest.fixture()
def localtemplateregistry(request):
Expand Down
21 changes: 14 additions & 7 deletions wheelcms_axle/tests/test_spoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ class BaseSpokeTest(object):
"""
type = None

def create_instance(self):
return self.type.model()

@classproperty
def typename(cls):
return cls.type.model.get_name()
Expand All @@ -47,7 +50,7 @@ def test_name(self, client):
"""
Name generation
"""
model = self.type.model()
model = self.create_instance()
model.save()
spoke = self.type(model)

Expand All @@ -58,7 +61,7 @@ def test_fields(self, client):
Test the fields() method that iterates over the
model instances fields
"""
model = self.type.model()
model = self.create_instance()
model.save()
spoke = self.type(model)
fields = dict(spoke.fields())
Expand All @@ -73,7 +76,7 @@ def test_spoke_save(self, client):
"""
M = self.type.model

model = M()
model = self.create_instance()
spoke = self.type(model)
spoke.save()
assert M.objects.all()[0] == model
Expand All @@ -84,6 +87,9 @@ class BaseSpokeTemplateTest(object):
"""
Test template related validation/behaviour
"""
def create_instance(self, **kw):
return self.type.model(**kw)

def valid_data(self):
""" return formdata required for validation to succeed """
return MockedQueryDict()
Expand All @@ -96,7 +102,7 @@ def test_empty(self, client, root):
""" An empty registry """
form = self.type.form(parent=root)
assert 'template' not in form.fields
model = self.type.model()
model = self.create_instance()
model.save()
assert self.type(model).view_template() == DEFAULT

Expand All @@ -106,13 +112,13 @@ def test_single(self, client, root):
form = self.type.form(parent=root)
assert 'template' in form.fields
assert form.fields['template'].choices == [('foo/bar', 'foo bar')]
model = self.type.model()
model = self.create_instance()
model.save()
assert self.type(model).view_template() == 'foo/bar'

def test_default(self, client):
""" If there's a default, it should be used """
model = self.type.model()
model = self.create_instance()
model.save()
template_registry.register(self.type, "foo/bar", "foo bar", default=False)
template_registry.register(self.type, "foo/bar2", "foo bar", default=True)
Expand All @@ -124,7 +130,7 @@ def test_explicit(self, client):
template_registry.register(self.type, "foo/bar", "foo bar", default=False)
template_registry.register(self.type, "foo/bar2", "foo bar", default=True)
template_registry.register(self.type, "foo/bar3", "foo bar", default=False)
model = self.type.model(template="foo/bar3")
model = self.create_instance(template="foo/bar3")
model.save()
assert self.type(model).view_template() == "foo/bar3"

Expand Down Expand Up @@ -251,6 +257,7 @@ def test_slug_generate_stopwords_empty_dashes(self, client):
data['title'] = 'are - a - they'
data['template'] = 'foo/bar'
data['language'] = 'en' ## use english stopwords
# import pytest; pytest.set_trace()

form = self.type.form(parent=p, data=data, files=self.valid_files())

Expand Down

0 comments on commit dc8e928

Please sign in to comment.