Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework -- Serializing many-to-many table relationships

I'm creating an API for a website for which I have a table with Books, another with Hashtags (which basically is a catalog of hashtags, e.g. "#traveling" ) and an intermediate table to establish many-to-many relationships between Books and Hashtags. My models are:

# models.py
class Books(models.Model):
    id_books = models.AutoField(primary_key=True)
    title = models.CharField(max_length=150)

class Hashtags(models.Model):
    id_hashtags = models.AutoField(primary_key=True)
    hashtag = models.CharField(max_length=150)

class Books_Hashtags(models.Model):
    id_books_hashtags = models.AutoField(primary_key=True)
    id_books = models.ForeignKey(Books, on_delete=models.CASCADE)
    id_hashtags = models.ForeignKey(Hashtags, on_delete=models.CASCADE)
    class Meta:
        unique_together = ("id_books", "id_hashtags")

I'd like to have an API that provides an output like this:

[
    {
        "id_books": 1,
        "title": "The Hobbit",
        "hashtags": [
            {
                "id_hashtags": 1,
                "hashtag": "fantasy"
            },
            {
                "id_hashtags": 2,
                "hashtag": "fiction"
            },
            {
                "id_hashtags": 3,
                "hashtag": "middle earth"
            },
        ]
    }
]    

For this purpose I've serialized the models above using DRF following some examples that I've found online:

#serializers.py
from rest_framework import serializers
from app.models import Books, Books_Hashtags, Hashtags

class HashtagsSerializer(serializers.ModelSerializer):
    hashtags = serializers.CharField()
    class Meta:
        model = Hashtags
        fields = ('hashtag',)

class Books_HashtagsSerializer(serializers.ModelSerializer):
    id_hashtag = HashtagsSerializer(many=True, read_only=True)
    class Meta:
        model = Books_Hashtags
        fields = ('id_hashtags',)

class BooksSerializer(serializers.ModelSerializer):
    id_books = serializers.IntegerField()
    id_books_hashtags = Books_HashtagsSerializer(many=True, read_only=True)
    title = serializers.CharField()

    class Meta:
        model = Books
        fields = ('id_books', 'title', 'id_books_hashtags',)

My views look like this:

#views.py
from ntgBackend.models import Books
from rest_framework import viewsets
from api.serializers import BooksSerializer

class BooksViewSet(viewsets.ModelViewSet):
    serializer_class = BooksSerializer
    queryset = Books.objects.all()

So far, I've managed to obtain the following output from the API:

[
    {
        "id_books": 1,
        "title": "The Hobbit",
    }
]

But no information regarding hashtags, which I know they exist in the DB. I'm using DRF 3.8.2 with a MySQL DB and Django 2.0.2. Please help!

like image 850
Victor Avatar asked May 22 '18 13:05

Victor


1 Answers

I was having the same problem and got to the following with some research:

In models.py only modify your Book model to include a ManyToManyField with Hashtags using Books_Hashtagas as your middle table. Also be sure to declare your Hashtag model before the Book one for the reference to work

# models.py
class Books(models.Model):
    id_books = models.AutoField(primary_key=True)
    title = models.CharField(max_length=150)
    hashtags = models.ManyToManyField(Hashtags, related_name='hashTg', through="Books_Hashtags"

In your serializers file be sure to add depth = 1 in order to get what you need, otherwise you'll only get the hashtag id (This is for a ListAPI only)

#serializers.py
class BooksSerializer(serializers.ModelSerializer):
    class Meta:
        model = Books
        #In case you want to filter out some fields:
        #fields = ('field_A','field_B' )
        fields = '__all__'
        depth = 1

In views I'm using generics since they solve most of my needs:

#views.py
from ntgBackend.models import Books
from rest_framework import generics
from api.serializers import BooksSerializer

class BooksViewSet(generics.ListCreateAPIView):
    serializer_class = BooksSerializer
    queryset = Books.objects.all()

I'm quite new to this, I hope it helps. I'm using almost the same versions as you except Django(2.0.4).

Good luck

like image 186
DiegoMC Avatar answered Nov 08 '22 19:11

DiegoMC