1 from django.db import models
2 from django import forms
3 from django.core.exceptions import FieldError, ValidationError
4 from django.utils import simplejson as json
5 from django.utils.text import capfirst
6 from philo.signals import entity_class_prepared
7 from philo.validators import TemplateValidator, json_validator
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 from philo.models.base import Entity
28 if issubclass(cls, Entity):
31 if self.verbose_name is None and name:
32 self.verbose_name = name.replace('_', ' ')
33 entity_class_prepared.connect(self.actually_contribute_to_class, sender=cls)
35 raise FieldError('%s instances can only be declared on Entity subclasses.' % self.__class__.__name__)
37 def formfield(self, *args, **kwargs):
38 raise NotImplementedError('EntityProxyField subclasses must implement a formfield method.')
40 def value_from_object(self, obj):
41 return getattr(obj, self.attname)
44 class AttributeFieldDescriptor(object):
45 def __init__(self, field):
48 def __get__(self, instance, owner):
50 if self.field.key in instance._added_attribute_registry:
51 return instance._added_attribute_registry[self.field.key]
52 if self.field.key in instance._removed_attribute_registry:
55 return instance.attributes[self.field.key]
59 raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
61 def __set__(self, instance, value):
62 if self.field.key in instance._removed_attribute_registry:
63 instance._removed_attribute_registry.remove(self.field.key)
64 instance._added_attribute_registry[self.field.key] = value
66 def __delete__(self, instance):
67 if self.field.key in instance._added_attribute_registry:
68 del instance._added_attribute_registry[self.field.key]
69 instance._removed_attribute_registry.append(self.field.key)
72 class AttributeField(EntityProxyField):
73 descriptor_class = AttributeFieldDescriptor
75 def __init__(self, field_template=None, key=None, **kwargs):
76 super(AttributeField, self).__init__(**kwargs)
78 if field_template is None:
79 field_template = models.CharField(max_length=255)
80 self.field_template = field_template
82 def contribute_to_class(self, cls, name):
83 super(AttributeField, self).contribute_to_class(cls, name)
87 def formfield(self, **kwargs):
88 defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
89 defaults.update(kwargs)
90 return self.field_template.formfield(**defaults)
93 class RelationshipFieldDescriptor(object):
94 def __init__(self, field):
97 def __get__(self, instance, owner):
99 if self.field.key in instance._added_relationship_registry:
100 return instance._added_relationship_registry[self.field.key]
101 if self.field.key in instance._removed_relationship_registry:
104 return instance.relationships[self.field.key]
108 raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
110 def __set__(self, instance, value):
111 if isinstance(value, (models.Model, type(None))):
112 if self.field.key in instance._removed_relationship_registry:
113 instance._removed_relationship_registry.remove(self.field.key)
114 instance._added_relationship_registry[self.field.key] = value
116 raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
118 def __delete__(self, instance):
119 if self.field.key in instance._added_relationship_registry:
120 del instance._added_relationship_registry[self.field.key]
121 instance._removed_relationship_registry.append(self.field.key)
124 class RelationshipField(EntityProxyField):
125 descriptor_class = RelationshipFieldDescriptor
127 def __init__(self, model, limit_choices_to=None, key=None, **kwargs):
128 super(RelationshipField, self).__init__(**kwargs)
131 if limit_choices_to is None:
132 limit_choices_to = {}
133 self.limit_choices_to = limit_choices_to
135 def contribute_to_class(self, cls, name):
136 super(RelationshipField, self).contribute_to_class(cls, name)
140 def formfield(self, form_class=forms.ModelChoiceField, **kwargs):
141 defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
142 defaults.update(kwargs)
143 return form_class(self.model._default_manager.complex_filter(self.limit_choices_to), **defaults)
145 def value_from_object(self, obj):
146 relobj = super(RelationshipField, self).value_from_object(obj)
147 return getattr(relobj, 'pk', None)
150 class TemplateField(models.TextField):
151 def __init__(self, allow=None, disallow=None, secure=True, *args, **kwargs):
152 super(TemplateField, self).__init__(*args, **kwargs)
153 self.validators.append(TemplateValidator(allow, disallow, secure))
156 class JSONFormField(forms.Field):
157 def clean(self, value):
159 return json.loads(value)
161 raise ValidationError(u'JSON decode error: %s' % e)
164 class JSONDescriptor(object):
165 def __init__(self, field):
168 def __get__(self, instance, owner):
170 raise AttributeError # ?
172 if self.field.name not in instance.__dict__:
173 json_string = getattr(instance, self.field.attname)
174 instance.__dict__[self.field.name] = json.loads(json_string)
176 return instance.__dict__[self.field.name]
178 def __set__(self, instance, value):
179 instance.__dict__[self.field.name] = value
180 setattr(instance, self.field.attname, json.dumps(value))
182 def __delete__(self, instance):
183 del(instance.__dict__[self.field.name])
184 setattr(instance, self.field.attname, json.dumps(None))
187 class JSONField(models.TextField):
188 def __init__(self, *args, **kwargs):
189 super(JSONField, self).__init__(*args, **kwargs)
190 self.validators.append(json_validator)
192 def get_attname(self):
193 return "%s_json" % self.name
195 def contribute_to_class(self, cls, name):
196 super(JSONField, self).contribute_to_class(cls, name)
197 setattr(cls, name, JSONDescriptor(self))
199 def formfield(self, *args, **kwargs):
200 kwargs["form_class"] = JSONFormField
201 return super(JSONField, self).formfield(*args, **kwargs)
205 from south.modelsinspector import add_introspection_rules
209 add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])
210 add_introspection_rules([], ["^philo\.models\.fields\.JSONField"])