Django generateschema ignores URLs - python

I am trying to learn to create a Backend with Django, together with an Angular frontend.
In order to make the api a little more consistent I tried to create a API schema to use the OpenAPI Generator.
I have run the command ./manage.py generateschema --file schema.yml. But: The yml file does not have any information about the users.url. I have added the get_schema_view from the rest_framework, with the same result.
The (main) app urls.py looks like this:
from django.conf.urls import include
from django.contrib import admin
from django.urls import path
from rest_framework.schemas import get_schema_view
urlpatterns = [
path('admin/', admin.site.urls),
path('api/users/', include('users.urls'), name="users"),
path('api/network/', include('networkController.urls'), name="network"),
path('api/files/', include('fileController.urls'), name="files"),
path('api/', get_schema_view(
title="API Documentation",
description="API for all things"
), name='openapi-schema')
]
The networkController.urls looks like this:
from django.urls import path
from . import views
urlpatterns = [
path('', views.startNetwork)
]
which is found by the schema generator.
The users.urls looks like this:
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login),
path('register/', views.registerUser)
]
I have tried to move all urls to the (main) backend.urls and include the views directly, I have tried "layering" them all.
# backend.urls:
path('api/', include('api.urls'))
# api.urls:
path('users/', include('users.urls'))
without any changes.
I tried looking up, why - but without success. If I run the server and make a GET-Request via curl to localhost:8000/api/users/login directly, it works perfectly fine.
Could you please help me figure out, what I did wrong or guide me to a tutorial, which would cover the topic a little more detailled?
(And yes. Maybe I should just switch to something like FastAPI, but I really love Djangos Auth.Users and the easy constant, connection to a database)
Thanks in advance!
(EDIT: You can find the whole code in my GitHub)

I just figure out why my code didn't work. I use Class Based Views but I think this will also work for Method Based ones.
It looks like generateschema does not like uppercase method names:
class ...(UpdateAPIView):
serializer_class = ...
permission_classes = [IsAuthenticated]
http_method_names = ['PUT']
...
Instead of ['PUT'] I used ['put']
class ...(UpdateAPIView):
serializer_class = ...
permission_classes = [IsAuthenticated]
http_method_names = ['put']
...
So, replace all the uppercase letters with lowercase letters.

Related

DRF: API root view doesn't list my APIs for some reasons

The bounty expires in 4 days. Answers to this question are eligible for a +50 reputation bounty.
varnie wants to draw more attention to this question.
I have the following urls.py for an app named service where I register API endpoints:
from .views import AccessViewSet, CheckViewSet
app_name = "api"
router = DefaultRouter()
router.register(r"access/(?P<endpoint>.+)", AccessViewSet, basename="access")
router.register(r"check/(?P<endpoint>.+)", CheckViewSet, basename="check")
urlpatterns = [
path("", include(router.urls)),
]
Below is my project's urls.py where I use it:
from django.conf import settings
from django.contrib import admin
from django.urls import include, path
import service.urls as service_urls
urlpatterns = [
# ...
path("service/", include('service.urls')),
]
The APIs themselves are functioning properly, but I am having trouble making them work with DRF's default API root view. The view is displaying an empty list of available endpoints. I'm not sure, but this issue may be related to the regular expressions I'm using when registering endpoints, such as r"access/(?P<endpoint>.+). If this is indeed the problem, how can I resolve it?"
DefaultRouter needs to run reverse(viewname) on your views to generate urls for the default view. It won't know about your dynamic parameter endpoint when it runs.
If it's acceptable to change your url structure to {endpoint}/access/... and {endpoint}/check/... you can:
router.register(r"access", AccessViewSet, basename="access")
router.register(r"check", CheckViewSet, basename="check")
urlpatterns = [
re_path(r"(?P<endpoint>.+)/", include(router.urls)),
]
After which, each {endpoint}/ view should have a working default API view.
If you need to preserve your url structure, you'll have to manually generate a list of available routes like this:
from collections import OrderedDict
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.reverse import reverse
class ApiRoot(APIView):
"""My API root view"""
def get(self, request, format=None):
"""List of available API endpoints"""
api_methods = [
"access-list",
"check-list",
]
endpoints = [
"endpoint-a",
"endpoint-b",
]
routes = OrderedDict()
for endpoint in endpoints:
for method in api_methods:
routes[endpoint + method] = reverse(
method, endpoint=endpoint, request=request, format=format
)
return Response(routes)
Notice the endpoint=endpoint in the reverse call.
If the code above doesn't help, please, expand your question with more details of what you're trying to archive in the first place. I have a feeling you might want to replace your viewsets with an EndpointViewSet with custom ViewSet actions for check and access.
Finally, if you're looking for routes like {endpoint_id}/access/{access_id}/... and {endpoint_id}/check/{check_id}/... check out drf-nested-routers - a drop-in replacement for DRF's Routers that supports nesting.

