路由器注册顺序影响输出

时间:2018-08-12 07:32:54

标签: python django django-rest-framework

我在尝试使用django rest框架实现Web服务时遇到了一个奇怪的问题。我有这两个API-一个用于根据类别(作为URL参数提供)获取新闻列表,另一个用于在提供新闻ID(作为url参数提供)的情况下获取新闻的详细信息。以下是我的应用的urls.py代码:

from django.urls import path
from rest_framework import routers
...
router = routers.DefaultRouter()
router.register('news_contents', NewsContentViewSet)
router.register('news_infos', NewsInfoViewSet)
router.register('categories', CategoryViewSet)
router.register(r'^articles/(?P<category_name>[-\w]+)', NewsItemViewSet, base_name="NewsInfo")
router.register(r'^articles/(?P<news_id>\d+)/details', NewsDetailViewSet, base_name="NewsInfo")

urlpatterns = router.urls

上面的调用代码: http://localhost:8000/rest/articles/categoryname 返回正确的输出,但调用: http://localhost:8000/rest/articles/4057/details/ 返回以下内容:

{"detail":"Not found."}

但是当我更改这两个API的注册顺序时,两个API都可以按预期工作。

工作urls.py

from django.urls import path
from rest_framework import routers
...
router = routers.DefaultRouter()
router.register('news_contents', NewsContentViewSet)
router.register('news_infos', NewsInfoViewSet)
router.register('categories', CategoryViewSet)
router.register(r'^articles/(?P<news_id>\d+)/details', NewsDetailViewSet, base_name="NewsInfo") #brought to above the listing API
router.register(r'^articles/(?P<category_name>[-\w]+)', NewsItemViewSet, base_name="NewsInfo")

urlpatterns = router.urls

为什么会这样?我感觉到,随着我添加更多API,无论其背后的原因是什么,都会使我陷入困境。

我在调试时注意到的另一件事是,我不想将许多其他端点自动列为可用端点:

Using the URLconf defined in restnews.urls, Django tried these URL patterns, in this order:
rest/ ^news_contents/$ [name='newscontent-list']
rest/ ^news_contents\.(?P<format>[a-z0-9]+)/?$ [name='newscontent-list']
rest/ ^news_contents/(?P<pk>[^/.]+)/$ [name='newscontent-detail']
rest/ ^news_contents/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='newscontent-detail']
rest/ ^news_infos/$ [name='newsinfo-list']
rest/ ^news_infos\.(?P<format>[a-z0-9]+)/?$ [name='newsinfo-list']
rest/ ^news_infos/(?P<pk>[^/.]+)/$ [name='newsinfo-detail']
rest/ ^news_infos/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='newsinfo-detail']
rest/ ^^categories/$/$ [name='newsinfo-list']
rest/ ^^categories/$\.(?P<format>[a-z0-9]+)/?$ [name='newsinfo-list']
rest/ ^^categories/$/(?P<pk>[^/.]+)/$ [name='newsinfo-detail']
rest/ ^^categories/$/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='newsinfo-detail']
rest/ ^^articles/(?P<news_id>\d+)/details/$ [name='NewsInfo-list']
rest/ ^^articles/(?P<news_id>\d+)/details\.(?P<format>[a-z0-9]+)/?$ [name='NewsInfo-list']
rest/ ^^articles/(?P<news_id>\d+)/details/(?P<pk>[^/.]+)/$ [name='NewsInfo-detail']
rest/ ^^articles/(?P<news_id>\d+)/details/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='NewsInfo-detail']
rest/ ^^articles/(?P<category_name>[-\w]+)/$ [name='NewsInfo-list']
rest/ ^^articles/(?P<category_name>[-\w]+)\.(?P<format>[a-z0-9]+)/?$ [name='NewsInfo-list']
rest/ ^^articles/(?P<category_name>[-\w]+)/(?P<pk>[^/.]+)/$ [name='NewsInfo-detail']
rest/ ^^articles/(?P<category_name>[-\w]+)/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='NewsInfo-detail']
rest/ ^$ [name='api-root']
rest/ ^\.(?P<format>[a-z0-9]+)/?$ [name='api-root']

我只提供了5个端点,不需要所有其余端点,因此需要将其删除。这些问题是否以某种方式联系在一起?我该怎么解决?

1 个答案:

答案 0 :(得分:1)

这是ModelViewset和DefaultRouter类的组合的行为方式。在DRF Doc中,它默认提供了一堆端点  如果您要进行CRUD Operations,则非常方便。

根据您的描述,我了解到您不是在处理 CRUD 操作,因此无法使用DefaultRouterModelViewset

因此,我建议您使用 rest_framework.views.APIView 类。

Example

views.py
from rest_framework.views import APIView
from rest_framework.response import Response


class MyViewClass(APIView):
    def get(self, *args, **kwargs):  # This fucntion will handle your "HTTP GET" requests
        # put your logic here
        return Response(data={"mymsg": "this is my response"})

    def post(self, *args, **kwargs):
        return Response("This is post method")

并在您的urls.py

from django.conf.urls import url

urlpatterns = [
    url(r'mysample/', MyViewClass.as_view())

]


参考
1. DRF Router
2. DRF viewset
3. DRF APIview

UPDATE-1
阅读Django document

  

Django如何处理请求

     

当用户请求您的页面   由Django驱动的网站,这是系统遵循的算法   确定要执行的Python代码:

     
      
  1. Django确定要使用的根URLconf模块。通常,这是ROOT_URLCONF设置的值,但是如果传入   HttpRequest对象具有一个称为urlconf的属性(由中间件设置   请求处理),其值将代替   ROOT_URLCONF设置。
  2.   
  3. Django加载该Python模块并查找变量urlpatterns。这应该是一个Python列表,格式为   函数django.conf.urls.patterns()。
  4.   
  5. Django按顺序运行每个URL模式,并在与请求的URL匹配的第一个URL模式处停止。
  6.   
  7. 一旦其中一个正则表达式匹配,Django就会导入并调用给定的视图,这是一个简单的Python函数。视图通过了   HttpRequest作为其第一个参数以及正则表达式中捕获的所有值   作为剩余的参数。
  8.   
  9. 如果没有正则表达式匹配,或者在此过程中的任何时候引发异常,则Django都会调用相应的错误处理视图。   请参阅下面的错误处理。
  10.