Version 2 (modified by Andrew Ladouceur, 14 years ago) ( diff )

Initial objects created were diamond and emerald, the rest of the sample code uses quartz and emerald. I left the hardness values alone, even though they're incorrect (quartz=7, emerald=7.5-8).

See http://www.djangoproject.com/documentation/models/generic_relations/.

Note: As of today, 13-DEC-06, this admonishment applies:

Generic relations: a "generic foreign key" feature is currently under development, 
but not yet completed. There is no support for it yet in the admin application, and 
it is probably unwise to attempt to use it in a production application.

--from http://code.djangoproject.com/

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.

Model Source Code

class AttributeManager(models.Manager):
    def with_attr(self, attr_name):
        return Attribute.objects.filter(name=attr_name)

    def type_with_attr(self, type_name, attr_name):
        return Attribute.objects.filter(content_type__name=type_name, name=attr_name)

    def type_with_attr_value(self, type_name, attr_name, value):
        return Attribute.objects.filter(content_type__name=type_name, name=attr_name, value=value)

    def obj_attr_list(self, obj):
        ctype = ContentType.objects.get_for_model(obj)
        return  Attribute.objects.filter(content_type__pk=ctype.id, object_id=obj.id)

    def obj_attr_value(self, obj, attr_name):
        ctype = ContentType.objects.get_for_model(obj)
        try:
            av = Attribute.objects.get(name=attr_name, content_type__pk=ctype.id, object_id=obj.id)
        except Attribute.DoesNotExist:
            return None
        return av.value

class Attribute(models.Model):
    name = models.CharField(maxlength=25)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    value = models.CharField(maxlength=255)
   
    content_object = models.GenericForeignKey()
    objects = AttributeManager()

    class meta:
        unique_together = (('name', 'content_type', 'object_id'),)
    
    def __str__(self):
        return "%s.%s.%s=%s" % (self.content_type.name, self.content_object, self.name, self.value,)

Sample API usage

This sample code assumes you added the above models to the sample found at http://www.djangoproject.com/documentation/models/generic_relations/.

>>> quartz = Mineral.objects.create(name="Quartz", hardness=10)
>>> emerald = Mineral.objects.create(name="Emerald", hardness=6)
>>> Attribute.objects.create(content_object=quartz, name='color', value='black')
<Attribute: mineral.Quartz.color=black>

>>> Attribute.objects.create(content_object=emerald, name='color', value='green')
<Attribute: mineral.Emerald.color=green>

>>> Attribute.objects.with_attr('color')
[<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>]

>>> Attribute.objects.type_with_attr('mineral', 'color')
[<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>]

>>> Attribute.objects.create(content_object=eggplant, name='color', value='purple')
<Attribute: vegetable.Eggplant.color=purple>

>>> Attribute.objects.with_attr('color')
[<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>, <Attribute: vegetable.Eggplant.color=
purple>]

>>> Attribute.objects.type_with_attr('mineral', 'color')
[<Attribute: mineral.Quartz.color=black>, <Attribute: mineral.Emerald.color=green>]

>>> Attribute.objects.type_with_attr_value('mineral', 'color', 'green')
[<Attribute: mineral.Emerald.color=green>]

>>> Attribute.objects.type_with_attr_value('mineral', 'color', 'red')
[]

>>> Attribute.objects.obj_attr_list(quartz)
[<Attribute: mineral.Quartz.color=black>]

>>> Attribute.objects.obj_attr_value(quartz, 'color')
'black'
Note: See TracWiki for help on using the wiki.
Back to Top