I'm looking for a way to query data from models such as Author with fields [pk, name] and Book [pk, author, title, published] so, in the end, I'd have got something like that

[
  {
    'pk': 'author_pk_1', 'name': 'author__name_1', 'books': [
      {
        'pk': 'book__pk_1',
        'title': 'book_name_1',
        'published': 'book_published_1',
      },
      {
        'pk': 'book__pk_2',
        'title': 'book_name_2',
        'published': 'book_published_2',
      }
    ]
  }
]

At the moment I achieved this by iterating through QuerySet and manually grouping them, but it does not look good at all:

authors = Author.objects.values('pk', 'name')
grouped = list()
for author in authors:
    entry = dict()
    entry['pk'] = author['pk']
    entry['name'] = author['name']
    entry['books'] = list(Book.objects.filter(author=author['pk']).values('pk', 'title', 'published'))
    grouped.append(entry)

UPDATE:

Well, it was easier than I thought; here what I've got so far:

view.py

@api_view(['GET'])
def api_get_grouped_books(request, *args, **kwargs):
  if request.method == 'GET':
    authors = Author.objects.all()
    serializer = AuthorSerializer(authors, many=True)
    return Response(serializer.data)

serializers.py

class BookSerializer(serializers.ModelSerializer):
  class Meta:
    model = Book
    fields = ('title', 'published', )


class AuthorSerializer(serializers.ModelSerializer):

  books = BookSerializer(many=True)

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

  def create(self, validated_data):
    books_data = validated_data.pop('books')

    author, _ = Author.objects.get_or_create(
      name=validated_data.get('name'),
      defaults=validated_data
    )

    for book_data in books_data:
      try:
        Book.objects.get(title=book_data.get('title'))
      except ObjectDoesNotExist:
        Book.objects.create(author=author, **book_data)

    return author

Note: At first, I was getting an AttributeError when I tried to post some data - it was complaining that the field books not part of Author model, so I ended up adding the attribute related_name='books' to author ForeignKey field in Book model.

0 Answers