From 2b808329bca3af8d614aa13af7b6ccf24092d4ce Mon Sep 17 00:00:00 2001 From: Joseph Spiros Date: Wed, 23 Jun 2010 12:26:10 -0400 Subject: [PATCH] Splitting models into submodules beside models.base. --- models/__init__.py | 12 +- models/base.py | 255 +----------------------------------------- models/collections.py | 45 ++++++++ models/nodes.py | 66 +++++++++++ models/pages.py | 146 ++++++++++++++++++++++++ 5 files changed, 272 insertions(+), 252 deletions(-) create mode 100644 models/collections.py create mode 100644 models/nodes.py create mode 100644 models/pages.py diff --git a/models/__init__.py b/models/__init__.py index b934cda..b9ea3ac 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -1 +1,11 @@ -from philo.models.base import * \ No newline at end of file +from philo.models.base import * +from philo.models.collections import * +from philo.models.nodes import * +from philo.models.pages import * +from django.contrib.auth.models import User, Group +from django.contrib.sites.models import Site + + +register_value_model(User) +register_value_model(Group) +register_value_model(Site) \ No newline at end of file diff --git a/models/base.py b/models/base.py index bd55e95..5ca9d93 100644 --- a/models/base.py +++ b/models/base.py @@ -1,25 +1,9 @@ -# encoding: utf-8 -from django.utils.translation import ugettext_lazy as _ -from django.contrib.auth.models import User, Group -from django.contrib.contenttypes import generic -from django.contrib.contenttypes.models import ContentType from django.db import models -from django.contrib.sites.models import Site -from philo.utils import fattr -from django.template import add_to_builtins as register_templatetags -from django.template import Template as DjangoTemplate -from django.template import TemplateDoesNotExist -from django.template import Context, RequestContext -from django.core.exceptions import ObjectDoesNotExist +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 UserDict import DictMixin -from philo.templatetags.containers import ContainerNode -from django.template.loader_tags import ExtendsNode, ConstantIncludeNode, IncludeNode -from django.template.loader import get_template -from django.http import Http404, HttpResponse, HttpResponseServerError, HttpResponseRedirect -from django.core.servers.basehttp import FileWrapper -from django.conf import settings -from philo.validators import RedirectValidator def register_value_model(model): @@ -106,43 +90,6 @@ class Entity(models.Model): app_label = 'philo' -class Collection(models.Model): - name = models.CharField(max_length=255) - description = models.TextField(blank=True, null=True) - - @fattr(short_description='Members') - def get_count(self): - return self.members.count() - - def __unicode__(self): - return self.name - - class Meta: - app_label = 'philo' - - -class CollectionMemberManager(models.Manager): - use_for_related_fields = True - - def with_model(self, model): - return model._default_manager.filter(pk__in=self.filter(member_content_type=ContentType.objects.get_for_model(model)).values_list('member_object_id', flat=True)) - - -class CollectionMember(models.Model): - objects = CollectionMemberManager() - collection = models.ForeignKey(Collection, related_name='members') - index = models.PositiveIntegerField(verbose_name='Index', help_text='This will determine the ordering of the item within the collection. (Optional)', null=True, blank=True) - member_content_type = models.ForeignKey(ContentType, verbose_name='Member type') - member_object_id = models.PositiveIntegerField(verbose_name='Member ID') - member = generic.GenericForeignKey('member_content_type', 'member_object_id') - - def __unicode__(self): - return u'%s - %s' % (self.collection, self.member) - - class Meta: - app_label = 'philo' - - class TreeManager(models.Manager): use_for_related_fields = True @@ -257,198 +204,4 @@ class InheritableTreeEntity(TreeEntity): class Meta: abstract = True - app_label = 'philo' - - -class Node(InheritableTreeEntity): - accepts_subpath = False - - def render_to_response(self, request, path=None, subpath=None): - return HttpResponseServerError() - - class Meta: - unique_together = (('parent', 'slug'),) - app_label = 'philo' - - -class MultiNode(Node): - accepts_subpath = True - - urlpatterns = [] - - def render_to_response(self, request, path=None, subpath=None): - if not subpath: - subpath = "" - subpath = "/" + subpath - from django.core.urlresolvers import resolve - view, args, kwargs = resolve(subpath, urlconf=self) - return view(request, *args, **kwargs) - - class Meta: - abstract = True - app_label = 'philo' - - -class Redirect(Node): - STATUS_CODES = ( - (302, 'Temporary'), - (301, 'Permanent'), - ) - target = models.CharField(max_length=200,validators=[RedirectValidator()]) - status_code = models.IntegerField(choices=STATUS_CODES, default=302, verbose_name='redirect type') - - def render_to_response(self, request, path=None, subpath=None): - response = HttpResponseRedirect(self.target) - response.status_code = self.status_code - return response - - class Meta: - app_label = 'philo' - - -class File(Node): - """ For storing arbitrary files """ - mimetype = models.CharField(max_length=255) - file = models.FileField(upload_to='philo/files/%Y/%m/%d') - - def render_to_response(self, request, path=None, subpath=None): - wrapper = FileWrapper(self.file) - response = HttpResponse(wrapper, content_type=self.mimetype) - response['Content-Length'] = self.file.size - return response - - class Meta: - app_label = 'philo' - - -class Template(TreeModel): - name = models.CharField(max_length=255) - documentation = models.TextField(null=True, blank=True) - mimetype = models.CharField(max_length=255, null=True, blank=True, help_text='Default: %s' % settings.DEFAULT_CONTENT_TYPE) - code = models.TextField(verbose_name='django template code') - - @property - def origin(self): - return 'philo.models.Template: ' + self.path - - @property - def django_template(self): - return DjangoTemplate(self.code) - - @property - def containers(self): - """ - Returns a tuple where the first item is a list of names of contentlets referenced by containers, - and the second item is a list of tuples of names and contenttypes of contentreferences referenced by containers. - This will break if there is a recursive extends or includes in the template code. - Due to the use of an empty Context, any extends or include tags with dynamic arguments probably won't work. - """ - def container_nodes(template): - def nodelist_container_nodes(nodelist): - nodes = [] - for node in nodelist: - try: - for nodelist_name in ('nodelist', 'nodelist_loop', 'nodelist_empty', 'nodelist_true', 'nodelist_false', 'nodelist_main'): - if hasattr(node, nodelist_name): - nodes.extend(nodelist_container_nodes(getattr(node, nodelist_name))) - if isinstance(node, ContainerNode): - nodes.append(node) - elif isinstance(node, ExtendsNode): - extended_template = node.get_parent(Context()) - if extended_template: - nodes.extend(container_nodes(extended_template)) - elif isinstance(node, ConstantIncludeNode): - included_template = node.template - if included_template: - nodes.extend(container_nodes(included_template)) - elif isinstance(node, IncludeNode): - included_template = get_template(node.template_name.resolve(Context())) - if included_template: - nodes.extend(container_nodes(included_template)) - except: - pass # fail for this node - return nodes - return nodelist_container_nodes(template.nodelist) - all_nodes = container_nodes(self.django_template) - contentlet_node_names = set([node.name for node in all_nodes if not node.references]) - contentreference_node_names = [] - contentreference_node_specs = [] - for node in all_nodes: - if node.references and node.name not in contentreference_node_names: - contentreference_node_specs.append((node.name, node.references)) - contentreference_node_names.append(node.name) - return contentlet_node_names, contentreference_node_specs - - def __unicode__(self): - return self.get_path(u' › ', 'name') - - @staticmethod - @fattr(is_usable=True) - def loader(template_name, template_dirs=None): # load_template_source - try: - template = Template.objects.get_with_path(template_name) - except Template.DoesNotExist: - raise TemplateDoesNotExist(template_name) - return (template.code, template.origin) - - class Meta: - app_label = 'philo' - - -class Page(Node): - """ - 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. - """ - template = models.ForeignKey(Template, related_name='pages') - title = models.CharField(max_length=255) - - def render_to_response(self, request, path=None, subpath=None): - return HttpResponse(self.template.django_template.render(RequestContext(request, {'page': self})), mimetype=self.template.mimetype) - - def __unicode__(self): - return self.get_path(u' › ', 'title') - - class Meta: - app_label = 'philo' - - -# the following line enables the selection of a node as the root for a given django.contrib.sites Site object -models.ForeignKey(Node, related_name='sites', null=True, blank=True).contribute_to_class(Site, 'root_node') - - -class Contentlet(models.Model): - page = models.ForeignKey(Page, related_name='contentlets') - name = models.CharField(max_length=255) - content = models.TextField() - dynamic = models.BooleanField(default=False) - - def __unicode__(self): - return self.name - - class Meta: - app_label = 'philo' - - -class ContentReference(models.Model): - page = models.ForeignKey(Page, related_name='contentreferences') - name = models.CharField(max_length=255) - content_type = models.ForeignKey(ContentType, verbose_name='Content type') - content_id = models.PositiveIntegerField(verbose_name='Content ID', blank=True, null=True) - content = generic.GenericForeignKey('content_type', 'content_id') - - def __unicode__(self): - return self.name - - class Meta: - app_label = 'philo' - - -register_templatetags('philo.templatetags.containers') - - -register_value_model(User) -register_value_model(Group) -register_value_model(Site) -register_value_model(Collection) -register_value_model(Template) -register_value_model(Page) + app_label = 'philo' \ No newline at end of file diff --git a/models/collections.py b/models/collections.py new file mode 100644 index 0000000..61b2608 --- /dev/null +++ b/models/collections.py @@ -0,0 +1,45 @@ +from django.db import models +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes import generic +from philo.models.base import register_value_model +from philo.utils import fattr + + +class Collection(models.Model): + name = models.CharField(max_length=255) + description = models.TextField(blank=True, null=True) + + @fattr(short_description='Members') + def get_count(self): + return self.members.count() + + def __unicode__(self): + return self.name + + class Meta: + app_label = 'philo' + + +class CollectionMemberManager(models.Manager): + use_for_related_fields = True + + def with_model(self, model): + return model._default_manager.filter(pk__in=self.filter(member_content_type=ContentType.objects.get_for_model(model)).values_list('member_object_id', flat=True)) + + +class CollectionMember(models.Model): + objects = CollectionMemberManager() + collection = models.ForeignKey(Collection, related_name='members') + index = models.PositiveIntegerField(verbose_name='Index', help_text='This will determine the ordering of the item within the collection. (Optional)', null=True, blank=True) + member_content_type = models.ForeignKey(ContentType, verbose_name='Member type') + member_object_id = models.PositiveIntegerField(verbose_name='Member ID') + member = generic.GenericForeignKey('member_content_type', 'member_object_id') + + def __unicode__(self): + return u'%s - %s' % (self.collection, self.member) + + class Meta: + app_label = 'philo' + + +register_value_model(Collection) \ No newline at end of file diff --git a/models/nodes.py b/models/nodes.py new file mode 100644 index 0000000..6bdc85d --- /dev/null +++ b/models/nodes.py @@ -0,0 +1,66 @@ +from django.db import models +from django.http import HttpResponse, HttpResponseServerError, HttpResponseRedirect +from django.core.servers.basehttp import FileWrapper +from philo.models.base import InheritableTreeEntity +from philo.validators import RedirectValidator + + +class Node(InheritableTreeEntity): + accepts_subpath = False + + def render_to_response(self, request, path=None, subpath=None): + return HttpResponseServerError() + + class Meta: + unique_together = (('parent', 'slug'),) + app_label = 'philo' + + +class MultiNode(Node): + accepts_subpath = True + + urlpatterns = [] + + def render_to_response(self, request, path=None, subpath=None): + if not subpath: + subpath = "" + subpath = "/" + subpath + from django.core.urlresolvers import resolve + view, args, kwargs = resolve(subpath, urlconf=self) + return view(request, *args, **kwargs) + + class Meta: + abstract = True + app_label = 'philo' + + +class Redirect(Node): + STATUS_CODES = ( + (302, 'Temporary'), + (301, 'Permanent'), + ) + target = models.CharField(max_length=200,validators=[RedirectValidator()]) + status_code = models.IntegerField(choices=STATUS_CODES, default=302, verbose_name='redirect type') + + def render_to_response(self, request, path=None, subpath=None): + response = HttpResponseRedirect(self.target) + response.status_code = self.status_code + return response + + class Meta: + app_label = 'philo' + + +class File(Node): + """ For storing arbitrary files """ + mimetype = models.CharField(max_length=255) + file = models.FileField(upload_to='philo/files/%Y/%m/%d') + + def render_to_response(self, request, path=None, subpath=None): + wrapper = FileWrapper(self.file) + response = HttpResponse(wrapper, content_type=self.mimetype) + response['Content-Length'] = self.file.size + return response + + class Meta: + app_label = 'philo' \ No newline at end of file diff --git a/models/pages.py b/models/pages.py new file mode 100644 index 0000000..3435511 --- /dev/null +++ b/models/pages.py @@ -0,0 +1,146 @@ +# encoding: utf-8 +from django.db import models +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes import generic +from django.conf import settings +from django.template import add_to_builtins as register_templatetags +from django.template import Template as DjangoTemplate +from django.template import TemplateDoesNotExist +from django.template import Context, RequestContext +from django.template.loader import get_template +from django.template.loader_tags import ExtendsNode, ConstantIncludeNode, IncludeNode +from django.http import HttpResponse +from django.contrib.sites.models import Site +from philo.models.base import TreeModel, register_value_model +from philo.models.nodes import Node +from philo.utils import fattr +from philo.templatetags.containers import ContainerNode + + +class Template(TreeModel): + name = models.CharField(max_length=255) + documentation = models.TextField(null=True, blank=True) + mimetype = models.CharField(max_length=255, null=True, blank=True, help_text='Default: %s' % settings.DEFAULT_CONTENT_TYPE) + code = models.TextField(verbose_name='django template code') + + @property + def origin(self): + return 'philo.models.Template: ' + self.path + + @property + def django_template(self): + return DjangoTemplate(self.code) + + @property + def containers(self): + """ + Returns a tuple where the first item is a list of names of contentlets referenced by containers, + and the second item is a list of tuples of names and contenttypes of contentreferences referenced by containers. + This will break if there is a recursive extends or includes in the template code. + Due to the use of an empty Context, any extends or include tags with dynamic arguments probably won't work. + """ + def container_nodes(template): + def nodelist_container_nodes(nodelist): + nodes = [] + for node in nodelist: + try: + for nodelist_name in ('nodelist', 'nodelist_loop', 'nodelist_empty', 'nodelist_true', 'nodelist_false', 'nodelist_main'): + if hasattr(node, nodelist_name): + nodes.extend(nodelist_container_nodes(getattr(node, nodelist_name))) + if isinstance(node, ContainerNode): + nodes.append(node) + elif isinstance(node, ExtendsNode): + extended_template = node.get_parent(Context()) + if extended_template: + nodes.extend(container_nodes(extended_template)) + elif isinstance(node, ConstantIncludeNode): + included_template = node.template + if included_template: + nodes.extend(container_nodes(included_template)) + elif isinstance(node, IncludeNode): + included_template = get_template(node.template_name.resolve(Context())) + if included_template: + nodes.extend(container_nodes(included_template)) + except: + raise # fail for this node + return nodes + return nodelist_container_nodes(template.nodelist) + all_nodes = container_nodes(self.django_template) + contentlet_node_names = set([node.name for node in all_nodes if not node.references]) + contentreference_node_names = [] + contentreference_node_specs = [] + for node in all_nodes: + if node.references and node.name not in contentreference_node_names: + contentreference_node_specs.append((node.name, node.references)) + contentreference_node_names.append(node.name) + return contentlet_node_names, contentreference_node_specs + + def __unicode__(self): + return self.get_path(u' › ', 'name') + + @staticmethod + @fattr(is_usable=True) + def loader(template_name, template_dirs=None): # load_template_source + try: + template = Template.objects.get_with_path(template_name) + except Template.DoesNotExist: + raise TemplateDoesNotExist(template_name) + return (template.code, template.origin) + + class Meta: + app_label = 'philo' + + +class Page(Node): + """ + 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. + """ + template = models.ForeignKey(Template, related_name='pages') + title = models.CharField(max_length=255) + + def render_to_response(self, request, path=None, subpath=None): + return HttpResponse(self.template.django_template.render(RequestContext(request, {'page': self})), mimetype=self.template.mimetype) + + def __unicode__(self): + return self.get_path(u' › ', 'title') + + class Meta: + app_label = 'philo' + + +# the following line enables the selection of a node as the root for a given django.contrib.sites Site object +models.ForeignKey(Node, related_name='sites', null=True, blank=True).contribute_to_class(Site, 'root_node') + + +class Contentlet(models.Model): + page = models.ForeignKey(Page, related_name='contentlets') + name = models.CharField(max_length=255) + content = models.TextField() + dynamic = models.BooleanField(default=False) + + def __unicode__(self): + return self.name + + class Meta: + app_label = 'philo' + + +class ContentReference(models.Model): + page = models.ForeignKey(Page, related_name='contentreferences') + name = models.CharField(max_length=255) + content_type = models.ForeignKey(ContentType, verbose_name='Content type') + content_id = models.PositiveIntegerField(verbose_name='Content ID', blank=True, null=True) + content = generic.GenericForeignKey('content_type', 'content_id') + + def __unicode__(self): + return self.name + + class Meta: + app_label = 'philo' + + +register_templatetags('philo.templatetags.containers') + + +register_value_model(Template) +register_value_model(Page) \ No newline at end of file -- 2.20.1