from django.contrib import admin
from django.contrib.contenttypes import generic
-from philo.models import Tag, Attribute, Relationship
+from philo.models import Tag, Attribute
+from philo.forms import AttributeForm, AttributeInlineFormSet
COLLAPSE_CLASSES = ('collapse', 'collapse-closed', 'closed',)
ct_fk_field = 'entity_object_id'
model = Attribute
extra = 1
- template = 'admin/philo/edit_inline/tabular_collapse.html'
- allow_add = True
- classes = COLLAPSE_CLASSES
-
-
-class RelationshipInline(generic.GenericTabularInline):
- ct_field = 'entity_content_type'
- ct_fk_field = 'entity_object_id'
- model = Relationship
- extra = 1
- template = 'admin/philo/edit_inline/tabular_collapse.html'
+ template = 'admin/philo/edit_inline/tabular_attribute.html'
allow_add = True
classes = COLLAPSE_CLASSES
+ form = AttributeForm
+ formset = AttributeInlineFormSet
+ exclude = ['value_object_id']
class EntityAdmin(admin.ModelAdmin):
- inlines = [AttributeInline, RelationshipInline]
+ inlines = [AttributeInline]
save_on_top = True
class TagAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
prepopulated_fields = {"slug": ("name",)}
+ search_fields = ["name"]
admin.site.register(Tag, TagAdmin)
\ No newline at end of file
from django.contrib import admin
from django import forms
-from philo.admin import widgets
from philo.admin.base import COLLAPSE_CLASSES
from philo.admin.nodes import ViewAdmin
from philo.models.pages import Page, Template, Contentlet, ContentReference
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+from philo.migrations import person_model, frozen_person
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'Blog'
+ db.create_table('penfield_blog', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ))
+ db.send_create_signal('penfield', ['Blog'])
+
+ # Adding model 'BlogEntry'
+ db.create_table('penfield_blogentry', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('blog', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='entries', null=True, to=orm['penfield.Blog'])),
+ ('author', self.gf('django.db.models.fields.related.ForeignKey')(related_name='blogentries', to=orm[person_model])),
+ ('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('content', self.gf('django.db.models.fields.TextField')()),
+ ('excerpt', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('penfield', ['BlogEntry'])
+
+ # Adding M2M table for field tags on 'BlogEntry'
+ db.create_table('penfield_blogentry_tags', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('blogentry', models.ForeignKey(orm['penfield.blogentry'], null=False)),
+ ('tag', models.ForeignKey(orm['philo.tag'], null=False))
+ ))
+ db.create_unique('penfield_blogentry_tags', ['blogentry_id', 'tag_id'])
+
+ # Adding model 'BlogView'
+ db.create_table('penfield_blogview', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('blog', self.gf('django.db.models.fields.related.ForeignKey')(related_name='blogviews', to=orm['penfield.Blog'])),
+ ('index_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='blog_index_related', to=orm['philo.Page'])),
+ ('entry_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='blog_entry_related', to=orm['philo.Page'])),
+ ('entry_archive_page', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='blog_entry_archive_related', null=True, to=orm['philo.Page'])),
+ ('tag_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='blog_tag_related', to=orm['philo.Page'])),
+ ('tag_archive_page', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='blog_tag_archive_related', null=True, to=orm['philo.Page'])),
+ ('entries_per_page', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
+ ('entry_permalink_style', self.gf('django.db.models.fields.CharField')(max_length=1)),
+ ('entry_permalink_base', self.gf('django.db.models.fields.CharField')(default='entries', max_length=255)),
+ ('tag_permalink_base', self.gf('django.db.models.fields.CharField')(default='tags', max_length=255)),
+ ('feed_suffix', self.gf('django.db.models.fields.CharField')(default='feed', max_length=255)),
+ ('feeds_enabled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)),
+ ))
+ db.send_create_signal('penfield', ['BlogView'])
+
+ # Adding model 'Newsletter'
+ db.create_table('penfield_newsletter', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ))
+ db.send_create_signal('penfield', ['Newsletter'])
+
+ # Adding model 'NewsletterArticle'
+ db.create_table('penfield_newsletterarticle', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('newsletter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='articles', to=orm['penfield.Newsletter'])),
+ ('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
+ ('lede', self.gf('philo.models.fields.TemplateField')(null=True, blank=True)),
+ ('full_text', self.gf('philo.models.fields.TemplateField')()),
+ ))
+ db.send_create_signal('penfield', ['NewsletterArticle'])
+
+ # Adding unique constraint on 'NewsletterArticle', fields ['newsletter', 'slug']
+ db.create_unique('penfield_newsletterarticle', ['newsletter_id', 'slug'])
+
+ # Adding M2M table for field authors on 'NewsletterArticle'
+ db.create_table('penfield_newsletterarticle_authors', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('newsletterarticle', models.ForeignKey(orm['penfield.newsletterarticle'], null=False)),
+ ('person', models.ForeignKey(orm[person_model], null=False))
+ ))
+ db.create_unique('penfield_newsletterarticle_authors', ['newsletterarticle_id', 'person_id'])
+
+ # Adding M2M table for field tags on 'NewsletterArticle'
+ db.create_table('penfield_newsletterarticle_tags', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('newsletterarticle', models.ForeignKey(orm['penfield.newsletterarticle'], null=False)),
+ ('tag', models.ForeignKey(orm['philo.tag'], null=False))
+ ))
+ db.create_unique('penfield_newsletterarticle_tags', ['newsletterarticle_id', 'tag_id'])
+
+ # Adding model 'NewsletterIssue'
+ db.create_table('penfield_newsletterissue', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('newsletter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='issues', to=orm['penfield.Newsletter'])),
+ ('numbering', self.gf('django.db.models.fields.CharField')(max_length=50)),
+ ))
+ db.send_create_signal('penfield', ['NewsletterIssue'])
+
+ # Adding unique constraint on 'NewsletterIssue', fields ['newsletter', 'numbering']
+ db.create_unique('penfield_newsletterissue', ['newsletter_id', 'numbering'])
+
+ # Adding M2M table for field articles on 'NewsletterIssue'
+ db.create_table('penfield_newsletterissue_articles', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('newsletterissue', models.ForeignKey(orm['penfield.newsletterissue'], null=False)),
+ ('newsletterarticle', models.ForeignKey(orm['penfield.newsletterarticle'], null=False))
+ ))
+ db.create_unique('penfield_newsletterissue_articles', ['newsletterissue_id', 'newsletterarticle_id'])
+
+ # Adding model 'NewsletterView'
+ db.create_table('penfield_newsletterview', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('newsletter', self.gf('django.db.models.fields.related.ForeignKey')(related_name='newsletterviews', to=orm['penfield.Newsletter'])),
+ ('index_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='newsletter_index_related', to=orm['philo.Page'])),
+ ('article_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='newsletter_article_related', to=orm['philo.Page'])),
+ ('article_archive_page', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='newsletter_article_archive_related', null=True, to=orm['philo.Page'])),
+ ('issue_page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='newsletter_issue_related', to=orm['philo.Page'])),
+ ('issue_archive_page', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='newsletter_issue_archive_related', null=True, to=orm['philo.Page'])),
+ ('article_permalink_style', self.gf('django.db.models.fields.CharField')(max_length=1)),
+ ('article_permalink_base', self.gf('django.db.models.fields.CharField')(default='articles', max_length=255)),
+ ('issue_permalink_base', self.gf('django.db.models.fields.CharField')(default='issues', max_length=255)),
+ ('feed_suffix', self.gf('django.db.models.fields.CharField')(default='feed', max_length=255)),
+ ('feeds_enabled', self.gf('django.db.models.fields.BooleanField')(default=False, blank=True)),
+ ))
+ db.send_create_signal('penfield', ['NewsletterView'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'Blog'
+ db.delete_table('penfield_blog')
+
+ # Deleting model 'BlogEntry'
+ db.delete_table('penfield_blogentry')
+
+ # Removing M2M table for field tags on 'BlogEntry'
+ db.delete_table('penfield_blogentry_tags')
+
+ # Deleting model 'BlogView'
+ db.delete_table('penfield_blogview')
+
+ # Deleting model 'Newsletter'
+ db.delete_table('penfield_newsletter')
+
+ # Deleting model 'NewsletterArticle'
+ db.delete_table('penfield_newsletterarticle')
+
+ # Removing unique constraint on 'NewsletterArticle', fields ['newsletter', 'slug']
+ db.delete_unique('penfield_newsletterarticle', ['newsletter_id', 'slug'])
+
+ # Removing M2M table for field authors on 'NewsletterArticle'
+ db.delete_table('penfield_newsletterarticle_authors')
+
+ # Removing M2M table for field tags on 'NewsletterArticle'
+ db.delete_table('penfield_newsletterarticle_tags')
+
+ # Deleting model 'NewsletterIssue'
+ db.delete_table('penfield_newsletterissue')
+
+ # Removing unique constraint on 'NewsletterIssue', fields ['newsletter', 'numbering']
+ db.delete_unique('penfield_newsletterissue', ['newsletter_id', 'numbering'])
+
+ # Removing M2M table for field articles on 'NewsletterIssue'
+ db.delete_table('penfield_newsletterissue_articles')
+
+ # Deleting model 'NewsletterView'
+ db.delete_table('penfield_newsletterview')
+
+
+ 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': {'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', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ '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': {'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'})
+ },
+ person_model: frozen_person,
+ 'penfield.blog': {
+ 'Meta': {'object_name': 'Blog'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.blogentry': {
+ 'Meta': {'object_name': 'BlogEntry'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blogentries'", 'to': "orm['%s']" % person_model}),
+ 'blog': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'entries'", 'null': 'True', 'to': "orm['penfield.Blog']"}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'blogentries'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['philo.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.blogview': {
+ 'Meta': {'object_name': 'BlogView'},
+ 'blog': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blogviews'", 'to': "orm['penfield.Blog']"}),
+ 'entries_per_page': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'entry_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'blog_entry_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'entry_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blog_entry_related'", 'to': "orm['philo.Page']"}),
+ 'entry_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'entries'", 'max_length': '255'}),
+ 'entry_permalink_style': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'feed_suffix': ('django.db.models.fields.CharField', [], {'default': "'feed'", 'max_length': '255'}),
+ 'feeds_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blog_index_related'", 'to': "orm['philo.Page']"}),
+ 'tag_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'blog_tag_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'tag_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blog_tag_related'", 'to': "orm['philo.Page']"}),
+ 'tag_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'tags'", 'max_length': '255'})
+ },
+ 'penfield.newsletter': {
+ 'Meta': {'object_name': 'Newsletter'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.newsletterarticle': {
+ 'Meta': {'unique_together': "(('newsletter', 'slug'),)", 'object_name': 'NewsletterArticle'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'newsletterarticles'", 'symmetrical': 'False', 'to': "orm['%s']" % person_model}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'full_text': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'lede': ('philo.models.fields.TemplateField', [], {'null': 'True', 'blank': 'True'}),
+ 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'articles'", 'to': "orm['penfield.Newsletter']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'newsletterarticles'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['philo.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.newsletterissue': {
+ 'Meta': {'unique_together': "(('newsletter', 'numbering'),)", 'object_name': 'NewsletterIssue'},
+ 'articles': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'issues'", 'symmetrical': 'False', 'to': "orm['penfield.NewsletterArticle']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['penfield.Newsletter']"}),
+ 'numbering': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.newsletterview': {
+ 'Meta': {'object_name': 'NewsletterView'},
+ 'article_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'newsletter_article_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'article_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletter_article_related'", 'to': "orm['philo.Page']"}),
+ 'article_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'articles'", 'max_length': '255'}),
+ 'article_permalink_style': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'feed_suffix': ('django.db.models.fields.CharField', [], {'default': "'feed'", 'max_length': '255'}),
+ 'feeds_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletter_index_related'", 'to': "orm['philo.Page']"}),
+ 'issue_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'newsletter_issue_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'issue_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletter_issue_related'", 'to': "orm['philo.Page']"}),
+ 'issue_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'issues'", 'max_length': '255'}),
+ 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletterviews'", 'to': "orm['penfield.Newsletter']"})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'json_value': ('django.db.models.fields.TextField', [], {}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['penfield']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+from philo.migrations import person_model, frozen_person
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding index on 'NewsletterArticle', fields ['full_text']
+ db.create_index('penfield_newsletterarticle', ['full_text'])
+
+
+ def backwards(self, orm):
+
+ # Removing index on 'NewsletterArticle', fields ['full_text']
+ db.delete_index('penfield_newsletterarticle', ['full_text'])
+
+
+ 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': {'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', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ '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': {'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'})
+ },
+ person_model: frozen_person,
+ 'penfield.blog': {
+ 'Meta': {'object_name': 'Blog'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.blogentry': {
+ 'Meta': {'object_name': 'BlogEntry'},
+ 'author': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blogentries'", 'to': "orm['%s']" % person_model}),
+ 'blog': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'entries'", 'null': 'True', 'to': "orm['penfield.Blog']"}),
+ 'content': ('django.db.models.fields.TextField', [], {}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'excerpt': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'blogentries'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['philo.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.blogview': {
+ 'Meta': {'object_name': 'BlogView'},
+ 'blog': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blogviews'", 'to': "orm['penfield.Blog']"}),
+ 'entries_per_page': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'entry_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'blog_entry_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'entry_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blog_entry_related'", 'to': "orm['philo.Page']"}),
+ 'entry_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'entries'", 'max_length': '255'}),
+ 'entry_permalink_style': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'feed_suffix': ('django.db.models.fields.CharField', [], {'default': "'feed'", 'max_length': '255'}),
+ 'feeds_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blog_index_related'", 'to': "orm['philo.Page']"}),
+ 'tag_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'blog_tag_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'tag_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blog_tag_related'", 'to': "orm['philo.Page']"}),
+ 'tag_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'tags'", 'max_length': '255'})
+ },
+ 'penfield.newsletter': {
+ 'Meta': {'object_name': 'Newsletter'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.newsletterarticle': {
+ 'Meta': {'unique_together': "(('newsletter', 'slug'),)", 'object_name': 'NewsletterArticle'},
+ 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'newsletterarticles'", 'symmetrical': 'False', 'to': "orm['%s']" % person_model}),
+ 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'full_text': ('philo.models.fields.TemplateField', [], {'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'lede': ('philo.models.fields.TemplateField', [], {'null': 'True', 'blank': 'True'}),
+ 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'articles'", 'to': "orm['penfield.Newsletter']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'newsletterarticles'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['philo.Tag']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.newsletterissue': {
+ 'Meta': {'unique_together': "(('newsletter', 'numbering'),)", 'object_name': 'NewsletterIssue'},
+ 'articles': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'issues'", 'symmetrical': 'False', 'to': "orm['penfield.NewsletterArticle']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'issues'", 'to': "orm['penfield.Newsletter']"}),
+ 'numbering': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'penfield.newsletterview': {
+ 'Meta': {'object_name': 'NewsletterView'},
+ 'article_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'newsletter_article_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'article_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletter_article_related'", 'to': "orm['philo.Page']"}),
+ 'article_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'articles'", 'max_length': '255'}),
+ 'article_permalink_style': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+ 'feed_suffix': ('django.db.models.fields.CharField', [], {'default': "'feed'", 'max_length': '255'}),
+ 'feeds_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletter_index_related'", 'to': "orm['philo.Page']"}),
+ 'issue_archive_page': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'newsletter_issue_archive_related'", 'null': 'True', 'to': "orm['philo.Page']"}),
+ 'issue_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletter_issue_related'", 'to': "orm['philo.Page']"}),
+ 'issue_permalink_base': ('django.db.models.fields.CharField', [], {'default': "'issues'", 'max_length': '255'}),
+ 'newsletter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'newsletterviews'", 'to': "orm['penfield.Newsletter']"})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['penfield']
from philo.models import Tag, Titled, Entity, MultiView, Page, register_value_model, TemplateField
from philo.exceptions import ViewCanNotProvideSubpath
from django.conf.urls.defaults import url, patterns, include
-from django.core.urlresolvers import reverse
from django.http import Http404
-from datetime import datetime
+from datetime import date, datetime
from philo.utils import paginate
from philo.contrib.penfield.validators import validate_pagination_count
from django.utils.feedgenerator import Atom1Feed, Rss201rev2Feed
def per_page(self):
return self.entries_per_page
- def get_subpath(self, obj):
+ def get_reverse_params(self, obj):
if isinstance(obj, BlogEntry):
if obj.blog == self.blog:
kwargs = {'slug': obj.slug}
kwargs.update({'month': str(obj.date.month).zfill(2)})
if self.entry_permalink_style == 'D':
kwargs.update({'day': str(obj.date.day).zfill(2)})
- return reverse(self.entry_view, urlconf=self, kwargs=kwargs)
+ return self.entry_view, [], kwargs
elif isinstance(obj, Tag):
if obj in self.blog.entry_tags:
- return reverse('entries_by_tag', urlconf=self, kwargs={'tag_slugs': obj.slug})
- elif isinstance(obj, (str, unicode)):
- split_obj = obj.split(':')
- if len(split_obj) > 1:
- kwargs = {}
- try:
- kwargs.update({'year': str(int(split_obj[1])).zfill(4)})
- if len(split_obj) > 2:
- kwargs.update({'month': str(int(split_obj[2])).zfill(2)})
- if len(split_obj) > 3:
- kwargs.update({'day': str(int(split_obj[3])).zfill(2)})
- return reverse('entries_by_day', urlconf=self, kwargs=kwargs)
- return reverse('entries_by_month', urlconf=self, kwargs=kwargs)
- return reverse('entries_by_year', urlconf=self, kwargs=kwargs)
- except:
- pass
+ return 'entries_by_tag', [], {'tag_slugs': obj.slug}
+ elif isinstance(obj, (date, datetime)):
+ kwargs = {
+ 'year': str(obj.year).zfill(4),
+ 'month': str(obj.month).zfill(2),
+ 'day': str(obj.day).zfill(2)
+ }
+ return 'entries_by_day', [], kwargs
raise ViewCanNotProvideSubpath
def get_context(self):
)
return urlpatterns
- def get_all_entries(self, request, node=None, extra_context=None):
+ def get_all_entries(self, request, extra_context=None):
return self.blog.entries.all(), extra_context
- def get_entries_by_ymd(self, request, year=None, month=None, day=None, node=None, extra_context=None):
+ def get_entries_by_ymd(self, request, year=None, month=None, day=None, extra_context=None):
if not self.entry_archive_page:
raise Http404
entries = self.blog.entries.all()
context.update({'year': year, 'month': month, 'day': day})
return entries, context
- def get_entries_by_tag(self, request, tag_slugs, node=None, extra_context=None):
+ def get_entries_by_tag(self, request, tag_slugs, extra_context=None):
tags = []
for tag_slug in tag_slugs.replace('+', '/').split('/'):
if tag_slug: # ignore blank slugs, handles for multiple consecutive separators (+ or /)
defaults.update(kwargs or {})
return super(BlogView, self).get_feed(feed_type, extra_context, defaults)
- def entry_view(self, request, slug, year=None, month=None, day=None, node=None, extra_context=None):
+ def entry_view(self, request, slug, year=None, month=None, day=None, extra_context=None):
entries = self.blog.entries.all()
if year:
entries = entries.filter(date__year=year)
context = self.get_context()
context.update(extra_context or {})
context.update({'entry': entry})
- return self.entry_page.render_to_response(node, request, extra_context=context)
+ return self.entry_page.render_to_response(request, extra_context=context)
- def tag_archive_view(self, request, node=None, extra_context=None):
+ def tag_archive_view(self, request, extra_context=None):
if not self.tag_archive_page:
raise Http404
context = {}
context.update(extra_context or {})
context.update({'blog': self.blog})
- return self.tag_archive_page.render_to_response(node, request, extra_context=context)
+ return self.tag_archive_page.render_to_response(request, extra_context=context)
class Newsletter(Entity, Titled):
authors = models.ManyToManyField(getattr(settings, 'PHILO_PERSON_MODULE', 'auth.User'), related_name='newsletterarticles')
date = models.DateTimeField(default=datetime.now)
lede = TemplateField(null=True, blank=True, verbose_name='Summary')
- full_text = TemplateField()
+ full_text = TemplateField(db_index=True)
tags = models.ManyToManyField(Tag, related_name='newsletterarticles', blank=True, null=True)
class Meta:
def __unicode__(self):
return self.newsletter.__unicode__()
- def get_subpath(self, obj):
+ def get_reverse_params(self, obj):
if isinstance(obj, NewsletterArticle):
if obj.newsletter == self.newsletter:
kwargs = {'slug': obj.slug}
kwargs.update({'month': str(obj.date.month).zfill(2)})
if self.article_permalink_style == 'D':
kwargs.update({'day': str(obj.date.day).zfill(2)})
- return reverse(self.article_view, urlconf=self, kwargs=kwargs)
+ return self.article_view, [], kwargs
elif isinstance(obj, NewsletterIssue):
if obj.newsletter == self.newsletter:
- return reverse('issue', urlconf=self, kwargs={'numbering': obj.numbering})
+ return 'issue', [], {'numbering': obj.numbering}
+ elif isinstance(obj, (date, datetime)):
+ kwargs = {
+ 'year': str(obj.year).zfill(4),
+ 'month': str(obj.month).zfill(2),
+ 'day': str(obj.day).zfill(2)
+ }
+ return 'articles_by_day', [], kwargs
raise ViewCanNotProvideSubpath
@property
def get_context(self):
return {'newsletter': self.newsletter}
- def get_all_articles(self, request, node, extra_context=None):
+ def get_all_articles(self, request, extra_context=None):
return self.newsletter.articles.all(), extra_context
- def get_articles_by_ymd(self, request, year, month=None, day=None, node=None, extra_context=None):
+ def get_articles_by_ymd(self, request, year, month=None, day=None, extra_context=None):
articles = self.newsletter.articles.filter(dat__year=year)
if month:
articles = articles.filter(date__month=month)
articles = articles.filter(date__day=day)
return articles
- def get_articles_by_issue(self, request, numbering, node=None, extra_context=None):
+ def get_articles_by_issue(self, request, numbering, extra_context=None):
try:
issue = self.newsletter.issues.get(numbering=numbering)
except:
context.update({'issue': issue})
return issue.articles.all(), context
- def article_view(self, request, slug, year=None, month=None, day=None, node=None, extra_context=None):
+ def article_view(self, request, slug, year=None, month=None, day=None, extra_context=None):
articles = self.newsletter.articles.all()
if year:
articles = articles.filter(date__year=year)
context = self.get_context()
context.update(extra_context or {})
context.update({'article': article})
- return self.article_page.render_to_response(node, request, extra_context=context)
+ return self.article_page.render_to_response(request, extra_context=context)
- def issue_archive_view(self, request, node=None, extra_context=None):
+ def issue_archive_view(self, request, extra_context=None):
if not self.issue_archive_page:
raise Http404
context = {}
context.update(extra_context or {})
context.update({'newsletter': self.newsletter})
- return self.issue_archive_page.render_to_response(node, request, extra_context=context)
+ return self.issue_archive_page.render_to_response(request, extra_context=context)
def add_item(self, feed, obj, kwargs=None):
defaults = {
"""
Wraps an object-fetching function and renders the results as a page.
"""
- def inner(request, node=None, extra_context=None, **kwargs):
- objects, extra_context = func(request=request, node=node, extra_context=extra_context, **kwargs)
+ def inner(request, extra_context=None, **kwargs):
+ objects, extra_context = func(request=request, extra_context=extra_context, **kwargs)
context = self.get_context()
context.update(extra_context or {})
else:
context.update({self.list_var: objects})
- return page.render_to_response(node, request, extra_context=context)
+ return page.render_to_response(request, extra_context=context)
return inner
"""
Wraps an object-fetching function and renders the results as a rss or atom feed.
"""
- def inner(request, node=None, extra_context=None, **kwargs):
- objects, extra_context = func(request=request, node=node, extra_context=extra_context, **kwargs)
+ def inner(request, extra_context=None, **kwargs):
+ objects, extra_context = func(request=request, extra_context=extra_context, **kwargs)
if 'HTTP_ACCEPT' in request.META and 'rss' in request.META['HTTP_ACCEPT'] and 'atom' not in request.META['HTTP_ACCEPT']:
feed_type = 'rss'
feed_type = 'atom'
current_site = Site.objects.get_current()
-
+ #Could this be done with request.path instead somehow?
feed_kwargs = {
- 'link': 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse(reverse_name, urlconf=self, kwargs=kwargs).strip('/'))
+ 'link': 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse(reverse_name, urlconf=self, kwargs=kwargs).strip('/'))
}
feed = self.get_feed(feed_type, extra_context, feed_kwargs)
for obj in objects:
kwargs = {
- 'link': 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), self.get_subpath(obj).strip('/'))
+ 'link': 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), self.get_subpath(obj).strip('/'))
}
self.add_item(feed, obj, kwargs=kwargs)
context.update(extra_dict or {})
return context
- def display_login_page(self, request, message, node=None, extra_context=None):
+ def display_login_page(self, request, message, extra_context=None):
request.session.set_test_cookie()
referrer = request.META.get('HTTP_REFERER', None)
redirect = '%s?%s' % (referrer[2], referrer[4])
if referrer is None:
- redirect = node.get_absolute_url()
+ redirect = request.node.get_absolute_url()
path = request.get_full_path()
if redirect != path:
'form': form
})
context.update(extra_context or {})
- return self.login_page.render_to_response(node, request, extra_context=context)
+ return self.login_page.render_to_response(request, extra_context=context)
- def login(self, request, node=None, extra_context=None):
+ def login(self, request, extra_context=None):
"""
Displays the login form for the given HttpRequest.
"""
if request.user.is_authenticated():
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
context = self.get_context(extra_context)
message = _("Please log in again, because your session has expired.")
else:
message = ""
- return self.display_login_page(request, message, node, context)
+ return self.display_login_page(request, message, context)
# Check that the user accepts cookies.
if not request.session.test_cookie_worked():
message = _("Looks like your browser isn't configured to accept cookies. Please enable cookies, reload this page, and try again.")
- return self.display_login_page(request, message, node, context)
+ return self.display_login_page(request, message, context)
else:
request.session.delete_test_cookie()
" Try '%s' instead.") % user.username
else:
message = _("Usernames cannot contain the '@' character.")
- return self.display_login_page(request, message, node, context)
+ return self.display_login_page(request, message, context)
# The user data is correct; log in the user in and continue.
else:
try:
redirect = request.session.pop('redirect')
except KeyError:
- redirect = node.get_absolute_url()
+ redirect = request.node.get_absolute_url()
return HttpResponseRedirect(redirect)
else:
- return self.display_login_page(request, ERROR_MESSAGE, node, context)
+ return self.display_login_page(request, ERROR_MESSAGE, context)
login = never_cache(login)
def logout(self, request):
return auth_views.logout(request, request.META['HTTP_REFERER'])
def login_required(self, view):
- def inner(request, node=None, *args, **kwargs):
+ def inner(request, *args, **kwargs):
if not request.user.is_authenticated():
login_url = reverse('login', urlconf=self).strip('/')
- return HttpResponseRedirect('%s%s/' % (node.get_absolute_url(), login_url))
- return view(request, node=node, *args, **kwargs)
+ return HttpResponseRedirect('%s%s/' % (request.node.get_absolute_url(), login_url))
+ return view(request, *args, **kwargs)
return inner
else:
send_mail(subject, text_content, from_email, [email])
- def password_reset(self, request, node=None, extra_context=None, token_generator=password_token_generator):
+ def password_reset(self, request, extra_context=None, token_generator=password_token_generator):
if request.user.is_authenticated():
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
if request.method == 'POST':
form = PasswordResetForm(request.POST)
current_site = Site.objects.get_current()
for user in form.users_cache:
token = token_generator.make_token(user)
- link = 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse('password_reset_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
+ link = 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse('password_reset_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
context = {
'link': link,
'username': user.username
context = self.get_context({'form': form})
context.update(extra_context or {})
- return self.password_reset_page.render_to_response(node, request, extra_context=context)
+ return self.password_reset_page.render_to_response(request, extra_context=context)
- def password_reset_confirm(self, request, node=None, extra_context=None, uidb36=None, token=None, token_generator=password_token_generator):
+ def password_reset_confirm(self, request, extra_context=None, uidb36=None, token=None, token_generator=password_token_generator):
"""
Checks that a given hash in a password reset link is valid. If so,
displays the password set form.
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS, "Password reset successful.")
- return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('login', urlconf=self).strip('/')))
+ return HttpResponseRedirect('/%s/%s/' % (request.node.get_absolute_url().strip('/'), reverse('login', urlconf=self).strip('/')))
else:
form = SetPasswordForm(user)
context = self.get_context({'form': form})
- return self.password_set_page.render_to_response(node, request, extra_context=context)
+ return self.password_set_page.render_to_response(request, extra_context=context)
raise Http404
- def password_change(self, request, node=None, extra_context=None):
+ def password_change(self, request, extra_context=None):
if request.method == 'POST':
form = PasswordChangeForm(request.user, request.POST)
if form.is_valid():
context = self.get_context({'form': form})
context.update(extra_context or {})
- return self.password_change_page.render_to_response(node, request, extra_context=context)
+ return self.password_change_page.render_to_response(request, extra_context=context)
- def register(self, request, node=None, extra_context=None, token_generator=registration_token_generator):
+ def register(self, request, extra_context=None, token_generator=registration_token_generator):
if request.user.is_authenticated():
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
if request.method == 'POST':
form = RegistrationForm(request.POST)
user = form.save()
current_site = Site.objects.get_current()
token = token_generator.make_token(user)
- link = 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse('register_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
+ link = 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse('register_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(user.id), 'token': token}).strip('/'))
context = {
'link': link
}
self.send_confirmation_email('Confirm account creation at %s' % current_site.name, user.email, self.register_confirmation_email, context)
messages.add_message(request, messages.SUCCESS, 'An email has been sent to %s with details on activating your account.' % user.email, fail_silently=True)
- return HttpResponseRedirect(node.get_absolute_url())
+ return HttpResponseRedirect(request.node.get_absolute_url())
else:
form = RegistrationForm()
context = self.get_context({'form': form})
context.update(extra_context or {})
- return self.register_page.render_to_response(node, request, extra_context=context)
+ return self.register_page.render_to_response(request, extra_context=context)
- def register_confirm(self, request, node=None, extra_context=None, uidb36=None, token=None, token_generator=registration_token_generator):
+ def register_confirm(self, request, extra_context=None, uidb36=None, token=None, token_generator=registration_token_generator):
"""
Checks that a given hash in a registration link is valid and activates
the given account. If so, log them in and redirect to
# if anything goes wrong, ABSOLUTELY make sure that the true password is restored.
user.password = true_password
user.save()
- return self.post_register_confirm_redirect(request, node)
+ return self.post_register_confirm_redirect(request)
raise Http404
- def post_register_confirm_redirect(self, request, node):
- return HttpResponseRedirect(node.get_absolute_url())
+ def post_register_confirm_redirect(self, request):
+ return HttpResponseRedirect(request.node.get_absolute_url())
class Meta:
abstract = True
return form_instances
- def account_view(self, request, node=None, extra_context=None, token_generator=email_token_generator, *args, **kwargs):
+ def account_view(self, request, extra_context=None, token_generator=email_token_generator, *args, **kwargs):
if request.method == 'POST':
form_instances = self.get_account_form_instances(request.user, request.POST)
current_email = request.user.email
current_site = Site.objects.get_current()
token = token_generator.make_token(request.user, email)
- link = 'http://%s/%s/%s/' % (current_site.domain, node.get_absolute_url().strip('/'), reverse('email_change_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(request.user.id), 'email': email.replace('@', '+'), 'token': token}).strip('/'))
+ link = 'http://%s/%s/%s/' % (current_site.domain, request.node.get_absolute_url().strip('/'), reverse('email_change_confirm', urlconf=self, kwargs={'uidb36': int_to_base36(request.user.id), 'email': email.replace('@', '+'), 'token': token}).strip('/'))
context = {
'link': link
}
'forms': form_instances
})
context.update(extra_context or {})
- return self.manage_account_page.render_to_response(node, request, extra_context=context)
+ return self.manage_account_page.render_to_response(request, extra_context=context)
def has_valid_account(self, user):
user_form, profile_form = self.get_account_forms()
inner = self.login_required(inner)
return inner
- def post_register_confirm_redirect(self, request, node):
+ def post_register_confirm_redirect(self, request):
messages.add_message(request, messages.INFO, 'Welcome! Please fill in some more information.', fail_silently=True)
- return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
+ return HttpResponseRedirect('/%s/%s/' % (request.node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
- def email_change_confirm(self, request, node=None, extra_context=None, uidb36=None, token=None, email=None, token_generator=email_token_generator):
+ def email_change_confirm(self, request, extra_context=None, uidb36=None, token=None, email=None, token_generator=email_token_generator):
"""
Checks that a given hash in an email change link is valid. If so, changes the email and redirects to the account page.
"""
user.email = email
user.save()
messages.add_message(request, messages.SUCCESS, 'Email changed successfully.')
- return HttpResponseRedirect('/%s/%s/' % (node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
+ return HttpResponseRedirect('/%s/%s/' % (request.node.get_absolute_url().strip('/'), reverse('account', urlconf=self).strip('/')))
raise Http404
+from django.core.exceptions import ImproperlyConfigured
+
+
+MIDDLEWARE_NOT_CONFIGURED = ImproperlyConfigured("""Philo requires the RequestNode middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'philo.middleware.RequestNodeMiddleware'.""")
+
+
class ViewDoesNotProvideSubpaths(Exception):
""" Raised by get_subpath when the View does not provide subpaths (the default). """
silent_variable_failure = True
--- /dev/null
+[
+ {
+ "pk": 1,
+ "model": "philo.tag",
+ "fields": {
+ "name": "Test tag",
+ "slug": "test-tag"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "philo.node",
+ "fields": {
+ "view_object_id": 1,
+ "slug": "never",
+ "parent": 3,
+ "view_content_type": [
+ "philo",
+ "page"
+ ]
+ }
+ },
+ {
+ "pk": 2,
+ "model": "philo.node",
+ "fields": {
+ "view_object_id": 1,
+ "slug": "blog",
+ "parent": 3,
+ "view_content_type": [
+ "penfield",
+ "blogview"
+ ]
+ }
+ },
+ {
+ "pk": 3,
+ "model": "philo.node",
+ "fields": {
+ "view_object_id": 1,
+ "slug": "root",
+ "parent": null,
+ "view_content_type": [
+ "philo",
+ "redirect"
+ ]
+ }
+ },
+ {
+ "pk": 1,
+ "model": "philo.redirect",
+ "fields": {
+ "status_code": 302,
+ "target": "never"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "philo.template",
+ "fields": {
+ "mimetype": "text/html",
+ "code": "Never is working!\r\n{% node_url %}",
+ "name": "Never",
+ "parent": null,
+ "documentation": "",
+ "slug": "never"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "philo.template",
+ "fields": {
+ "mimetype": "text/html",
+ "code": "An index page!\r\n{% node_url %}\r\n\r\n{% for entry in entries %}\r\n<h4><a href='{% node_url with entry %}'>{{ entry.title }}</a></h4>\r\n<div class='post content'>\r\n{{ entry.content }}\r\n</div>\r\n{% endfor %}",
+ "name": "Index",
+ "parent": null,
+ "documentation": "",
+ "slug": "index"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "philo.template",
+ "fields": {
+ "mimetype": "text/html",
+ "code": "Entry detail page.",
+ "name": "Entry",
+ "parent": null,
+ "documentation": "",
+ "slug": "entry"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "philo.template",
+ "fields": {
+ "mimetype": "text/html",
+ "code": "Tag page!",
+ "name": "Tag",
+ "parent": null,
+ "documentation": "",
+ "slug": "tag"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "philo.template",
+ "fields": {
+ "mimetype": "text/html",
+ "code": "Entry archive page!",
+ "name": "Entry Archives!",
+ "parent": null,
+ "documentation": "",
+ "slug": "entry-archives"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "philo.template",
+ "fields": {
+ "mimetype": "text/html",
+ "code": "tag archives...",
+ "name": "Tag Archives",
+ "parent": null,
+ "documentation": "",
+ "slug": "tag-archives"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "philo.page",
+ "fields": {
+ "template": 1,
+ "title": "Never"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "philo.page",
+ "fields": {
+ "template": 2,
+ "title": "Index"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "philo.page",
+ "fields": {
+ "template": 3,
+ "title": "Entry"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "philo.page",
+ "fields": {
+ "template": 4,
+ "title": "Tag"
+ }
+ },
+ {
+ "pk": 5,
+ "model": "philo.page",
+ "fields": {
+ "template": 5,
+ "title": "Entry Archive"
+ }
+ },
+ {
+ "pk": 6,
+ "model": "philo.page",
+ "fields": {
+ "template": 6,
+ "title": "Tag Archive Page"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "penfield.blog",
+ "fields": {
+ "slug": "free-lovin",
+ "title": "Free lovin'"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "penfield.blogentry",
+ "fields": {
+ "content": "Lorem ipsum.\r\n\r\nDolor sit amet.",
+ "author": 1,
+ "title": "First Entry",
+ "excerpt": "",
+ "blog": 1,
+ "date": "2010-10-20 10:38:58",
+ "slug": "first-entry",
+ "tags": [
+ 1
+ ]
+ }
+ },
+ {
+ "pk": 1,
+ "model": "penfield.blogview",
+ "fields": {
+ "entry_archive_page": 5,
+ "tag_page": 4,
+ "feed_suffix": "feed",
+ "entry_permalink_style": "D",
+ "tag_permalink_base": "tags",
+ "feeds_enabled": true,
+ "entries_per_page": null,
+ "tag_archive_page": 6,
+ "blog": 1,
+ "entry_permalink_base": "entries",
+ "index_page": 2,
+ "entry_page": 3
+ }
+ }
+]
from django import forms
from django.contrib.admin.widgets import AdminTextareaWidget
+from django.contrib.contenttypes.generic import BaseGenericInlineFormSet
+from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.db.models import Q
from django.forms.models import model_to_dict, fields_for_model, ModelFormMetaclass, ModelForm, BaseInlineFormSet
from django.template import loader, loader_tags, TemplateDoesNotExist, Context, Template as DjangoTemplate
from django.utils.datastructures import SortedDict
from philo.admin.widgets import ModelLookupWidget
-from philo.models import Entity, Template, Contentlet, ContentReference
-from philo.models.fields import RelationshipField
+from philo.models import Entity, Template, Contentlet, ContentReference, Attribute
from philo.utils import fattr
ignored = []
opts = entity_model._entity_meta
for f in opts.proxy_fields:
+ if not f.editable:
+ continue
if fields and not f.name in fields:
continue
if exclude and f.name in exclude:
instance = super(EntityForm, self).save(commit=False)
for f in instance._entity_meta.proxy_fields:
+ if not f.editable or not f.name in cleaned_data:
+ continue
if self._meta.fields and f.name not in self._meta.fields:
continue
+ if self._meta.exclude and f.name in self._meta.exclude:
+ continue
setattr(instance, f.attname, cleaned_data[f.name])
if commit:
return instance
+class AttributeForm(ModelForm):
+ def __init__(self, *args, **kwargs):
+ super(AttributeForm, self).__init__(*args, **kwargs)
+
+ # This is necessary because model forms store changes to self.instance in their clean method.
+ # Mutter mutter.
+ self._cached_value_ct = self.instance.value_content_type
+ self._cached_value = self.instance.value
+
+ if self.instance.value is not None:
+ value_field = self.instance.value.value_formfield()
+ if value_field:
+ self.fields['value'] = value_field
+ if hasattr(self.instance.value, 'content_type'):
+ self.fields['content_type'] = self.instance.value._meta.get_field('content_type').formfield(initial=getattr(self.instance.value.content_type, 'pk', None))
+
+ def save(self, *args, **kwargs):
+ # At this point, the cleaned_data has already been stored on self.instance.
+ if self.instance.value_content_type != self._cached_value_ct:
+ if self.instance.value is not None:
+ self._cached_value.delete()
+ if 'value' in self.cleaned_data:
+ del(self.cleaned_data['value'])
+
+ if self.instance.value_content_type is not None:
+ # Make a blank value of the new type! Run special code for content_type attributes.
+ if hasattr(self.instance.value_content_type.model_class(), 'content_type'):
+ if self._cached_value and hasattr(self._cached_value, 'content_type'):
+ new_ct = self._cached_value.content_type
+ else:
+ new_ct = None
+ new_value = self.instance.value_content_type.model_class().objects.create(content_type=new_ct)
+ else:
+ new_value = self.instance.value_content_type.model_class().objects.create()
+
+ new_value.apply_data(self.cleaned_data)
+ new_value.save()
+ self.instance.value = new_value
+ else:
+ # The value type is the same, but one of the fields has changed.
+ # Check to see if the changed value was the content type. We have to check the
+ # cleaned_data because self.instance.value.content_type was overridden.
+ if hasattr(self.instance.value, 'content_type') and 'content_type' in self.cleaned_data and 'value' in self.cleaned_data and (not hasattr(self._cached_value, 'content_type') or self._cached_value.content_type != self.cleaned_data['content_type']):
+ self.cleaned_data['value'] = None
+
+ self.instance.value.apply_data(self.cleaned_data)
+ self.instance.value.save()
+
+ super(AttributeForm, self).save(*args, **kwargs)
+ return self.instance
+
+ class Meta:
+ model = Attribute
+
+
+class AttributeInlineFormSet(BaseGenericInlineFormSet):
+ "Necessary to force the GenericInlineFormset to use the form's save method for new objects."
+ def save_new(self, form, commit):
+ setattr(form.instance, self.ct_field.get_attname(), ContentType.objects.get_for_model(self.instance).pk)
+ setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk)
+ return form.save()
+
+
class ContainerForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ContainerForm, self).__init__(*args, **kwargs)
--- /dev/null
+from django.contrib.sites.models import Site
+from philo.models import Node
+
+
+class LazyNode(object):
+ def __get__(self, request, obj_type=None):
+ if not hasattr(request, '_cached_node_path'):
+ return None
+
+ if not hasattr(request, '_found_node'):
+ try:
+ current_site = Site.objects.get_current()
+ except Site.DoesNotExist:
+ current_site = None
+
+ try:
+ node, subpath = Node.objects.get_with_path(request._cached_node_path, root=getattr(current_site, 'root_node', None), absolute_result=False)
+ except Node.DoesNotExist:
+ node = None
+
+ if node:
+ node.subpath = subpath
+
+ request._found_node = node
+
+ return request._found_node
+
+
+class RequestNodeMiddleware(object):
+ """Middleware to process the request's path and attach the closest ancestor node."""
+ def process_request(self, request):
+ request.__class__.node = LazyNode()
+
+ def process_view(self, request, view_func, view_args, view_kwargs):
+ request._cached_node_path = view_kwargs.get('path', '/')
\ No newline at end of file
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'Tag'
+ db.create_table('philo_tag', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=255, db_index=True)),
+ ))
+ db.send_create_signal('philo', ['Tag'])
+
+ # Adding model 'Attribute'
+ db.create_table('philo_attribute', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('entity_content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+ ('entity_object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('key', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('json_value', self.gf('django.db.models.fields.TextField')()),
+ ))
+ db.send_create_signal('philo', ['Attribute'])
+
+ # Adding unique constraint on 'Attribute', fields ['key', 'entity_content_type', 'entity_object_id']
+ db.create_unique('philo_attribute', ['key', 'entity_content_type_id', 'entity_object_id'])
+
+ # Adding model 'Relationship'
+ db.create_table('philo_relationship', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('entity_content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='relationship_entity_set', to=orm['contenttypes.ContentType'])),
+ ('entity_object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('key', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('value_content_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='relationship_value_set', null=True, to=orm['contenttypes.ContentType'])),
+ ('value_object_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('philo', ['Relationship'])
+
+ # Adding unique constraint on 'Relationship', fields ['key', 'entity_content_type', 'entity_object_id']
+ db.create_unique('philo_relationship', ['key', 'entity_content_type_id', 'entity_object_id'])
+
+ # Adding model 'Collection'
+ db.create_table('philo_collection', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('philo', ['Collection'])
+
+ # Adding model 'CollectionMember'
+ db.create_table('philo_collectionmember', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('collection', self.gf('django.db.models.fields.related.ForeignKey')(related_name='members', to=orm['philo.Collection'])),
+ ('index', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+ ('member_content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+ ('member_object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ))
+ db.send_create_signal('philo', ['CollectionMember'])
+
+ # Adding model 'Node'
+ db.create_table('philo_node', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['philo.Node'])),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('view_content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='node_view_set', to=orm['contenttypes.ContentType'])),
+ ('view_object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ))
+ db.send_create_signal('philo', ['Node'])
+
+ # Adding model 'Redirect'
+ db.create_table('philo_redirect', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('target', self.gf('django.db.models.fields.CharField')(max_length=200)),
+ ('status_code', self.gf('django.db.models.fields.IntegerField')(default=302)),
+ ))
+ db.send_create_signal('philo', ['Redirect'])
+
+ # Adding model 'File'
+ db.create_table('philo_file', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('mimetype', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+ ))
+ db.send_create_signal('philo', ['File'])
+
+ # Adding model 'Template'
+ db.create_table('philo_template', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['philo.Template'])),
+ ('slug', self.gf('django.db.models.fields.SlugField')(max_length=255, db_index=True)),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('documentation', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ('mimetype', self.gf('django.db.models.fields.CharField')(default='text/html', max_length=255)),
+ ('code', self.gf('philo.models.fields.TemplateField')()),
+ ))
+ db.send_create_signal('philo', ['Template'])
+
+ # Adding model 'Page'
+ db.create_table('philo_page', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('template', self.gf('django.db.models.fields.related.ForeignKey')(related_name='pages', to=orm['philo.Template'])),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('philo', ['Page'])
+
+ # Adding model 'Contentlet'
+ db.create_table('philo_contentlet', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='contentlets', to=orm['philo.Page'])),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('content', self.gf('philo.models.fields.TemplateField')()),
+ ))
+ db.send_create_signal('philo', ['Contentlet'])
+
+ # Adding model 'ContentReference'
+ db.create_table('philo_contentreference', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('page', self.gf('django.db.models.fields.related.ForeignKey')(related_name='contentreferences', to=orm['philo.Page'])),
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
+ ('content_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('philo', ['ContentReference'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'Tag'
+ db.delete_table('philo_tag')
+
+ # Deleting model 'Attribute'
+ db.delete_table('philo_attribute')
+
+ # Removing unique constraint on 'Attribute', fields ['key', 'entity_content_type', 'entity_object_id']
+ db.delete_unique('philo_attribute', ['key', 'entity_content_type_id', 'entity_object_id'])
+
+ # Deleting model 'Relationship'
+ db.delete_table('philo_relationship')
+
+ # Removing unique constraint on 'Relationship', fields ['key', 'entity_content_type', 'entity_object_id']
+ db.delete_unique('philo_relationship', ['key', 'entity_content_type_id', 'entity_object_id'])
+
+ # Deleting model 'Collection'
+ db.delete_table('philo_collection')
+
+ # Deleting model 'CollectionMember'
+ db.delete_table('philo_collectionmember')
+
+ # Deleting model 'Node'
+ db.delete_table('philo_node')
+
+ # Deleting model 'Redirect'
+ db.delete_table('philo_redirect')
+
+ # Deleting model 'File'
+ db.delete_table('philo_file')
+
+ # Deleting model 'Template'
+ db.delete_table('philo_template')
+
+ # Deleting model 'Page'
+ db.delete_table('philo_page')
+
+ # Deleting model 'Contentlet'
+ db.delete_table('philo_contentlet')
+
+ # Deleting model 'ContentReference'
+ db.delete_table('philo_contentreference')
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'json_value': ('django.db.models.fields.TextField', [], {}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding field 'Attribute.value'
+ db.add_column('philo_attribute', 'value', self.gf('philo.models.fields.JSONField')(default=''), keep_default=False)
+
+
+ def backwards(self, orm):
+
+ # Deleting field 'Attribute.value'
+ db.delete_column('philo_attribute', 'value_json')
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'json_value': ('django.db.models.fields.TextField', [], {}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ for attribute in orm.Attribute.objects.all():
+ attribute.value_json = attribute.json_value
+ attribute.save()
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+ for attribute in orm.Attribute.objects.all():
+ attribute.json_value = attribute.value_json
+ attribute.save()
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'json_value': ('django.db.models.fields.TextField', [], {}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting field 'Attribute.json_value'
+ db.delete_column('philo_attribute', 'json_value')
+
+
+ def backwards(self, orm):
+
+ # Adding field 'Attribute.json_value'
+ db.add_column('philo_attribute', 'json_value', self.gf('django.db.models.fields.TextField')(default=''), keep_default=False)
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'ManyToManyValue'
+ db.create_table('philo_manytomanyvalue', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='many_to_many_value_set', null=True, to=orm['contenttypes.ContentType'])),
+ ('object_ids', self.gf('django.db.models.fields.CommaSeparatedIntegerField')(max_length=300, null=True, blank=True)),
+ ))
+ db.send_create_signal('philo', ['ManyToManyValue'])
+
+ # Adding model 'JSONValue'
+ db.create_table('philo_jsonvalue', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('value', self.gf('philo.models.fields.JSONField')()),
+ ))
+ db.send_create_signal('philo', ['JSONValue'])
+
+ # Adding model 'ForeignKeyValue'
+ db.create_table('philo_foreignkeyvalue', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='foreign_key_value_set', null=True, to=orm['contenttypes.ContentType'])),
+ ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+ ))
+ db.send_create_signal('philo', ['ForeignKeyValue'])
+
+ # Adding field 'Attribute.value_content_type'
+ db.add_column('philo_attribute', 'value_content_type', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='attribute_value_set', null=True, to=orm['contenttypes.ContentType']), keep_default=False)
+
+ # Adding field 'Attribute.value_object_id'
+ db.add_column('philo_attribute', 'value_object_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True), keep_default=False)
+
+ # Adding unique constraint on 'Attribute', fields ['value_object_id', 'value_content_type']
+ db.create_unique('philo_attribute', ['value_object_id', 'value_content_type_id'])
+
+ # Manual addition! This is necessary to immediately cause contenttype creation.
+ # (needed for the next migration)
+ db.send_pending_create_signals()
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'ManyToManyValue'
+ db.delete_table('philo_manytomanyvalue')
+
+ # Deleting model 'JSONValue'
+ db.delete_table('philo_jsonvalue')
+
+ # Deleting model 'ForeignKeyValue'
+ db.delete_table('philo_foreignkeyvalue')
+
+ # Deleting field 'Attribute.value_content_type'
+ db.delete_column('philo_attribute', 'value_content_type_id')
+
+ # Deleting field 'Attribute.value_object_id'
+ db.delete_column('philo_attribute', 'value_object_id')
+
+ # Removing unique constraint on 'Attribute', fields ['value_object_id', 'value_content_type']
+ db.delete_unique('philo_attribute', ['value_object_id', 'value_content_type_id'])
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attribute_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.foreignkeyvalue': {
+ 'Meta': {'object_name': 'ForeignKeyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'foreign_key_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.jsonvalue': {
+ 'Meta': {'object_name': 'JSONValue'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.manytomanyvalue': {
+ 'Meta': {'object_name': 'ManyToManyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'many_to_many_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_ids': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+ def forwards(self, orm):
+ "Write your forwards methods here."
+ ContentType = orm['contenttypes.ContentType']
+ json_ct = ContentType.objects.get(app_label='philo', model='jsonvalue')
+ fk_ct = ContentType.objects.get(app_label='philo', model='foreignkeyvalue')
+
+ for attribute in orm.Attribute.objects.all():
+ value = orm.JSONValue(value_json=attribute.value_json)
+ value.save()
+ attribute.value_content_type = json_ct
+ attribute.value_object_id = value.id
+ attribute.save()
+
+ for relationship in orm.Relationship.objects.all():
+ attribute = orm.Attribute(entity_content_type=relationship.entity_content_type, entity_object_id=relationship.entity_object_id, key=relationship.key)
+ value = orm.ForeignKeyValue(content_type=relationship.value_content_type, object_id=relationship.value_object_id)
+ value.save()
+ attribute.value_content_type = fk_ct
+ attribute.value_object_id = value.id
+ attribute.save()
+ relationship.delete()
+
+
+ def backwards(self, orm):
+ "Write your backwards methods here."
+ if orm.ManyToManyValue.objects.count():
+ raise NotImplementedError("ManyToManyValue objects cannot be unmigrated.")
+
+ ContentType = orm['contenttypes.ContentType']
+ json_ct = ContentType.objects.get(app_label='philo', model='jsonvalue')
+ fk_ct = ContentType.objects.get(app_label='philo', model='foreignkeyvalue')
+
+ for attribute in orm.Attribute.objects.all():
+ if attribute.value_content_type == json_ct:
+ value = orm.JSONValue.objects.get(pk=attribute.value_object_id)
+ attribute.value_json = value.value_json
+ attribute.save()
+ elif attribute.value_content_type == fk_ct:
+ value = orm.ForeignKeyValue.objects.get(pk=attribute.value_object_id)
+ relationship = orm.Relationship(value_content_type=value.content_type, value_object_id=value.object_id, key=attribute.key, entity_content_type=attribute.entity_content_type, entity_object_id=attribute.entity_object_id)
+ relationship.save()
+ attribute.delete()
+ else:
+ ct = attribute.value_content_type
+ raise NotImplementedError("Class cannot be converted: %s.%s" % (ct.app_label, ct.model))
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attribute_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.foreignkeyvalue': {
+ 'Meta': {'object_name': 'ForeignKeyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'foreign_key_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.jsonvalue': {
+ 'Meta': {'object_name': 'JSONValue'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.manytomanyvalue': {
+ 'Meta': {'object_name': 'ManyToManyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'many_to_many_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_ids': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.relationship': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'),)", 'object_name': 'Relationship'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'relationship_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'relationship_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting model 'Relationship'
+ db.delete_table('philo_relationship')
+
+ # Deleting field 'Attribute.value'
+ db.delete_column('philo_attribute', 'value_json')
+
+
+ def backwards(self, orm):
+
+ # Adding model 'Relationship'
+ db.create_table('philo_relationship', (
+ ('key', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('entity_content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='relationship_entity_set', to=orm['contenttypes.ContentType'])),
+ ('entity_object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('value_object_id', self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True)),
+ ('value_content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='relationship_value_set', null=True, to=orm['contenttypes.ContentType'], blank=True)),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ))
+ db.send_create_signal('philo', ['Relationship'])
+
+ # Adding field 'Attribute.value'
+ db.add_column('philo_attribute', 'value', self.gf('philo.models.fields.JSONField')(default='null'), keep_default=False)
+
+
+ models = {
+ 'contenttypes.contenttype': {
+ 'Meta': {'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'})
+ },
+ 'philo.attribute': {
+ 'Meta': {'unique_together': "(('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))", 'object_name': 'Attribute'},
+ 'entity_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_entity_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'entity_object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'value_content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attribute_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'value_object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.collection': {
+ 'Meta': {'object_name': 'Collection'},
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.collectionmember': {
+ 'Meta': {'object_name': 'CollectionMember'},
+ 'collection': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'members'", 'to': "orm['philo.Collection']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'index': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'member_content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'member_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.contentlet': {
+ 'Meta': {'object_name': 'Contentlet'},
+ 'content': ('philo.models.fields.TemplateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentlets'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.contentreference': {
+ 'Meta': {'object_name': 'ContentReference'},
+ 'content_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
+ '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': '255'}),
+ 'page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contentreferences'", 'to': "orm['philo.Page']"})
+ },
+ 'philo.file': {
+ 'Meta': {'object_name': 'File'},
+ 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.foreignkeyvalue': {
+ 'Meta': {'object_name': 'ForeignKeyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'foreign_key_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'philo.jsonvalue': {
+ 'Meta': {'object_name': 'JSONValue'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'value': ('philo.models.fields.JSONField', [], {})
+ },
+ 'philo.manytomanyvalue': {
+ 'Meta': {'object_name': 'ManyToManyValue'},
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'many_to_many_value_set'", 'null': 'True', 'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'object_ids': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'max_length': '300', 'null': 'True', 'blank': 'True'})
+ },
+ 'philo.node': {
+ 'Meta': {'object_name': 'Node'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Node']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'view_content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'node_view_set'", 'to': "orm['contenttypes.ContentType']"}),
+ 'view_object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'philo.page': {
+ 'Meta': {'object_name': 'Page'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['philo.Template']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'philo.redirect': {
+ 'Meta': {'object_name': 'Redirect'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'status_code': ('django.db.models.fields.IntegerField', [], {'default': '302'}),
+ 'target': ('django.db.models.fields.CharField', [], {'max_length': '200'})
+ },
+ 'philo.tag': {
+ 'Meta': {'object_name': 'Tag'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'})
+ },
+ 'philo.template': {
+ 'Meta': {'object_name': 'Template'},
+ 'code': ('philo.models.fields.TemplateField', [], {}),
+ 'documentation': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'mimetype': ('django.db.models.fields.CharField', [], {'default': "'text/html'", 'max_length': '255'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['philo.Template']"}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'db_index': 'True'})
+ }
+ }
+
+ complete_apps = ['philo']
--- /dev/null
+from south.creator.freezer import prep_for_freeze
+from django.conf import settings
+from django.db import models
+
+
+person_model = getattr(settings, 'PHILO_PERSON_MODULE', 'auth.User')
+
+
+def freeze_person_model():
+ try:
+ app_label, model = person_model.split('.')
+ except ValueError:
+ raise ValueError("Invalid PHILO_PERSON_MODULE definition: %s" % person_model)
+
+ model = models.get_model(app_label, model.lower())
+
+ if model is None:
+ raise ValueError("PHILO_PERSON_MODULE not found: %s" % person_model)
+
+ return prep_for_freeze(model)
+
+
+frozen_person = freeze_person_model()
\ No newline at end of file
+from django import forms
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from django.utils import simplejson as json
from django.core.exceptions import ObjectDoesNotExist
from philo.exceptions import AncestorDoesNotExist
-from philo.utils import ContentTypeRegistryLimiter
+from philo.models.fields import JSONField
+from philo.utils import ContentTypeRegistryLimiter, ContentTypeSubclassLimiter
from philo.signals import entity_class_prepared
from philo.validators import json_validator
from UserDict import DictMixin
abstract = True
-class Attribute(models.Model):
- entity_content_type = models.ForeignKey(ContentType, verbose_name='Entity type')
- entity_object_id = models.PositiveIntegerField(verbose_name='Entity ID')
- entity = generic.GenericForeignKey('entity_content_type', 'entity_object_id')
- key = models.CharField(max_length=255)
- json_value = models.TextField(verbose_name='Value (JSON)', help_text='This value must be valid JSON.', validators=[json_validator])
-
- def get_value(self):
- return json.loads(self.json_value)
-
- def set_value(self, value):
- self.json_value = json.dumps(value)
-
- def delete_value(self):
- self.json_value = json.dumps(None)
+value_content_type_limiter = ContentTypeRegistryLimiter()
+
+
+def register_value_model(model):
+ value_content_type_limiter.register_class(model)
+
+
+def unregister_value_model(model):
+ value_content_type_limiter.unregister_class(model)
+
+
+class AttributeValue(models.Model):
+ attribute = generic.GenericRelation('Attribute', content_type_field='value_content_type', object_id_field='value_object_id')
+ def apply_data(self, data):
+ raise NotImplementedError
- value = property(get_value, set_value, delete_value)
+ def value_formfield(self, **kwargs):
+ raise NotImplementedError
def __unicode__(self):
- return u'"%s": %s' % (self.key, self.value)
+ return unicode(self.value)
class Meta:
- app_label = 'philo'
- unique_together = ('key', 'entity_content_type', 'entity_object_id')
+ abstract = True
-value_content_type_limiter = ContentTypeRegistryLimiter()
+attribute_value_limiter = ContentTypeSubclassLimiter(AttributeValue)
-def register_value_model(model):
- value_content_type_limiter.register_class(model)
+class JSONValue(AttributeValue):
+ value = JSONField() #verbose_name='Value (JSON)', help_text='This value must be valid JSON.')
+
+ def __unicode__(self):
+ return self.value_json
+
+ def value_formfield(self, **kwargs):
+ kwargs['initial'] = self.value_json
+ return self._meta.get_field('value').formfield(**kwargs)
+
+ def apply_data(self, cleaned_data):
+ self.value = cleaned_data.get('value', None)
+
+ class Meta:
+ app_label = 'philo'
-def unregister_value_model(model):
- value_content_type_limiter.unregister_class(model)
+class ForeignKeyValue(AttributeValue):
+ content_type = models.ForeignKey(ContentType, related_name='foreign_key_value_set', limit_choices_to=value_content_type_limiter, verbose_name='Value type', null=True, blank=True)
+ object_id = models.PositiveIntegerField(verbose_name='Value ID', null=True, blank=True)
+ value = generic.GenericForeignKey()
+
+ def value_formfield(self, form_class=forms.ModelChoiceField, **kwargs):
+ if self.content_type is None:
+ return None
+ kwargs.update({'initial': self.object_id, 'required': False})
+ return form_class(self.content_type.model_class()._default_manager.all(), **kwargs)
+
+ def apply_data(self, cleaned_data):
+ if 'value' in cleaned_data and cleaned_data['value'] is not None:
+ self.value = cleaned_data['value']
+ else:
+ self.content_type = cleaned_data.get('content_type', None)
+ # If there is no value set in the cleaned data, clear the stored value.
+ self.object_id = None
+
+ class Meta:
+ app_label = 'philo'
+
+class ManyToManyValue(AttributeValue):
+ # TODO: Change object_ids to object_pks.
+ content_type = models.ForeignKey(ContentType, related_name='many_to_many_value_set', limit_choices_to=value_content_type_limiter, verbose_name='Value type', null=True, blank=True)
+ object_ids = models.CommaSeparatedIntegerField(max_length=300, verbose_name='Value IDs', null=True, blank=True)
+
+ def get_object_id_list(self):
+ if not self.object_ids:
+ return []
+ else:
+ return self.object_ids.split(',')
+
+ def get_value(self):
+ if self.content_type is None:
+ return None
+
+ return self.content_type.model_class()._default_manager.filter(id__in=self.get_object_id_list())
+
+ def set_value(self, value):
+ if value is None:
+ self.object_ids = ""
+ return
+ if not isinstance(value, models.query.QuerySet):
+ raise TypeError("Value must be a QuerySet.")
+ self.content_type = ContentType.objects.get_for_model(value.model)
+ self.object_ids = ','.join([`value` for value in value.values_list('id', flat=True)])
+
+ value = property(get_value, set_value)
+
+ def value_formfield(self, form_class=forms.ModelMultipleChoiceField, **kwargs):
+ if self.content_type is None:
+ return None
+ kwargs.update({'initial': self.get_object_id_list(), 'required': False})
+ return form_class(self.content_type.model_class()._default_manager.all(), **kwargs)
+
+ def apply_data(self, cleaned_data):
+ if 'value' in cleaned_data and cleaned_data['value'] is not None:
+ self.value = cleaned_data['value']
+ else:
+ self.content_type = cleaned_data.get('content_type', None)
+ # If there is no value set in the cleaned data, clear the stored value.
+ self.object_ids = ""
+
+ class Meta:
+ app_label = 'philo'
-class Relationship(models.Model):
- entity_content_type = models.ForeignKey(ContentType, related_name='relationship_entity_set', verbose_name='Entity type')
+class Attribute(models.Model):
+ entity_content_type = models.ForeignKey(ContentType, related_name='attribute_entity_set', verbose_name='Entity type')
entity_object_id = models.PositiveIntegerField(verbose_name='Entity ID')
entity = generic.GenericForeignKey('entity_content_type', 'entity_object_id')
- key = models.CharField(max_length=255)
- value_content_type = models.ForeignKey(ContentType, related_name='relationship_value_set', limit_choices_to=value_content_type_limiter, verbose_name='Value type', null=True, blank=True)
+
+ value_content_type = models.ForeignKey(ContentType, related_name='attribute_value_set', limit_choices_to=attribute_value_limiter, verbose_name='Value type', null=True, blank=True)
value_object_id = models.PositiveIntegerField(verbose_name='Value ID', null=True, blank=True)
value = generic.GenericForeignKey('value_content_type', 'value_object_id')
+ key = models.CharField(max_length=255)
+
def __unicode__(self):
return u'"%s": %s' % (self.key, self.value)
class Meta:
app_label = 'philo'
- unique_together = ('key', 'entity_content_type', 'entity_object_id')
+ unique_together = (('key', 'entity_content_type', 'entity_object_id'), ('value_content_type', 'value_object_id'))
class QuerySetMapper(object, DictMixin):
def __getitem__(self, key):
try:
- return self.queryset.get(key__exact=key).value
+ value = self.queryset.get(key__exact=key).value
except ObjectDoesNotExist:
if self.passthrough is not None:
return self.passthrough.__getitem__(key)
raise KeyError
+ else:
+ if value is not None:
+ return value.value
+ return value
def keys(self):
keys = set(self.queryset.values_list('key', flat=True).distinct())
__metaclass__ = EntityBase
attribute_set = generic.GenericRelation(Attribute, content_type_field='entity_content_type', object_id_field='entity_object_id')
- relationship_set = generic.GenericRelation(Relationship, content_type_field='entity_content_type', object_id_field='entity_object_id')
@property
def attributes(self):
return QuerySetMapper(self.attribute_set)
- @property
- def relationships(self):
- return QuerySetMapper(self.relationship_set)
-
@property
def _added_attribute_registry(self):
if not hasattr(self, '_real_added_attribute_registry'):
self._real_removed_attribute_registry = []
return self._real_removed_attribute_registry
- @property
- def _added_relationship_registry(self):
- if not hasattr(self, '_real_added_relationship_registry'):
- self._real_added_relationship_registry = {}
- return self._real_added_relationship_registry
-
- @property
- def _removed_relationship_registry(self):
- if not hasattr(self, '_real_removed_relationship_registry'):
- self._real_removed_relationship_registry = []
- return self._real_removed_relationship_registry
-
def save(self, *args, **kwargs):
super(Entity, self).save(*args, **kwargs)
self.attribute_set.filter(key__exact=key).delete()
del self._removed_attribute_registry[:]
- for key, value in self._added_attribute_registry.items():
+ for field, value in self._added_attribute_registry.items():
try:
- attribute = self.attribute_set.get(key__exact=key)
+ attribute = self.attribute_set.get(key__exact=field.key)
except Attribute.DoesNotExist:
attribute = Attribute()
attribute.entity = self
- attribute.key = key
- attribute.value = value
+ attribute.key = field.key
+
+ field.set_attribute_value(attribute, value)
attribute.save()
self._added_attribute_registry.clear()
-
- for key in self._removed_relationship_registry:
- self.relationship_set.filter(key__exact=key).delete()
- del self._removed_relationship_registry[:]
-
- for key, value in self._added_relationship_registry.items():
- try:
- relationship = self.relationship_set.get(key__exact=key)
- except Relationship.DoesNotExist:
- relationship = Relationship()
- relationship.entity = self
- relationship.key = key
- relationship.value = value
- relationship.save()
- self._added_relationship_registry.clear()
class Meta:
abstract = True
-from django.db import models
from django import forms
-from django.core.exceptions import FieldError
+from django.core.exceptions import FieldError, ValidationError
+from django.db import models
+from django.db.models.fields import NOT_PROVIDED
+from django.utils import simplejson as json
from django.utils.text import capfirst
-from philo.models.base import Entity
from philo.signals import entity_class_prepared
-from philo.validators import TemplateValidator
+from philo.validators import TemplateValidator, json_validator
-__all__ = ('AttributeField', 'RelationshipField')
+__all__ = ('JSONAttribute', 'ForeignKeyAttribute', 'ManyToManyAttribute')
class EntityProxyField(object):
descriptor_class = None
- def __init__(self, *args, **kwargs):
+ def __init__(self, verbose_name=None, help_text=None, default=NOT_PROVIDED, editable=True, *args, **kwargs):
if self.descriptor_class is None:
raise NotImplementedError('EntityProxyField subclasses must specify a descriptor_class.')
- self.verbose_name = kwargs.get('verbose_name', None)
- self.help_text = kwargs.get('help_text', None)
+ self.verbose_name = verbose_name
+ self.help_text = help_text
+ self.default = default
+ self.editable = editable
def actually_contribute_to_class(self, sender, **kwargs):
sender._entity_meta.add_proxy_field(self)
setattr(sender, self.attname, self.descriptor_class(self))
def contribute_to_class(self, cls, name):
+ from philo.models.base import Entity
if issubclass(cls, Entity):
self.name = name
self.attname = name
def value_from_object(self, obj):
return getattr(obj, self.attname)
+
+ def has_default(self):
+ return self.default is not NOT_PROVIDED
class AttributeFieldDescriptor(object):
def __get__(self, instance, owner):
if instance:
- if self.field.key in instance._added_attribute_registry:
- return instance._added_attribute_registry[self.field.key]
- if self.field.key in instance._removed_attribute_registry:
+ if self.field in instance._added_attribute_registry:
+ return instance._added_attribute_registry[self.field]
+ if self.field in instance._removed_attribute_registry:
return None
try:
return instance.attributes[self.field.key]
except KeyError:
return None
else:
- raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
+ return None
def __set__(self, instance, value):
- if self.field.key in instance._removed_attribute_registry:
- instance._removed_attribute_registry.remove(self.field.key)
- instance._added_attribute_registry[self.field.key] = value
+ raise NotImplementedError('AttributeFieldDescriptor subclasses must implement a __set__ method.')
def __delete__(self, instance):
- if self.field.key in instance._added_attribute_registry:
- del instance._added_attribute_registry[self.field.key]
- instance._removed_attribute_registry.append(self.field.key)
+ if self.field in instance._added_attribute_registry:
+ del instance._added_attribute_registry[self.field]
+ instance._removed_attribute_registry.append(self.field)
+
+
+class JSONAttributeDescriptor(AttributeFieldDescriptor):
+ def __set__(self, instance, value):
+ if self.field in instance._removed_attribute_registry:
+ instance._removed_attribute_registry.remove(self.field)
+ instance._added_attribute_registry[self.field] = value
+
+
+class ForeignKeyAttributeDescriptor(AttributeFieldDescriptor):
+ def __set__(self, instance, value):
+ if isinstance(value, (models.Model, type(None))):
+ if self.field in instance._removed_attribute_registry:
+ instance._removed_attribute_registry.remove(self.field)
+ instance._added_attribute_registry[self.field] = value
+ else:
+ raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
+
+
+class ManyToManyAttributeDescriptor(AttributeFieldDescriptor):
+ def __set__(self, instance, value):
+ if isinstance(value, models.QuerySet):
+ if self.field in instance._removed_attribute_registry:
+ instance._removed_attribute_registry.remove(self.field)
+ instance._added_attribute_registry[self.field] = value
+ else:
+ raise AttributeError('The \'%s\' attribute can only be set to a QuerySet.' % self.field.name)
class AttributeField(EntityProxyField):
- descriptor_class = AttributeFieldDescriptor
+ def contribute_to_class(self, cls, name):
+ super(AttributeField, self).contribute_to_class(cls, name)
+ if self.key is None:
+ self.key = name
+
+ def set_attribute_value(self, attribute, value, value_class):
+ if not isinstance(attribute.value, value_class):
+ if isinstance(attribute.value, models.Model):
+ attribute.value.delete()
+ new_value = value_class()
+ else:
+ new_value = attribute.value
+ new_value.value = value
+ new_value.save()
+ attribute.value = new_value
+
+
+class JSONAttribute(AttributeField):
+ descriptor_class = JSONAttributeDescriptor
def __init__(self, field_template=None, key=None, **kwargs):
super(AttributeField, self).__init__(**kwargs)
field_template = models.CharField(max_length=255)
self.field_template = field_template
- def contribute_to_class(self, cls, name):
- super(AttributeField, self).contribute_to_class(cls, name)
- if self.key is None:
- self.key = name
-
def formfield(self, **kwargs):
defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ if self.has_default():
+ defaults['initial'] = self.default
defaults.update(kwargs)
return self.field_template.formfield(**defaults)
-
-
-class RelationshipFieldDescriptor(object):
- def __init__(self, field):
- self.field = field
- def __get__(self, instance, owner):
- if instance:
- if self.field.key in instance._added_relationship_registry:
- return instance._added_relationship_registry[self.field.key]
- if self.field.key in instance._removed_relationship_registry:
- return None
- try:
- return instance.relationships[self.field.key]
- except KeyError:
- return None
- else:
- raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
-
- def __set__(self, instance, value):
- if isinstance(value, (models.Model, type(None))):
- if self.field.key in instance._removed_relationship_registry:
- instance._removed_relationship_registry.remove(self.field.key)
- instance._added_relationship_registry[self.field.key] = value
- else:
- raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
+ def value_from_object(self, obj):
+ try:
+ return getattr(obj, self.attname)
+ except AttributeError:
+ return None
- def __delete__(self, instance):
- if self.field.key in instance._added_relationship_registry:
- del instance._added_relationship_registry[self.field.key]
- instance._removed_relationship_registry.append(self.field.key)
+ def set_attribute_value(self, attribute, value, value_class=None):
+ if value_class is None:
+ from philo.models.base import JSONValue
+ value_class = JSONValue
+ super(JSONAttribute, self).set_attribute_value(attribute, value, value_class)
-class RelationshipField(EntityProxyField):
- descriptor_class = RelationshipFieldDescriptor
+class ForeignKeyAttribute(AttributeField):
+ descriptor_class = ForeignKeyAttributeDescriptor
def __init__(self, model, limit_choices_to=None, key=None, **kwargs):
- super(RelationshipField, self).__init__(**kwargs)
+ super(ForeignKeyAttribute, self).__init__(**kwargs)
self.key = key
self.model = model
if limit_choices_to is None:
limit_choices_to = {}
self.limit_choices_to = limit_choices_to
- def contribute_to_class(self, cls, name):
- super(RelationshipField, self).contribute_to_class(cls, name)
- if self.key is None:
- self.key = name
-
def formfield(self, form_class=forms.ModelChoiceField, **kwargs):
defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ if self.has_default():
+ defaults['initial'] = self.default
defaults.update(kwargs)
return form_class(self.model._default_manager.complex_filter(self.limit_choices_to), **defaults)
def value_from_object(self, obj):
- relobj = super(RelationshipField, self).value_from_object(obj)
+ try:
+ relobj = super(ForeignKeyAttribute, self).value_from_object(obj)
+ except AttributeError:
+ return None
return getattr(relobj, 'pk', None)
+
+ def set_attribute_value(self, attribute, value, value_class=None):
+ if value_class is None:
+ from philo.models.base import ForeignKeyValue
+ value_class = ForeignKeyValue
+ super(ForeignKeyAttribute, self).set_attribute_value(attribute, value, value_class)
+
+
+class ManyToManyAttribute(ForeignKeyAttribute):
+ descriptor_class = ManyToManyAttributeDescriptor
+
+ def formfield(self, form_class=forms.ModelMultipleChoiceField, **kwargs):
+ return super(ManyToManyAttribute, self).formfield(form_class, **kwargs)
+
+ def set_attribute_value(self, attribute, value, value_class=None):
+ if value_class is None:
+ from philo.models.base import ManyToManyValue
+ value_class = ManyToManyValue
+ super(ManyToManyAttribute, self).set_attribute_value(attribute, value, value_class)
class TemplateField(models.TextField):
self.validators.append(TemplateValidator(allow, disallow, secure))
+class JSONFormField(forms.Field):
+ def clean(self, value):
+ try:
+ return json.loads(value)
+ except Exception, e:
+ raise ValidationError(u'JSON decode error: %s' % e)
+
+
+class JSONDescriptor(object):
+ def __init__(self, field):
+ self.field = field
+
+ def __get__(self, instance, owner):
+ if instance is None:
+ raise AttributeError # ?
+
+ if self.field.name not in instance.__dict__:
+ json_string = getattr(instance, self.field.attname)
+ instance.__dict__[self.field.name] = json.loads(json_string)
+
+ return instance.__dict__[self.field.name]
+
+ def __set__(self, instance, value):
+ instance.__dict__[self.field.name] = value
+ setattr(instance, self.field.attname, json.dumps(value))
+
+ def __delete__(self, instance):
+ del(instance.__dict__[self.field.name])
+ setattr(instance, self.field.attname, json.dumps(None))
+
+
+class JSONField(models.TextField):
+ def __init__(self, *args, **kwargs):
+ super(JSONField, self).__init__(*args, **kwargs)
+ self.validators.append(json_validator)
+
+ def get_attname(self):
+ return "%s_json" % self.name
+
+ def contribute_to_class(self, cls, name):
+ super(JSONField, self).contribute_to_class(cls, name)
+ setattr(cls, name, JSONDescriptor(self))
+
+ def formfield(self, *args, **kwargs):
+ kwargs["form_class"] = JSONFormField
+ return super(JSONField, self).formfield(*args, **kwargs)
+
+
try:
from south.modelsinspector import add_introspection_rules
except ImportError:
pass
else:
- add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])
\ No newline at end of file
+ add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])
+ add_introspection_rules([], ["^philo\.models\.fields\.JSONField"])
\ No newline at end of file
from django.contrib.contenttypes import generic
from django.contrib.sites.models import Site
from django.http import HttpResponse, HttpResponseServerError, HttpResponseRedirect
+from django.core.exceptions import ViewDoesNotExist
from django.core.servers.basehttp import FileWrapper
-from django.core.urlresolvers import resolve, clear_url_caches
+from django.core.urlresolvers import resolve, clear_url_caches, reverse
from django.template import add_to_builtins as register_templatetags
from inspect import getargspec
+from philo.exceptions import MIDDLEWARE_NOT_CONFIGURED
from philo.models.base import TreeEntity, Entity, QuerySetMapper, register_value_model
from philo.utils import ContentTypeSubclassLimiter
from philo.validators import RedirectValidator
return self.view.accepts_subpath
return False
- def render_to_response(self, request, path=None, subpath=None, extra_context=None):
- return self.view.render_to_response(self, request, path, subpath, extra_context)
+ def render_to_response(self, request, extra_context=None):
+ return self.view.render_to_response(request, extra_context)
def get_absolute_url(self):
- root = Site.objects.get_current().root_node
try:
- return '/%s' % self.get_path(root=root)
- except AncestorDoesNotExist:
+ root = Site.objects.get_current().root_node
+ except Site.DoesNotExist:
+ root = None
+
+ try:
+ path = self.get_path(root=root)
+ if path:
+ path += '/'
+ root_url = reverse('philo-root')
+ return '%s%s' % (root_url, path)
+ except AncestorDoesNotExist, ViewDoesNotExist:
return None
class Meta:
def attributes_with_node(self, node):
return QuerySetMapper(self.attribute_set, passthrough=node.attributes)
- def relationships_with_node(self, node):
- return QuerySetMapper(self.relationship_set, passthrough=node.relationships)
-
- def render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def render_to_response(self, request, extra_context=None):
+ if not hasattr(request, 'node'):
+ raise MIDDLEWARE_NOT_CONFIGURED
+
extra_context = extra_context or {}
- view_about_to_render.send(sender=self, node=node, request=request, path=path, subpath=subpath, extra_context=extra_context)
- response = self.actually_render_to_response(node, request, path, subpath, extra_context)
+ view_about_to_render.send(sender=self, request=request, extra_context=extra_context)
+ response = self.actually_render_to_response(request, extra_context)
view_finished_rendering.send(sender=self, response=response)
return response
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
raise NotImplementedError('View subclasses must implement render_to_response.')
class Meta:
class MultiView(View):
accepts_subpath = True
- urlpatterns = []
+ @property
+ def urlpatterns(self, obj):
+ raise NotImplementedError("MultiView subclasses must implement urlpatterns.")
+
+ def get_reverse_params(self, obj):
+ """This method should return a view_name, args, kwargs tuple suitable for reversing a url for the given obj using self as the urlconf."""
+ raise NotImplementedError("MultiView subclasses must implement get_subpath.")
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
clear_url_caches()
+ subpath = request.node.subpath
if not subpath:
subpath = ""
subpath = "/" + subpath
if 'extra_context' in kwargs:
extra_context.update(kwargs['extra_context'])
kwargs['extra_context'] = extra_context
- if 'node' in view_args[0] or view_args[2] is not None:
- kwargs['node'] = node
return view(request, *args, **kwargs)
class Meta:
target = models.CharField(max_length=200, validators=[RedirectValidator()])
status_code = models.IntegerField(choices=STATUS_CODES, default=302, verbose_name='redirect type')
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
response = HttpResponseRedirect(self.target)
response.status_code = self.status_code
return response
app_label = 'philo'
+# Why does this exist?
class File(View):
""" For storing arbitrary files """
mimetype = models.CharField(max_length=255)
file = models.FileField(upload_to='philo/files/%Y/%m/%d')
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
+ def actually_render_to_response(self, request, extra_context=None):
wrapper = FileWrapper(self.file)
response = HttpResponse(wrapper, content_type=self.mimetype)
response['Content-Length'] = self.file.size
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
+from django.core.exceptions import ValidationError
from django.db import models
from django.http import HttpResponse
from django.template import TemplateDoesNotExist, Context, RequestContext, Template as DjangoTemplate, add_to_builtins as register_templatetags
return self._containers
containers = property(get_containers)
- def render_to_string(self, node=None, request=None, path=None, subpath=None, extra_context=None):
+ def render_to_string(self, request=None, extra_context=None):
context = {}
context.update(extra_context or {})
- context.update({'page': self, 'attributes': self.attributes, 'relationships': self.relationships})
- if node and request:
- context.update({'node': node, 'attributes': self.attributes_with_node(node), 'relationships': self.relationships_with_node(node)})
- page_about_to_render_to_string.send(sender=self, node=node, request=request, extra_context=context)
+ context.update({'page': self, 'attributes': self.attributes})
+ if request:
+ context.update({'node': request.node, 'attributes': self.attributes_with_node(request.node)})
+ page_about_to_render_to_string.send(sender=self, request=request, extra_context=context)
string = self.template.django_template.render(RequestContext(request, context))
else:
- page_about_to_render_to_string.send(sender=self, node=node, request=request, extra_context=context)
+ page_about_to_render_to_string.send(sender=self, request=request, extra_context=context)
string = self.template.django_template.render(Context(context))
page_finished_rendering_to_string.send(sender=self, string=string)
return string
- def actually_render_to_response(self, node, request, path=None, subpath=None, extra_context=None):
- return HttpResponse(self.render_to_string(node, request, path, subpath, extra_context), mimetype=self.template.mimetype)
+ def actually_render_to_response(self, request, extra_context=None):
+ return HttpResponse(self.render_to_string(request, extra_context), mimetype=self.template.mimetype)
def __unicode__(self):
return self.title
+ def clean_fields(self, exclude=None):
+ try:
+ super(Page, self).clean_fields(exclude)
+ except ValidationError, e:
+ errors = e.message_dict
+ else:
+ errors = {}
+
+ if 'template' not in errors and 'template' not in exclude:
+ try:
+ self.template.clean_fields()
+ self.template.clean()
+ except ValidationError, e:
+ errors['template'] = e.messages
+
+ if errors:
+ raise ValidationError(errors)
+
class Meta:
app_label = 'philo'
entity_class_prepared = Signal(providing_args=['class'])
-view_about_to_render = Signal(providing_args=['node', 'request', 'path', 'subpath', 'extra_context'])
+view_about_to_render = Signal(providing_args=['request', 'extra_context'])
view_finished_rendering = Signal(providing_args=['response'])
-page_about_to_render_to_string = Signal(providing_args=['node', 'request', 'path', 'subpath', 'extra_context'])
+page_about_to_render_to_string = Signal(providing_args=['request', 'extra_context'])
page_finished_rendering_to_string = Signal(providing_args=['string'])
<th{% if forloop.first %} colspan="2"{% endif %}{% if field.required %} class="required"{% endif %}>{{ field.label|capfirst }}</th>
{% endif %}
{% endfor %}
+ <th>Content Type</th>
+ <th>Value</th>
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
</tr></thead>
{% endfor %}
{% endfor %}
{% endfor %}
+ <td>{% with inline_admin_form.form.content_type as field %}{{ field.errors.as_ul }}{{ field }}{% endwith %}</td>
+ <td>{% with inline_admin_form.form.value as field %}{{ field.errors.as_ul }}{{ field }}{% endwith %}</td>
{% if inline_admin_formset.formset.can_delete %}
<td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
{% endif %}
from django import template
from django.conf import settings
from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse, NoReverseMatch
+from django.template.defaulttags import kwarg_re
+from django.utils.encoding import smart_str
+from philo.exceptions import ViewCanNotProvideSubpath
register = template.Library()
class NodeURLNode(template.Node):
- def __init__(self, node, with_obj, as_var):
- if node is not None:
- self.node = template.Variable(node)
- else:
- self.node = None
-
- if with_obj is not None:
- self.with_obj = template.Variable(with_obj)
- else:
- self.with_obj = None
-
+ def __init__(self, node, as_var, with_obj=None, view_name=None, args=None, kwargs=None):
self.as_var = as_var
+ self.view_name = view_name
+
+ # Because the following variables have already been compiled as filters if they exist, they don't need to be re-scanned as template variables.
+ self.node = node
+ self.with_obj = with_obj
+ self.args = args
+ self.kwargs = kwargs
def render(self, context):
- try:
- if self.node:
- node = self.node.resolve(context)
- else:
- node = context['node']
- current_site = Site.objects.get_current()
- if node.has_ancestor(current_site.root_node):
- url = '/' + node.get_path(root=current_site.root_node)
- if self.with_obj:
- with_obj = self.with_obj.resolve(context)
- subpath = node.view.get_subpath(with_obj)
- if subpath[0] is '/':
- subpath = subpath[1:]
- url += subpath
- else:
+ if self.node:
+ node = self.node.resolve(context)
+ else:
+ node = context['node']
+
+ if not node:
+ return settings.TEMPLATE_STRING_IF_INVALID
+
+ if self.with_obj is None and self.view_name is None:
+ url = node.get_absolute_url()
+ else:
+ if not node.view.accepts_subpath:
return settings.TEMPLATE_STRING_IF_INVALID
- if self.as_var:
- context[self.as_var] = url
- return settings.TEMPLATE_STRING_IF_INVALID
+ if self.with_obj is not None:
+ try:
+ view_name, args, kwargs = node.view.get_reverse_params(self.with_obj.resolve(context))
+ except ViewCanNotProvideSubpath:
+ return settings.TEMPLATE_STRING_IF_INVALID
+ else: # self.view_name is not None
+ view_name = self.view_name
+ args = [arg.resolve(context) for arg in self.args]
+ kwargs = dict([(smart_str(k, 'ascii'), v.resolve(context)) for k, v in self.kwargs.items()])
+
+ url = ''
+ try:
+ subpath = reverse(view_name, urlconf=node.view, args=args, kwargs=kwargs)
+ except NoReverseMatch:
+ if self.as_var is None:
+ raise
else:
- return url
- except:
- return settings.TEMPLATE_STRING_IF_INVALID
+ if subpath[0] == '/':
+ subpath = subpath[1:]
+
+ url = node.get_absolute_url() + subpath
+
+ if self.as_var:
+ context[self.as_var] = url
+ return ''
+ else:
+ return url
@register.tag(name='node_url')
def do_node_url(parser, token):
"""
- {% node_url [<node>] [with <obj>] [as <var>] %}
+ {% node_url [for <node>] [as <var] %}
+ {% node_url with <obj> [for <node>] [as <var>] %}
+ {% node_url <view_name> [<arg1> [<arg2> ...] ] [for <node>] [as <var>] %}
+ {% node_url <view_name> [<key1>=<value1> [<key2>=<value2> ...] ] [for <node>] [as <var>]%}
"""
params = token.split_contents()
tag = params[0]
+ as_var = None
+ with_obj = None
+ node = None
+ params = params[1:]
- if len(params) <= 6:
- node = None
- with_obj = None
- as_var = None
- remaining_tokens = params[1:]
- while remaining_tokens:
- option_token = remaining_tokens.pop(0)
- if option_token == 'with':
- try:
- with_obj = remaining_tokens.pop(0)
- except IndexError:
- raise template.TemplateSyntaxError('"%s" template tag option "with" requires an argument specifying an object handled by the view on the node' % tag)
- elif option_token == 'as':
- try:
- as_var = remaining_tokens.pop(0)
- except IndexError:
- raise template.TemplateSyntaxError('"%s" template tag option "as" requires an argument specifying a variable name' % tag)
- else: # node
- node = option_token
- return NodeURLNode(node=node, with_obj=with_obj, as_var=as_var)
- else:
- raise template.TemplateSyntaxError('"%s" template tag cannot accept more than five arguments' % tag)
\ No newline at end of file
+ if len(params) >= 2 and params[-2] == 'as':
+ as_var = params[-1]
+ params = params[:-2]
+
+ if len(params) >= 2 and params[-2] == 'for':
+ node = parser.compile_filter(params[-1])
+ params = params[:-2]
+
+ if len(params) >= 2 and params[-2] == 'with':
+ with_obj = parser.compile_filter(params[-1])
+ params = params[:-2]
+
+ if with_obj is not None:
+ if params:
+ raise template.TemplateSyntaxError('`%s` template tag accepts no arguments or keyword arguments if with <obj> is specified.' % tag)
+ return NodeURLNode(with_obj=with_obj, node=node, as_var=as_var)
+
+ if params:
+ args = []
+ kwargs = {}
+ view_name = params.pop(0)
+ for param in params:
+ match = kwarg_re.match(param)
+ if not match:
+ raise TemplateSyntaxError("Malformed arguments to `%s` tag" % tag)
+ name, value = match.groups()
+ if name:
+ kwargs[name] = parser.compile_filter(value)
+ else:
+ args.append(parser.compile_filter(value))
+ return NodeURLNode(view_name=view_name, args=args, kwargs=kwargs, node=node, as_var=as_var)
+
+ return NodeURLNode(node=node, as_var=as_var)
\ No newline at end of file
--- /dev/null
+from django.test import TestCase
+from django import template
+from django.conf import settings
+from philo.models import Node, Page, Template
+from philo.contrib.penfield.models import Blog, BlogView, BlogEntry
+
+
+class NodeURLTestCase(TestCase):
+ """Tests the features of the node_url template tag."""
+ urls = 'philo.urls'
+ fixtures = ['test_fixtures.json']
+
+ def setUp(self):
+ if 'south' in settings.INSTALLED_APPS:
+ from south.management.commands.migrate import Command
+ command = Command()
+ command.handle(all_apps=True)
+
+ self.templates = [template.Template(string) for string in
+ [
+ "{% node_url %}", # 0
+ "{% node_url for node2 %}", # 1
+ "{% node_url as hello %}<p>{{ hello|slice:'1:' }}</p>", # 2
+ "{% node_url for nodes|first %}", # 3
+ "{% node_url with entry %}", # 4
+ "{% node_url with entry for node2 %}", # 5
+ "{% node_url with tag for node2 %}", # 6
+ "{% node_url with date for node2 %}", # 7
+ "{% node_url entries_by_day year=date|date:'Y' month=date|date:'m' day=date|date:'d' for node2 as goodbye %}<em>{{ goodbye|upper }}</em>", # 8
+ "{% node_url entries_by_month year=date|date:'Y' month=date|date:'m' for node2 %}", # 9
+ "{% node_url entries_by_year year=date|date:'Y' for node2 %}", # 10
+ ]
+ ]
+
+ nodes = Node.objects.all()
+ blog = Blog.objects.all()[0]
+
+ self.context = template.Context({
+ 'node': nodes[0],
+ 'node2': nodes[1],
+ 'nodes': nodes,
+ 'entry': BlogEntry.objects.all()[0],
+ 'tag': blog.entry_tags.all()[0],
+ 'date': blog.entry_dates['day'][0]
+ })
+
+ def test_nodeurl(self):
+ for i, template in enumerate(self.templates):
+ t = template.render(self.context)
+
+ if i == 0:
+ self.assertEqual(t, "/root/never/")
+ elif i == 1:
+ self.assertEqual(t, "/root/blog/")
+ elif i == 2:
+ self.assertEqual(t, "<p>root/never/</p>")
+ elif i == 3:
+ self.assertEqual(t, "/root/never/")
+ elif i == 4:
+ self.assertEqual(t, settings.TEMPLATE_STRING_IF_INVALID)
+ elif i == 5:
+ self.assertEqual(t, "/root/blog/2010/10/20/first-entry")
+ elif i == 6:
+ self.assertEqual(t, "/root/blog/tags/test-tag/")
+ elif i == 7:
+ self.assertEqual(t, "/root/blog/2010/10/20")
+ elif i == 8:
+ self.assertEqual(t, "<em>/ROOT/BLOG/2010/10/20</em>")
+ elif i == 9:
+ self.assertEqual(t, "/root/blog/2010/10")
+ elif i == 10:
+ self.assertEqual(t, "/root/blog/2010/")
+ else:
+ print "Rendered as:\n%s\n\n" % t
\ No newline at end of file
def json_validator(value):
try:
json.loads(value)
- except:
- raise ValidationError(u'\'%s\' is not valid JSON' % value)
+ except Exception, e:
+ raise ValidationError(u'JSON decode error: %s' % e)
class TemplateValidationParser(Parser):
from django.http import Http404, HttpResponse
from django.template import RequestContext
from django.views.decorators.vary import vary_on_headers
+from philo.exceptions import MIDDLEWARE_NOT_CONFIGURED
from philo.models import Node
@vary_on_headers('Accept')
def node_view(request, path=None, **kwargs):
- node = None
- subpath = None
- if path is None:
- path = '/'
- current_site = Site.objects.get_current()
- try:
- node, subpath = Node.objects.get_with_path(path, root=current_site.root_node, absolute_result=False)
- except Node.DoesNotExist:
- raise Http404
+ if not hasattr(request, 'node'):
+ raise MIDDLEWARE_NOT_CONFIGURED
- if not node:
+ if not request.node:
raise Http404
+ node = request.node
+ subpath = request.node.subpath
+
try:
if subpath and not node.accepts_subpath:
raise Http404
- return node.render_to_response(request, path=path, subpath=subpath)
+ return node.render_to_response(request, kwargs)
except Http404, e:
if settings.DEBUG:
raise
extra_context = {'exception': e}
- return Http404View.render_to_response(node, request, path, subpath, extra_context)
+ return Http404View.render_to_response(request, extra_context)
except Exception, e:
if settings.DEBUG:
raise
extra_context = {'exception': e}
- return Http500View.render_to_response(node, request, path, subpath, extra_context)
+ return Http500View.render_to_response(request, extra_context)
except:
raise e
\ No newline at end of file