【问题标题】:Django REST Framework: Serializer to update object if it exists (by unique together)Django REST Framework:序列化器更新对象(如果存在)(通过唯一的一起)
【发布时间】:2018-04-25 09:19:08
【问题描述】:

我有模型 Run。它表示数据的时间段度量:

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"
}

一个Run属于一个Chamber并且有一个start_timeend_time

Run 不能共享相同的 Chamberstart_time。如果我的请求数据简介中的 start_timeChamber 相同,则表示 Run 仍在进行中,需要更新。 p>

当请求第一次通过时,我创建了一个 Run 对象,它工作正常。发送请求的传感器随后将发送相同的 POST 请求数据简介,只要“运行”正在进行,仅更改数据简介的 end_time

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

我想要的行为是:

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

这里是序列化器:

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 作为字符串标识符发送。然后,我必须对照我的 Chamber 表检查该字符串并检索对象实例,然后修改请求数据以用 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"),我看不到我的健全性检查输出,所以我知道我没有达到那个点)。

我怀疑这与我在视图上覆盖 create 方法有关,但我不确定。

如果您知道如何更新对象,并且可能让序列化程序/视图处理 Chamber 名称的字符串,而不必干预用于替换 ID 字符串的方法,我将不胜感激帮助。

【问题讨论】:

标签: python django django-rest-framework django-serializer


【解决方案1】:

正如您提到的,您将chamber 作为字符串获取,因此要正确处理该关系,请在序列化程序中使用chamber = serializer.StringRelatedField()。删除您在序列化程序/视图中覆盖的任何方法。只需在您的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的值自动处理创建或更新。

【讨论】:

    【解决方案2】:

    我们可以简单地覆盖方法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)
    

    【讨论】:

    • 我是在使用这个而不是 create 方法吗?旁边?在里面?我试过这个,但它还没有工作。如果我退出(我的覆盖)创建方法并使用它,我会得到"chamber": [ "Incorrect type. Expected pk value, received str." ]",因为传感器正在传递一个腔室名称字符串,而不是数据库中的 ID
    猜你喜欢
    • 1970-01-01
    • 2020-03-05
    • 2018-02-10
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 2014-07-07
    • 2016-11-21
    • 1970-01-01
    相关资源
    最近更新 更多