Generic relation vs Many to Many

When should we use Generic relation or ManytoMany relation?

Standard foreign key

class Object1(models.Model):
    ...

class Object1Task(models.Model):
    object1 = models.ForeignKey(Object1)
    ...

class Object20(models.Model):
    ...

class Object20Task(models.Model):
    object20 = models.ForeignKey(Object20)

If you have many class (Object1 to Object20) that have similar table (Object1Task to Object20Task) that contains exact fields. It’s not DRY.

Generic relation

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class Task(models.Model):
    content_type = models.ForeignKey(
        ContentType,
        null=True,
        on_delete=models.SET_NULL,
        related_name="task",
    )
    object_id = models.PositiveBigIntegerField(
        blank=True,
        null=True,
    )
    content_object = GenericForeignKey(
        "content_type",
        "object_id",
    )
    ...

By using Generic relation, we don’t need to create Object1Task to Object20Task, just Task table is fine. It is DRY and create fewer database tables than foreign key & manytomany approach, but the issue is that querying is not easy, we need to use GenericRelation.

ManytoMany

class Task(models.Model):
    object1 = models.ManyToManyField(object1)
    ...
    object20 = models.ManyToManyField(object20)

If we use ManyToManyField, it’s DRY and easier to query than generic relation.

There are actually three choices when faced with this type of situation.

  • You can recognize that database integrity is more important than the principle of DRY, and create the straight-forward code and models.

  • You can recognize that database integrity is important, give up some clarity in your code, and use some not-so-obvious metaprogramming with your models to kinda maintain DRY.

  • You can believe that DRY is more important than the integrity of your data, and rely upon generic relations and Generic Foreign Keys (GFK).

(Note: I’m not going to address the potential performance implications of any of these options here. Performance-related issues are highly context-sensitive, and the real implications of these types of situations can only be evaluated in the context of the live deployment.)

<opinion>
Mentally, I put generic relations into the same category as signals. Frequently, they’re recommended and used in areas where they don’t belong or aren’t needed.

Yes, GFKs can be useful - but not in as many situations as people may think.

Yes, GFKs are sometimes even necessary.

But I would never refactor code to create GFKs, regardless of the amount of code that might be removed.

Our approach is always to consider a GFK as the “solution of last resort.” Our principle for schema design is more like “how can we avoid using a GFK here?” rather than “let’s use a GFK to make this easier.” (And yes, sometimes the answer is to use a GFK.)
</opinion>

1 Like