Merge branch 'master' of git://github.com/melinath/philo
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Mon, 9 Aug 2010 09:09:35 +0000 (05:09 -0400)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Mon, 9 Aug 2010 09:09:35 +0000 (05:09 -0400)
* 'master' of git://github.com/melinath/philo:
  Added unique_together constraint to Attributes and Relationships.
  Added a bit to newsletters to streamline interactions with them.

1  2 
contrib/penfield/models.py
models/base.py

@@@ -15,11 -15,6 +15,11 @@@ class Blog(Entity, Titled)
        def entry_tags(self):
                """ Returns a QuerySet of Tags that are used on any entries in this blog. """
                return Tag.objects.filter(blogentries__blog=self).distinct()
 +      
 +      @property
 +      def entry_dates(self):
 +              dates = {'year': self.entries.dates('date', 'year', order='DESC'), 'month': self.entries.dates('date', 'month', order='DESC'), 'day': self.entries.dates('date', 'day', order='DESC')}
 +              return dates
  
  
  register_value_model(Blog)
@@@ -80,17 -75,6 +80,17 @@@ class BlogView(MultiView)
                elif isinstance(obj, Tag):
                        if obj in self.blog.entry_tags:
                                return reverse(self.tag_view, urlconf=self, kwargs={'tag_slugs': obj.slug})
 +              elif isinstance(obj, (str, unicode)):
 +                      split_obj = obj.split(':')
 +                      if len(split_obj) > 1:
 +                              entry_archive_view_args = {}
 +                              if split_obj[0].lower() == 'archives':
 +                                      entry_archive_view_args.update({'year': str(int(split_obj[1])).zfill(4)})
 +                                      if len(split_obj) > 2:
 +                                              entry_archive_view_args.update({'month': str(int(split_obj[2])).zfill(2)})
 +                                              if len(split_obj) > 3:
 +                                                      entry_archive_view_args.update({'day': str(int(split_obj[3])).zfill(2)})
 +                                      return reverse(self.entry_archive_view, urlconf=self, kwargs=entry_archive_view_args)
                raise ViewCanNotProvideSubpath
        
        @property
@@@ -230,6 -214,7 +230,7 @@@ class NewsletterArticle(Entity, Titled)
        full_text = models.TextField()
        
        class Meta:
+               get_latest_by = 'date'
                ordering = ['-date']
                unique_together = (('newsletter', 'slug'),)
  
@@@ -240,7 -225,7 +241,7 @@@ register_value_model(NewsletterArticle
  class NewsletterIssue(Entity, Titled):
        newsletter = models.ForeignKey(Newsletter, related_name='issues')
        number = models.PositiveIntegerField()
-       articles = models.ManyToManyField(NewsletterArticle)
+       articles = models.ManyToManyField(NewsletterArticle, related_name='issues')
        
        class Meta:
                ordering = ['-number']
diff --combined models/base.py
@@@ -4,7 -4,6 +4,7 @@@ from django.contrib.contenttypes impor
  from django.utils import simplejson as json
  from django.core.exceptions import ObjectDoesNotExist
  from philo.utils import ContentTypeRegistryLimiter
 +from philo.signals import entity_class_prepared
  from UserDict import DictMixin
  
  
@@@ -53,6 -52,7 +53,7 @@@ class Attribute(models.Model)
        
        class Meta:
                app_label = 'philo'
+               unique_together = ('key', 'entity_content_type', 'entity_object_id')
  
  
  value_content_type_limiter = ContentTypeRegistryLimiter()
@@@ -72,8 -72,8 +73,8 @@@ class Relationship(models.Model)
        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=value_content_type_limiter, verbose_name='Value type')
 -      value_object_id = models.PositiveIntegerField(verbose_name='Value ID')
 +      value_content_type = models.ForeignKey(ContentType, related_name='relationship_value_set', limit_choices_to=value_content_type_limiter, verbose_name='Value type', null=True, blank=True)
 +      value_object_id = models.PositiveIntegerField(verbose_name='Value ID', null=True, blank=True)
        value = generic.GenericForeignKey('value_content_type', 'value_object_id')
        
        def __unicode__(self):
