Merge branch 'sobol-templates-hotfix' into release
[philo.git] / philo / models / base.py
index 01fdd64..1726d19 100644 (file)
@@ -1,25 +1,31 @@
+from UserDict import DictMixin
+
 from django import forms
 from django import forms
-from django.db import models
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.validators import RegexValidator
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.validators import RegexValidator
+from django.db import models
 from django.utils import simplejson as json
 from django.utils.encoding import force_unicode
 from django.utils import simplejson as json
 from django.utils.encoding import force_unicode
+from mptt.models import MPTTModel, MPTTModelBase, MPTTOptions
+
 from philo.exceptions import AncestorDoesNotExist
 from philo.models.fields import JSONField
 from philo.exceptions import AncestorDoesNotExist
 from philo.models.fields import JSONField
-from philo.utils import ContentTypeRegistryLimiter, ContentTypeSubclassLimiter
 from philo.signals import entity_class_prepared
 from philo.signals import entity_class_prepared
+from philo.utils import ContentTypeRegistryLimiter, ContentTypeSubclassLimiter
 from philo.validators import json_validator
 from philo.validators import json_validator
-from UserDict import DictMixin
-from mptt.models import MPTTModel, MPTTModelBase, MPTTOptions
 
 
 class Tag(models.Model):
 
 
 class Tag(models.Model):
+       """A simple, generic model for tagging."""
+       #: A CharField (max length 255) which contains the name of the tag.
        name = models.CharField(max_length=255)
        name = models.CharField(max_length=255)
+       #: A CharField (max length 255) which contains the tag's unique slug.
        slug = models.SlugField(max_length=255, unique=True)
        
        def __unicode__(self):
        slug = models.SlugField(max_length=255, unique=True)
        
        def __unicode__(self):
+               """Returns the value of the :attr:`name` field"""
                return self.name
        
        class Meta:
                return self.name
        
        class Meta:
@@ -38,18 +44,12 @@ class Titled(models.Model):
                abstract = True
 
 
                abstract = True
 
 
+#: An instance of :class:`ContentTypeRegistryLimiter` which is used to track the content types which can be related to by :class:`ForeignKeyValue`\ s and :class:`ManyToManyValue`\ s.
 value_content_type_limiter = ContentTypeRegistryLimiter()
 value_content_type_limiter = ContentTypeRegistryLimiter()
-"""
-An instance of :class:`ContentTypeRegistryLimiter` which is used to track the content types which can be related to by ForeignKeyValues and ManyToManyValues.
-
-"""
 
 
 def register_value_model(model):
 
 
 def register_value_model(model):
-       """
-       Registers a model as a valid content type for a :class:`ForeignKeyValue` or :class:`ManyToManyValue` through the :data:`value_content_type_limiter`.
-       
-       """
+       """Registers a model as a valid content type for a :class:`ForeignKeyValue` or :class:`ManyToManyValue` through the :data:`value_content_type_limiter`."""
        value_content_type_limiter.register_class(model)
 
 
        value_content_type_limiter.register_class(model)
 
 
@@ -57,10 +57,7 @@ register_value_model(Tag)
 
 
 def unregister_value_model(model):
 
 
 def unregister_value_model(model):
-       """
-       Registers a model as a valid content type for a :class:`ForeignKeyValue` or :class:`ManyToManyValue` through the :data:`value_content_type_limiter`.
-       
-       """
+       """Registers a model as a valid content type for a :class:`ForeignKeyValue` or :class:`ManyToManyValue` through the :data:`value_content_type_limiter`."""
        value_content_type_limiter.unregister_class(model)
 
 
        value_content_type_limiter.unregister_class(model)
 
 
@@ -71,11 +68,10 @@ class AttributeValue(models.Model):
        AttributeValue subclasses are expected to supply access to a clean version of their value through an attribute called "value".
        
        """
        AttributeValue subclasses are expected to supply access to a clean version of their value through an attribute called "value".
        
        """
+       
+       #: :class:`GenericRelation` to :class:`Attribute`
        attribute_set = generic.GenericRelation('Attribute', content_type_field='value_content_type', object_id_field='value_object_id')
        attribute_set = generic.GenericRelation('Attribute', content_type_field='value_content_type', object_id_field='value_object_id')
