Reverse() в тесте возвращает только относительный URL в тесте для django-rest-framework, что вызывает ошибку 404

Я пытаюсь протестировать конечные точки для своего API, используя это руководство. В частности, этот блок должен проверять запрос на получение:

class GetAllPuppiesTest(TestCase):
    """ Test module for GET all puppies API """

    def setUp(self):
        Puppy.objects.create(
            name='Casper', age=3, breed='Bull Dog', color='Black')
        Puppy.objects.create(
            name='Muffin', age=1, breed='Gradane', color='Brown')
        Puppy.objects.create(
            name='Rambo', age=2, breed='Labrador', color='Black')
        Puppy.objects.create(
            name='Ricky', age=6, breed='Labrador', color='Brown')

    def test_get_all_puppies(self):
        # get API response
        response = client.get(reverse('get_post_puppies'))
        # get data from db
        puppies = Puppy.objects.all()
        serializer = PuppySerializer(puppies, many=True)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Когда я пытаюсь адаптировать это к своему собственному тесту, это выглядит так:

from ..models import DemanderFeature, DemanderFeatureCollection

from rest_framework import status
from django.test import TestCase, Client
from django.urls import reverse

from ..serializers import DemanderFeatureCollectionSerializer

class GetAllDemanderFeatureCollections(TestCase):

    def setUp(self):
        DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection0')
        DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection1')

    def test_get_all_demandercollections(self):
        # get API response
        response = client.get(reverse('demandercollections-list'))
        # get data from db
        demanderfeaturecollections = DemanderFeatureCollection.objects.all()
        serializer = DemanderFeatureCollectionSerializer(demanderfeaturecollections, many=True)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Однако проблема заключается в том, что метод reverse() возвращает только относительный URL-адрес (/demandercollections/), а затем client.get(reverse(...)) возвращает 404. Я не понимаю, как заставить его использовать фактический явный URL-адрес во время тестирования.

Я использую Джанго 3.

Мой основной urls.py выглядит так:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include("app.urls")),
    path('api-auth/', include('rest_framework.urls')),
]

И мой модуль urls.py выглядит так:

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from app import views

# Create a router and register our viewsets with it.
router = DefaultRouter()
router.register(r'demanders', views.DemanderFeatureViewSet)
router.register(r'demandercollections', views.DemanderFeatureCollectionViewSet, basename = "demandercollections")
router.register(r'producers', views.ProducerFeatureViewSet)
router.register(r'producercollections', views.ProducerFeatureCollectionViewSet)
router.register(r'pathfinderrunconfigurations', views.PathfinderRunConfigurationViewSet)
router.register(r'users', views.UserViewSet)

# The API URLs are now determined automatically by the router.
urlpatterns = [
    path('', include(router.urls)),
]

DemanderCollectionViewSet в views.py выглядит так:

class DemanderFeatureCollectionViewSet(
    mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.ListModelMixin,
    viewsets.GenericViewSet
):

    queryset = DemanderFeatureCollection.objects.all()
    serializer_class = DemanderFeatureCollectionSerializer
    lookup_field = 'name'

    @action(detail=True, methods=["get"])
    def geojson(self, request, *args, **kwargs):
        demanders = DemanderFeature.objects.filter(demandercollection=self.get_object())
        return Response(serialize('geojson', demanders, geometry_field='geom', fields=('name',)))

    @action(detail=True, methods=["patch"])
    def commit(self, request, *args, **kwargs):
        demandercollection = self.get_object()
        if not request.data["committed"]:
            # User is trying to "uncommit", do not allow this
            return Response("You may not un-commit a DemanderCollection. You must copy it and make modifications on the copy.", status=status.HTTP_400_BAD_REQUEST)
        demandercollection.committed = True
        demandercollection.save()
        return Response(status=status.HTTP_204_NO_CONTENT)

    def get_queryset(self):
        user = get_object_or_404(User, username=self.request.user)
        return DemanderFeatureCollection.objects.filter(deleted=False).filter(owner=user).order_by("name")

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

    def destroy(self, request, *args, **kwargs):
        demandercollection = self.get_object()
        demandercollection.deleted = True
        demandercollection.save()
        return Response(f"Successfully deleted DemanderCollection.")

