【问题标题】:reverse() in a test only returns relative URL in a test for django-rest-framework and that causes a 404测试中的 reverse() 仅在 django-rest-framework 测试中返回相对 URL,这会导致 404
【发布时间】:2020-12-21 20:41:26
【问题描述】:

我正在尝试使用this 指南测试我的 API 的端点。具体来说,这个块应该测试get请求:

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测试。

我正在使用 Django 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)),
]

views.py 中的DemanderCollectionViewSet 如下所示:

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', 'a@b.com', 'test_user')
        self.other_user = User.objects.create_user('other_user', 'a@b.com', '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()乐趣)
  • 我在测试代码块中添加了reverse() 的导入语句以及DemanderFeatureCollectionViewSet 的源代码
  • 在基本检查中,我可以看到您正在过滤带有一些 owner 字段的 get_queryset(),这可能是 404 错误的原因
  • 这似乎是问题的症结所在。我不仅没有以测试用户身份登录,而且在没有关联用户作为所有者的情况下创建了 DemanderFeatureCollection 对象(似乎我仍然允许创建无所有者 DemanderFeatureCollection 模型对象,这也是一个问题)。我在setUp() 方法中创建了一个测试用户,并在test_get_all_demandercollections() 方法中通过client.login() 登录到它。然后它作为例外工作如果您想让您的评论成为答案,我会接受它。

标签: django-rest-framework


【解决方案1】:

DemanderFeatureCollectionViewSet 类的 get_queryset() 方法中,您正在过滤具有 owner 字段的模型实例以针对 登录用户

在您的测试用例中,您正在创建 DemanderFeatureCollection 实例没有链接 user,因此 DRF 会引发 HTTP 404 错误。因此,将用户附加到实例并使用同一用户发出请求将为您提供来自 API 的正确响应。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-31
    • 2016-06-25
    • 1970-01-01
    • 2020-07-08
    • 1970-01-01
    • 2022-11-01
    • 1970-01-01
    • 2018-05-14
    相关资源
    最近更新 更多