-       """
-       :class:`GenericRelation` to :class:`Attribute`
        
        
-       """
        def set_value(self, value):
                """Given a ``value``, sets the appropriate fields so that it can be correctly stored in the database."""
                raise NotImplementedError
        def set_value(self, value):
                """Given a ``value``, sets the appropriate fields so that it can be correctly stored in the database."""
                raise NotImplementedError
@@ -87,6 +83,7 @@ class AttributeValue(models.Model):
                :returns: A dictionary mapping field names to formfields.
                
                """
                :returns: A dictionary mapping field names to formfields.
                
                """
+               
                raise NotImplementedError
        
        def construct_instance(self, **kwargs):
                raise NotImplementedError
        
        def construct_instance(self, **kwargs):
@@ -100,11 +97,8 @@ class AttributeValue(models.Model):
                abstract = True
 
 
                abstract = True
 
 
+#: An instance of :class:`ContentTypeSubclassLimiter` which is used to track the content types which are considered valid value models for an :class:`Attribute`.
 attribute_value_limiter = ContentTypeSubclassLimiter(AttributeValue)
 attribute_value_limiter = ContentTypeSubclassLimiter(AttributeValue)
-"""
-An instance of :class:`ContentTypeSubclassLimiter` which is used to track the content types which are considered valid value models for an :class:`Attribute`.
-
-"""
 
 
 class JSONValue(AttributeValue):
 
 
 class JSONValue(AttributeValue):
@@ -249,31 +243,21 @@ class ManyToManyValue(AttributeValue):
 
 
 class Attribute(models.Model):
 
 
 class Attribute(models.Model):
-       """
-       Represents an arbitrary key/value pair on an arbitrary :class:`Model` where the key consists of word characters and the value is a subclass of :class:`AttributeValue`.
-       
-       """
+       """Represents an arbitrary key/value pair on an arbitrary :class:`Model` where the key consists of word characters and the value is a subclass of :class:`AttributeValue`."""
        entity_content_type = models.ForeignKey(ContentType, related_name='attribute_entity_set', verbose_name='Entity type')
        entity_object_id = models.PositiveIntegerField(verbose_name='Entity ID', db_index=True)
        entity_content_type = models.ForeignKey(ContentType, related_name='attribute_entity_set', verbose_name='Entity type')
        entity_object_id = models.PositiveIntegerField(verbose_name='Entity ID', db_index=True)
-       entity = generic.GenericForeignKey('entity_content_type', 'entity_object_id')
-       """
-       :class:`GenericForeignKey` to anything (generally an instance of an Entity subclass).
        
        
-       """
+       #: :class:`GenericForeignKey` to anything (generally an instance of an Entity subclass).
+       entity = generic.GenericForeignKey('entity_content_type', 'entity_object_id')
        
        value_content_type = models.ForeignKey(ContentType, related_name='attribute_value_set', limit_choices_to=attribute_value_limiter, verbose_name='Value type', null=True, blank=True)
        value_object_id = models.PositiveIntegerField(verbose_name='Value ID', null=True, blank=True, db_index=True)
        
        value_content_type = models.ForeignKey(ContentType, related_name='attribute_value_set', limit_choices_to=attribute_value_limiter, verbose_name='Value type', null=True, blank=True)
        value_object_id = models.PositiveIntegerField(verbose_name='Value ID', null=True, blank=True, db_index=True)
-       value = generic.GenericForeignKey('value_content_type', 'value_object_id')
-       """
-       :class:`GenericForeignKey` to an instance of a subclass of :class:`AttributeValue` as determined by the :data:`attribute_value_limiter`.
        
        
-       """
+       #: :class:`GenericForeignKey` to an instance of a subclass of :class:`AttributeValue` as determined by the :data:`attribute_value_limiter`.
+       value = generic.GenericForeignKey('value_content_type', 'value_object_id')
        
        
+       #: :class:`CharField` containing a key (up to 255 characters) consisting of alphanumeric characters and underscores.
        key = models.CharField(max_length=255, validators=[RegexValidator("\w+")], help_text="Must contain one or more alphanumeric characters or underscores.", db_index=True)
        key = models.CharField(max_length=255, validators=[RegexValidator("\w+")], help_text="Must contain one or more alphanumeric characters or underscores.", db_index=True)