@@@ -81,6 -81,7 +82,7 @@@
        
        class Meta:
                app_label = 'philo'
+               unique_together = ('key', 'entity_content_type', 'entity_object_id')
  
  
  class QuerySetMapper(object, DictMixin):
                return list(keys)
  
  
 +class EntityOptions(object):
 +      def __init__(self, options):
 +              if options is not None:
 +                      for key, value in options.__dict__.items():
 +                              setattr(self, key, value)
 +              if not hasattr(self, 'proxy_fields'):
 +                      self.proxy_fields = []
 +      
 +      def add_proxy_field(self, proxy_field):
 +              self.proxy_fields.append(proxy_field)
 +
 +
 +class EntityBase(models.base.ModelBase):
 +      def __new__(cls, name, bases, attrs):
 +              new = super(EntityBase, cls).__new__(cls, name, bases, attrs)
 +              entity_options = attrs.pop('EntityMeta', None)
 +              setattr(new, '_entity_meta', EntityOptions(entity_options))
 +              entity_class_prepared.send(sender=new)
 +              return new
 +
 +
  class Entity(models.Model):
 +      __metaclass__ = EntityBase
 +      
        attribute_set = generic.GenericRelation(Attribute, content_type_field='entity_content_type', object_id_field='entity_object_id')
        relationship_set = generic.GenericRelation(Relationship, content_type_field='entity_content_type', object_id_field='entity_object_id')
        
        def relationships(self):
                return QuerySetMapper(self.relationship_set)
        
 +      @property
 +      def _added_attribute_registry(self):
 +              if not hasattr(self, '_real_added_attribute_registry'):
 +                      self._real_added_attribute_registry = {}
 +              return self._real_added_attribute_registry
 +      
 +      @property
 +      def _removed_attribute_registry(self):
 +              if not hasattr(self, '_real_removed_attribute_registry'):
 +                      self._real_removed_attribute_registry = []
 +              return self._real_removed_attribute_registry
 +      
 +      @property
 +      def _added_relationship_registry(self):
 +              if not hasattr(self, '_real_added_relationship_registry'):
 +                      self._real_added_relationship_registry = {}
 +              return self._real_added_relationship_registry
 +      
 +      @property
 +      def _removed_relationship_registry(self):
 +              if not hasattr(self, '_real_removed_relationship_registry'):
 +                      self._real_removed_relationship_registry = []
 +              return self._real_removed_relationship_registry
 +      
 +      def save(self, *args, **kwargs):
 +              super(Entity, self).save(*args, **kwargs)
 +              
 +              for key in self._removed_attribute_registry:
 +                      self.attribute_set.filter(key__exact=key).delete()
 +              del self._removed_attribute_registry[:]
 +              
 +              for key, value in self._added_attribute_registry.items():
 +                      try:
 +                              attribute = self.attribute_set.get(key__exact=key)
 +                      except Attribute.DoesNotExist:
 +                              attribute = Attribute()
 +                              attribute.entity = self
 +                              attribute.key = key
 +                      attribute.value = value
 +                      attribute.save()
 +              self._added_attribute_registry.clear()
 +              
 +              for key in self._removed_relationship_registry:
 +                      self.relationship_set.filter(key__exact=key).delete()
 +              del self._removed_relationship_registry[:]
 +              
 +              for key, value in self._added_relationship_registry.items():
 +                      try:
 +                              relationship = self.relationship_set.get(key__exact=key)
 +                      except Relationship.DoesNotExist:
 +                              relationship = Relationship()
 +                              relationship.entity = self
 +                              relationship.key = key
 +                      relationship.value = value
 +                      relationship.save()
 +              self._added_relationship_registry.clear()
 +      
        class Meta:
                abstract = True