Initial implementation of AttributeField and RelationshipField.
authorJoseph Spiros <joseph.spiros@ithinksw.com>
Sat, 31 Jul 2010 03:16:52 +0000 (23:16 -0400)
committerJoseph Spiros <joseph.spiros@ithinksw.com>
Sat, 31 Jul 2010 03:16:52 +0000 (23:16 -0400)
These objects are not actual Fields, but rather they simply add descriptors to Entity subclasses which provide transparent access to specific attributes and relationships.

models/__init__.py
models/fields.py [new file with mode: 0644]

index b9ea3ac..5d39ac6 100644 (file)
@@ -2,6 +2,7 @@ from philo.models.base import *
 from philo.models.collections import *
 from philo.models.nodes import *
 from philo.models.pages import *
+from philo.models.fields import *
 from django.contrib.auth.models import User, Group
 from django.contrib.sites.models import Site
 
diff --git a/models/fields.py b/models/fields.py
new file mode 100644 (file)
index 0000000..7f76b9c
--- /dev/null
@@ -0,0 +1,96 @@
+from django.db import models
+from django.db.models import signals
+from django.core.exceptions import ObjectDoesNotExist, FieldError
+from django.contrib.contenttypes.models import ContentType
+from functools import partial
+from philo.models.base import Attribute, Relationship, Entity
+
+
+__all__ = ('AttributeField', 'RelationshipField')
+
+
+class AttributeFieldDescriptor(object):
+       def __init__(self, field):
+               self.field = field
+       
+       def __get__(self, instance, owner):
+               if instance:
+                       try:
+                               return instance.attribute_set.get(key__exact=self.field.key).value
+                       except ObjectDoesNotExist:
+                               return None
+               else:
+                       raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
+       
+       def __set__(self, instance, value):
+               try:
+                       attribute = instance.attribute_set.get(key__exact=self.field.key)
+               except ObjectDoesNotExist:
+                       attribute = Attribute()
+                       attribute.entity = instance
+                       attribute.key = self.field.key
+               attribute.value = value
+               attribute.save()
+       
+       def __delete__(self, instance):
+               instance.attribute_set.filter(key__exact=self.field.key).delete()
+
+
+class AttributeField(object):
+       def __init__(self, key):
+               self.key = key
+       
+       def actually_contribute_to_class(self, sender, **kwargs):
+               setattr(sender, self.name, AttributeFieldDescriptor(self))
+       
+       def contribute_to_class(self, cls, name):
+               if issubclass(cls, Entity):
+                       self.name = name
+                       signals.class_prepared.connect(self.actually_contribute_to_class, sender=cls)
+               else:
+                       raise FieldError('AttributeFields can only be declared on Entity subclasses.')
+
+
+class RelationshipFieldDescriptor(object):
+       def __init__(self, field):
+               self.field = field
+       
+       def __get__(self, instance, owner):
+               if instance:
+                       try:
+                               return instance.relationship_set.get(key__exact=self.field.key).value
+                       except ObjectDoesNotExist:
+                               return None
+               else:
+                       raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
+       
+       def __set__(self, instance, value):
+               if isinstance(value, (models.Model, type(None))):
+                       try:
+                               relationship = instance.relationship_set.get(key__exact=self.field.key)
+                       except ObjectDoesNotExist:
+                               relationship = Relationship()
+                               relationship.entity = instance
+                               relationship.key = self.field.key
+                       relationship.value = value
+                       relationship.save()
+               else:
+                       raise AttributeError('The \'%\' attribute can only be set using existing Model objects.' % self.field.name)
+       
+       def __delete__(self, instance):
+               instance.relationship_set.filter(key__exact=self.field.key).delete()
+
+
+class RelationshipField(object):
+       def __init__(self, key):
+               self.key = key
+       
+       def actually_contribute_to_class(self, sender, **kwargs):
+               setattr(sender, self.name, RelationshipFieldDescriptor(self))
+       
+       def contribute_to_class(self, cls, name):
+               if issubclass(cls, Entity):
+                       self.name = name
+                       signals.class_prepared.connect(self.actually_contribute_to_class, sender=cls)
+               else:
+                       raise FieldError('RelationshipFields can only be declared on Entity subclasses.')
\ No newline at end of file