S1M0N38
S1M0N38

Reputation: 163

RelatedField Serializer Django Rest Framework

I have this tables store in a Postgres DB

users

| id |     email       | password | gender |
| 01 | [email protected] | pass1    |  male  |
| 02 | [email protected] | pass3    | female |
| 03 | [email protected] | pass2    |  male  |

marks

| id | school_mark | subject | user_id | status |
| 01 |      9      | history |    01   | True   |
| 02 |      8      | english |    02   | True   |
| 03 |      7      | math    |    01   | True   |
| 02 |      7      | english |    01   | False  |
| 03 |      6      | math    |    03   | False  |
...

# api/models.py

from django.db import models


class User(models.Model):
    email = models.EmailField()
    password = models.CharField(max_length=100)
    gender = models.CharField(max_length=100)

class Mark(models.Model):
    school_mark = models.IntegerField()
    subject = models.CharField(max_length=100)
    user_id = models.ForeignKey(User, on_delete=models.CASCADE)
    status = models.BooleanField(default=False)

I want my final result to be like this /api/marks/male

[
    {"id" : 01,
        "marks": [
            {"id": 01,
             "subject": "history",
             "school_mark": 9},
            {"id": 03,
             "subject": "history",
             "school_mark": 7}]}
    {"id" : 03,
    "marks": []}
]

First select only 'male' form user, then search marks for selected users but return in the response only the marks that have status=TRUE

I start to think the serializers.py like this:

# api/serializers.py

from rest_framework import serializers
from .models import User, Mark

class MarkSerializer(serializers.ModelSerializer):
    class Meta:
        model = Mark
        exclude = ('status', 'user_id')


class UserSerializer(serializers.ModelSerializer):
    marks = MarkSerializer(many=True, read_only=True)

    class Meta:
        model = User
        fields = ('id', 'marks')

But then I'm stuck on views.py

# api/views.py

from rest_framework import generics

from .models import User, Mark
from .serializers import UserSerializer

class MarkList(generics.ListAPIView):
    serializer_class = UserSerializer

    def get_queryset(self):
        # "/api/marks/<str:gender>/"
        gender = kwargs['gender']
        ???

Any idea how to go on ?

Upvotes: 2

Views: 440

Answers (1)

RishiG
RishiG

Reputation: 2830

Are you set on the url/response structure? I realize this is not exactly what you asked for, but in order to give the best answer I can, there are some structural things I would probably change about your setup:

  • Normally for a RESTful API, /api/marks/ would return a list of serialized Mark objects as opposed to what you have shown, which is a list of Users where each has a list of serialized Marks. Since you only need the user id, DRF provides a simple non-nested mechanism for serializing this the other way around (using PrimaryKeyRelatedField).

  • Next, instead of /api/marks/male I would probably use a query parameter (/api/marks?gender=male). This is a little bit of personal preference but has some basis in REST best practices (see this SO answer for a nice description)

  • Finally, the ForeignKey field on Mark should be named user, not user_id. Django automatically appends _id to the database column name when storing a foreign key field, so the model as you have written it will result in a DB column named user_id_id.

With these modifications, the following code would get you the filtering you requested (although the serialized response is a little different than your initial request as described above):

# api/serializers.py

from rest_framework import serializers
from .models import Mark

class MarkSerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(read_only=True)
    class Meta:
        model = Mark
        exclude = ('status')


# api/views.py

from rest_framework import generics

from .models import Mark
from .serializers import MarkSerializer

class MarkList(generics.ListAPIView):
    serializer_class = MarkSerializer

    def get_queryset(self):
        queryset = Mark.objects.filter(status=True)
        gender = self.request.query_params.get('gender')
        if gender is not None:
            queryset = queryset.filter(user__gender=gender)
        return queryset

I have not tested this out, but barring any typos, requesting /api/marks?gender=male from your example above should return something like this:

[
    {
        "id" : 01,
        "subject": "history",
        "school_mark": 9,
        "user": 01
    },
    {
        "id" : 03,
        "subject": "math",
        "school_mark": 7,
        "user": 01
    }
]

Upvotes: 2

Related Questions