I'm designing a new Django app and due to several possibilities, I'm not sure which would be the best, thus I'd like opinions, and hopefully improve what I got so far.
This question comes close but not quite. This one touches the flat/nested subject which is helpful, while still not answering the question. There are many others on the same subject, and yet none tell me what I want to know.
Background
The models have each unique properties with some shared attributes, and I need to reference them in another model, optimally with a single entry point rather than having a field for each possible model.
I want to be able to do complex Django ORM queries involving the Base class and filter by SubClass when needed. E.g Event.objects.all()
to return all events. I'm aware of Django model utils Inheritance Manager and intend to use it if possible.
Also, I'll be using django admin to create and manage the objects, so an easy integration is a must. I want to be able to create a new SubEvent directly, without having first to create a Event instance.
Example
To illustrate, let's say I have the following models for app A
.
class Event(models.Model):
commom_field = models.BooleanField()
class Meta:
abstract = True
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# and so on
And also an app B
, where I want to be able to reference a event which can be of any type, like:
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
# This won't work, because `A.Event` is abstract.
Possible solutions
Use a GenericForeignKey.
# B.models.py class OtherModel(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() event = GenericForeignKey('content_type', 'object_id')
What I don't like about this is that I'll lose the querying capabilities Django ORM has, and I might need to do additional fiddling to get it working on admin. Not sure, never dealt with this before
Flatten
Event
I can bring it all up to the base class and have flags or checks outside the model definition, something like:
class Event(models.Model): commom_field = models.BooleanField() email = models.EmailField(blank=True) title = models.TextField(blank=True) number = models.IntegerField(default=10)
This might seem like the best idea at first, but of course there are other kind of fields, and that forces me to allow nulls/blanks for most of them (like the email field), losing the db level integrity check.
OneToOne relationships
Rather than abstract like on 1 or flatten on 2 it is possible to have a db table for each, where the models will look like:
class Event(models.Model): commom_field = models.BooleanField() class SubEventA(models.Model): event = models.OneToOneField(Event) email = models.EmailField(unique=True) class SubEventB(models.Model): event = models.OneToOneField(Event) title = models.TextField(blank=True) class SubEventC(models.Model): event = models.OneToOneField(Event) number = models.IntegerField(default=10)
So far it solved the two initial problems, but now when I get to the admin interface, I'll have to customize each form to create the base
Event
before saving aSubEvent
instance.
Questions
Is there a better approach?
Can any of the choices I present be improved in any direction (ORM query, DB constraints, admin interface)?