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
38 """
39 Handles initializing an object with the generic FK instaed of
40 content-type/object-id fields.
41 """
42 if self.name in kwargs:
43 value = kwargs.pop(self.name)
44 kwargs[self.ct_field] = self.get_content_type(obj=value)
45 kwargs[self.fk_field] = value._get_pk_val()
46
47 - def get_content_type(self, obj=None, id=None):
48
49
50 ContentType = get_model("contenttypes", "contenttype")
51 if obj:
52 return ContentType.objects.get_for_model(obj)
53 elif id:
54 return ContentType.objects.get_for_id(id)
55 else:
56
57 raise Exception("Impossible arguments to GFK.get_content_type!")
58
59 - def __get__(self, instance, instance_type=None):
60 if instance is None:
61 raise AttributeError, u"%s must be accessed via instance" % self.name
62
63 try:
64 return getattr(instance, self.cache_attr)
65 except AttributeError:
66 rel_obj = None
67
68
69
70
71
72 f = self.model._meta.get_field(self.ct_field)
73 ct_id = getattr(instance, f.get_attname(), None)
74 if ct_id:
75 ct = self.get_content_type(id=ct_id)
76 try:
77 rel_obj = ct.get_object_for_this_type(pk=getattr(instance, self.fk_field))
78 except ObjectDoesNotExist:
79 pass
80 setattr(instance, self.cache_attr, rel_obj)
81 return rel_obj
82
83 - def __set__(self, instance, value):
84 if instance is None:
85 raise AttributeError, u"%s must be accessed via instance" % self.related.opts.object_name
86
87 ct = None
88 fk = None
89 if value is not None:
90 ct = self.get_content_type(obj=value)
91 fk = value._get_pk_val()
92
93 setattr(instance, self.ct_field, ct)
94 setattr(instance, self.fk_field, fk)
95 setattr(instance, self.cache_attr, value)
96
98 """Provides an accessor to generic related objects (i.e. comments)"""
99
101 kwargs['verbose_name'] = kwargs.get('verbose_name', None)
102 kwargs['rel'] = GenericRel(to,
103 related_name=kwargs.pop('related_name', None),
104 limit_choices_to=kwargs.pop('limit_choices_to', None),
105 symmetrical=kwargs.pop('symmetrical', True))
106
107
108 self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
109 self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
110
111 kwargs['blank'] = True
112 kwargs['editable'] = False
113 kwargs['serialize'] = False
114 Field.