[Django]-How to code a float list ModelSerializer for DRF a model?

5👍

Let’s try to analyze the situation here. We need to input the temperature data with a list. So, let’s use ListField for this purpose. So, we should start like this:

class PatientSerializer(ModelSerializer):
    temperatures = ListField(child=FloatField())

What this will do is expect a list of floats from the user.
But how will we get the array of the temperatures of the created patient when using GET operation. For this, let’s add a source to the field. Whatever is written on the source param will get converted into the patient.field for example:

class PatientSerializer(ModelSerializer):
    temperatures = ListField(child=FloatField(), source="temperature_list")

This source="temperature_list will user patient.temperature_list and show the result when serializing the object. Now let’s add some property to the original model so that we can get the clean temperature array like this:

class Patient(models.Model):
    created_at = models.DateField()
    name = models.CharField(max_length=200)
    updated_at = models.DateField()

    @property
    def temperature_list(self):
        return [temperature.value for temperature in self.temperatures.all()]

We are almost done. Now we need to override the create method for PatientSerializer so that it can save the temperatures present in the list.
Now, to get the temperature data from the validated_data we need to use the same field that is used in the source argument on the ListField, which is temperature_list. So, we need to pop this data, create the user, validate the temperature list items, create temperature objects that are related to the patient. Let’s create a TemperatureSerializer like this:

class TemperatureSerializer(ModelSerializer):
    class Meta:
        model = Temperature
        fields = ("created_at", "value", "updated_at", "patient")
        extra_kwargs = {
            "patient": {"write_only": True},
            "created_at": {"required": False},
            "updated_at": {"required": False},
        }

Now, all we need to do is, write the create method. We can write it like this:

def create(self, validated_data):
    temperatures = validated_data.pop("temperature_list")
    now = datetime.now().strftime("%Y-%m-%d")
    patient = Patient.objects.create(
        created_at=now, updated_at=now, **validated_data
    )
    for temperature in temperatures:
        now = datetime.now().strftime("%Y-%m-%d")
        temperature_serializer = TemperatureSerializer(
            data={
                "value": temperature,
                "created_at": now,
                "updated_at": now,
                "patient": patient.id,
            }
        )
        temperature_serializer.is_valid(raise_exception=True)
        temperature_serializer.save()
    return patient

Finally, let’s put together all the things and this is how the serializers.py file would look like:

from rest_framework.serializers import ModelSerializer, ListField, FloatField
from datetime import datetime

from .models import Patient, Temperature


class TemperatureSerializer(ModelSerializer):
    class Meta:
        model = Temperature
        fields = ("created_at", "value", "updated_at", "patient")
        extra_kwargs = {
            "patient": {"write_only": True},
            "created_at": {"required": False},
            "updated_at": {"required": False},
        }


class PatientSerializer(ModelSerializer):
    temperatures = ListField(child=FloatField(), source="temperature_list")

    def create(self, validated_data):
        temperatures = validated_data.pop("temperature_list")
        now = datetime.now().strftime("%Y-%m-%d")
        patient = Patient.objects.create(
            created_at=now, updated_at=now, **validated_data
        )
        for temperature in temperatures:
            now = datetime.now().strftime("%Y-%m-%d")
            temperature_serializer = TemperatureSerializer(
                data={
                    "value": temperature,
                    "created_at": now,
                    "updated_at": now,
                    "patient": patient.id,
                }
            )
            temperature_serializer.is_valid(raise_exception=True)
            temperature_serializer.save()
        return patient

    class Meta:
        model = Patient
        fields = ("created_at", "name", "temperatures", "updated_at")
        extra_kwargs = {
            "created_at": {"required": False},
            "updated_at": {"required": False},
        }

This is how the views.py would look like:

from rest_framework.generics import ListCreateAPIView, ListAPIView

from .models import Patient, Temperature
from .serializers import PatientSerializer, TemperatureSerializer

class PatientListCreateView(ListCreateAPIView):
    serializer_class = PatientSerializer
    queryset = Patient.objects.all()


class TemperatureListView(ListAPIView):
    serializer_class = TemperatureSerializer

    def get_queryset(self, *args, **kwargs):
        patient_id = self.kwargs["pk"]
        return Temperature.objects.filter(patient_id=patient_id)

And the urls.py file:

from django.urls import path

from . import views

urlpatterns = [
    path("patients", views.PatientListCreateView.as_view(), name="patient-list"),
    path(
        "patients/<int:pk>/temperatures/",
        views.TemperatureListView.as_view(),
        name="patient-temperature-list",
    ),
]

This is how the POST method would work:
enter image description here

And this is how the temperatures of a patient would return:
enter image description here

I hope this answers your questions.

Leave a comment