Search code examples
djangodjango-modelsforeign-keyscomments

Django: Can I make a ForeignKey relation between one model and several others in Django?


I am new to Django, and not very advanced at this point.

My goal is to make one "Comment" model that works for different "Post" models.

My structure is as follows:

class TunedCarPost(models.Model):
    .....
    .....


class ConceptCarPost(models.Model):
    .....
    .....


class ArticlePost(models.Model):
    .....
    .....


class Comment(models.Model):
    post = models.ForeignKey(
        [TunedCarPost, ConceptCarPost, ArticlePost],
        related_name="comments",
        on_delete=models.CASCADE,
    )
    .....
    .....

I can't do such thing as:

class Comment(models.Model):
    post = models.ForeignKey(
        [TunedCarPost, ConceptCarPost, ArticlePost],
        related_name="comments",
        on_delete=models.CASCADE,
    )

I tried to use an abstract model, so "Comment" only links to the abstract model:

class Post(models.Model):
    .....
    .....


class TunedCarPost(Post):
    .....
    .....


class ConceptCarPost(Post):
    .....
    .....


class ArticlePost(Post):
    .....
    .....


class Comment(models.Model):
    post = models.ForeignKey(
        Post,
        related_name="comments",
        on_delete=models.CASCADE,
    )
    .....
    .....

But Django throws an exception that ForeignKey can't link to an abstract model.

Is there any easy solutions for my case or my structure is total mess?


Solution

  • There are some different options to achieve this:

    1. You could use Generic relations:
    from django.contrib.contenttypes.fields import GenericForeignKey
    from django.contrib.contenttypes.models import ContentType
    from django.db import models
    
    class Comment(models.Model):
        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
        object_id = models.PositiveIntegerField()
        content_object = GenericForeignKey('content_type', 'object_id')
    
        class Meta:
            indexes = [
                models.Index(fields=["content_type", "object_id"]),
            ]
    

    This means that each Comment instance stores the content_type of related model e.g. TunedCarPost and primary key (object_id) of that particular instance.

    To create a new comment:

    tuned_car_post = TunedCarPost.objects.get(pk=1)
    comment = Comment(content_object=tuned_car_post)
    comment.save()
    

    To access the content object

    comment.content_object
    
    1. You could use ManyToManyField on each of the models
    class TunedCarPost(models.Model):
        comments = models.ManyToManyField(Comment)
    
    class ConceptCarPost(models.Model):
        comments = models.ManyToManyField(Comment)
    
    class ArticlePost(models.Model):
        comments = models.ManyToManyField(Comment)
    

    This would mean that each one of the post types would have their own comments database table.