Reputation: 13552
Context
I am in the process of modeling my data using Django models.
The main model is an Article
. It holds the actual content.
Then each Article
must be attached to a group of articles. Those group may be a Blog
, a Category
a Portfolio
or a Story
. Every Article
must be attached to one, and exactly one of those. That is, either a blog, a category or a story. Those models have very different fields and features.
I thought of three ways to reach that goal (and a bonus one that really looks wrong).
Option #1: A generic foreign key
As in django.contrib.contenttypes.fields.GenericForeignKey
. It would 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, that means no relation actually exists between the models, they are enforced by Django.
Option #2: Multitable inheritance
Make article groups all inherit from an ArticleGroup
model. This would 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 that table as their primary key.
Sidenote: I know there is a package that automates the bookkeeping of such constructions.
Option #3: manual OneToOneFields
On the database side, it is equivalent to option #2. But in the code, all relations are made 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 don't really see what the point of that would be, apart from making explicit what Django's inheritance magic implicitly does.
Bonus: multicolumn
It seems pretty dirty so I just add it as a bonus, but it would also be possible to define a nullable ForeignKey to each of Category
, Blog
, ... directly on the Article
model.
So...
...I cannot really decide between those. What are the pros and cons of each approach? Are there some best practices? Did I miss a better approach?
If that matters, I'm using Django 1.8.
Upvotes: 14
Views: 2027
Reputation: 13552
It seems noone had advice to share on that one. I eventually chose the multicolumn option, despite having said it looked ugly. It all came down to 3 things:
Option #1
Option #2
extra()
or custom queries alot, but at some point there is no reason to use an ORM anymore.Option #3
Multicolumn
select_related('category', 'blog', ...)
.Article
's table as well) and limiting the possible number of types, I'm unlikely to run into those.Hope it helps anyone with the same dilemma, and still interested in hearing other opinions.
Upvotes: 11