Using Django all-auth in a standalone app: paths that do not start with 'accounts/' are NOT FOUND 404

I am using Django 3.2, and am creating a standalone user registration and management app, that uses django-allauth to handle all of the registration functionality.
My stand alone app directory structure is like this:
core
/core
settings.py
urls.py
# ...
/django-myown-userprofile
/userprofile
models.py
urls.py
# ...
myapp.tox
setup.py
setup.cfg
# ...
core/core/urls.py
from django.urls import path, include
urlpatterns = [
# ...
path(user, include('userprofile.urls')),
path(admin, admin.site.urls)
]
core/django-myown-userprofile/userprofile/urls.py
from django.urls import path, include
urlpatterns = [
path('accounts', include('allauth.urls')),
path('accounts/profile', ProfileView.as_view(), 'user-profile'),
# ...
]
When I attempt to access any of the allauth endpoints (e.g.):
http://localhost:8000/user/accounts/login
I get the error 404 Not found. It seems that django-allauth is expecting the path to be of the form:
http://localhost:8000/accounts/...
How do I change this behaviour of django-allauth ?
I think you're missing a trailing slash / in your accounts path in userprofile/urls.py:
from django.urls import path, include
urlpatterns = [
path('accounts/', include('allauth.urls')),
path('accounts/profile', ProfileView.as_view(), 'user-profile'),
# ...
]
With that it should work fine. Ensure also that the variables user and admin have string like values that refer to a route ending with a trailing slash. It's nice to be consistent with the trailing slash. You could add it to your ProfileView route to keep the consistency, but it won't matter much.
As an extra thing to keep in mind is that the routes and names you use for your custom views when defining urlpatterns shouldn't overlap the ones used by the accounts app in django-allauth (see https://github.com/pennersr/django-allauth/blob/master/allauth/account/urls.py to check the routes and names used by django-allauth account views). Hope this helped!

How to route specific urls in a django app?

I would like to know if there is a way to include only specific url endpoints in my Django urls.py.
Lets say i have a app called auth with this auth/urls.py
urlpatterns = [
url(r'^password/reset/$', PasswordResetView.as_view(),
name='rest_password_reset'),
url(r'^password/reset/confirm/$', PasswordResetConfirmView.as_view(),
name='rest_password_reset_confirm'),
url(r'^login/$', LoginView.as_view(), name='rest_login'),
url(r'^logout/$', LogoutView.as_view(), name='rest_logout'),
url(r'^user/$', UserDetailsView.as_view(), name='rest_user_details'),
url(r'^password/change/$', PasswordChangeView.as_view(),
name='rest_password_change'),
]
Now I have a urls.py like that:
urlpatterns = [
path('/', include('dj_rest_auth.urls'))
]
this includes all endpoints from auth/urls.py.
Is there a way to select (in urls.py) which URL to include? Lets say I only want login and logout to be included on my urls.py.
urlpatterns = [
path('/', include('dj_rest_auth.urls.rest_login')),
path('/', include('dj_rest_auth.urls.rest_logout'))
]
Something like that, how can I make it work?
I did this long before.
# inside view.py just create your own custom view
# rest and rest_auth import
from rest_auth.views import LoginView, LogoutView
class CustomLogoutView(LogoutView):
# yes u can keep it blank.
# over-riding just to distinguish from library views.
pass
Then inside any app's urls.py import required views and and pass then into path.
this way only required url will be exposed.
from .views import CustomLoginView
urlpatterns = [
path('login/', CustomLoginView.as_view(), name='login'),
]
Also you can directly import that views (not urls) which are included in library. and then create urls (path) from them.

