from django.contrib import admin
from philo.admin import EntityAdmin
-from philo.contrib.penfield.models import BlogEntry, Blog, BlogView, Newsletter, NewsletterArticle, NewsletterIssue, NewsletterView, Test
+from philo.contrib.penfield.models import BlogEntry, Blog, BlogView, Newsletter, NewsletterArticle, NewsletterIssue, NewsletterView, Embed
class TitledAdmin(EntityAdmin):
pass
-class TestAdmin(admin.ModelAdmin):
+class EmbedAdmin(admin.ModelAdmin):
pass
-admin.site.register(Test, TestAdmin)
+admin.site.register(Embed, EmbedAdmin)
admin.site.register(Blog, BlogAdmin)
admin.site.register(BlogEntry, BlogEntryAdmin)
admin.site.register(BlogView, BlogViewAdmin)
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models
-from django.template import Template, loader, loader_tags
+from django.template import loader, loader_tags, Parser, Lexer, Template
import re
+from philo.models.fields import TemplateField
from philo.contrib.penfield.templatetags.embed import EmbedNode
+from philo.utils import nodelist_crawl
embed_re = re.compile("{% embed (?P<app_label>\w+)\.(?P<model>\w+) (?P<pk>)\w+ %}")
-class TemplateField(models.TextField):
- def validate(self, value, model_instance):
- """For value (a template), make sure that all included templates exist."""
- super(TemplateField, self).validate(value, model_instance)
- try:
- self.validate_template(self.to_template(value))
- except Exception, e:
- raise ValidationError("Template code invalid. Error was: %s: %s" % (e.__class__.__name__, e))
-
- def validate_template(self, template):
- for node in template.nodelist:
- if isinstance(node, loader_tags.ExtendsNode):
- extended_template = node.get_parent(Context())
- self.validate_template(extended_template)
- elif isinstance(node, loader_tags.IncludeNode):
- included_template = loader.get_template(node.template_name.resolve(Context()))
- self.validate_template(extended_template)
-
- def to_template(self, value):
- return Template(value)
-
-
class Embed(models.Model):
embedder_content_type = models.ForeignKey(ContentType, related_name="embedder_related")
embedder_object_id = models.PositiveIntegerField()
class EmbedField(TemplateField):
- _embedded_instances = set()
+ def process_node(self, node, results):
+ if isinstance(node, EmbedNode) and node.instance is not None:
+ if not node.instance:
+ raise ValidationError("Instance with content type %s.%s and id %s does not exist." % (node.content_type.app_label, node.content_type.model, node.object_pk))
+
+ results.append(node.instance)
- def validate_template(self, template):
- """Check to be sure that the embedded instances and templates all exist."""
- for node in template.nodelist:
- if isinstance(node, loader_tags.ExtendsNode):
- extended_template = node.get_parent(Context())
- self.validate_template(extended_template)
- elif isinstance(node, loader_tags.IncludeNode):
- included_template = loader.get_template(node.template_name.resolve(Context()))
- self.validate_template(extended_template)
- elif isinstance(node, EmbedNode):
- if node.template_name is not None:
- embedded_template = loader.get_template(node.template_name)
- self.validate_template(embedded_template)
- elif node.object_pk is not None:
- self._embedded_instances.add(node.model.objects.get(pk=node.object_pk))
-
- def pre_save(self, model_instance, add):
+ def clean(self, value, model_instance):
+ value = super(EmbedField, self).clean(value, model_instance)
+
if not hasattr(model_instance, '_embedded_instances'):
model_instance._embedded_instances = set()
- model_instance._embedded_instances |= self._embedded_instances
- return getattr(model_instance, self.attname)
+
+ model_instance._embedded_instances |= set(nodelist_crawl(Template(value).nodelist, self.process_node))
+
+ return value
+
+
+try:
+ from south.modelsinspector import add_introspection_rules
+except ImportError:
+ pass
+else:
+ add_introspection_rules([], ["^philo\.contrib\.penfield\.embed\.EmbedField"])
# Add a post-save signal function to run the syncer.
for embed in embeds:
embed.delete()
Embed.objects.filter(embedder_content_type=ct, embedder_object_id=instance.id).delete()
-models.signals.post_delete.connect(post_delete_cascade)
-
-
-class Test(models.Model):
- template = TemplateField()
- embedder = EmbedField()
-
- class Meta:
- app_label = 'penfield'
\ No newline at end of file
+models.signals.post_delete.connect(post_delete_cascade)
\ No newline at end of file
newsletter = models.ForeignKey(Newsletter, related_name='articles')
authors = models.ManyToManyField(getattr(settings, 'PHILO_PERSON_MODULE', 'auth.User'), related_name='newsletterarticles')
date = models.DateTimeField(default=datetime.now)
- lede = models.TextField(null=True, blank=True)
- full_text = models.TextField()
+ lede = EmbedField(null=True, blank=True, verbose_name='Summary')
+ full_text = EmbedField()
tags = models.ManyToManyField(Tag, related_name='newsletterarticles', blank=True, null=True)
class Meta:
from django import template
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
+from philo.utils import LOADED_TEMPLATE_ATTR
register = template.Library()
class EmbedNode(template.Node):
- def __init__(self, model, varname, object_pk=None, template_name=None):
+ def __init__(self, content_type, varname, object_pk=None, template_name=None):
assert template_name is not None or object_pk is not None
- app_label, model = model.split('.')
- self.model = ContentType.objects.get(app_label=app_label, model=model).model_class()
+ self.content_type = content_type
self.varname = varname
- self.object_pk = object_pk
- self.template_name = template_name
-
- def render(self, context):
- if self.template_name is not None:
- template_name = self.template_name.resolve(context)
+ if object_pk is not None:
+ self.object_pk = object_pk
+ try:
+ self.instance = content_type.get_object_for_this_type(pk=object_pk)
+ except content_type.model_class().DoesNotExist:
+ self.instance = False
+ else:
+ self.instance = None
+
+ if template_name is not None:
try:
- t = template.loader.get_template(template_name)
+ self.template = template.loader.get_template(template_name)
except template.TemplateDoesNotExist:
+ self.template = False
+ else:
+ self.template = None
+
+ def render(self, context):
+ if self.template_name is not None:
+ if self.template is False:
return settings.TEMPLATE_STRING_IF_INVALID
- else:
- if self.varname not in context:
- context[self.varname] = {}
- context[self.varname][self.model] = t
+
+ if self.varname not in context:
+ context[self.varname] = {}
+ context[self.varname][self.content_type] = self.template
+
return ''
- # Otherwise self.object_pk is set. Render the instance with the appropriate template!
- try:
- instance = self.model.objects.get(pk=self.object_pk.resolve(context))
- except self.model.DoesNotExist:
+ # Otherwise self.instance should be set. Render the instance with the appropriate template!
+ if self.instance is None or self.instance is False:
return settings.TEMPLATE_STRING_IF_INVALID
try:
- t = context[self.varname][self.model]
+ t = context[self.varname][self.content_type]
except KeyError:
return settings.TEMPLATE_STRING_IF_INVALID
context.push()
- context['embedded'] = instance
+ context['embedded'] = self.instance
t_rendered = t.render(context)
context.pop()
return t_rendered
+def get_embedded(self):
+ return template.loader.get_template(self.template_name)
+
+
+setattr(EmbedNode, LOADED_TEMPLATE_ATTR, property(get_embedded))
+
+
def do_embed(parser, token):
"""
The {% embed %} tag can be used in three ways:
return template.defaulttags.CommentNode()
if '.' not in args[1]:
- raise template.TemplateSyntaxError('"%s" template tag expects the first argument to be of the type app_label.model' % tag)
+ raise template.TemplateSyntaxError('"%s" template tag expects the first argument to be of the form app_label.model' % tag)
+
+ app_label, model = args[1].split('.')
+ try:
+ ct = ContentType.objects.get(app_label=app_label, model=model)
+ except ContentType.DoesNotExist:
+ raise template.TemplateSyntaxError('"%s" template tag option "references" requires an argument of the form app_label.model which refers to an installed content type (see django.contrib.contenttypes)' % tag)
if len(args) == 3:
- return EmbedNode(args[1], object_pk=args[2], varname=getattr(parser, '_embedNodeVarName', 'embed'))
+
+ return EmbedNode(ct, object_pk=args[2], varname=getattr(parser, '_embedNodeVarName', 'embed'))
else:
# 3 args
if args[2] != "with":
template_name = args[3].strip('"\'')
- return EmbedNode(args[1], template_name=template_name, varname=getattr(parser, '_embedNodeVarName', 'embed'))
+ return EmbedNode(ct, template_name=template_name, varname=getattr(parser, '_embedNodeVarName', 'embed'))
register.tag('embed', do_embed)
\ No newline at end of file