1

Okay, here's a challenge, Djangonistas!

My let's say my project has 3 types of data.

  • Project
  • Audio
  • Video

Projects can have references to an unlimited number of Video and Audio objects, as well as unlimited references to other Projects.

What is the best way to model this? It must be done in a generic way such that new object types can be added in the future (Images, Links, Smells, etc.), and such that objects can be referenced by multiple Projects.

I have 3 theories on how to do this.

#1: Inheritance

Create a BaseModel class and have all other objects inherit from it.

class BaseModel(models.Model)
class Project(BaseModel)
class Video(BaseModel)
etc

then, have a ManyToMany field to all BaseModels inside of the Project class.

class Project(BaseModel):
    content = models.ManyToManyField(BaseModel)

Will this work and be performant?

#2: Generic Foreign Keys

I could also set up system which would use a GenericForeignKey field which could use the ContentTypes framework

content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')

(I think this is probably the worst idea.)

#3: Generic ManytoMany Keys

Finally, I could have a separate table which maintained all of the relationships between objects.

class RelatedObject(models.Model):
    """
    A generic many-to-many implementation where diverse objects are related
    across a single model to other diverse objects -> using a dual GFK
    """
    # SOURCE OBJECT:
    parent_type = models.ForeignKey(ContentType, related_name="child_%(class)s")
    parent_id = models.IntegerField(db_index=True)
    parent = GenericForeignKey(ct_field="parent_type", fk_field="parent_id")

    # ACTUAL RELATED OBJECT:
    object_type = models.ForeignKey(ContentType, related_name="related_%(class)s")
    object_id = models.IntegerField(db_index=True)
    object = GenericForeignKey(ct_field="object_type", fk_field="object_id")

    alias = models.CharField(max_length=255, blank=True)
    creation_date = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-creation_date',)

    def __unicode__(self):
        return '%s related to %s ("%s")' % (self.parent, self.object, self.alias)

and then

class Project(models.Model):
    related = RelatedObjectsDescriptor()

This is laid out in greater detail by Charles Leifer here: http://charlesleifer.com/blog/connecting-anything-to-anything-with-django/ and his django-generic-m2m project: https://github.com/coleifer/django-generic-m2m

What to do?

So, what are the pros and cons of each of these approaches to storing relationships? Which is the fastest, which is the easier to write and maintain? What approaches have you used in the past?

Thanks very much!

Rich Jones
  • 1,422
  • 1
  • 15
  • 17

1 Answers1

0

I would say that you are going to want to use 'self' as an argument to your Project models' ManyToManyField (instead of the thing with the inheritance in your example). This allows you to define self-referential models.

If you look at https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey – you'll see that two lines down, there's this note:

To create a recursive relationship – an object that has a many-to-one relationship with itself – use models.ForeignKey('self').

... I first learned about this from this blog entry, from 2008 (which may now be outdated in some ways, but I still refer to it when using this technique):

http://eflorenzano.com/blog/2008/05/17/exploring-mixins-django-model-inheritance/

Indeed, good luck sir.

fish2000
  • 4,289
  • 2
  • 37
  • 76