Removed referential integrity code. Moved embed templatetag into core. Differentiated...
[philo.git] / models / fields.py
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
8
9
10 __all__ = ('AttributeField', 'RelationshipField')
11
12
13 class EntityProxyField(object):
14         descriptor_class = None
15         
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)
21         
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))
25         
26         def contribute_to_class(self, cls, name):
27                 if issubclass(cls, Entity):
28                         self.name = name
29                         self.attname = name
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)
33                 else:
34                         raise FieldError('%s instances can only be declared on Entity subclasses.' % self.__class__.__name__)
35         
36         def formfield(self, *args, **kwargs):
37                 raise NotImplementedError('EntityProxyField subclasses must implement a formfield method.')
38         
39         def value_from_object(self, obj):
40                 return getattr(obj, self.attname)
41
42
43 class AttributeFieldDescriptor(object):
44         def __init__(self, field):
45                 self.field = field
46         
47         def __get__(self, instance, owner):
48                 if instance:
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:
52                                 return None
53                         try:
54                                 return instance.attributes[self.field.key]
55                         except KeyError:
56                                 return None
57                 else:
58                         raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
59         
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
64         
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)
69
70
71 class AttributeField(EntityProxyField):
72         descriptor_class = AttributeFieldDescriptor
73         
74         def __init__(self, field_template=None, key=None, **kwargs):
75                 super(AttributeField, self).__init__(**kwargs)
76                 self.key = key
77                 if field_template is None:
78                         field_template = models.CharField(max_length=255)
79                 self.field_template = field_template
80         
81         def contribute_to_class(self, cls, name):
82                 super(AttributeField, self).contribute_to_class(cls, name)
83                 if self.key is None:
84                         self.key = name
85         
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)
90
91
92 class RelationshipFieldDescriptor(object):
93         def __init__(self, field):
94                 self.field = field
95         
96         def __get__(self, instance, owner):
97                 if instance:
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:
101                                 return None
102                         try:
103                                 return instance.relationships[self.field.key]
104                         except KeyError:
105                                 return None
106                 else:
107                         raise AttributeError('The \'%s\' attribute can only be accessed from %s instances.' % (self.field.name, owner.__name__))
108         
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
114                 else:
115                         raise AttributeError('The \'%s\' attribute can only be set using existing Model objects.' % self.field.name)
116         
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)
121
122
123 class RelationshipField(EntityProxyField):
124         descriptor_class = RelationshipFieldDescriptor
125         
126         def __init__(self, model, limit_choices_to=None, key=None, **kwargs):
127                 super(RelationshipField, self).__init__(**kwargs)
128                 self.key = key
129                 self.model = model
130                 if limit_choices_to is None:
131                         limit_choices_to = {}
132                 self.limit_choices_to = limit_choices_to
133         
134         def contribute_to_class(self, cls, name):
135                 super(RelationshipField, self).contribute_to_class(cls, name)
136                 if self.key is None:
137                         self.key = name
138         
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)
143         
144         def value_from_object(self, obj):
145                 relobj = super(RelationshipField, self).value_from_object(obj)
146                 return getattr(relobj, 'pk', None)
147
148
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))
153
154
155 try:
156         from south.modelsinspector import add_introspection_rules
157 except ImportError:
158         pass
159 else:
160         add_introspection_rules([], ["^philo\.models\.fields\.TemplateField"])