Search code examples
djangodjango-modelsforeign-keysgeneric-foreign-key

Is there an alternative to using generic foreign keys to handle similar model trees?


My scenario: I'm trying to build a database to track production schedules for different types of shows. I've mapped that out with the following model structure.

class Studio(models.Model):
   ...

class Series(models.Model):
   studio = models.ForeignKey(Studio)
   ...

class Season(models.Model):
   series = models.ForeignKey(Series)
   ...

class Episode(models.Model):
   season = models.ForeignKey(Season)
   ...

class Production(models.Model):
   episode = models.ForeignKey(Episode)

But I'm now interested in tracking production of movies as well. This presents a challenge though, because movies don't have the same tree structure as TV. Something like this would be more fitting:

class Studio(models.Model):
   ...

class Movie(models.Model):
   studio = models.ForeignKey(Studio)
   ...

class Production(models.Model):
   movie = models.ForeignKey(Movie)

The problem here is that Production and Studio are exactly the same for both movies and TV (at least in this scenario), so I'm hesitant to have totally separate trees, because this would necessitate duplicating Production. One for TV and one for Movies, when the only difference is the foreign key.

Does it make sense to use a GenericForeignKey here? Where a production can either point at an Episode or Movie? I'm hesitant to do so because it sounds like the consensus is to avoid generic foreign keys, but I'm not sure how else to do so.


Solution

  • I faced a similar issue a while back, and I came to the conclusion that it would be more efficient (and less trouble later on) with 2 foreign keys, and 2 boolean variables informing the type in these tables.

    Also from your code I see no reason to duplicate the Studio model as it is identical, and contains no unique foreign key field. Production model however can be written like this

    class Production(models.Model):
      movie = models.ForeignKey(Movie, null=True)
      episode = models.ForeignKey(Episode, null=True)
      is_movie = models.BooleanField(null=False)
      is_episode = models.BooleanField(null=False)
    

    What you choose to identify the foreignkey field up to you but boolean is known for its small size in the database.