【问题标题】:Django: Update a specific field with a GET instead of a PATCHDjango:使用 GET 而不是 PATCH 更新特定字段
【发布时间】:2020-10-02 02:20:27
【问题描述】:

上下文

对于特定用例,我需要能够使用 GET 请求而不是 PATCH 请求来更新我的 Visitor 模型的单个字段。

我的相关访问者模型如下所示:

# models.py

class Visitor(models.Model):
    visitor_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    customers = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='customer_visitors')
    audiences = models.ManyToManyField(Audience, related_name='audience_visitors')
    cid = models.CharField(max_length=255, unique=True)
    uid = models.CharField(max_length=255)
    cup = JSONField(null=True)

    def __str__(self):
        return self.cid

    class Meta:
        db_table = 'visitor'

我正在使用这样一个简单的序列化程序:

# serializers.py

class VisitorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Visitor
        fields = ('customers', 'cid', 'uid', 'cup')

我可以只更新特定 Visitorcup 字段,该字段使用具有如下 PATCH 的唯一 cid 字段进行查找:

# views.py

class VisitorViewSet(viewsets.ModelViewSet):
    serializer_class = VisitorSerializer
    queryset = Visitor.objects.all()
    lookup_field = 'cid'

    def list(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.serializer_class(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

问题

问题是我无法使用 GET 请求根据给定的唯一 cid 字段更新访客的 cup 字段。

我尝试了什么

正如this answer by Uri Shalit 建议的那样,我尝试在VisitorViewSet 中覆盖get_serializer(),并尝试在list() 中使用它,如下所示:

# views.py

class VisitorViewSet(viewsets.ModelViewSet):
    serializer_class = VisitorSerializer
    queryset = Visitor.objects.all()
    lookup_field = 'cid'

    def get_serializer(self, *args, **kwargs):
        kwargs['partial'] = True
        return super(VisitorViewSet, self).get_serializer(*args, **kwargs)

    def list(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

但是,基于 cid 字段仅更新特定访客的 cup 字段适用于 PATCH 请求,但不会使用 GET 请求更新所述字段。也没有错误。

预期行为

发出一个 GET 请求,其中包含 cid 以识别访客,cup 包含需要为给定访客更新的数据。我知道它违反了 REST 原则,但对于这个用例,我需要使用 GET 请求而不是 PATCH 请求来进行部分更新。

任何正确方向的帮助或指示将不胜感激!

【问题讨论】:

  • 我认为你不应该使用 GET 请求来更新,因为 GET 请求可以被多次调用(例如大多数在线爬虫使用带有所有 url 名称的 GET 请求进行爬虫)。我认为您应该在参数/data/1 中有一个带有 id 的 PUT 请求,并在正文中传递更新字段
  • 这个用例要求它是一个 GET 请求。我可以使用 PUT 或 PATCH 请求来实现我想要的,但它必须是 GET 请求,因为它用于跟踪。
  • 您要更新单个访问者实例还是实例列表?您要用于更新的数据是来自前端还是后端?如果它来自前面,那么您只能将其作为 url 参数传递,因为 GET 请求没有正文。必须回答这些问题才能为您提供帮助
  • 我想更新单个访问者实例。数据作为 GET 请求来自前端,数据作为 URL 参数传递

标签: python django django-rest-framework


【解决方案1】:

在你的模型中添加一个类方法。

class Visitor(models.Model):
    visitor_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
    customers = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='customer_visitors')
    audiences = models.ManyToManyField(Audience, related_name='audience_visitors')
    cid = models.CharField(max_length=255, unique=True)
    uid = models.CharField(max_length=255)
    cup = JSONField(null=True)

    def __str__(self):
        return self.cid

    class Meta:
        db_table = 'visitor'


    @classmethod
    def update_cup(cls, cid, cup_new):
        instance = cls.objects.get(cid=cid)
        instance.cup = new_cup
        instance.save()

在 ModelViewSet 中重写 get_queryset 方法,见下文:

IDK 你如何计算 new_cup 我猜你把它作为一个查询参数来获取

def get_queryset(self):
    queryset = Visitor.objects.all()

    cup_new = self.request.query_params.get('cup_new', None)
    cid = self.request.query_params.get('cid', None)

    [obj.update_cup(obj.cid, cup_new) for obj in queryset if obj.cid == cid]
    return queryset

【讨论】:

    【解决方案2】:

    我建议使用 api_view 来完成你想要的。 api_view 是其余框架提供的注释,因此它应该已经在您的情况下可用。

    @api_view(["GET"])
    def update_function(request):
        query_params = request.GET  # Getting the parameters from request
        cid =  query_params["cid"]
        cup =  query_params["cup"]
    
        visitor = Visitor.objects.get(cid = cid)
        visitor["cup"] = cup
    
        serializer = VisitorSerializer(data = visitor, partial=True)
        if serializer.is_valid():
            serializer.save()
        else:
            print(serializer.errors)
    

    但是我不确定语法,但方法足以解决您的问题。 确保将函数添加到 urls.py 并查看文档以获得比我的 Api Views 更好的信息。但不要指望它会提供有关您特定问题的信息。在您的情况下,您必须了解 api_view 概念并根据您的需要进行调整。

    【讨论】:

    • 它不是模型视图集 - 所以它并没有真正回答问题
    • 谁说它必须是模型视图集? apiviews 非常适合解决这个问题,而且他已经在使用 restframework,所以不需要额外的导入。
    • 我不明白你的意思。但你似乎也对演讲不感兴趣。
    【解决方案3】:

    我想我没有完全理解这个问题。为什么不能简单地覆盖视图中的 get_object() 方法并在其中执行自定义逻辑来更新对象?

    def get_object(self):
        obj = super().get_object()
        serializer = self.get_serializer(obj, data=self.request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return obj
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-09
      • 1970-01-01
      • 2011-12-23
      • 2018-03-31
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      相关资源
      最近更新 更多