What are the advantages and disadvantages of using GenericForeignKey vs multitable inheritance vs OneToOneField?

Context

In the process of data modeling, I use Django models. The main model is Article . It contains actual content.

Then each Article should be attached to a group of articles. This group can be Blog , a Category a Portfolio or Story . Each Article should be attached to one and exactly one of them. That is, it is a blog, category or story. These models have very different fields and functions.

I thought of three ways to achieve this goal (and a bonus that really looks wrong).

Option # 1: Shared Foreign Key

As in django.contrib.contenttypes.fields.GenericForeignKey . It will look like this:

 class Category(Model): # some fields class Blog(Model): # some fields class Article(Model): group_type = ForeignKey(ContentType) group_id = PositiveIntegerField() group = GenericForeignKey('group_type', 'group_id') # some fields 

On the database side, this means that there is no relationship between the models; they are executed by Django.

Option # 2: Multipage Inheritance

Make article groups all inherited from the ArticleGroup model. It will look like this:

 class ArticleGroup(Model): group_type = ForeignKey(ContentType) class Category(ArticleGroup): # some fields class Blog(ArticleGroup): # some fields class Article(Model): group = ForeignKey(ArticleGroup) # some fields 

On the database side, this creates an additional table for ArticleGroup , then Category and Blog have an implicit foreign key to this table as the primary key.

Sidenote: I know that there is a package that automates the accounting of such designs.

Option # 3: Manual OneToOneFields

On the database side, this is equivalent to option # 2. But in the code, all relationships become explicit:

 class ArticleGroup(Model): group_type = ForeignKey(ContentType) class Category(Model): id = OneToOneField(ArticleGroup, primary_key=True) # some fields class Blog(Model): id = OneToOneField(ArticleGroup, primary_key=True) # some fields class Article(Model): group = ForeignKey(ArticleGroup) # some fields 

I really don't understand what is the point of this, besides the fact that an explicit indication of what Django inheritance magic inherits from is implicitly doing.

Bonus: multi-column

This seems pretty dirty, so I just add it as a bonus, but you can also define a NULL key value for each of the Category , Blog , ... directly in the Article model.

So...

... I can not decide between them. What are the pros and cons of each approach? Is there a best practice? Did I miss the best approach?

If that matters, I am using Django 1.8.

+7
database django
source share
1 answer

Someone seemed to have no advice to share this. In the end, I chose the multi-column option, despite the fact that I said that it looks ugly. It all came down to 3 things:

  • Database Availability.
  • How Django ORM works with various designs.
  • My own needs (namely, collection requests in a group to get a list of elements and individual requests to elements for a group).

Option number 1

  • Cannot perform database-level enforcement.
  • It can be efficient for queries, because the way it is built does not fall into the usual typical foreign key traps. This happens when items are shared, not collections.
  • However, due to the way ORM handles GFK, it is not possible to use the custom manager that I need, because my articles are translated using django-hvad .

Option number 2

  • It can be applied at the database level.
  • It may be somewhat effective, but works with ORM restrictions that are not explicitly built around this use. If I do not use extra() or user queries, but at some point it makes no sense to use ORM.

Option number 3

  • In fact, this will be slightly better than # 2, since creating explicit things makes it easier to optimize queries when using ORM.

Multicolumn

  • Turns out it's not so bad. It can be applied at the database level (FK restrictions plus manual CHECK to ensure that only one of the columns is not null).
  • Easy and effective. One intuitive query does the job: select_related('category', 'blog', ...) .
  • Although the problem is that it’s more difficult to extend (any new type will require changing the Article table) and limit the possible number of types, I’m unlikely to come across them.

Hope this helps anyone with the same dilemma and still interested in hearing different opinions.

+4
source share

All Articles