1 from django.db import models
2 from django import forms
3 from django.core.exceptions import FieldError
4 from philo.models.base import Entity
5 from philo.signals import entity_class_prepared
8 __all__ = ('AttributeField', 'RelationshipField')
11 class EntityProxyField(object):
12 descriptor_class = None
14 def __init__(self, *args, **kwargs):
15 if self.descriptor_class is None:
16 raise NotImplementedError('EntityProxyField subclasses must specify a descriptor_class.')
18 def actually_contribute_to_class(self, sender, **kwargs):
19 sender._entity_meta.add_proxy_field(self)
20 setattr(sender, self.attname, self.descriptor_class(self))
22 def contribute_to_class(self, cls, name):
23 if issubclass(cls, Entity):
26 entity_class_prepared.connect(self.actually_contribute_to_class, sender=cls)
28 raise FieldError('%s instances can only be declared on Entity subclasses.' % self.__class__.__name__)
30 def formfield(self, *args, **kwargs):
31 raise NotImplementedError('EntityProxyField subclasses must implement a formfield method.')
33 def value_from_object(self, obj):
34 return getattr(obj, self.attname)
37 class AttributeFieldDescriptor(object):
38 def __init__(self, field):
41 def __get__(self, instance, owner):
43 if self.field.key in instance._added_attribute_registry:
44 return instance._added_attribute_registry[self.field.key]
45 if self.field.key in instance._removed_attribute_registry:
48 return instance.attributes[self.field.key]
52 raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
54 def __set__(self, instance, value):
55 if self.field.key in instance._removed_attribute_registry:
56 instance._removed_attribute_registry.remove(self.field.key)
57 instance._added_attribute_registry[self.field.key] = value
59 def __delete__(self, instance):
60 if self.field.key in instance._added_attribute_registry:
61 del instance._added_attribute_registry[self.field.key]
62 instance._removed_attribute_registry.append(self.field.key)
65 class AttributeField(EntityProxyField):
66 descriptor_class = AttributeFieldDescriptor
68 def __init__(self, key, field_template=None):
70 if field_template is None:
71 field_template = models.CharField(max_length=255)
72 self.field_template = field_template
74 def formfield(self, *args, **kwargs):
75 field = self.field_template.formfield(*args, **kwargs)
76 field.required = False
80 class RelationshipFieldDescriptor(object):
81 def __init__(self, field):
84 def __get__(self, instance, owner):
86 if self.field.key in instance._added_relationship_registry:
87 return instance._added_relationship_registry[self.field.key]
88 if self.field.key in instance._removed_relationship_registry:
91 return instance.relationships[self.field.key]
95 raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
97 def __set__(self, instance, value):
98 if isinstance(value, (models.Model, type(None))):
99 if self.field.key in instance._removed_relationship_registry:
100 instance._removed_relationship_registry.remove(self.field.key)
101 instance._added_relationship_registry[self.field.key] = value
103 raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
105 def __delete__(self, instance):
106 if self.field.key in instance._added_relationship_registry:
107 del instance._added_relationship_registry[self.field.key]
108 instance._removed_relationship_registry.append(self.field.key)
111 class RelationshipField(EntityProxyField):
112 descriptor_class = RelationshipFieldDescriptor
114 def __init__(self, key, model, limit_choices_to=None):
117 if limit_choices_to is None:
118 limit_choices_to = {}
119 self.limit_choices_to = limit_choices_to
121 def formfield(self, form_class=forms.ModelChoiceField, **kwargs):
122 field = form_class(self.model._default_manager.complex_filter(self.limit_choices_to), **kwargs)
123 field.required = False
126 def value_from_object(self, obj):
127 relobj = super(RelationshipField, self).value_from_object(obj)
128 return getattr(relobj, 'pk', None)