如何使用 django rest 框架从 GET 请求的查询参数中过滤多个 id?

Posted

技术标签:

【中文标题】如何使用 django rest 框架从 GET 请求的查询参数中过滤多个 id?【英文标题】:How to filter for multiple ids from a query param on a GET request with django rest framework? 【发布时间】:2015-12-28 20:10:10 【问题描述】:

我正在尝试制作一个网络应用 API。我想提出一个可以提交多个 id 的 API 请求。

django rest framework tutorial 展示了如何从模型中获取所有记录。例如http://127.0.0.1:8000/snippets/ 将返回所有的 sn-p 记录。本教程还展示了如何从模型中检索单个项目。 http://127.0.0.1:8000/snippets/2/ 将只返回 pk=2 的 sn-p 记录。

我希望能够请求多条记录,但不是所有记录。

如何更改此代码以便请求多个 sn-ps?

sn-ps/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

sn-ps/views.py

def snippet_detail(request, *pk):
    try:
        snippet = Snippet.objects.filter(pk__in=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JSONResponse(serializer.data)

【问题讨论】:

多个sn-ps基于什么?您拉或不拉 sn-p 的标准是什么? @Gocht 所以喜欢...127.0.0.1:8000/snippets/2;3;4 如果我想要 ID 为 2、3 和 4 的 sn-ps 【参考方案1】:

django-filter 文档给出了一个很好的例子:https://django-filter.readthedocs.io/en/stable/ref/filters.html?highlight=ids#baseinfilter

对于您的情况:

class SnippetFilterSet(BaseInFilter, NumberFilter):
    pass

class F(FilterSet):
    ids = NumberInFilter(field_name='id', lookup_expr='in')

    class Meta:
        model = Snippet

【讨论】:

【参考方案2】:

您可以使用django-filter 并将其设置为过滤器,避免在视图集上过度使用 get_queryset 方法

鉴于此要求

/api/snippets/?ids=1,2,3,4

然后写一个django过滤器集和方法

import django_filters


def filter_by_ids(queryset, name, value):
    values = value.split(',')
    return queryset.filter(id__in=values)


class SnippetFilterSet(django_filters.FilterSet):
   ids = django_filters.CharFilter(method=filter_by_ids)
    
   class Meta:
      model = Snippet
      fields = ['ids']

然后在你的 ModelViewSet 中

from rest_framework.viewsets import ModelViewSet
from app.snippets.models import Snippet
from app.snippets.filters import SnippetFilterSet # path to filterset

class SnippetView(ModelViewSet):
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    filterset_class = SnippetFilterSet

【讨论】:

我已经有其他用字典语法指定的fields。例如。 fields = 'foo': ('lt', 'lte', 'exact', 'gt', 'gte') 。如何将ids 整合到那个字典中?【参考方案3】:

虽然没有明确的标准,但大多数 Web 框架允许将多个值与单个字段相关联(例如 field1=value1&field1=value2&field2=value3)。

Django 也支持这个。 要获取值列表,您可以使用:ids = request.GET.getlist('ids')。 来自MultiValueDict docs 的更多示例:

    >>> d = MultiValueDict('name': ['Adrian', 'Simon'], 'position': ['Developer'])
    >>> d['name']
    'Simon'
    >>> d.getlist('name')
    ['Adrian', 'Simon']
    >>> d.getlist('doesnotexist')
    []
    >>> d.getlist('doesnotexist', ['Adrian', 'Simon'])
    ['Adrian', 'Simon']
    >>> d.get('lastname', 'nonexistent')
    'nonexistent'
    >>> d.setlist('lastname', ['Holovaty', 'Willison'])

【讨论】:

【参考方案4】:

按照 Django REST Framework 主要教程和Filtering against query parameters 上的文档,我发现这可以工作,稍作调整。这允许单个 url 从两个 GET 请求返回数据:一个返回其 id 与作为参数给出的对象匹配的对象,另一个在未提供参数时返回所有对象。

http://127.0.0.1:8000/snippets/ 返回所有 sn-ps http://127.0.0.1:8000/snippets/?ids=2,3,7 仅返回 id 为 2、3 和 7 的 sn-ps

sn-ps/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
   .... (code for other urls here)
   url(r'^snippets/$', views.SnippetList.as_view(), name='snippet-list'),
   ....
]

