Added SlugTreeEntity to models.base.__all__.
[philo.git] / philo / models / base.py
index b41a00e..86569b7 100644 (file)
@@ -1,7 +1,7 @@
 from django import forms
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
 from django import forms
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
-from django.core.exceptions import ObjectDoesNotExist
+from django.core.exceptions import ValidationError
 from django.core.validators import RegexValidator
 from django.db import models
 from django.utils import simplejson as json
 from django.core.validators import RegexValidator
 from django.db import models
 from django.utils import simplejson as json
@@ -16,7 +16,7 @@ from philo.utils.entities import AttributeMapper, TreeAttributeMapper
 from philo.validators import json_validator
 
 
 from philo.validators import json_validator
 
 
-__all__ = ('Tag', 'value_content_type_limiter', 'register_value_model', 'unregister_value_model', 'JSONValue', 'ForeignKeyValue', 'ManyToManyValue', 'Attribute', 'Entity', 'TreeEntity')
+__all__ = ('Tag', 'value_content_type_limiter', 'register_value_model', 'unregister_value_model', 'JSONValue', 'ForeignKeyValue', 'ManyToManyValue', 'Attribute', 'Entity', 'TreeEntity', 'SlugTreeEntity')
 
 
 class Tag(models.Model):
 
 
 class Tag(models.Model):
@@ -35,18 +35,6 @@ class Tag(models.Model):
                ordering = ('name',)
 
 
                ordering = ('name',)
 
 
-class Titled(models.Model):
-       # Use of this model is deprecated.
-       title = models.CharField(max_length=255)
-       slug = models.SlugField(max_length=255)
-       
-       def __unicode__(self):
-               return self.title
-       
-       class Meta:
-               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()
 
 #: 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()
 
@@ -337,10 +325,18 @@ class Entity(models.Model):
                abstract = True
 
 
                abstract = True
 
 
-class TreeManager(models.Manager):
+class TreeEntityBase(MPTTModelBase, EntityBase):
+       def __new__(meta, name, bases, attrs):
+               attrs['_mptt_meta'] = MPTTOptions(attrs.pop('MPTTMeta', None))
+               cls = EntityBase.__new__(meta, name, bases, attrs)
+               
+               return meta.register(cls)
+
+
+class TreeEntityManager(models.Manager):
        use_for_related_fields = True
        
        use_for_related_fields = True
        
-       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='pk'):
                """
                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).
                
                """
                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).
                
@@ -450,10 +446,12 @@ class TreeManager(models.Manager):
                return find_obj(segments, len(segments)/2 or len(segments))
 
 
                return find_obj(segments, len(segments)/2 or len(segments))
 
 
-class TreeModel(MPTTModel):
-       objects = TreeManager()
+class TreeEntity(Entity, MPTTModel):
+       """An abstract subclass of Entity which represents a tree relationship."""
+       
+       __metaclass__ = TreeEntityBase
+       objects = TreeEntityManager()
        parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
        parent = models.ForeignKey('self', related_name='children', null=True, blank=True)
-       slug = models.SlugField(max_length=255)
        
        def get_path(self, root=None, pathsep='/', field='slug'):
                """
        
        def get_path(self, root=None, pathsep='/', field='slug'):
                """
@@ -468,7 +466,7 @@ class TreeModel(MPTTModel):
                        return ''
                
                if root is None and self.is_root_node():
                        return ''
                
                if root is None and self.is_root_node():
-                       return self.slug
+                       return getattr(self, field, '?')
                
                if root is not None and not self.is_descendant_of(root):
                        raise AncestorDoesNotExist(root)
                
                if root is not None and not self.is_descendant_of(root):
                        raise AncestorDoesNotExist(root)
@@ -481,27 +479,6 @@ class TreeModel(MPTTModel):
                return pathsep.join([getattr(parent, field, '?') for parent in qs])
        path = property(get_path)
        
                return pathsep.join([getattr(parent, field, '?') for parent in qs])
        path = property(get_path)
        
-       def __unicode__(self):
-               return self.path
-       
-       class Meta:
-               unique_together = (('parent', 'slug'),)
-               abstract = True
-
-
-class TreeEntityBase(MPTTModelBase, EntityBase):
-       def __new__(meta, name, bases, attrs):
-               attrs['_mptt_meta'] = MPTTOptions(attrs.pop('MPTTMeta', None))
-               cls = EntityBase.__new__(meta, name, bases, attrs)
-               
-               return meta.register(cls)
-
-
-class TreeEntity(Entity, TreeModel):
-       """An abstract subclass of Entity which represents a tree relationship."""
-       
-       __metaclass__ = TreeEntityBase
-       
        def get_attribute_mapper(self, mapper=None):
                """
                Returns a :class:`.TreeAttributeMapper` or :class:`.AttributeMapper` which can be used to retrieve related :class:`Attribute`\ s' values directly. If an :class:`Attribute` with a given key is not related to the :class:`Entity`, then the mapper will check the parent's attributes.
        def get_attribute_mapper(self, mapper=None):
                """
                Returns a :class:`.TreeAttributeMapper` or :class:`.AttributeMapper` which can be used to retrieve related :class:`Attribute`\ s' values directly. If an :class:`Attribute` with a given key is not related to the :class:`Entity`, then the mapper will check the parent's attributes.
@@ -525,5 +502,35 @@ class TreeEntity(Entity, TreeModel):
                return super(TreeEntity, self).get_attribute_mapper(mapper)
        attributes = property(get_attribute_mapper)
        
                return super(TreeEntity, self).get_attribute_mapper(mapper)
        attributes = property(get_attribute_mapper)
        
+       def __unicode__(self):
+               return self.path
+       
+       class Meta:
+               abstract = True
+
+
+class SlugTreeEntityManager(TreeEntityManager):
+       def get_with_path(self, path, root=None, absolute_result=True, pathsep='/', field='slug'):
+               return super(SlugTreeEntityManager, self).get_with_path(path, root, absolute_result, pathsep, field)
+
+
+class SlugTreeEntity(TreeEntity):
+       objects = SlugTreeEntityManager()
+       slug = models.SlugField(max_length=255)
+       
+       def get_path(self, root=None, pathsep='/', field='slug'):
+               return super(SlugTreeEntity, self).get_path(root, pathsep, field)
+       path = property(get_path)
+       
+       def clean(self):
+               if self.parent is None:
+                       try:
+                               self._default_manager.exclude(pk=self.pk).get(slug=self.slug, parent__isnull=True)
+                       except self.DoesNotExist:
+                               pass
+                       else:
+                               raise ValidationError(self.unique_error_message(self.__class__, ('parent', 'slug')))
+       
        class Meta:
        class Meta:
+               unique_together = ('parent', 'slug')
                abstract = True
\ No newline at end of file
                abstract = True
\ No newline at end of file