React.js 前端的 Django REST 框架后端过滤问题
Django REST framework backend filtering issues with React.js frontend
所以我正在尝试组装一个不那么简单的 Todo 应用程序,使用一些可能是多余的实践和技术来展示开发技能。该应用程序是一种 Trello 副本。后端是 Django,利用 django REST 框架处理来自前端 React.js 的 RESTful api 请求。简而言之,我的 django_backend 遇到了问题,可能特别是 django-rest-framework,它似乎不喜欢被要求过滤其查询集。我已经尝试了几个 django 包以及 DRF 文档中描述的方法,我将对此进行详细说明,但我的尝试没有奏效,我希望得到一些指导。谢谢!
后端设置
我刚刚完成了 redux-saga 的实现,我有一些非常基本的 sagas,可以获取每个模型的每个实例。但是我显然不想请求每个实例,所以我们应该过滤响应。
Django REST 框架使用内置的 class 序列化程序和 ViewSets 到 return 模型实例列表,rest-framework 可以用这些列表响应请求。这些是我用于测试后端请求的初始视图集,它们都 return 将适当的 JSON 对象编辑到我的前端:
viewsets.py
from rest_framework import viewsets
from corkboard.models import Card, Stage, Board
from .serializers import CardSerializer, StageSerializer, BoardSerializer
# Card is the model for a "Todo" Instance
# Todo Cards viewset
class CardViewSet(viewsets.ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
# Stages of completion model viewsets
class StageViewSet(viewsets.ModelViewSet):
queryset = Stage.objects.all()
serializer_class = StageSerializer
# Board that "houses" all stages and cards
class BoardViewSet(viewsets.ModelViewSet):
queryset = Board.objects.all()
serializer_class = BoardSerializer
viewseturls.py:
from rest_framework import routers
from .views import CardViewSet, StageViewSet, BoardViewSet
# registering router paths for each of the viewsets
router = routers.DefaultRouter()
router.register('cards', CardViewSet, 'cards')
router.register('stages', StageViewSet, 'stages')
router.register('boards', BoardViewSet, 'boards')
urlpatterns = router.urls
并且这些 url 用于前端通过 axios 的调用。最初我尝试简单地将请求中的 url 更改为
axios.get(`http://localhost:8000/api/cards)
到
axios.get(`http://localhost:800/api/cards/?stage=1`)
这个不同的 url 响应相同的 JSON 对象(所有 Card 实例,无论阶段如何)
查看 DRF filtering docs 发现了一些似乎有用的过滤方法。它们涉及创建另一个视图 class 来重写 get_queryset 方法:
from rest-framework import generics
class CardsFilteredByStageViewSet(generics.ListAPIView):
serializer_class = TodoSerializer
def get_queryset(self):
"""
This view should return a list of all the todo cards for
the stage as determined by the stage portion of the URL.
"""
stage = self.kwargs['stage']
return Card.objects.filter(stage=stage)
请注意,这涉及使用
rest-framework.generics.ListAPIView
而且我还尝试继承自
views.ViewSets
两者之后,我去编辑 urls.py
中的路由器 url:
from .views import StageCardViewSet
router.register('stage_cards', StageCardViewSet, 'stage_cards')
然后尝试
http://localhost:8000/api/stage_cards/
和
http://localhost:8000/api/stage_cards/?stage=1
但是两者都出现了 KeyError:
Request Method: GET
Request URL: http://localhost:8000/api/stage_cards/?stage=1
Django Version: 3.0.5
Exception Type: KeyError
Exception Value: 'stage'
DRF 文档还提供了 django-filter 等包的源代码,安装似乎很顺利。我用了
pipenv install django-filter
正如您稍后在本 post 中看到的那样,此软件包出现在我的 requirements.txt 以及我的 Pipfile 中,因此它肯定已安装。它也被添加到 django_backend.settings。 :
INSTALLED_APPS = [
# confusing pluralization
'django-filters',
...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
],
...
}
如果没有作为包添加,应该会报错,对吗?
将其添加为已安装的应用程序后,开发现有的视图集(或通用列表,具体取决于您使用的是什么)
from django_filters.rest_framework import DjangoFilterBackend #added
class CardViewSet(viewsets.ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
filter_backends = [DjangoFilterBackend] #added
filterset_fields = ['stage', 'id'] #added
但我在 IDE 中收到“无法导入”错误。探索这个错误揭示了 which suggested addition of django-rest-framework-filters, but immediately after installation, I began getting errors about not having django.utils.six, which I found has been removed from django > 3,但是 django-rest-framework-filters 是必需的。尽管其他一些文档提到了很多 d-r-r-f 功能被添加回 django-filter。 Removing/uninstalling d-r-f-f 修复了错误并允许服务器 运行,但我仍然收到无法将 django-filter 导入我的视图集的红色下划线:
Unable to import 'django_filters.rest_framework'pylint(import-error)
在更新的视图中:
from django_filters.rest_framework import DjangoFilterBackend
#^^^ here is the unable to import error underline
我只是想过滤我的模型实例,这样我就可以 return 将相关信息发送到前端。请帮忙。
额外的,可能是相关的信息
尽管我的项目还包含一个用于本地 VDE 的 Pipfile,它还有一个 requirements.txt 文件用于安装 Docker 构建的依赖项。我把两者都包括在内。
requirements.txt:
appdirs==1.4.3
asgiref==3.2.7
astroid==2.3.3
certifi==2019.11.28
distlib==0.3.0
Django==3.0.5
django-environ==0.4.5
django-filter==2.2.0
djangorestframework==3.11.0
filelock==3.0.12
isort==4.3.21
lazy-object-proxy==1.4.3
mccabe==0.6.1
pipenv==2018.11.26
pylint==2.4.4
pylint-django==2.0.15
pylint-plugin-utils==0.6
pytz==2019.3
six==1.14.0
sqlparse==0.3.1
virtualenv==20.0.2
virtualenv-clone==0.5.3
wrapt==1.11.2
Pip文件:
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
appdirs = "==1.4.3"
asgiref = "==3.2.7"
astroid = "==2.3.3"
certifi = "==2019.11.28"
distlib = "==0.3.0"
djangorestframework = "==3.11.0"
filelock = "==3.0.12"
isort = "==4.3.21"
lazy-object-proxy = "==1.4.3"
mccabe = "==0.6.1"
pipenv = "==2018.11.26"
pylint = "==2.4.4"
pytz = "==2019.3"
six = "==1.14.0"
sqlparse = "==0.3.1"
virtualenv = "==20.0.2"
virtualenv-clone = "==0.5.3"
wrapt = "==1.11.2"
Django = "==3.0.5"
django-environ = "*"
django-filter = "*"
[requires]
python_version = "3.8"
Docker文件 django_backend:
# Use an official Python runtime as a parent image
FROM python:latest
# Adding backend directory to make absolute filepaths consistent across services
WORKDIR /usr/src/app/django-backend
# Install Python dependencies
ADD requirements.txt .
RUN pip3 install --upgrade pip -r requirements.txt
# Add the rest of the code
ADD . .
# Make port 8000 available for the app
EXPOSE 8000
# Be sure to use 0.0.0.0 for the host within the Docker container,
# otherwise the browser won't be able to find it
CMD python3 manage.py runserver 0.0.0.0:8000
前端
前端是 React.js,带有 redux 和 redux-saga。我不认为它与此 post 相关,因为在我开始更改 django_backend
中的代码之前一切都运行良好
当然,如果我不够清楚,请提出任何问题,在此先感谢。
我的第一个 post 因一个愚蠢的错误而告终。自己回答,因为为什么不呢。
pylint 错误是一个很大的标志。这可能只是一个 IDE 配置问题,如果我能够使用 python manage.py runserver
在本地托管而没有任何错误,那么您可能正确地实施了它。如果我得到的只是一个 pylint 错误,那么无论如何都要尝试 运行。正如你所说,它 运行 在删除 django-rest-framework-filters 后没有错误。
我给出的代码部分似乎是 django-filter 的正确实现,因为 DRF 文档很好地解释了如何使用它进行基本过滤。他们还推荐另一个名为 django-rest-framework-filters 的包,但我知道 django.utils.six
是 存在问题。该线程的评论之一包括通过安装据称与 Django 3.0.4 一起使用的 six
包进行修复,因此我将探讨实例相互关系是否对过滤很重要。
确实,我已经正确地实现了它。我的猜测是我错过了它,因为这个过程中有一些其他错误重叠,而 pylint 错误让我失去了灵感。我只是错过了它实际上是 运行;我只是没有测试它的功能。我也想开始使用 Whosebug 作为开发资源,但通过这个过程让我找到了答案。好笑。
重申正确的执行程序:
pipenv install django-filter
添加到django_backend.settings:
INSTALLED_APPS = [
# confusing pluralization
'django-filters',
...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
],
...
}
和视图集重构:
from django_filters.rest_framework import DjangoFilterBackend # added
# Todo Card viewset now filterable via filterset_fields
class CardViewSet(viewsets.ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
filter_backends = [DjangoFilterBackend] # added
filterset_fields = ['stage', 'title', 'id'] # added
现在在 python manage.py runserver
之后向右转 url (http://localhost:8000/api/cards/?stage=1),你应该只会看到阶段 1 中的卡片。同样的 url 结构也有望成为可从前端调用。但是django rest framework UI returns 一个过滤列表。成功。
通过转到我的 IDE 的 settings.json
文件(恰好是 VSCode,尽管我正在考虑替代方案)并进行调整,可以轻松解决 pylint 错误:
{
"python.PythonPath": "/user/local/bin", // added
...
}
导入错误消失。
所以我正在尝试组装一个不那么简单的 Todo 应用程序,使用一些可能是多余的实践和技术来展示开发技能。该应用程序是一种 Trello 副本。后端是 Django,利用 django REST 框架处理来自前端 React.js 的 RESTful api 请求。简而言之,我的 django_backend 遇到了问题,可能特别是 django-rest-framework,它似乎不喜欢被要求过滤其查询集。我已经尝试了几个 django 包以及 DRF 文档中描述的方法,我将对此进行详细说明,但我的尝试没有奏效,我希望得到一些指导。谢谢!
后端设置
我刚刚完成了 redux-saga 的实现,我有一些非常基本的 sagas,可以获取每个模型的每个实例。但是我显然不想请求每个实例,所以我们应该过滤响应。
Django REST 框架使用内置的 class 序列化程序和 ViewSets 到 return 模型实例列表,rest-framework 可以用这些列表响应请求。这些是我用于测试后端请求的初始视图集,它们都 return 将适当的 JSON 对象编辑到我的前端: viewsets.py
from rest_framework import viewsets
from corkboard.models import Card, Stage, Board
from .serializers import CardSerializer, StageSerializer, BoardSerializer
# Card is the model for a "Todo" Instance
# Todo Cards viewset
class CardViewSet(viewsets.ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
# Stages of completion model viewsets
class StageViewSet(viewsets.ModelViewSet):
queryset = Stage.objects.all()
serializer_class = StageSerializer
# Board that "houses" all stages and cards
class BoardViewSet(viewsets.ModelViewSet):
queryset = Board.objects.all()
serializer_class = BoardSerializer
viewseturls.py:
from rest_framework import routers
from .views import CardViewSet, StageViewSet, BoardViewSet
# registering router paths for each of the viewsets
router = routers.DefaultRouter()
router.register('cards', CardViewSet, 'cards')
router.register('stages', StageViewSet, 'stages')
router.register('boards', BoardViewSet, 'boards')
urlpatterns = router.urls
并且这些 url 用于前端通过 axios 的调用。最初我尝试简单地将请求中的 url 更改为
axios.get(`http://localhost:8000/api/cards)
到
axios.get(`http://localhost:800/api/cards/?stage=1`)
这个不同的 url 响应相同的 JSON 对象(所有 Card 实例,无论阶段如何)
查看 DRF filtering docs 发现了一些似乎有用的过滤方法。它们涉及创建另一个视图 class 来重写 get_queryset 方法:
from rest-framework import generics
class CardsFilteredByStageViewSet(generics.ListAPIView):
serializer_class = TodoSerializer
def get_queryset(self):
"""
This view should return a list of all the todo cards for
the stage as determined by the stage portion of the URL.
"""
stage = self.kwargs['stage']
return Card.objects.filter(stage=stage)
请注意,这涉及使用
rest-framework.generics.ListAPIView
而且我还尝试继承自
views.ViewSets
两者之后,我去编辑 urls.py
中的路由器 url:
from .views import StageCardViewSet
router.register('stage_cards', StageCardViewSet, 'stage_cards')
然后尝试
http://localhost:8000/api/stage_cards/
和
http://localhost:8000/api/stage_cards/?stage=1
但是两者都出现了 KeyError:
Request Method: GET
Request URL: http://localhost:8000/api/stage_cards/?stage=1
Django Version: 3.0.5
Exception Type: KeyError
Exception Value: 'stage'
DRF 文档还提供了 django-filter 等包的源代码,安装似乎很顺利。我用了
pipenv install django-filter
正如您稍后在本 post 中看到的那样,此软件包出现在我的 requirements.txt 以及我的 Pipfile 中,因此它肯定已安装。它也被添加到 django_backend.settings。
INSTALLED_APPS = [
# confusing pluralization
'django-filters',
...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
],
...
}
如果没有作为包添加,应该会报错,对吗?
将其添加为已安装的应用程序后,开发现有的视图集(或通用列表,具体取决于您使用的是什么)
from django_filters.rest_framework import DjangoFilterBackend #added
class CardViewSet(viewsets.ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
filter_backends = [DjangoFilterBackend] #added
filterset_fields = ['stage', 'id'] #added
但我在 IDE 中收到“无法导入”错误。探索这个错误揭示了
Unable to import 'django_filters.rest_framework'pylint(import-error)
在更新的视图中:
from django_filters.rest_framework import DjangoFilterBackend
#^^^ here is the unable to import error underline
我只是想过滤我的模型实例,这样我就可以 return 将相关信息发送到前端。请帮忙。
额外的,可能是相关的信息
尽管我的项目还包含一个用于本地 VDE 的 Pipfile,它还有一个 requirements.txt 文件用于安装 Docker 构建的依赖项。我把两者都包括在内。
requirements.txt:
appdirs==1.4.3
asgiref==3.2.7
astroid==2.3.3
certifi==2019.11.28
distlib==0.3.0
Django==3.0.5
django-environ==0.4.5
django-filter==2.2.0
djangorestframework==3.11.0
filelock==3.0.12
isort==4.3.21
lazy-object-proxy==1.4.3
mccabe==0.6.1
pipenv==2018.11.26
pylint==2.4.4
pylint-django==2.0.15
pylint-plugin-utils==0.6
pytz==2019.3
six==1.14.0
sqlparse==0.3.1
virtualenv==20.0.2
virtualenv-clone==0.5.3
wrapt==1.11.2
Pip文件:
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
appdirs = "==1.4.3"
asgiref = "==3.2.7"
astroid = "==2.3.3"
certifi = "==2019.11.28"
distlib = "==0.3.0"
djangorestframework = "==3.11.0"
filelock = "==3.0.12"
isort = "==4.3.21"
lazy-object-proxy = "==1.4.3"
mccabe = "==0.6.1"
pipenv = "==2018.11.26"
pylint = "==2.4.4"
pytz = "==2019.3"
six = "==1.14.0"
sqlparse = "==0.3.1"
virtualenv = "==20.0.2"
virtualenv-clone = "==0.5.3"
wrapt = "==1.11.2"
Django = "==3.0.5"
django-environ = "*"
django-filter = "*"
[requires]
python_version = "3.8"
Docker文件 django_backend:
# Use an official Python runtime as a parent image
FROM python:latest
# Adding backend directory to make absolute filepaths consistent across services
WORKDIR /usr/src/app/django-backend
# Install Python dependencies
ADD requirements.txt .
RUN pip3 install --upgrade pip -r requirements.txt
# Add the rest of the code
ADD . .
# Make port 8000 available for the app
EXPOSE 8000
# Be sure to use 0.0.0.0 for the host within the Docker container,
# otherwise the browser won't be able to find it
CMD python3 manage.py runserver 0.0.0.0:8000
前端
前端是 React.js,带有 redux 和 redux-saga。我不认为它与此 post 相关,因为在我开始更改 django_backend
中的代码之前一切都运行良好当然,如果我不够清楚,请提出任何问题,在此先感谢。
我的第一个 post 因一个愚蠢的错误而告终。自己回答,因为为什么不呢。
pylint 错误是一个很大的标志。这可能只是一个 IDE 配置问题,如果我能够使用 python manage.py runserver
在本地托管而没有任何错误,那么您可能正确地实施了它。如果我得到的只是一个 pylint 错误,那么无论如何都要尝试 运行。正如你所说,它 运行 在删除 django-rest-framework-filters 后没有错误。
我给出的代码部分似乎是 django-filter 的正确实现,因为 DRF 文档很好地解释了如何使用它进行基本过滤。他们还推荐另一个名为 django-rest-framework-filters 的包,但我知道 django.utils.six
是 six
包进行修复,因此我将探讨实例相互关系是否对过滤很重要。
确实,我已经正确地实现了它。我的猜测是我错过了它,因为这个过程中有一些其他错误重叠,而 pylint 错误让我失去了灵感。我只是错过了它实际上是 运行;我只是没有测试它的功能。我也想开始使用 Whosebug 作为开发资源,但通过这个过程让我找到了答案。好笑。
重申正确的执行程序:
pipenv install django-filter
添加到django_backend.settings:
INSTALLED_APPS = [
# confusing pluralization
'django-filters',
...
]
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_filters.rest_framework.DjangoFilterBackend',
],
...
}
和视图集重构:
from django_filters.rest_framework import DjangoFilterBackend # added
# Todo Card viewset now filterable via filterset_fields
class CardViewSet(viewsets.ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
filter_backends = [DjangoFilterBackend] # added
filterset_fields = ['stage', 'title', 'id'] # added
现在在 python manage.py runserver
之后向右转 url (http://localhost:8000/api/cards/?stage=1),你应该只会看到阶段 1 中的卡片。同样的 url 结构也有望成为可从前端调用。但是django rest framework UI returns 一个过滤列表。成功。
通过转到我的 IDE 的 settings.json
文件(恰好是 VSCode,尽管我正在考虑替代方案)并进行调整,可以轻松解决 pylint 错误:
{
"python.PythonPath": "/user/local/bin", // added
...
}
导入错误消失。