sn-ps/views.py

.... 
from snippet.serializers import SnippetSerializer
....

class SnippetList(generics.ListCreateAPIView):
    serializer_class = SnippetSerializer

    def get_queryset(self):

        # Get URL parameter as a string, if exists 
        ids = self.request.query_params.get('ids', None)

        # Get snippets for ids if they exist
        if ids is not None:
            # Convert parameter string to list of integers
            ids = [ int(x) for x in ids.split(',') ]
            # Get objects for all parameter ids 
            queryset = Product.objects.filter(pk__in=ids)

        else:
            # Else no parameters, return all objects
            queryset = Product.objects.all()

        return queryset

sn-ps/serializers.py

....
class SnippetSerializer(serializers.ModelSerializer):

    class Meta:
        model = Snippet
        fields = ('url', 'id', 'title', 'code', 'linenos', 'language', 'style')

【讨论】:

谢谢!这也适用于 viewsets.ModelViewSet 类,您只需要在注册路由器时添加参数'base_name',如下所示:router.register(question', views.QuestionViewSet, base_name='question')【参考方案5】:

这就是我最终的结果:

没有改变 sn-p/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

http://127.0.0.1:8000/snippets/?ids=2,3,4 被接收

sn-p/views.py

from rest_framework.decorators import api_view

@api_view(['GET', 'POST'])    
def snippet_list(request):
    if request.method == 'GET':
        ids = request.query_params.get('ids')  # u'2,3,4' <- this is unicode
        ids = ids.split(',')
        snippets = Snippet.objects.filter(pk__in=ids)
        serializer = SnippetSerializer(snippet, many=True)
        return JSONResponse(serializer.data)

【讨论】:

【参考方案6】:

根据您的评论,您可以通过 url 发送 ID:

127.0.0.1:8000/snippets/?ids=2,3,4

在你看来

...
ids = request.GET.get('ids')  # u'2,3,4' <- this is unicode
ids = ids.split(',')  # [u'2',u'3',u'4'] <- this is a list of unicodes with ids values

然后可以查询到 Snippet 模型:

Snippet.objects.filter(pk__in=ids)

如果 url 中的 id 之间有空格,这可能会给您带来一些问题:

127.0.0.1:8000/snippets/?ids=2, 3 , 4

您可能需要在执行查询之前处理每个值

【讨论】:

【参考方案7】:

一种可能的方法是将 pk(s) 列表作为 GET 请求数据发送,如下所示:

GET 请求到“/sn-ps”

请求正文:"list_of_pk": [1,2,3...]

然后:

sn-ps/urls.py

from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]

sn-ps/views.py

def snippet_list(request):
    if request.method == 'GET':
        pk_list = request.GET.get('list_of_pk')
        if pk_list:
            snippets = Snippet.objects.filter(pk__in=pk_list)
        else:
            snippets = Snippet.objects.all()
        #the serialization...

【讨论】:

以上是关于如何使用 django rest 框架从 GET 请求的查询参数中过滤多个 id?的主要内容,如果未能解决你的问题,请参考以下文章

Django Rest 框架中的发布请求处理

使用 django-rest 框架中的 GET 方法将 url 作为参数传递?

我们如何使用基于函数的视图在 django rest 框架中发布数据?

Django rest 框架和跨源请求

无法从 reactjs 中的 axios 向 id=1 文章的 django REST API 后端发出 GET 请求

如何从 django rest 框架访问 jwt 令牌到 Angular 前端