Проблемы с внутренней фильтрацией инфраструктуры Django REST с интерфейсом React.js

Итак, я пытаюсь собрать не очень простое приложение Todo, используя некоторые, возможно, лишние методы и технологии, чтобы продемонстрировать навыки разработки. Приложение задумано как своего рода реплика Trello. Бэкэнд - это Django, использующий структуру Django REST для запросов RESTful api из внешнего интерфейса, которым является React.js. Короче говоря, у меня проблемы с моим django_backend и, возможно, с django-rest-framework, который, похоже, не любит, когда его просят фильтровать его наборы запросов. Я пробовал несколько пакетов django, а также метод, описанный в документации DRF, который я подробно расскажу, но мои попытки не работают, и я был бы признателен за некоторые рекомендации. Спасибо!

Настройка бэкэнда

Я только что завершил реализацию redux-saga, и у меня есть несколько очень простых саг, которые получают КАЖДЫЙ экземпляр каждой из моделей. Но я, очевидно, не хочу запрашивать каждый экземпляр, поэтому мы должны фильтровать ответ.

Фреймворк Django REST использует встроенные классы, такие как сериализаторы и ViewSets, для возврата списков экземпляров модели, с помощью которых фреймворк rest может отвечать на запрос. Это были мои начальные представления для тестирования запросов к бэкэнду, и все они вернули соответствующий объект 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)

to

axios.get(`http://localhost:800/api/cards/?stage=1`)

Этот другой URL-адрес ответил одним и тем же объектом JSON (ВСЕ экземпляры карты, независимо от стадии)

Просмотр документов DRF-фильтрации показал, что некоторые методы фильтрации кажутся полезными. . Они включают создание другого класса представления для переопределения метода 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

После обоих я отредактирую URL-адрес маршрутизатора в urls.py:

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

Как вы увидите позже в этом посте, этот пакет присутствует в моем файле 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. Изучение этой ошибки выявило эту статью, в которой предлагалось добавить django-rest -framework-filters, но сразу после установки я начал получать сообщения об отсутствии django.utils.six, которые я обнаружил удален из django› 3, но необходим для django-rest-framework-filters. Хотя в некоторых других документах упоминается множество функций d-r-r-f, добавляемых обратно в django-filter. Удаление / удаление 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 

Я просто хочу отфильтровать экземпляры моих моделей, чтобы я мог вернуть соответствующую информацию во внешний интерфейс. Пожалуйста помоги.

Дополнительная, возможно, актуальная информация

Хотя мой проект также содержит файл Pipfile для моего локального VDE, в нем также есть файл 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

Pipfile:

[[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"

Dockerfile для 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. Я не верю, что это имеет отношение к этому сообщению, поскольку все работало хорошо, пока я не начал изменять код в django_backend

Конечно, задавайте любые вопросы, если я не достаточно ясна, и заранее спасибо.


person Dahlrad333    schedule 21.05.2020    source источник


Ответы (1)


Мой первый пост оказался из-за глупой ошибки. Отвечаю на него сам, потому что почему бы и нет.

Ошибка pylint - это большой флаг. Это может быть просто проблема конфигурации IDE, и если я могу размещать локально с python manage.py runserver без каких-либо ошибок, возможно, вы реализовали это правильно. Если все, что я получаю, это ошибка pylint, все равно попробуйте запустить ее. Как вы сказали, он работал без ошибок сразу после удаления django-rest-framework-filters.

Разделы кода, которые я привел, казались правильной реализацией django-filter, поскольку документы DRF неплохо объясняют, как использовать его для базовой фильтрации. Они также рекомендуют другой пакет под названием django-rest-framework-filters, как уже упоминалось, но я понимаю, что есть проблемы с django.utils.six, являющимся удалено из Django> 3. Один из комментариев этого потока включает исправление путем установки пакета six, который якобы работает с Django 3.0.4, поэтому я хотел бы изучить это, если взаимосвязь экземпляров важна для фильтрации.

Действительно, я реализовал это правильно. Я предполагаю, что я пропустил это из-за некоторого наложения других ошибок в процессе, и ошибка pylint сбила меня с толку. Я просто упустил из виду, что он действительно работает; Просто не тестировал его работоспособность. Я также хотел начать использовать Stackoverflow в качестве ресурса для разработки, но, пройдя через этот процесс, я пришел к ответу. Смешной.

Повторяю правильную процедуру реализации:

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 возвращает отфильтрованный список. Успех.

Ошибка pylint была легко решена путем перехода к settings.json файлу моей среды IDE (который оказался VSCode, хотя я рассматриваю альтернативы) и соответствующей настройки:

{
    "python.PythonPath": "/user/local/bin", // added
    ...
}

Ошибка импорта исчезла.

person Dahlrad333    schedule 21.05.2020