Initial extension of containers to support referencing objects of arbitrary type.
[philo.git] / models.py
index 6691aca..41e9a15 100644 (file)
--- a/models.py
+++ b/models.py
@@ -23,12 +23,12 @@ from django.http import Http404, HttpResponse, HttpResponseServerError, HttpResp
 from django.core.servers.basehttp import FileWrapper
 
 
 from django.core.servers.basehttp import FileWrapper
 
 
-def _ct_model_name(model):
-       opts = model._meta
-       while opts.proxy:
-               model = opts.proxy_for_model
-               opts = model._meta
-       return opts.object_name.lower()
+def register_value_model(model):
+       pass
+
+
+def unregister_value_model(model):
+       pass
 
 
 class Attribute(models.Model):
 
 
 class Attribute(models.Model):
@@ -38,40 +38,27 @@ class Attribute(models.Model):
        key = models.CharField(max_length=255)
        json_value = models.TextField(verbose_name='Value (JSON)', help_text='This value must be valid JSON.')
        
        key = models.CharField(max_length=255)
        json_value = models.TextField(verbose_name='Value (JSON)', help_text='This value must be valid JSON.')
        
-       @property
-       def value(self):
+       def get_value(self):
                return json.loads(self.json_value)
        
                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 = property(get_value, set_value, delete_value)
+       
        def __unicode__(self):
                return u'"%s": %s' % (self.key, self.value)
 
 
 class Relationship(models.Model):
        def __unicode__(self):
                return u'"%s": %s' % (self.key, self.value)
 
 
 class Relationship(models.Model):
