<int:pk> not working inside router in Django REST framework - django

I've the following code inside urls.py:
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import SentenceListViewSet, SentenceViewSet
router = DefaultRouter()
router.register('lists', SentenceListViewSet, basename='SentenceList')
router.register('lists/<int:pk>/sentences/', SentenceViewSet, basename='Sentence')
app_name = 'api_app'
urlpatterns = [
path('', include(router.urls), name='lists')
]
It's the second router registry that's causing problem. I get "page not found" if I navigate to localhost:8000/lists/8/sentences. However, I can access localhost:8000/lists/<int:pk>/sentences.
How can I make DRF capture 8 as a URL parameter instead of int:pk getting treated as a literal part of the URL?

Related

Setting up simple Django ViewSet APIs from multiple apps

I’m still learning django and assume this may be easy for some. I’m trying to figure out the best way of simply setting up the API URLs (and so that they all display in the api root and can actually be used in the project - in my case at /api/). I’m using django rest framework, and can’t seem to set up more than one API - it works for just one, but the problem occurs when trying to set up another.
So I have an app called pages and accounts (and core - the default where the main urls.py is). I’ve created another urls.py inside the pages app and another inside the accounts app.
accounts/urls.py:
from . import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"accounts", views.AccountsViewSet, basename="accounts")
urlpatterns = router.urls
pages/urls.py:
from . import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r"pages", views.PagesViewSet, basename="pages")
urlpatterns = router.urls
And the core urls.py:
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from rest_framework.routers import DefaultRouter
router = routers.DefaultRouter()
urlpatterns = [
path("admin/", admin.site.urls),
path("api/", include("pages.urls")), # works but root only shows pages API
# path("api/", include("pages.urls", "accounts.urls")), # fails with error: “django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.”
# path("api/", include(router.urls)), # no error but root api is empty
]
I would assume, possibly incorrectly, that just router.urls should include all the apis when visiting the root. The root apis currently looks like this when using just include("pages.urls”):
{
"pages": "http://localhost:8000/api/pages/"
}
How can I get it to correctly show all apis? The only way I could do it was by putting router.register(r"accounts", views.AccountsViewSet, basename="accounts”) in the pages urls.py, which is very undesirable, especially as the project grows even further.
Thank you
Have you tried to use:
path("api/pages/", include("pages.urls")),
path("api/accounts/", include("accounts.urls")),
In your urls.py?
Possibly that would mean your routes would be:
{
"pages": "http://localhost:8000/api/pages/pages/"
"accounts": "http://localhost:8000/api/accounts/accounts/"
}
In that case you could try to use
router.register("", views.AccountsViewSet, basename="accounts")
in accounts/urls.py.
And similarly,
router.register("", views.AccountsViewSet, basename="pages")
in pages/urls.py.
That way, you might achieve to have routes like:
{
"pages": "http://localhost:8000/api/pages/"
"accounts": "http://localhost:8000/api/accounts/"
}
if that is what you want.

Django not getting path not matching

I am trying to make a certain url on my localhost show "Alex!" when it is entered in. For some reason I am getting this error: "Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order:
admin/
The current path, sauce, didn’t match any of these."
The app name is "main"
Here is my code:
main.urls
from django.urls import path
from . import views
urlpatterns = [
path('sauce/', views.index, name='index'),
]
main.views
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Alex!")
mysite.urls
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('sauce/', include('main.urls')),
path('admin/', admin.site.urls),
]
I also tried doing this but with the homepage and not "sauce/" but that didn't work either.
Your path is not matching because you have "sauce" in both main.urls and mysite.urls. When you use include the urls from main.urls will be appended to the path you specify in mysite.urls, so the combined url becomes:
http://127.0.0.1:8000/sauce/sauce
I would replace the "sauce/" in mysite.urls to just "".

Django - reverse nested url with drf-nested-routers

I configured my api url as
localhost:port/app_name/students/{student_id}/macro/{macro_id}/lto
using drf-nested-routers extension. Basically, each students has some macro categories assigned, that in turns have some Long Term Objectives (LTOs). I've tested it using curl and Postman and everything seems to work.
Now I need to write a more precise test case for my LTO model.
This is my urls.py
from django.urls import path, re_path
from django.conf.urls import include
from rest_framework import routers
from app_name.views.views import UserViewSet, StudentViewSet, MacroViewSet, LTOViewSet, MacroAssignmentViewSet
from rest_framework_nested import routers as nested_routers
# application namespace
app_name = 'app_name'
router = routers.DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'macro', MacroViewSet, basename='macro')
router.register(r'macro-assignments', MacroAssignmentViewSet, basename='macro-assignment')
student_router = routers.DefaultRouter()
student_router.register(r'students', StudentViewSet, basename='student')
lto_router = nested_routers.NestedSimpleRouter(student_router, r'students', lookup='student')
lto_router.register(r'macro/(?P<macro_pk>.+)/lto', LTOViewSet, basename='lto')
urlpatterns = [
re_path('^', include(router.urls)),
re_path('^', include(student_router.urls)),
re_path('^', include(lto_router.urls)),
]
The issue is that I cannot use the reverse() method correctly to get the url of my LTOViewSet to test it.
self.url = reverse('app_name:student-detail:lto', {getattr(self.student, 'id'), getattr(self.macro, 'id')})
This gives the following error
django.urls.exceptions.NoReverseMatch: 'student-detail' is not a registered namespace inside 'app_name'
In other test cases, I use very similar sentences and those work fine
self.list_url = reverse('app_name:student-list')
reverse('app_name:student-detail', {post_response.data['id']})
So here's the minimally reproducible example:
# main/viewsets.py
from rest_framework.viewsets import ModelViewSet
from django.contrib.auth.models import User, Group
class StudentViewSet(ModelViewSet):
model = User
class LTOViewSet(ModelViewSet):
model = Group
# main/urls.py
from django.urls import re_path, include
from rest_framework import routers
from rest_framework_nested import routers as nested_routers
from .viewsets import StudentViewSet, LTOViewSet
# application namespace
app_name = "main"
student_router = routers.DefaultRouter()
student_router.register(r"students", StudentViewSet, basename="student")
lto_router = nested_routers.NestedSimpleRouter(
student_router, r"students", lookup="student"
)
lto_router.register(r"macro/(?P<macro_pk>.+)/lto", LTOViewSet, basename="lto")
urlpatterns = [
re_path("^", include(student_router.urls)),
re_path("^", include(lto_router.urls)),
]
reverse('main:lto-detail', args=(1,1,1))
Out[5]: '/api/students/1/macro/1/lto/1/'
So indeed your error was passing just the router basename not a final endpoint to reverse and because of the nesting we were thrown off by student-detail not reversing (which I still don't get).

How to resolve an error 404 for django-social?

I want to be able to login through social media. Followed all the steps (registered app), the login works just fine but does not go through because django does not recognize my url.
This is my call to the api endpoint
facebookLogin(token: string):any{
return this.http.post(environment.api + 'fblogin/', {token:this.token}).subscribe(
(onSucess:any) => {
localStorage.setItem(this._tokenKey, onSucess.token)
}, onFail => {
console.log(onFail)
}
);
}
But I get the following error : POST http://127.0.0.1:8000/api/fblogin/ 404 (Not Found). From this I know there to be something wrong with my django urls. And indeed going to http://127.0.0.1:8000/api/fblogin/ gave me a page not found error and that it tried to match several other urls.
However I can't see what is wrong with my urls
URLS in my app
from django.conf.urls import url, include
from rest_framework import routers
from . import views
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
from social_django import urls
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'fblogin/', include(urls)),
url(r'auth/', obtain_jwt_token),
url(r'refresh/', refresh_jwt_token)
]
URLS in my project
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('Backend.api.urls'))
]
Other URLS like http://127.0.0.1:8000/api/users/ do work. I also am under the impression that all of my settings are in order.
I suggest that I might be because of the order and way in which you have defined URLs.py try the below format
from django.conf.urls import url, include
from rest_framework import routers
from . import views
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
from social_django import urls
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
urlpatterns = [
url(r'^fblogin/', include(urls)),
url(r'^auth/', obtain_jwt_token),
url(r'^refresh/', refresh_jwt_token)
] + router.urls

How to fix "error path not found" in django rest framework

I am trying to build an API of my blogging website using Django rest framework, but my URL is not matching.
I am trying Django Rest framework for the first time so I am not quite able to fix this. But I think I mess this up in url_patterns.
Here is my URL code from the main directory(the directory which contains settings.py) .
`
from django.conf.urls import url,include
from django.contrib import admin
from django.urls import path, include
from blog import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'apipost',views.PostViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'',include('blog.urls')),
path('api-auth/',include('rest_framework.urls',namespace='rest_framework')),
]
`
I am trying url http://127.0.0.1:8000/apipost and expect to get value in json format.
You need to add router.urls to your urlpatterns.
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'',include('blog.urls')),
path('api-auth/',include('rest_framework.urls',namespace='rest_framework')),
]
urlpatterns += router.urls
Django REST Framework Won't magically register your router in urlpatterns, you have to do it by yourself. You can use urlpatterns += router.urls if you want to add them to the root of your urlpatterns, or url(r'^api/', include((router.urls, 'app_name'))), if you want to set subpath for them.