【问题标题】:Creating custom Django REST url using multiple fields使用多个字段创建自定义 Django REST url
【发布时间】:2015-09-22 20:13:29
【问题描述】:

我正在构建一个与 Django 数据库对话的 SMS API,该数据库是数百人的联系信息列表。字段如下:名字、姓氏、电话号码和职位。

当我在本地服务器上使用此 url 时收到响应:

http://localhost:8000/sources/id

我想做的是使用这个 url 向同一个数据库发出请求:

http://localhost:8000/sources/first_name-last_name

我调查了多个有关字段查找的问题,但没有任何帮助。这是我的 serializers.py 的样子:

from rest_framework import serializers, viewsets
from text_source.models import Source

class SourceSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Source
        fields = ('id','first_name','last_name','title','organization','city','cell_phone','office_phone')
        lookup_field = 'first_name'

class SourceViewSet(viewsets.ModelViewSet):
    queryset = Source.objects.all()
    serializer_class = SourceSerializer
    lookup_field = ('first_name')

我不确定使用 /first_name-last_name 作为 url 的端点是否是最佳实践,但理论上我正在做的事情,它会起作用。

理想情况下,我希望有人在文本中键入 FIRSTNAME LASTNAME,并通过将全名连接到数据库中的 ID 让 API 返回正确的信息。任何实现这一目标的技巧将不胜感激。

urls.py

from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic.base import TemplateView
from django.contrib.auth.models import User

from rest_framework import routers, serializers, viewsets
from app import views
from app.serializers import SourceSerializer, SourceViewSet

router = routers.DefaultRouter()
router.register(r'sources', SourceViewSet)

urlpatterns = [

    url(r'^page/$', TemplateView.as_view(template_name='base.html')),

    url(r'^page/', include(router.urls)),

    url(r'^admin/', include(admin.site.urls)),

    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),

    url(r'^page/incoming/message$', views.incoming_message)
]

【问题讨论】:

  • 在视图中,为什么要把lookup_field = ('first_name') 括在圆括号中?
  • 阅读here 了解一个不错的mixin,您可以使用它来查找多个字段(页面底部,查找MultipleFieldLookupMixin)。然后确保更新urls.py 以正确匹配和命名关键字参数(姓名和姓氏)

标签: python django django-rest-framework


【解决方案1】:

我会尝试执行以下示例。您可以将 kwargs 更改为您最终使用的任何内容。

序列化器:

class SourceUrlField(serializers.HyperlinkedIdentityField):
    def get_url(self, obj, view_name, request, format):
        kwargs = {
            'first_name': obj.first_name,
            'last_name': obj.last_name
        }
        return reverse(view_name, kwargs=kwargs,
                       request=request, format=format)

class SourceSerializer(serializers.HyperlinkedModelSerializer):
    url = SourceUrlField("view_name")

    class Meta:
        model = Source
        fields = (
            'id',
            'url',
            'first_name',
            'last_name',
            'title',
            'organization',
            'city',
            'cell_phone',
            'office_phone',
        )

根据 Joey 的推理,如果可能,我会避免使用 first_namelast_name。但是,我刚刚向您展示的示例适用于您选择使用的任何 kwarg。

【讨论】:

  • 很好的答案,我正在学习新东西。如果我理解正确,这允许Source 对象被序列化,其中url 字段填充了所需的格式。我的问题是:使用这样的 url 时查找会起作用吗?
  • @Pynchia 是的,应该。
  • 感谢您的回复。我收到了这个错误:Exception Value: reverse() got an unexpected keyword argument 'request' 并修复了这个错误:from rest_framework.reverse import reverse。现在我正在解决这个错误:Exception Value: Could not resolve URL for hyperlinked relationship using view name "view_name". You may have failed to include the related model in your API, or incorrectly configured the lookup_field` 这个字段的属性。`
  • @AldoTheApache 您需要将url = SourceUrlField("view_name") 中的"view_name" 更改为您实际使用的视图的名称。那只是一个占位符。
  • 对不起。我不明白。什么观点?我使用我的数据查看 /sources/ 页面的方式是通过我发布的代码示例底部的 SourceViewSet。该视图集是在我的 urls.py 页面中配置的。我将我的 urls.py 代码添加到问题中。
【解决方案2】:

我认为您可能会遇到使用/sources/first_name-last_name 作为端点的问题,因为您永远无法保证名称的唯一性,并且名称也可能包含破折号。我建议将/sources/id(可能是/sources/)保留为您的端点,并且只允许过滤。

您可以阅读in the DRF documentation 了解有关过滤的更多详细信息,但一个基本示例(安装 django-filter 之后)如下所示:

from rest_framework import filters, serializers, viewsets
from text_source.models import Source

class SourceSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Source
        fields = ('id','first_name','last_name','title','organization','city','cell_phone','office_phone')


class SourceViewSet(viewsets.ModelViewSet):
    queryset = Source.objects.all()
    serializer_class = SourceSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('first_name', 'last_name')

一旦你有了这个,你就可以按照/sources/?first_name=Bob&last_name=Dobbs 的行来构造 URL 来执行你的搜索并返回 ID。

另请注意,您的示例中有一个非常重要的错误。您将 ViewSet 上的lookup_field 指定为('first_name')。由于元组中只有一个元素,这种精确的语法将产生非常不希望的副作用,将字符串 'first_name' 视为可迭代,创建一个看起来像 ('f', 'i', 'r', 's', 't', '_', 'n', 'a', 'm', 'e') 的元组。为了防止这种情况,您想要使用的是('first_name', )。注意结尾的逗号。

【讨论】:

  • 谢谢,乔伊。我已经尝试过这种方法,虽然我确实从浏览器获得了结果,但是当我通过 curl 发送 POST 请求时它不起作用:curl -F Body='First Last' http://localhost:8000/page/incoming/message 也许这可能与我的观点有关。
【解决方案3】:

您可以在运行时覆盖 lookup_field,具体取决于您在 GET 参数中获得的属性。您不能为此使用 kwargs,只要您只想定义一个视图和 url。

示例网址:-

/sources/?parameter=first_name&first_name='someone'

相应地更改您的视图集:-

class SourceViewSet(viewsets.ModelViewSet):
     queryset = Source.objects.all()
     serializer_class = SourceSerializer
     lookup_field = ('first_name')

     def initial(self, request, *args, **kwargs):
         parameter = request.data.get('parameter')
         self.lookup_field = parameter
         kwargs[parameter] = request.data.get(parameter)

         super(SourceViewSet, self).initial(request, *args, **kwargs)

【讨论】:

  • 谢谢,但是如果两个人有名字会发生什么?我认为这不会正常工作。 @Joey Wilhelm 提到在 URL 中省略名字和姓氏,并专注于 ID。但是,我不知道如何在文本中发送“名字姓氏”,并让 API 将收到的名称与相应的 ID 对应起来。我想我可能不得不将我的模型修改为只是名称,其中包括名字和姓氏。
  • 您可以在序列化程序中创建一个 full_name 字段并使用该参数,从而维护模型中的封装。
  • 你也需要覆盖get_queryset
猜你喜欢
  • 2014-04-30
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 2018-05-21
  • 1970-01-01
  • 2022-01-10
  • 2013-11-15
  • 1970-01-01
相关资源
最近更新 更多