1 """
2 Classes allowing "generic" relations through ContentType and object-id fields.
3 """
4
5 from django import oldforms
6 from django.core.exceptions import ObjectDoesNotExist
7 from django.db import connection
8 from django.db.models import signals
9 from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
10 from django.db.models.loading import get_model
11 from django.dispatch import dispatcher
12 from django.utils.functional import curry
13
15 """
16 Provides a generic relation to any object through content-type/object-id
17 fields.
18 """
19
20 - def __init__(self, ct_field="content_type", fk_field="object_id"):
21 self.ct_field = ct_field
22 self.fk_field = fk_field
23
36
44
45 - def get_content_type(self, obj):
46
47 ContentType = get_model("contenttypes", "contenttype")
48 return ContentType.objects.get_for_model(obj)
49
50 - def __get__(self, instance, instance_type=None):
51 if instance is None:
52 raise AttributeError, u"%s must be accessed via instance" % self.name
53
54 try:
55 return getattr(instance, self.cache_attr)
56 except AttributeError:
57 rel_obj = None
58 ct = getattr(instance, self.ct_field)
59 if ct:
60 try:
61 rel_obj = ct.get_object_for_this_type(pk=getattr(instance, self.fk_field))
62 except ObjectDoesNotExist:
63 pass
64 setattr(instance, self.cache_attr, rel_obj)
65 return rel_obj
66
67 - def __set__(self, instance, value):
68 if instance is None:
69 raise AttributeError, u"%s must be accessed via instance" % self.related.opts.object_name
70
71 ct = None
72 fk = None
73 if value is not None:
74 ct = self.get_content_type(value)
75 fk = value._get_pk_val()
76
77 setattr(instance, self.ct_field, ct)
78 setattr(instance, self.fk_field, fk)
79 setattr(instance, self.cache_attr, value)
80
82 """Provides an accessor to generic related objects (i.e. comments)"""
83
85 kwargs['verbose_name'] = kwargs.get('verbose_name', None)
86 kwargs['rel'] = GenericRel(to,
87 related_name=kwargs.pop('related_name', None),
88 limit_choices_to=kwargs.pop('limit_choices_to', None),
89 symmetrical=kwargs.pop('symmetrical', True))
90
91
92 self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
93 self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
94
95 kwargs['blank'] = True
96 kwargs['editable'] = False
97 kwargs['serialize'] = False
98 Field.__init__(self, **kwargs)
99
103
106
108 new_data = {}
109 if obj:
110 instance_ids = [instance._get_pk_val() for instance in getattr(obj, self.name).all()]
111 new_data[self.name] = instance_ids
112 return new_data
113
116
118 return self.object_id_field_name
119
121 return self.model._meta.pk.column
122
131
134
137
139 return "ManyToManyField"
140
190
215
216 def get_query_set(self):
217 query = {
218 '%s__pk' % self.content_type_field_name : self.content_type.id,
219 '%s__exact' % self.object_id_field_name : self.pk_val,
220 }
221 return superclass.get_query_set(self).filter(**query)
222
223 def add(self, *objs):
224 for obj in objs:
225 setattr(obj, self.content_type_field_name, self.content_type)
226 setattr(obj, self.object_id_field_name, self.pk_val)
227 obj.save()
228 add.alters_data = True
229
230 def remove(self, *objs):
231 for obj in objs:
232 obj.delete()
233 remove.alters_data = True
234
235 def clear(self):
236 for obj in self.all():
237 obj.delete()
238 clear.alters_data = True
239
240 def create(self, **kwargs):
241 kwargs[self.content_type_field_name] = self.content_type
242 kwargs[self.object_id_field_name] = self.pk_val
243 obj = self.model(**kwargs)
244 obj.save()
245 return obj
246 create.alters_data = True
247
248 return GenericRelatedObjectManager
249
251 - def __init__(self, to, related_name=None, limit_choices_to=None, symmetrical=True):
252 self.to = to
253 self.num_in_admin = 0
254 self.related_name = related_name
255 self.filter_interface = None
256 self.limit_choices_to = limit_choices_to or {}
257 self.edit_inline = False
258 self.raw_id_admin = False
259 self.symmetrical = symmetrical
260 self.multiple = True
261 assert not (self.raw_id_admin and self.filter_interface), \
262 "Generic relations may not use both raw_id_admin and filter_interface"
263