ПОСТАВИТЬ ОТВЕТ РЕДАКТИРОВАТЬ

Мало того, что принятый ответ действительно был виновником, но он также показал, что создаваемые объекты DemanderFeatureCollection также должны быть созданы с атрибутом owner, а объект client должен вызывать свой метод login() для действительной пары учетных данных пользователя.

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

class GetAllDemanderFeatureCollections(TestCase):
    """ Test module for GET all puppies API """

    def setUp(self):
        self.test_user = User.objects.create_user('test_user', '[email protected]', 'test_user')
        self.other_user = User.objects.create_user('other_user', '[email protected]', 'other_user')
        client.login(username = "test_user", password = "test_user")
        DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection0', owner=self.test_user)
        DemanderFeatureCollection.objects.create(name='testdemanderfeaturecollection1', owner=self.test_user)
        DemanderFeatureCollection.objects.create(name='otherdemanderfeaturecollection0', owner=self.other_user)

    def test_get_all_demandercollections_for_user(self):
        # get API response
        response = client.get(reverse('demandercollections-list'))
        # get data from db
        demanderfeaturecollections = DemanderFeatureCollection.objects.filter(owner=self.test_user).all()
        serializer = DemanderFeatureCollectionSerializer(demanderfeaturecollections, many=True)
        self.assertEqual(response.data, serializer.data)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Добавьте свой класс DemanderFeatureCollectionViewSet, а также ссылку на функцию reverse(), которую вы использовали (дело в том, что есть два reverse() удовольствия)

JPG 21.12.2020 12:28

Я добавил оператор импорта для reverse() в тестовый блок кода, а также источник DemanderFeatureCollectionViewSet

wfgeo 21.12.2020 12:35

при базовой проверке я вижу, что вы фильтруете get_queryset() с некоторым полем owner, и это может быть причиной ошибки 404.

JPG 21.12.2020 12:44

Похоже, в этом была суть вопроса. Я не только не регистрировался в качестве тестового пользователя, но и объекты DemanderFeatureCollection создавались без связанного пользователя в качестве владельца (кажется, я все еще разрешал создавать бесхозные объекты модели DemanderFeatureCollection, что также является проблемой). Я создал тестового пользователя в методе setUp() и вошел в него через client.login() в методе test_get_all_demandercollections(). Тогда это сработало как исключение. Если вы хотите сделать свой комментарий ответом, я приму его.

wfgeo 21.12.2020 12:56
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
847
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В методе get_queryset() класса DemanderFeatureCollectionViewSet вы фильтруете экземпляры модели с полем owner по вошедшему в систему пользователю.

В ваших тестовых примерах вы создаете экземпляры DemanderFeatureCollection без привязки user и, следовательно, DRF вызывает ошибку HTTP 404. Таким образом, присоединение пользователя к экземпляру и выполнение запроса с тем же пользователем даст вам правильный ответ от API.

Другие вопросы по теме

Как использовать каналы Django для передачи вновь созданных данных на сторону клиента без перезагрузки и без полного изменения существующего кода обычного просмотра
Django - добавить контекст к запросу, который будет использоваться представлением
Запрещен HTTP 403 DRF + ReactJS request.session
Как удалить файл в python внутри контейнера докеров?
Джанго REST-фреймворк. Как получить полный URL-адрес страницы с протоколом, доменом и путем?
DRF решает циклическую зависимость импорта вложенных сериализаторов
Каков процесс использования JWT во внешнем интерфейсе?
Создание нескольких типов пользователей в django rest framework получило неожиданный ключевой аргумент 'last_login'
Джанго + ReactJS. Файл cookie Httponly не сохраняется в браузере на стороне React. Я также дал {withcredentials : true} с обеих сторон
Django Rest Framework Дополнительные поля kwargs для пароля и уникального адреса электронной почты