如何为 DRF 模型编写浮点列表 ModelSerializer?

JP *_*ura 3 python django django-models django-rest-framework

TL; DR

如何创建 和serializers.Patientserializers.Temperature以这样的方式:

  1. models.Patient与 具有一对多关系models.Temperatures
  2. serializers.Patient是一个子类serializers.ModelSerializer
  3. serializers.Patient(反)将温度序列化为浮点数列表

细节

给定一个使用Django 框架实现的快速脏病人医疗记录 RESTful API 。

患者定义为models.Patient

class Patient(models.Model):
    created_at = models.DateField()
    name = models.CharField(max_length=200)
    updated_at = models.DateField()
Run Code Online (Sandbox Code Playgroud)

models.Temperature

class Temperature(models.Model):
    created_at = models.DateField()
    patient = models.ForeignKey(
        Patient,
        db_column='patient',
        related_name='temperatures',
        on_delete=models.CASCADE,
    )
    updated_at = models.DateField()
    value = models.FloatField()

Run Code Online (Sandbox Code Playgroud)

CRUD 操作/patients(反)序列化为models.Temperature列表float,因此 aPOST应该只需要:

{
  "name": "John Connor",
  "temperatures": [36.7, 40, 35.9]
}
Run Code Online (Sandbox Code Playgroud)

GET进行手术时

{
  "created_at": "1985-03-25",
  "name": "John Connor",
  "temperatures": [36.7, 40, 35.9],
  "updated_at": "2021-08-29"
}
Run Code Online (Sandbox Code Playgroud)

但是端点处的操作/patients/<id>/temperatures/应返回所有属性

[
  {
    "created_at": "1985-03-25",
    "value": 36.7,
    "updated_at": "2021-08-29"
  },
  {
    "created_at": "1985-03-25",
    "value": 40.0,
    "updated_at": "2021-08-29"
  },
  {
    "created_at": "1985-03-25",
    "value": 35.9,
    "updated_at": "2021-08-29"
  }
]
Run Code Online (Sandbox Code Playgroud)

此功能可以通过子类化标准DRF序列化器来实现还是需要自定义serializers.Serializer子类?

小智 5

我们来尝试分析一下这里的情况。我们需要用列表输入温度数据。因此,让我们用于ListField此目的。所以,我们应该这样开始:

class PatientSerializer(ModelSerializer):
    temperatures = ListField(child=FloatField())
Run Code Online (Sandbox Code Playgroud)

这样做的目的是期望用户提供一个浮点数列表。GET但是我们在使用操作的时候如何得到创建的病人的体温数组呢?为此,我们向该字段添加一个源。参数上写的任何内容source都会被转换为patient.field例如:

class PatientSerializer(ModelSerializer):
    temperatures = ListField(child=FloatField(), source="temperature_list")
Run Code Online (Sandbox Code Playgroud)

source="temperature_list将在序列化对象时使用patient.temperature_list并显示结果。现在让我们向原始模型添加一些属性,以便我们可以获得如下所示的干净温度数组:

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()]
Run Code Online (Sandbox Code Playgroud)

我们快完成了。现在我们需要重写 的create方法,PatientSerializer以便它可以保存列表中存在的温度。现在,要从 中获取温度数据,validated_data我们需要使用source与 的参数中使用的相同字段ListField,即temperature_list。因此,我们需要弹出此数据,创建用户,验证温度列表项,创建与患者相关的温度对象。让我们创建一个TemperatureSerializer这样的:

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},
        }
Run Code Online (Sandbox Code Playgroud)

现在,我们需要做的就是编写 create 方法。我们可以这样写:

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
Run Code Online (Sandbox Code Playgroud)

最后,让我们将所有内容放在一起,文件如下所示serializers.py

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},
        }
Run Code Online (Sandbox Code Playgroud)

views.py看起来是这样的:

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)
Run Code Online (Sandbox Code Playgroud)

urls.py文件:

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",
    ),
]
Run Code Online (Sandbox Code Playgroud)

POST该方法的工作 原理如下:在此输入图像描述

这就是病人体温恢复的方式: 在此输入图像描述

我希望这回答了你的问题。