2 from django.conf import settings
3 from django.contrib.contenttypes.models import ContentType
4 from django.contrib.contenttypes import generic
5 from django.core.exceptions import ValidationError
6 from django.db import models
7 from django.http import HttpResponse
8 from django.template import TemplateDoesNotExist, Context, RequestContext, Template as DjangoTemplate, add_to_builtins as register_templatetags
9 from philo.models.base import TreeModel, register_value_model
10 from philo.models.fields import TemplateField
11 from philo.models.nodes import View
12 from philo.templatetags.containers import ContainerNode
13 from philo.utils import fattr, nodelist_crawl
14 from philo.validators import LOADED_TEMPLATE_ATTR
15 from philo.signals import page_about_to_render_to_string, page_finished_rendering_to_string
18 class Template(TreeModel):
19 name = models.CharField(max_length=255)
20 documentation = models.TextField(null=True, blank=True)
21 mimetype = models.CharField(max_length=255, default=getattr(settings, 'DEFAULT_CONTENT_TYPE', 'text/html'))
22 code = TemplateField(secure=False, verbose_name='django template code')
26 return 'philo.models.Template: ' + self.path
29 def django_template(self):
30 return DjangoTemplate(self.code)
35 Returns a tuple where the first item is a list of names of contentlets referenced by containers,
36 and the second item is a list of tuples of names and contenttypes of contentreferences referenced by containers.
37 This will break if there is a recursive extends or includes in the template code.
38 Due to the use of an empty Context, any extends or include tags with dynamic arguments probably won't work.
40 def process_node(node, nodes):
41 if isinstance(node, ContainerNode):
44 all_nodes = nodelist_crawl(self.django_template.nodelist, process_node)
45 contentlet_node_names = set([node.name for node in all_nodes if not node.references])
46 contentreference_node_names = []
47 contentreference_node_specs = []
48 for node in all_nodes:
49 if node.references and node.name not in contentreference_node_names:
50 contentreference_node_specs.append((node.name, node.references))
51 contentreference_node_names.append(node.name)
52 return contentlet_node_names, contentreference_node_specs
54 def __unicode__(self):
55 return self.get_path(pathsep=u' › ', field='name')
58 @fattr(is_usable=True)
59 def loader(template_name, template_dirs=None): # load_template_source
61 template = Template.objects.get_with_path(template_name)
62 except Template.DoesNotExist:
63 raise TemplateDoesNotExist(template_name)
64 return (template.code, template.origin)
72 Represents a page - something which is rendered according to a template. The page will have a number of related Contentlets depending on the template selected - but these will appear only after the page has been saved with that template.
74 template = models.ForeignKey(Template, related_name='pages')
75 title = models.CharField(max_length=255)
77 def get_containers(self):
78 if not hasattr(self, '_containers'):
79 self._containers = self.template.containers
80 return self._containers
81 containers = property(get_containers)
83 def render_to_string(self, request=None, extra_context=None):
85 context.update(extra_context or {})
86 context.update({'page': self, 'attributes': self.attributes})
88 context.update({'node': request.node, 'attributes': self.attributes_with_node(request.node)})
89 page_about_to_render_to_string.send(sender=self, request=request, extra_context=context)
90 string = self.template.django_template.render(RequestContext(request, context))
92 page_about_to_render_to_string.send(sender=self, request=request, extra_context=context)
93 string = self.template.django_template.render(Context(context))
94 page_finished_rendering_to_string.send(sender=self, string=string)
97 def actually_render_to_response(self, request, extra_context=None):
98 return HttpResponse(self.render_to_string(request, extra_context), mimetype=self.template.mimetype)
100 def __unicode__(self):
103 def clean_fields(self, exclude=None):
105 super(Page, self).clean_fields(exclude)
106 except ValidationError, e:
107 errors = e.message_dict
111 if 'template' not in errors and 'template' not in exclude:
113 self.template.clean_fields()
114 self.template.clean()
115 except ValidationError, e:
116 errors['template'] = e.messages
119 raise ValidationError(errors)
125 class Contentlet(models.Model):
126 page = models.ForeignKey(Page, related_name='contentlets')
127 name = models.CharField(max_length=255)
128 content = TemplateField()
130 def __unicode__(self):
137 class ContentReference(models.Model):
138 page = models.ForeignKey(Page, related_name='contentreferences')
139 name = models.CharField(max_length=255)
140 content_type = models.ForeignKey(ContentType, verbose_name='Content type')
141 content_id = models.PositiveIntegerField(verbose_name='Content ID', blank=True, null=True)
142 content = generic.GenericForeignKey('content_type', 'content_id')
144 def __unicode__(self):
151 register_templatetags('philo.templatetags.containers')
154 register_value_model(Template)
155 register_value_model(Page)