-       _value_models = []
-       
-       @staticmethod
-       def register_value_model(model):
-               if issubclass(model, models.Model):
-                       model_name = _ct_model_name(model)
-                       if model_name not in Relationship._value_models:
-                               Relationship._value_models.append(model_name)
-               else:
-                       raise TypeError('Relationship.register_value_model only accepts subclasses of django.db.models.Model')
-       
-       @staticmethod
-       def unregister_value_model(model):
-               if issubclass(model, models.Model):
-                       model_name = _ct_model_name(model)
-                       if model_name in Relationship._value_models:
-                               Relationship._value_models.remove(model_name)
-               else:
-                       raise TypeError('Relationship.unregister_value_model only accepts subclasses of django.db.models.Model')
-       
        entity_content_type = models.ForeignKey(ContentType, related_name='relationship_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)
        entity_content_type = models.ForeignKey(ContentType, related_name='relationship_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={'model__in':_value_models}, verbose_name='Value type')
+       value_content_type = models.ForeignKey(ContentType, related_name='relationship_value_set', verbose_name='Value type')
        value_object_id = models.PositiveIntegerField(verbose_name='Value ID')
        value = generic.GenericForeignKey('value_content_type', 'value_object_id')
        
        value_object_id = models.PositiveIntegerField(verbose_name='Value ID')
        value = generic.GenericForeignKey('value_content_type', 'value_object_id')
        
@@ -118,44 +105,22 @@ class Collection(models.Model):
        description = models.TextField(blank=True, null=True)
 
 
        description = models.TextField(blank=True, null=True)
 
 
+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):
 class CollectionMember(models.Model):
-       _value_models = []
-       
-       @staticmethod
-       def register_value_model(model):
-               if issubclass(model, models.Model):
-                       model_name = _ct_model_name(model)
-                       if model_name not in CollectionMember._value_models:
-                               CollectionMember._value_models.append(model_name)
-               else:
-                       raise TypeError('CollectionMember.register_value_model only accepts subclasses of django.db.models.Model')
-       
-       @staticmethod
-       def unregister_value_model(model):
-               if issubclass(model, models.Model):
-                       model_name = _ct_model_name(model)
-                       if model_name in CollectionMember._value_models:
-                               CollectionMember._value_models.remove(model_name)
-               else:
-                       raise TypeError('CollectionMember.unregister_value_model only accepts subclasses of django.db.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)
        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, limit_choices_to={'model__in':_value_models}, verbose_name='Member type')
+       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')
 
 
        member_object_id = models.PositiveIntegerField(verbose_name='Member ID')
        member = generic.GenericForeignKey('member_content_type', 'member_object_id')
 
 
-def register_value_model(model):
-       Relationship.register_value_model(model)
-       CollectionMember.register_value_model(model)
-
-
-def unregister_value_model(model):
-       Relationship.unregister_value_model(model)
-       CollectionMember.unregister_value_model(model)
-
-
 class TreeManager(models.Manager):
        use_for_related_fields = True
        
 class TreeManager(models.Manager):
        use_for_related_fields = True
        
@@ -233,6 +198,15 @@ class TreeEntity(TreeModel, Entity):
 class Node(TreeEntity):
        instance_type = models.ForeignKey(ContentType, editable=False)
        
 class Node(TreeEntity):
        instance_type = models.ForeignKey(ContentType, editable=False)
        
+       def get_path(self, pathsep='/', field='slug'):
+               path = getattr(self.instance, field)
+               parent = self.parent
+               while parent:
+                       path = getattr(parent.instance, field) + pathsep + path
+                       parent = parent.parent
+               return path
+       path = property(get_path)
+       
        def save(self, force_insert=False, force_update=False):
                if not hasattr(self, 'instance_type_ptr'):
                        self.instance_type = ContentType.objects.get_for_model(self.__class__)
        def save(self, force_insert=False, force_update=False):
                if not hasattr(self, 'instance_type_ptr'):
                        self.instance_type = ContentType.objects.get_for_model(self.__class__)
@@ -251,9 +225,7 @@ class Node(TreeEntity):
 class MultiNode(Node):
        accepts_subpath = True
        
 class MultiNode(Node):
        accepts_subpath = True
        
-       @property
-       def urlpatterns(self):
-               return []
+       urlpatterns = []
        
        def render_to_response(self, request, path=None, subpath=None):
                if not subpath:
        
        def render_to_response(self, request, path=None, subpath=None):
                if not subpath:
@@ -310,37 +282,46 @@ class Template(TreeModel):
        @property
        def containers(self):
                """
        @property
        def containers(self):
                """
-               Returns a list of names of contentlets referenced by containers. 
+               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.
                """
                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_node_names(template):
-                       def nodelist_container_node_names(nodelist):
-                               names = []
+               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'):
                                                        if hasattr(node, nodelist_name):
                                for node in nodelist:
                                        try:
                                                for nodelist_name in ('nodelist', 'nodelist_loop', 'nodelist_empty', 'nodelist_true', 'nodelist_false'):
                                                        if hasattr(node, nodelist_name):
-                                                               names.extend(nodelist_container_node_names(getattr(node, nodelist_name)))
+                                                               nodes.extend(nodelist_container_nodes(getattr(node, nodelist_name)))
                                                if isinstance(node, ContainerNode):
                                                if isinstance(node, ContainerNode):
-                                                       names.append(node.name)
+                                                       nodes.append(node)
                                                elif isinstance(node, ExtendsNode):
                                                        extended_template = node.get_parent(Context())
                                                        if extended_template:
                                                elif isinstance(node, ExtendsNode):
                                                        extended_template = node.get_parent(Context())
                                                        if extended_template:
-                                                               names.extend(container_node_names(extended_template))
+                                                               nodes.extend(container_nodes(extended_template))
                                                elif isinstance(node, ConstantIncludeNode):
                                                        included_template = node.template
                                                        if included_template:
                                                elif isinstance(node, ConstantIncludeNode):
                                                        included_template = node.template
                                                        if included_template:
-                                                               names.extend(container_node_names(included_template))
+                                                               nodes.extend(container_nodes(included_template))
                                                elif isinstance(node, IncludeNode):
                                                        included_template = get_template(node.template_name.resolve(Context()))
                                                        if included_template:
                                                elif isinstance(node, IncludeNode):
                                                        included_template = get_template(node.template_name.resolve(Context()))
                                                        if included_template:
-                                                               names.extend(container_node_names(included_template))
+                                                               nodes.extend(container_nodes(included_template))
                                        except:
                                                pass # fail for this node
                                        except:
                                                pass # fail for this node
-                               return names
-                       return nodelist_container_node_names(template.nodelist)
-               return set(container_node_names(self.django_template))
+                               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')
        
        def __unicode__(self):
                return self.get_path(u' › ', 'name')
@@ -377,6 +358,14 @@ class Contentlet(models.Model):
        dynamic = models.BooleanField(default=False)
 
 
        dynamic = models.BooleanField(default=False)
 
 
+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')
+       content = generic.GenericForeignKey('content_type', 'content_id')
+
+
 register_templatetags('philo.templatetags.containers')
 
 
 register_templatetags('philo.templatetags.containers')