2

As noted in other questions, Django experts like Two Scoops recommend explicit OneToOneFields rather than multi-table inheritance to avoid performance penalties of implicit joins. I have tried to follow such an approach, and my design is actually object composition, but have a few questions. All three of these are concrete tables.

class Widget:
  ... many shared fields ...

class FunWidget:
  parent = models.OneToOneField(Widget, related_name='child', primary_key=True)
  ... fun-specific fields ...

class WorkWidget:
  parent = models.OneToOneField(Widget, related_name='child', primary_key=True)
  ... work-specific fields ...

My questions are:

  1. Related name: Given a Widget, it'd be nice to refer to its related subtype with the same name, e.g., Widget.child. However, I get a Reverse query name for ... clashes with reverse query name for ... error. Therefore, it seems I must provide different related_names, so that a given Widget would have either a .funwidget or a .workwidget. To generalize I could provide a method Widget.child() which checks each of the possible related_names, but that won't help me in queries.

  2. Filtering by Subtype: Given a QuerySet of Widget, I need to filter to all which are of a given "subtype". I realize you'd normally just query the child model, but this is for a custom query set for Widget where I need to perform different filtering based on the type of "subclass". In other words, I want something like this Widget.objects.filter(isinstance(child, WorkWidget)) which I realize isn't possible. Currently I am "duck typing" by checking for some different property of Widget (my dilemma from question 1 provides such a feature). Cleaner would be to store an explicit .type field, if I must.

I've failed to find a simple example with this kind of explicit one-to-one mapping.

Community
  • 1
  • 1
John Lehmann
  • 7,975
  • 4
  • 58
  • 71

1 Answers1

1
  1. You are right, the related names must differ.
  2. Simply check for the existence of the given subtype:

    Widget.objects.select_related('workwidget').filter(workwidget__isnull=False)

Roodie
  • 247
  • 1
  • 8