Django Path To Resource Failure?

When I try a GET request on one of my API endpoints it can't find the endpoint.
urls.py file looks like this
from django.urls import path, include
from django.contrib import admin
from api.resources import NoteResource
note_resource = NoteResource()
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include(note_resource.urls)),
]
api.resources looks like this
from tastypie.resources import ModelResource
from api.models import Note
class NoteResource(ModelResource):
class Meta:
queryset = Note.objects.all()
resource_name = 'note'
Any idea why this is happening?
Solution: It appears that http://127.0.0.1:8000/api/note/ works properly.. why would this be?
You should also have one url entry in note_resource.urls for only /api request. Something similar to
path('api/', APIClass).
But, you never need that endpoint. Because, /api does not represent any actual request in your system.
I rather suggest to have following endpoints :
path('api/notes/',include(note_resource.urls))
in your main urls.py.
So that you can have multiple urls in main urls.py file representing each app.
path('api/<APP_NAME>(s)/',include(<APP_NAME>.urls))
And, you will manage other endpoints in your app urls.py file:
# Create a new Note
path(
'create/',
NoteCreate.as_view()
),

Django REST Framework move all urls to router

I have little experience in python. Please could you help me. There is an old project which has the following structure
# -*- coding: utf-8 -*-
import re
from django.conf import settings
from django.conf.urls import include, url
from django.contrib import admin
from django.views.static import serve
from rest_framework import routers # i added it
import home.views
router = routers.DefaultRouter() # i added it
urlpatterns = [
url(r'^$', home.views.HomeView.as_view()),
url(r'^api/v2/', include('api.v2.urls', namespace='api-v2')),
url(r'^help/', include('helps.urls', namespace='helps')),
url(r'^admin/', include(admin.site.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), # i added it
url(r'^', include(router.urls)) # i added it
]
I would like to make REST API so that you can see all possible routes. So i try to add rest_framework.urls. As I understood it is necessary to use VeiwSet to add it to router. I do not quite understand how I can use what I already have to see links in a REST API? Or for each link i need to create a Veiwset?
For example api.v2.urls contains next: (similar in helps.urls, etc)
# -*- coding: utf-8 -*-
from django.conf.urls import url
import api.v2.views
urlpatterns = [
url(r'^data/info', api.v2.views.info_data),
url(r'^visits$', api.v2.views.visits),
url(r'^additional_info/', api.v2.views.additional_info),
]
a Viewset combines several views into a same class. You can have it for example to provide CRUD api for one of your Django model.
I recommend to read http://www.django-rest-framework.org/api-guide/viewsets/
You can register the Viewset in a router. I will generate an url for every view of the views
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset for viewing and editing user instances.
"""
serializer_class = UserSerializer
queryset = User.objects.all()
in urls.py
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, base_name='user')
urlpatterns = [
...
url(r'^', include(router.urls))
]
If you print the router.urls
for url in router.urls: print(url)
You will see something like:
<RegexURLPattern user-list ^ users/$>
<RegexURLPattern user-list ^ users\.(?P<format>[a-z0-9]+)/?$>
<RegexURLPattern user-detail ^ users/(?P<pk>[^/.]+)/$>
<RegexURLPattern user-detail ^ users/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$>
It shows the url for every view of the viewset
You can also have regular View for your REST API. http://www.django-rest-framework.org/api-guide/views/ which can be added to your urls like any usual Django view.
I hope it helps
I believe you will need to declare explicitly the urls that you want to show:
This is what i have in one of my projects:
urlpatterns = [
# Your stuff: custom urls includes go here
url(r'^api/$', api_root, name='api-root'),
]
#api_view(['GET'])
#permission_classes((IsAdminUser, ))
def api_root(request, format=None):
return Response({
'apples': reverse('my-api:oranges-all'),
'oranges': reverse('my-api:oranges-list'),
})

Categories

Resources