Adjusted to match the new master TemplateField changes. Switched the setting of _embe...
authorStephen Burrows <stephen.r.burrows@gmail.com>
Mon, 4 Oct 2010 15:09:54 +0000 (11:09 -0400)
committerStephen Burrows <stephen.r.burrows@gmail.com>
Mon, 4 Oct 2010 15:12:52 +0000 (11:12 -0400)
contrib/penfield/admin.py
contrib/penfield/embed.py
contrib/penfield/models.py
contrib/penfield/templatetags/embed.py

index a326d22..4be78c0 100644 (file)
@@ -1,6 +1,6 @@
 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):
@@ -36,11 +36,11 @@ class NewsletterViewAdmin(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)
index 7f875cc..ca49baa 100644 (file)
@@ -2,36 +2,16 @@ from django.contrib.contenttypes import generic
 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()
@@ -81,29 +61,30 @@ def sync_embedded_instances(model_instance, embedded_instances):
 
 
 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.
@@ -122,12 +103,4 @@ def post_delete_cascade(sender, instance, **kwargs):
        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
index 555d9a2..30480eb 100644 (file)
@@ -259,8 +259,8 @@ class NewsletterArticle(Entity, Titled):
        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:
index f8ab3f0..8da99af 100644 (file)
@@ -1,52 +1,69 @@
 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:
@@ -67,10 +84,17 @@ def do_embed(parser, token):
                        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":
@@ -83,7 +107,7 @@ def do_embed(parser, token):
                        
                        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