router registration order influences output
Clash Royale CLAN TAG#URR8PPP
router registration order influences output
I am facing a strange issue trying to implement a webservice using django rest frameworks. I have this two APIs - one for getting the list of news based on category(provided as URL parameter) and another to get the details of a news provided the news ID(provided as url parameter). Following is my app's urls.py
code:
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
The above code on calling:
http://localhost:8000/rest/articles/categoryname
returns the correct output, but calling:
http://localhost:8000/rest/articles/4057/details/
returns the following:
"detail":"Not found."
But when I change the order in which these two APIs are registered, both APIs start to work as expected.
Working urls.py
:
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
Why is this happening? I am getting a feeling that as I add more APIs, whatever the reason behind this is going to get me stuck.
Another thing I noticed while debugging this was that, there are plenty of other endpoints which I don't want are automatically getting listed as available endpoints:
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']
I have only provided 5 end points and all the remaining ones are not needed and hence need to be removed. Are these issues somehow connected? How can I solve these?
1 Answer
1
This is how the combination of ModelViewset and DefaultRouter class behave. From the DRF Doc, it provides a bunch of end-point by default
which is very very handy if you are doing CRUD
Operations.
From your description, I understood that you are not dealing with CRUD
operations, and hence you can't use the Magic of the DefaultRouter
and ModelViewset
.
So, What I suggest is, use rest_framework.views.APIView
class for your purpose.Example
CRUD
CRUD
DefaultRouter
ModelViewset
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")
and in your urls.py
urls.py
from django.conf.urls import url
urlpatterns = [
url(r'mysample/', MyViewClass.as_view())
]
References
1. DRF Router
2. DRF viewset
3. DRF APIview
UPDATE-1
Read the Django document
How Django processes a request
When a user requests a page from your
Django-powered site, this is the algorithm the system follows to
determine which Python code to execute:
AFAIK, you can't remove those auto-generated urls if you are using
Router
and if you are not using the router, you have to manually put all the URL patterns, which is a kind of reinventing the wheel– JPG
Aug 12 at 8:06
Router
Is your api provides
CRUD
functionality?– JPG
Aug 12 at 8:07
CRUD
updated the answer. don't miss the 3rd point
– JPG
Aug 12 at 8:16
Sure, first let me look into what CRUD is. I am frontend guy, just putting my head into backed technologies. SO pretty new with all these terms.
– Harikrishnan
Aug 12 at 8:29
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Currently I am using ModelViewSet, I need to change that?
– Harikrishnan
Aug 12 at 8:01