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__ = ('JSONAttribute', 'ForeignKeyAttribute', 'ManyToManyAttribute')
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 raise NotImplementedError('AttributeFieldDescriptor subclasses must implement a __set__ method.')
64 def __delete__(self, instance):
65 if self.field.key in instance._added_attribute_registry:
66 del instance._added_attribute_registry[self.field.key]
67 instance._removed_attribute_registry.append(self.field.key)
70 class JSONAttributeDescriptor(AttributeFieldDescriptor):
71 def __set__(self, instance, value):
72 if self.field.key in instance._removed_attribute_registry:
73 instance._removed_attribute_registry.remove(self.field.key)
74 instance._added_attribute_registry[self.field.key] = value
77 class ForeignKeyAttributeDescriptor(AttributeFieldDescriptor):
78 def __set__(self, instance, value):
79 if isinstance(value, (models.Model, type(None))):
80 if self.field.key in instance._removed_attribute_registry:
81 instance._removed_attribute_registry.remove(self.field.key)
82 instance._added_attribute_registry[self.field.key] = value
84 raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
87 class ManyToManyAttributeDescriptor(AttributeFieldDescriptor):
88 def __set__(self, instance, value):
89 if isinstance(value, models.QuerySet):
90 if self.field.key in instance._removed_attribute_registry:
91 instance._removed_attribute_registry.remove(self.field.key)
92 instance._added_attribute_registry[self.field.key] = value
94 raise AttributeError('The \'%s\' attribute can only be set to a QuerySet.' % self.field.name)
97 class AttributeField(EntityProxyField):
98 def contribute_to_class(self, cls, name):
99 super(AttributeField, self).contribute_to_class(cls, name)
104 class JSONAttribute(AttributeField):
105 descriptor_class = JSONAttributeDescriptor
107 def __init__(self, field_template=None, key=None, **kwargs):
108 super(AttributeField, self).__init__(**kwargs)
110 if field_template is None:
111 field_template = models.CharField(max_length=255)
112 self.field_template = field_template
114 def formfield(self, **kwargs):
115 defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
116 defaults.update(kwargs)
117 return self.field_template.formfield(**defaults)
119 def value_from_object(self, obj):
120 return getattr(obj, self.attname).value
123 class ForeignKeyAttribute(AttributeField):
124 descriptor_class = ForeignKeyAttributeDescriptor
126 def __init__(self, model, limit_choices_to=None, key=None, **kwargs):
127 super(ForeignKeyAttribute, self).__init__(**kwargs)
130 if limit_choices_to is None:
131 limit_choices_to = {}
132 self.limit_choices_to = limit_choices_to
134 def formfield(self, form_class=forms.ModelChoiceField, **kwargs):
135 defaults = {'required': False, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
136 defaults.update(kwargs)
137 return form_class(self.model._default_manager.complex_filter(self.limit_choices_to), **defaults)
139 def value_from_object(self, obj):
140 relobj = super(ForeignKeyAttribute, self).value_from_object(obj).value
141 return getattr(relobj, 'pk', None)
144 class ManyToManyAttribute(AttributeField):
145 descriptor_class = ManyToManyAttributeDescriptor
146 #FIXME: Add __init__ and formfield methods
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))
155 class JSONFormField(forms.Field):
156 def clean(self, value):
158 return json.loads(value)
160 raise ValidationError(u'JSON decode error: %s' % e)
163 class JSONDescriptor(object):
164 def __init__(self, field):
167 def __get__(self, instance, owner):
169 raise AttributeError # ?
171 if self.field.name not in instance.__dict__:
172 json_string = getattr(instance, self.field.attname)
173 instance.__dict__[self.field.name] = json.loads(json_string)
175 return instance.__dict__[self.field.name]
177 def __set__(self, instance, value):
178 instance.__dict__[self.field.name] = value
179 setattr(instance, self.field.attname, json.dumps(value))
181 def __delete__(self, instance):
182 del(instance.__dict__[self.field.name])
183 setattr(instance, self.field.attname, json.dumps(None))
186 class JSONField(models.TextField):
187 def __init__(self, *args, **kwargs):
188 super(JSONField, self).__init__(*args, **kwargs)
189 self.validators.append(json_validator)
191 def get_attname(self):
192 return "%s_json" % self.name
194 def contribute_to_class(self, cls, name):
195 super(JSONField, self).contribute_to_class(cls, name)
196 setattr(cls, name, JSONDescriptor(self))
198 def formfield(self, *args, **kwargs):
199 kwargs["form_class"] = JSONFormField
200 return super(JSONField, self).formfield(*args, **kwargs)
204 from south.modelsinspector import add_introspection_rules
208 add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])
209 add_introspection_rules([], ["^philo\.models\.fields\.JSONField"])