Home > Net >  Unable to get related data from ManyToManyField
Unable to get related data from ManyToManyField

Time:11-15

I'm trying to fetch related objects from below two models.

Following django models with ManyToManyField relationship.

Book

class Book(models.Model):
authors = models.ManyToManyField(
    to=Author, verbose_name="Authors", related_name="books_author"
)
bookshelves = models.ManyToManyField(
    to=Bookshelf, verbose_name="Bookshelf", related_name="books_shelves"
)
copyright = models.NullBooleanField()
download_count = models.PositiveIntegerField(blank=True, null=True)
book_id = models.PositiveIntegerField(unique=True, null=True)
languages = models.ManyToManyField(
    to=Language, verbose_name=_("Languages"), related_name="books_languages"
)

Author

class Author(models.Model):
birth_year = models.SmallIntegerField(blank=True, null=True)
death_year = models.SmallIntegerField(blank=True, null=True)
name = models.CharField(max_length=128)

def __str__(self):
    return self.name

class Meta:
    verbose_name = _("Author")
    verbose_name_plural = _("Author")

I have to fetch all the Auhtors with their related books. I have tried a lot of different ways none is working for me.

First way : using prefetch_related

class AuthorListAPIView(APIErrorsMixin, generics.ListAPIView):

serializer_class = AuthorSerializer
queryset = Author.objects.exclude(name__isnull=True)

def get_queryset(self):
    auths = queryset.prefetch_related(Prefetch("books_author"))

Second way using related_name 'books_auhtor'

class AuthorListAPIView(APIErrorsMixin, generics.ListAPIView):

serializer_class = AuthorSerializer
queryset = Author.objects.exclude(name__isnull=True)

def get_queryset(self):
    auths = queryset.books_author.all()

None of the above ways worked for me. I want to prepare a list of Authors and their associated books.

For ex:-

[{'Author1':['Book1','Book2'],... }]

CodePudding user response:

Prefetching is not necessary, but can be used to boost efficiency, you can work with:

class AuthorListAPIView(APIErrorsMixin, generics.ListAPIView):
    serializer_class = AuthorWithBooksSerializer
    queryset = Author.objects.exclude(name=None).prefetch_related('books_author')

In the AuthorWithBooksSerializer, you can then add the data of the books, for example:

from rest_framework import serializers


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ('book_id', 'copyright')


class AuthorWithBooksSerializer(serializers.ModelSerializer):
    books = BookSerializer(source='books_author', many=True)

    class Meta:
        model = Author
        fields = ('name', 'books')

Here the books will use the BookSerializer and thus encode a list of dictionaries.

While you can use the name of the author as object key, I strongly advise against this: it makes the object less accessible since the keys are no longer fixed and if these contain spaces, it can also result in more trouble obtaining the value(s) associated with a given attribute name.

  • Related