Django REST Framework:序列化程序以更新对象(如果存在)(通过唯一一起)

Django REST Framework: Serializer to update object if it exists (by unique together)

我有型号 运行。它代表数据的一个时间段测量:

class Run(models.Model):
    start_time      = models.DateTimeField(db_index=True)
    end_time        = models.DateTimeField()
    chamber         = models.ForeignKey(Chamber, on_delete=models.CASCADE)

    class Meta:
        unique_together=(('start_time', 'chamber'),)

这是一个 POST 请求此端点的数据简介的示例:

{
    "start_time": "2018-11-11T12:00:00Z",
    "end_time": "2018-11-11T12:00:01Z",
    "chamber": "My Test Chamber"
}

一个运行属于一个Chamber并且有一个start_timeend_time

A 运行 不能共享同一个 Chamberstart_time.如果我在请求数据 blurb 中得到相同的 start_timeChamber,这意味着 运行仍在进行中,需要更新。

第一次收到请求时,我创建了一个 运行 对象,效果很好。发送请求的传感器随后将发送相同的 POST 请求数据 blurb,仅更改数据 blurb 的 end_time,只要“运行”正在进行中。

传感器不发送 PUT 或 PATCH,它只发送 POST,这可能是个问题,但无法修复,所以我不得不凑合着 POST 请求。

我想要的行为是:

  1. 第一次发送运行数据blurb,创建一个新的运行对象
  2. 只要我在数据简介中收到相同的 Chamberrun_start,(检查此组合是否存在), 更新 运行end_time (a 运行 进行中)

这是序列化程序:

class RunsCreateSerializer(ModelSerializer):
    class Meta:
        model = Run
        fields = [
            'id',
            'chamber',
            'start_time',
            'end_time'
        ]

这是 API 端点上的 views.py:

class RunsCreateAPIView(CreateAPIView):
    queryset = Run.objects.all()
    queryset = queryset.prefetch_related('chamber')
    serializer_class = RunsCreateSerializer
    permission_classes = [IsAuthenticated]

    def create(self, request, *args, **kwargs):
        try:
            chamber = Chamber.objects.get(chamber_name=request.data["chamber"])
            request.data["chamber"] = chamber.id
        except:
            print("Invalid chamber name")

        serializer = RunsCreateSerializer(data=request.data)
        print(serializer)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors)

现在,我发现我必须稍微修改 views.py 上的“create”,因为传感器发送 Chamber 作为字符串标识符。然后我必须根据我的 Chambers table 检查该字符串并检索对象实例,然后修改请求数据以替换 Chamber 字符串作为它在我的数据库中的 ID。我不确定这是否是正确的方法,但它对我有用。

现在我尝试解决这个问题:

在 serializers.py:

def create(self, validated_data):
    run, created = Run.objects.update_or_create(
                    chamber=validated_data.get('chamber'),
                    start_time=validated_data.get('start_time'),
                    end_time=validated_data.get('end_time'))
    return run

def update(self, run, validated_data):
    run.chamber = validated_data.get('chamber', run.chamber)
    run.start_time = validated_data.get('start_time', run.start_time)
    run.created = validated_data.get('created', run.created)
    run.save()
    return run

我想通过在我的序列化程序上这样做我可以更新对象,但它似乎没有达到那个点(尝试在创建和更新时使用 print("Sanity check")方法,我没有看到我的健全性检查输出,所以我知道我没有达到那个点)。

我怀疑这与我重写视图上的创建方法有关,但我不确定。

如果您知道如何更新对象,并且可能让 serializer/view 处理 Chamber 名称的字符串,而不必干预替换字符串的方法ID,非常感谢你的帮助。

我们可以简单地覆盖方法def perform_create(self, serializer):

class RunsCreateAPIView(CreateAPIView):
  # some code
  def perform_create(self, serializer):
    chamber=serializer.validated_data.get('chamber'),
    chamber = Chamber.objects.get(chamber_name=chamber)
    start_time=serializer.validated_data.get('start_time')
    end_time=serializer.validated_data.get('end_time'),

    obj_lst = Run.objects.filter(chamber=chamber, start_time=start_time)
    if obj_lst:
        obj_lst.update(
                chamber=chamber,
                start_time=start_time,
                end_time=end_time)
    else:
        Run.objects.create(chamber=chamber,
                start_time=start_time,
                end_time=end_time)

正如您提到的,您正在获取 chamber 作为字符串,因此要正确处理该关系,请在序列化程序中使用 chamber = serializer.StringRelatedField()。删除您在 serializer/view 中覆盖的任何方法。只需在 RunsCreateAPIView 中添加以下内容。

def perform_create(self, serializer):
    chamber=serializer.validated_data.get('chamber'),
    start_time=serializer.validated_data.get('start_time')
    end_time=serializer.validated_data.get('end_time')
    Run.objects.update_or_create(
                  chamber=chamber, 
                  start_time=start_time, 
                  defaults = {"end_time": end_time}
                  )

update_or_create会根据chamber的值自动处理创建或更新,start_time.