-       """
-       :class:`CharField` containing a key (up to 255 characters) consisting of alphanumeric characters and underscores.
-       
-       """
        
        def __unicode__(self):
                return u'"%s": %s' % (self.key, self.value)
        
        def __unicode__(self):
                return u'"%s": %s' % (self.key, self.value)
@@ -346,7 +330,9 @@ class Entity(models.Model):
                        u'eggs'
                        >>> entity.attributes['spam']
                        u'eggs'
                        u'eggs'
                        >>> entity.attributes['spam']
                        u'eggs'
+               
                """
                """
+               
                return QuerySetMapper(self.attribute_set.all())
        
        class Meta:
                return QuerySetMapper(self.attribute_set.all())
        
        class Meta:
@@ -358,7 +344,7 @@ class TreeManager(models.Manager):
        
        def get_with_path(self, path, root=None, absolute_result=True, pathsep='/', field='slug'):
                """
        
        def get_with_path(self, path, root=None, absolute_result=True, pathsep='/', field='slug'):
                """
-               If ``absolute_result`` is ``True``, returns the object at ``path`` (starting at ``root``) or raises a :exception:`DoesNotExist` exception. Otherwise, returns a tuple containing the deepest object found along ``path`` (or ``root`` if no deeper object is found) and the remainder of the path after that object as a string (or None if there is no remaining path).
+               If ``absolute_result`` is ``True``, returns the object at ``path`` (starting at ``root``) or raises an :class:`~django.core.exceptions.ObjectDoesNotExist` exception. Otherwise, returns a tuple containing the deepest object found along ``path`` (or ``root`` if no deeper object is found) and the remainder of the path after that object as a string (or None if there is no remaining path).
                
                .. note:: If you are looking for something with an exact path, it is faster to use absolute_result=True, unless the path depth is over ~40, in which case the high cost of the absolute query may make a binary search (i.e. non-absolute) faster.
                
                
                .. note:: If you are looking for something with an exact path, it is faster to use absolute_result=True, unless the path depth is over ~40, in which case the high cost of the absolute query may make a binary search (i.e. non-absolute) faster.
                
@@ -369,9 +355,11 @@ class TreeManager(models.Manager):
                :param absolute_result: Whether to return an absolute result or do a binary search
                :param pathsep: The path separator used in ``path``
                :param field: The field on the model which should be queried for ``path`` segment matching.
                :param absolute_result: Whether to return an absolute result or do a binary search
                :param pathsep: The path separator used in ``path``
                :param field: The field on the model which should be queried for ``path`` segment matching.
-               :returns: An instance if absolute_result is True or (instance, remaining_path) otherwise.
+               :returns: An instance if ``absolute_result`` is ``True`` or an (instance, remaining_path) tuple otherwise.
+               :raises django.core.exceptions.ObjectDoesNotExist: if no object can be found matching the input parameters.
                
                """
                
                """
+               
                segments = path.split(pathsep)
                
                # Clean out blank segments. Handles multiple consecutive pathseps.
                segments = path.split(pathsep)
                
                # Clean out blank segments. Handles multiple consecutive pathseps.
@@ -477,6 +465,7 @@ class TreeModel(MPTTModel):
                :returns: A string representation of an object's path.
                
                """
                :returns: A string representation of an object's path.
                
                """
+               
                if root == self:
                        return ''
                
                if root == self:
                        return ''
                
@@ -508,10 +497,8 @@ class TreeEntityBase(MPTTModelBase, EntityBase):
 
 
 class TreeEntity(Entity, TreeModel):
 
 
 class TreeEntity(Entity, TreeModel):
-       """
-       An abstract subclass of Entity which represents a tree relationship.
+       """An abstract subclass of Entity which represents a tree relationship."""
        
        
-       """
        __metaclass__ = TreeEntityBase
        
        @property
        __metaclass__ = TreeEntityBase
        
        @property
@@ -530,6 +517,7 @@ class TreeEntity(Entity, TreeModel):
                        u'eggs'
                
                """
                        u'eggs'
                
                """
+               
                if self.parent:
                        return QuerySetMapper(self.attribute_set.all(), passthrough=self.parent.attributes)
                return super(TreeEntity, self).attributes
                if self.parent:
                        return QuerySetMapper(self.attribute_set.all(), passthrough=self.parent.attributes)
                return super(TreeEntity, self).attributes