| 1 | See http://www.djangoproject.com/documentation/models/generic_relations/. |
| 2 | |
| 3 | '''Note''': As of today, 13-DEC-06, this admonishment applies: |
| 4 | {{{ |
| 5 | Generic relations: a "generic foreign key" feature is currently under development, |
| 6 | but not yet completed. There is no support for it yet in the admin application, and |
| 7 | it is probably unwise to attempt to use it in a production application. |
| 8 | }}} |
| 9 | --from http://code.djangoproject.com/ |
| 10 | |
| 11 | Also, I do not think that this is particularly good data modeling if you have a choice, but when migrating an application and dealing with legacy data as you go, it can be useful if it is not abused. |
| 12 | |
| 13 | == Model Source Code == |
| 14 | {{{ |
| 15 | #!python |
| 16 | class AttributeManager(models.Manager): |
| 17 | def with_attr(self, attr_name): |
| 18 | return Attribute.objects.filter(name=attr_name) |
| 19 | |
| 20 | def type_with_attr(self, type_name, attr_name): |
| 21 | return Attribute.objects.filter(content_type__name=type_name, name=attr_name) |
| 22 | |
| 23 | def type_with_attr_value(self, type_name, attr_name, value): |
| 24 | return Attribute.objects.filter(content_type__name=type_name, name=attr_name, value=value) |
| 25 | |
| 26 | def obj_attr_list(self, obj): |
| 27 | ctype = ContentType.objects.get_for_model(obj) |
| 28 | return Attribute.objects.filter(content_type__pk=ctype.id, object_id=obj.id) |
| 29 | |
| 30 | def obj_attr_value(self, obj, attr_name): |
| 31 | ctype = ContentType.objects.get_for_model(obj) |
| 32 | try: |
| 33 | av = Attribute.objects.get(name=attr_name, content_type__pk=ctype.id, object_id=obj.id) |
| 34 | except Attribute.DoesNotExist: |
| 35 | return None |
| 36 | return av.value |
| 37 | |
| 38 | class Attribute(models.Model): |
| 39 | name = models.CharField(maxlength=25) |
| 40 | content_type = models.ForeignKey(ContentType) |
| 41 | object_id = models.PositiveIntegerField() |
| 42 | value = models.CharField(maxlength=255) |
| 43 | |
| 44 | content_object = models.GenericForeignKey() |
| 45 | objects = AttributeManager() |
| 46 | |
| 47 | class meta: |
| 48 | unique_together = (('name', 'content_type', 'object_id'),) |
| 49 | |
| 50 | def __str__(self): |
| 51 | return "%s.%s.%s=%s" % (self.content_type.name, self.content_object, self.name, self.value,) |
| 52 | }}} |
| 53 | |
| 54 | == Sample API usage == |
| 55 | This sample code assumes you added the above models to the sample found at http://www.djangoproject.com/documentation/models/generic_relations/. |
| 56 | |
| 57 | {{{ |
| 58 | #!python |
| 59 | >>> diamond = Mineral.objects.create(name="Diamond", hardness=10) |
| 60 | >>> emerald = Mineral.objects.create(name="Emerald", hardness=6) |
| 61 | >>> Attribute.objects.create(content_object=quartz, name='color', value='black') |
| 62 | <Attribute: mineral.Quartz.color=black> |
| 63 | |
| 64 | >>> Attribute.objects.create(content_object=emerald, name='color', value='green') |
| 65 | <Attribute: mineral.Emerald.color=green> |
| 66 | |
| 67 | >>> Attribute.objects.with_attr('color') |
| 68 | [<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>] |
| 69 | |
| 70 | >>> Attribute.objects.type_with_attr('mineral', 'color') |
| 71 | [<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>] |
| 72 | |
| 73 | >>> Attribute.objects.create(content_object=eggplant, name='color', value='purple') |
| 74 | <Attribute: vegetable.Eggplant.color=purple> |
| 75 | |
| 76 | >>> Attribute.objects.with_attr('color') |
| 77 | [<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>, <Attribute: vegetable.Eggplant.color= |
| 78 | purple>] |
| 79 | |
| 80 | >>> Attribute.objects.type_with_attr('mineral', 'color') |
| 81 | [<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>] |
| 82 | |
| 83 | >>> Attribute.objects.type_with_attr_value('mineral', 'color', 'green') |
| 84 | [<Attribute: mineral.Emerald.color=green>] |
| 85 | |
| 86 | >>> Attribute.objects.type_with_attr_value('mineral', 'color', 'red') |
| 87 | [] |
| 88 | |
| 89 | >>> Attribute.objects.obj_attr_list(quartz) |
| 90 | [<Attribute: mineral.Quartz.color=black>] |
| 91 | |
| 92 | >>> Attribute.objects.obj_attr_value(quartz, 'color') |
| 93 | 'black' |
| 94 | }}} |