【问题标题】:Update/put request in Django REST Framework在 Django REST 框架中更新/放置请求
【发布时间】:2018-07-24 10:53:27
【问题描述】:

我是 Django REST 框架的新手,我正在为我正在从事的一个新项目尝试它。因此,根据官方教程,我尝试创建几个 get/post/put 请求,但使用 put 请求时出现以下错误:

预期视图 ExampleUpdateView 将使用名为“pk”的 URL 关键字参数调用。修正您的 URL 配置,或正确设置视图上的 .lookup_field 属性。

这是我需要的文件:

models.py

class ExampleModel(models.Model):
    foo_field = models.CharField(primary_key=True, max_length=15)
    bar_field = models.CharField(max_length=30)
    last_updated_by = models.CharField(max_length=15)
    last_updated_on = models.DateTimeField()

    class Meta:
        managed = True
        db_table = 'example_db'
        unique_together = (('foo_field', 'bar_field'),)

serializers.py

class ExampleSerializer(serializers.ModelSerializer):
    class Meta:
        model = ExampleModel
        fields = ('foo_field', 'bar_field', 'last_updated_by', 'last_updated_on')

urls.py

url(r'^get_example/$', views.ExampleCreateView.as_view()),
url(r'^update_example/(?P<pk>\d+)/$',views.ExampleUpdateView.as_view()),

views.py

class ExampleCreateView(generics.CreateAPIView):
    serializer_class = ExampleSerializer
    queryset = ExampleModel.objects.all()

class ExampleUpdateView(generics.UpdateAPIView):
    queryset = ExampleModel.objects.all()
    serializer_class = ExampleSerializer

我认为我可能遇到的问题是我有一个复合键。我尝试了其他通用视图(CreateAPIView 和 ListAPIView),它们工作得非常好。我需要更新def_update 方法吗?我需要更改serializers.py 中的任何内容吗?

这是我从 GET 请求中获得并尝试更新的现有 JSON 对象:

{
    "foo_field": "john",
    "bar_field": "doe",
    "last_updated_by": "batman",
    "last_updated_on": "2017-02-09"
}

我查看了以下重复项,但这些解决方案似乎都不适合我:

Django Rest Framework: Unclear error message

Django REST Updateview with PUT POST

How do I use an UpdateView to update a Django Model?

【问题讨论】:

  • 您的urls.py 是一个明显的反模式。那不是休息!您没有将动词放入 URL 中! HTTP 请求方法应该映射到 CRUD:POST - CREATE;获取 - 检索;放——更新;删除 - 删除。
  • 您已将foo_field 设置为主键,因此您不使用复合键。 Django 不支持复合键。您拥有的是unique_together,即UNIQUE KEY,而不是复合键。顺便说一句,使用字符串作为主键是一种不好的做法。
  • 您需要使用CreateAPIViewUpdateAPIView 的真正原因是什么?为什么不ListCreateAPIViewRetrieveUpdateDestroyAPIView?或者为什么不直接进入视图集?您有什么特殊要求?
  • CreateAPIView 和 UpdateAPIView 只是示例。我尝试使用 RetrieveUpdateDestroyAPIView 但后者中的 put 请求仍然给我同样的错误

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


【解决方案1】:

首先,我将重构您的代码并展示代码示例。之后我会解释这些变化。

models.py

class ExampleModel(models.Model):
    foo_field = models.CharField(primary_key=True, max_length=15)
    # rest ...

您已将foo_field 设置为主键。使用字符串 (VARCHAR) 作为主键是一种不好的做法。强烈建议使用surrogate keys。这将由 Django 自动完成。它添加了一个字段id 作为主键。

serializers.py 现在可以正常工作了。无需更改。 views.py 也应该无需更改即可工作。不过,我将介绍以 foo_field 作为主键的选项。

views.py

类 ExampleUpdateView(generics.UpdateAPIView):
    # 休息 ...
    查找字段 = `foo_field`

您也可以将其省略,但重要的是对您的 urls.py 进行一些更改。

urls.py

url(r'^examples/$', views.ExampleCreateView.as_view()),
url(r'^示例/(?P\d+)/$',views.ExampleUpdateView.as_view()),

为了使 Web 服务成为 RESTful,它们必须遵守 REST 原则。最重要的规则之一是我们不应该在 URL 中使用动词来描述动作。另一种约定是资源名称以复数形式用于描述集合,而标识符用于访问单个资源。

您的资源名为 example,因此我们使用 examples 作为集合名称。像 get 或 update 这样的动词在 URL 中没有位置。 HTTP 方法负责正确的操作。

如果您想使用foo_field 作为主键,那么您必须更改更新视图的 url:

url(r'^examples/(?P&lt;pk&gt;<b>[\w-]+</b>)/$',views.ExampleUpdateView.as_view()),

这是因为您的 foo_fieldCharField(或字符串),因此传递给它的参数应匹配任何字母数字字符 (\w) 和连字符 (-)。
如果您愿意,您可以在 url 正则表达式中使用 foo_field 作为命名组,而不是 pk。请注意在您的视图中调整它,为lookup_fieldlookup_url_kwarg 设置正确的值。

我再次强烈建议使用代理键,让 Django 来完成这项任务。

现在您可以理解为什么 ListAPIViewCreateAPIView 会起作用了。它们不需要传递给 url 的参数并且被正确调用。
您的 UpdateAPIView 无法工作,因为匹配的 url 只接受数字并且与您的主键 foo_field 不匹配。

【讨论】:

  • 非常感谢您的详细回答。我现在明白了为什么 ListAPIView 和 ListAPIView 有效但 UpdateAPIView 没有。不幸的是,尽管我很想使用代理键(在这种情况下是 Django 的 Autofield),但我无法选择它(我的技术负责人拒绝了)。我无法控制数据库(现在使用 inspectdb)。我必须使用复合键,并且也使用 VARCHAR。
  • @VasuGaur 在这种情况下,使用foo_field 作为主键并注意在视图中设置lookup_field。使用 URL 的第二个示例,以匹配任何字母数字字符或任何您的要求。
【解决方案2】:

update()UpdateAPIView 一起使用。

class ExampleUpdateView(generics.UpdateAPIView):
        queryset = ExampleModel.objects.all()
        serializer_class = ExampleSerializer

        def update(self, request, *args, **kwargs):
            instance = self.get_object()
            instance.foo_field = request.data.get("foo_field")
            instance.bar_field = request.data.get("bar_field")
            instance.last_updated_by = request.data.get("last_updated_by")
            instance.last_updated_on = request.data.get("last_updated_on")
            instance.save()
            serializer = self.get_serializer(instance)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
            return Response(serializer.data)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 2015-06-18
    • 1970-01-01
    • 2016-08-09
    • 1970-01-01
    • 2017-05-22
    • 2023-03-21
    相关资源
    最近更新 更多