Generic one-to-one relation in Django
I recently came across this problem. What you have done is fine, but you can generalise it a little bit more by creating a mixin that reverses the relationship transparently:
class Event(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class Meta:
unique_together = ('content_type', 'object_id')
class EventMixin(object):
@property
def get_event(self):
ctype = ContentType.objects.get_for_model(self.__class__)
try:
event = Event.objects.get(content_type__pk = ctype.id, object_id=self.id)
except:
return None
return event
class Action1(EventMixin, models.Model):
# Don't need to mess up the models fields (make sure the mixing it placed before models.Model)
...
and
action = Action1.object.get(id=1)
event = action.get_event
You might want to add caching to the reverse relationship too
GenericRelation
is a class for representing a Generic Many to One and adding a first_event
property allow to represent a Generic One to One.
class Event(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
class Meta:
unique_together = ('content_type', 'object_id') # Important
class Action1(models.Model):
events = generic.GenericRelation(Event)
@property
def first_event(self):
return self.events.first()
In addition, I recommend using .first()
inside Action1.first_event
instead of .get()
due to, it returning None
if the event doesn't exist. .get()
raises an exception if the event doesn't exist and this behavior can be unwanted.