2

I have a ManyToMany field in my models. In serializer, I am able to get the nested serialized data, but I want to normalize it.

models.py

class Authors(models.Model):
   name = models.CharField(max_length=20)
    
   class Mets:
      db_table = "authors"

class Books(models.Model):
   book_name = models.CharField(max_length=100)
   authors = models.ManyToManyField(Authors, related_names="books")

   class Meta:
      db_table = "books"

serializers.py

class AuthorSerializer(serializer.ModelSerializer):
   name = serializer.CharField()
   
   class Meta:
      model = Authors
      field = ("name",)

class BooksSerializer(serializer.ModelSerializer):
   authors = AuthorSerializer()
   
   class Meta:
      model = Books
      field = ("book_name", "authors")

The output of the above will be:

"result": [
 {
   "book_name": "Sample",
   "authors": [
    {
      "name": "Person 1",
    },
    {
      "name": "Person 2",
    }
   ]
  }
]

But I want to output something like this:

"result": [
 {
   "book_name": "Sample",
   "author_name": "Person 1",
 },
 {
   "book_name": "Sample",
   "author_name": "Person 2",
 },
]

UPDATE

views.py

class ReportViewSet(XLSXFileMixin, viewsets.ReadOnlyModelViewSet):
    serializer_class = BookSerializer
    renderer_classes = [XLSXRenderer]
    filename = 'my_export.xlsx'

    def get_queryset(self):
            queryset = Book.objects.all()
            book_id = self.request.query_params.get("pk", None)
            if book_id is not None:
                queryset = queryset.filter(id=book_id)
            return queryset

The excel report should have columns: book_name, author_name

But with the solution given by @Klim Bim, I get an empty excel report with just column names: book_name and authors.

Thanks in advance.

Reema Parakh
  • 1,347
  • 3
  • 19
  • 46

2 Answers2

0

If you would like to change the behaviour of displaying, you have to override to_representation()

def to_representation(self, instance):
   data = super().to_representation(instance)
   authors = data.pop('authors')
   retval = list()
   for author in authors:
     retval.append({**data, "author_name": author.get("name")})

   return {"result": retval}
Klim Bim
  • 484
  • 2
  • 7
  • Thanks for this. But now I want to download this as a excel report. For this I am using DRF XLSRenderer. I will update my question with the views files too. With your solution, I get empty excel report with just the headers. Please see updated question. – Reema Parakh Oct 12 '21 at 09:21
0

you should use list instead of get_queryset :

class ReportViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
    serializer_class = BookSerializer
    renderer_classes = [XLSXRenderer]
    filename = 'my_export.xlsx'
    
    def list(self, request, *arg, **kwargs):
            queryset = Books.objects.filter(#some_filter#)
            return Response(data=self.serializer_class(queryset, many=True).data)

and never forget to change your urls.py as below:

urlpatterns = [
    ...
    path('download-my-file/', ReportViewSet.as_view({'get': 'list'})),
    ....
    ]
hadi ahadi
  • 116
  • 1
  • 1
  • 6