44

I want to connect a single ForeignKey to two different models.

For example:

I have two models named Casts and Articles, and a third model, Faves, for favoriting either of the other models. How can I make the ForeignKey dynamic?

class Articles(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

class Casts(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

class Faves(models.Model):
    post = models.ForeignKey(**---CASTS-OR-ARTICLES---**)
    user = models.ForeignKey(User,unique=True)

Is this possible?

Lutz Prechelt
  • 36,608
  • 11
  • 63
  • 88
Anakin
  • 3,070
  • 4
  • 21
  • 12

2 Answers2

65

Here is how I do it:

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields


class Photo(models.Model):
    picture = models.ImageField(null=True, upload_to='./images/')
    caption = models.CharField(_("Optional caption"),max_length=100,null=True, blank=True)

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = fields.GenericForeignKey('content_type', 'object_id')

class Article(models.Model):
    ....
    images     = fields.GenericRelation(Photo)

You would add something like

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = fields.GenericForeignKey('content_type', 'object_id')

to Faves and

    fields.GenericRelation(Faves)

to Article and Cast

contenttypes docs

Vadorequest
  • 16,593
  • 24
  • 118
  • 215
vikingosegundo
  • 52,040
  • 14
  • 137
  • 178
23

Here's an approach. (Note that the models are singular, Django automatically pluralizes for you.)

class Article(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

class Cast(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

FAVE_CHOICES = ( 
    ('A','Article'),
    ('C','Cast'),
)
class Fave(models.Model):
    type_of_fave = models.CharField( max_length=1, choices=FAVE_CHOICES )
    cast = models.ForeignKey(Casts,null=True)
    article= models.ForeigKey(Articles,null=True)
    user = models.ForeignKey(User,unique=True)

This rarely presents profound problems. It may require some clever class methods, depending on your use cases.

Class
  • 3,149
  • 3
  • 22
  • 31
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 14
    +1 I think generic contenttypes, as in accepted answer, are better for "pluggable" models where you don't know anything about the relations. Your answer is better for situations where you have control and complete knowledge of all the models. Better means easier to write queries and less hits on the database. – Van Gale Jun 24 '09 at 19:39
  • 3
    You forgot "null=True" in the cast and article FK field args because each Fave instance will only not set the one FK field to None which corresponds to the setting in the type_of_fave field – Geradeausanwalt Aug 05 '10 at 06:47