1 from django.db import models
2 from django import forms
3 from django.core.exceptions import FieldError
4 from django.utils.text import capfirst
5 from philo.models.base import Entity
6 from philo.signals import entity_class_prepared
7 from philo.validators import TemplateValidator
10 __all__ = ('AttributeField', 'RelationshipField')
13 class EntityProxyField(object):
14 descriptor_class = None
16 def __init__(self, *args, **kwargs):
17 if self.descriptor_class is None:
18 raise NotImplementedError('EntityProxyField subclasses must specify a descriptor_class.')
19 self.verbose_name = kwargs.get('verbose_name', None)
20 self.help_text = kwargs.get('help_text', None)
22 def actually_contribute_to_class(self, sender, **kwargs):
23 sender._entity_meta.add_proxy_field(self)
24 setattr(sender, self.attname, self.descriptor_class(self))
26 def contribute_to_class(self, cls, name):
27 if issubclass(cls, Entity):
30 if self.verbose_name is None and name:
31 self.verbose_name = name.replace('_', ' ')
32 entity_class_prepared.connect(self.actually_contribute_to_class, sender=cls)
34 raise FieldError('%s instances can only be declared on Entity subclasses.' % self.__class__.__name__)
36 def formfield(self, *args, **kwargs):
37 raise NotImplementedError('EntityProxyField subclasses must implement a formfield method.')
39 def value_from_object(self, obj):
40 return getattr(obj, self.attname)
43 class AttributeFieldDescriptor(object):
44 def __init__(self, field):
47 def __get__(self, instance, owner):
49 if self.field.key in instance._added_attribute_registry:
50 return instance._added_attribute_registry[self.field.key]
51 if self.field.key in instance._removed_attribute_registry:
54 return instance.attributes[self.field.key]
58 raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
60 def __set__(self, instance, value):
61 if self.field.key in instance._removed_attribute_registry:
62 instance._removed_attribute_registry.remove(self.field.key)
63 instance._added_attribute_registry[self.field.key] = value
65 def __delete__(self, instance):
66 if self.field.key in instance._added_attribute_registry:
67 del instance._added_attribute_registry[self.field.key]
68 instance._removed_attribute_registry.append(self.field.key)
71 class AttributeField(EntityProxyField):
72 descriptor_class = AttributeFieldDescriptor
74 def __init__(self, field_template=None, key=None, **kwargs):
75 super(AttributeField, self).__init__(**kwargs)
77 if field_template is None:
78 field_template = models.CharField(max_length=255)
79 self.field_template = field_template
81 def contribute_to_class(self, cls, name):
82 super(AttributeField, self).contribute_to_class(cls, name)
86 def formfield(self, **kwargs):
87 defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
88 defaults.update(kwargs)
89 return self.field_template.formfield(**defaults)
92 class RelationshipFieldDescriptor(object):
93 def __init__(self, field):
96 def __get__(self, instance, owner):
98 if self.field.key in instance._added_relationship_registry:
99 return instance._added_relationship_registry[self.field.key]
100 if self.field.key in instance._removed_relationship_registry:
103 return instance.relationships[self.field.key]
107 raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
109 def __set__(self, instance, value):
110 if isinstance(value, (models.Model, type(None))):
111 if self.field.key in instance._removed_relationship_registry:
112 instance._removed_relationship_registry.remove(self.field.key)
113 instance._added_relationship_registry[self.field.key] = value
115 raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
117 def __delete__(self, instance):
118 if self.field.key in instance._added_relationship_registry:
119 del instance._added_relationship_registry[self.field.key]
120 instance._removed_relationship_registry.append(self.field.key)
123 class RelationshipField(EntityProxyField):
124 descriptor_class = RelationshipFieldDescriptor
126 def __init__(self, model, limit_choices_to=None, key=None, **kwargs):
127 super(RelationshipField, self).__init__(**kwargs)
130 if limit_choices_to is None:
131 limit_choices_to = {}
132 self.limit_choices_to = limit_choices_to
134 def contribute_to_class(self, cls, name):
135 super(RelationshipField, self).contribute_to_class(cls, name)
139 def formfield(self, form_class=forms.ModelChoiceField, **kwargs):
140 defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
141 defaults.update(kwargs)
142 return form_class(self.model._default_manager.complex_filter(self.limit_choices_to), **defaults)
144 def value_from_object(self, obj):
145 relobj = super(RelationshipField, self).value_from_object(obj)
146 return getattr(relobj, 'pk', None)
149 class TemplateField(models.TextField):
150 def __init__(self, allow=None, disallow=None, secure=True, *args, **kwargs):
151 super(TemplateField, self).__init__(*args, **kwargs)
152 self.validators.append(TemplateValidator(allow, disallow, secure))
156 from south.modelsinspector import add_introspection_rules
160 add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])