Search code examples
python-3.xdjangodjango-rest-frameworkdjango-ormdjango-serializer

One to many relationship query in Django ORM, Where I want to query on One table and want to get output with many table data as a list


Here is my model:

from django.db import models
from django.db.models.expressions import F


class Publication(models.Model):
    name = models.CharField('name of publication', max_length=1024, null=True, blank=True) 

    def __str__(self):

        return f'{self.name}' 

    class Meta:
        verbose_name = "Publication"
        verbose_name_plural = "Publications" 


class Book(models.Model):
    name = models.CharField('name of book', max_length=1024, null=True, blank=True) 
    publication = models.ForeignKey(Publication, on_delete=models.CASCADE, related_name='books_publications', null=True, blank=True)

    def __str__(self):

        return f'{self.name}//{self.publication.name}'

    class Meta:
        verbose_name = "Book"
        verbose_name_plural = "Book" 

I want outcome json as single publication with list of book (Please show me using Django ORM query and Django Serializer OR using DRF):

Want Output JSON like below:

[
    {
        "id": 1,
        "name": "publication 001",
        "book": [
            {
                "id": 1,
                "name": "Book 001"
            },
            {
                "id": 2,
                "name": "Book 002"
            }
        ]
    },
    {
        "id": 2,
        "name": "publication 002",
        "book": [
            {
                "id": 3,
                "name": "Book 003"
            },
            {
                "id": 4,
                "name": "Book 004"
            }
        ]
    }
]

I tried it through several way but unable to solve it using django orm query. I've done it through raw query. But I wantto implement it through Django ORM Query. SO please help me. TIA


Solution

  • The following should do the trick using simple serializers

    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        name = serializer.CharField()
       
    
    class PublicationSerializer(serializers.Serializer):
        id = serializers.IntegerField()
        name = serializers.CharField()
        book = BookSerializer(source="books_publications.all", many=True)
    
    qs = Publication.objects.all()
    data = PublicationSerializer(qs, many=True).data
    

    source="book_set" allows for an object field to be mapped to a different serializer field.
    book_set is the default related_name provided by Django. This means that my_publication.book_set.all() is equivalent to Book.objects.filter(publication=my_publication).
    You should consider defining your own related_name.

    publication = models.ForeignKey(Publication, on_delete=models.CASCADE, null=True, blank=True, related_name="books")
    

    Note that the above code will result in N+1 queries to the database. A first one to get all publications and one for each publication to get related books.
    You can and should use prefetch_related to reduce the number of query to two.

    qs = Publication.objects.all()
    qs = qs.prefetch_related("books_publications")
    data = PublicationSerializer(qs, many=True).data
    

    Alternatively you could use ModelSerializer

    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = Book
            fields = ("id", "name")
    
    
    class PublicationSerializer(serializers.ModelSerializer):
        book = BookSerializer(source="books_publications.all", many=True)
    
        class Meta:
            model = Publication
            fields = ("id", "name", "book")