Swagger navigation like https://petstore.swagger.io/#/pet doesn't work in my projects - django-rest-framework

All the Swagger apps I've seen have navigation between endpoints-related blocks like this: https://petstore.swagger.io/#/store/getInventory
When you click on certain blocks the URL changes. Can't understand what I'm doing wrong, but I have nothing like this. My browser shows the same URL all the time, i.e. it's like 'https://petstore.swagger.io/' without #, etc.
Some example of how I'm using Swagger. To make sure it's not about my project, tried with a tutorial one, but it has the same problem
serializers.py:
class PostSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Post
fields = ('author', 'title')
views.py:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
urls.py:
router = DefaultRouter()
router.register('posts', views.PostViewSet)
schema_view = get_swagger_view(title='Posts API')
urlpatterns = [
url('^$', schema_view),
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
What I'm doing wrong? Thanks!!

Enable deeplinking in your swagger ui config file:
deepLinking: true
https://github.com/swagger-api/swagger-ui/blob/8dab91f184103be794ae2fcfeb67b02126730e88/docs/deep-linking.md

Related

Parameters URL with DRF routers

I'm using Django Rest Framework for created a API. In this project i want to capture parameters in the URL. For example i want to capture the username and password of a user and my idea is like this:
http://localhost:8000/accounts/login/?unsername=username&password=password
But i cant, I' usin routers and django-filter, but i cant get the parameters url. My project files are there:
view.py:
class AccountsData(viewsets.ModelViewSet):
queryset = models.UserData.objects.all()
serializer_class = serializers.AccountsDataSerializer
permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ['username', 'password']
lookup_url_kwarg = 'username'
#action(methods=['get'], detail=True, url_name='login', url_path='login')
def login(self, request, pk=None):
return Response({"Login successfully"}, 200)
urls.py:
from api import views
router = routers.SimpleRouter()
router.register(r'accounts', views.AccountsData)
Request query parameters have nothing to do with routing, they are passed with the request independently of how you configure the route. You have access to them in request.query_params, for example, request.query_params.get('username') would get the value of the username parameter.
Being said that, your idea has a terrible mistake: password or any kind of confidential data should NEVER go in query parameters, you should use an http verb that carries the data in its body (POST, for example).

How to disable the browsable API for non staff users (is_staff=False)?

in my case I am using Django REST Framework (DRF) as internal api. it is not intended to be consumed by regular users. therefore I would like to disable it for regular users.
an admin (is_staff=True) should be able to access it and see it:
https://restframework.herokuapp.com/
a non staff user (is_staff=False) should just get the JSON response of a GET request like:
https://restframework.herokuapp.com/?format=json
he should not(!) see the browsable api. this applies for the root view and all endpoints.
to configure this, I applied the following:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
}
my endpoints are the following (to keep the example simple I just show 1):
# api/urls.py
from django.urls import include, path
from rest_framework import routers
from . import views
app_name = 'api'
router = routers.DefaultRouter() # browseable api
router.register('segments', views.SegmentViewSet)
# there are a lot more...
urlpatterns = [
path('', include(router.urls)),
]
based on answer https://stackoverflow.com/a/58894198/420953 my settings.py looks like this:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication'],
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
# enable JSON renderer by default
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
}
and my api/views.py:
# api/views.py
from django_filters import rest_framework as drf_filters
from rest_framework import filters, renderers, viewsets
from . import serializers
from segment.models import Segment
class StaffBrowsableAPIMixin:
def get_renderers(self):
"""
add BrowsableAPIRenderer if user is staff (regular users see JSONRenderer response)
"""
# explicitly set renderer to JSONRenderer (the default for non staff users)
rends = [renderers.JSONRenderer]
if self.request.user.is_staff:
# staff users see browsable API
rends.append(renderers.BrowsableAPIRenderer)
return [renderer() for renderer in rends]
class SegmentViewSet(StaffBrowsableAPIMixin, viewsets.ReadOnlyModelViewSet):
queryset = Segment.objects.all()
serializer_class = serializers.SegmentSerializer
this works fine for all endpoints (when a regular user calls the endpoint via GET, they only see the JSON, not the browsable API). Unfortunately it does not work for APIRootView (the root view of the api, e.g. https://restframework.herokuapp.com/).
how to get this to work for APIRootView as well?
I believe you can lock the base URL of your API down pretty simply (the mixin should probably be moved to another file but just kept everything together for clarity):
# api/urls.py
from django.urls import include, path
from rest_framework import permissions, renderers, routers
from . import views
app_name = 'api'
class StaffBrowsableAPIMixin:
def get_renderers(self):
"""
add BrowsableAPIRenderer if user is staff (regular users see JSONRenderer response)
"""
# explicitly set renderer to JSONRenderer (the default for non staff users)
rends = [renderers.JSONRenderer]
if self.request.user.is_staff:
# staff users see browsable API
rends.append(renderers.BrowsableAPIRenderer)
return [renderer() for renderer in rends]
class CustomAPIRootView(StaffBrowsableAPIMixin, routers.APIRootView):
permission_classes = (permissions.IsAdminUser,)
class CustomDefaultRouter(routers.DefaultRouter):
APIRootView = CustomAPIRootView
router = CustomDefaultRouter() # browseable api
router.register('segments', views.SegmentViewSet)
# there are a lot more...
urlpatterns = [
path('', include(router.urls)),
]
The permission_classes will handle not showing any of your endpoints to non-Admin users but the Browsable API template will still be shown. To remove that as well, you need to change the renderer using the StaffBrowsableAPIMixin.
Original Answer
One way to do this is using DRF's renderer settings and methods.
In your settings.py:
REST_FRAMEWORK = {
# Only enable JSON renderer by default.
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
}
And in your views.py:
from rest_framework import generics, renderers
class StaffBrowsableMixin(object):
def get_renderers(self):
"""
Add Browsable API renderer if user is staff.
"""
rends = self.renderer_classes
if self.request.user and self.request.user.is_staff:
rends.append(renderers.BrowsableAPIRenderer)
return [renderer() for renderer in rends]
class CustomListApiView(StaffBrowsableMixin, generics.ListAPIView):
"""
List view.
"""
# normal stuff here
Basically, use StaffBrowsableMixin for any APIView you want the BrowsableAPI to be enabled for staff.
Similar question, as linked above in comments, and my answer there as well: https://stackoverflow.com/a/58762483/4599228

Django rest framework - exclude endpoint from authentication

I need to make view accessible without authentication, based on a variable passed in urls.py.
My idea is something like this:
urls.py
url(r'^oidc-api/', include('api.urls'), {'logged': True})
views.py
class ExampleViewSet(ModelViewSet):
if logged: # How can I get this variable, passed in urls.py?
permission_classes = () # This will exclude current view from authentication
queryset = Widget.objects.filter(visible=True)
serializer_class = ExampleSerializer
filter_backends = (DjangoFilterBackend,)
filter_fields = ('example_id',)
However, I can not access logged variable that is passed from urls.py.
John
You can't do that.
What you could do is, remove those permission classes from the views manually or set those to an empty set as,
class ExampleViewSet(ModelViewSet):
permission_classes = ()

Post from Angular 5 front-end to Django Rest Framework back-end

I am trying to create a post request from Angular 5 to DRF. The field is a form that a user submits. I created the serializer and i can post something from DRF interface
models.py
class UserForm(models.Model):
id_user_form = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, db_column='idUser', unique=False)
name = models.CharField(max_length=50)
type = models.CharField(max_length=25)
location = models.CharField(max_length=200)
serilizers.py
`class UserFormSerializer(serializers.ModelSerializer):
class Meta:
model = UserForm
fields = ('user', 'name', 'type', 'location')
def create(self, validated_data):
user_data = validated_data.pop('user')
user_form = UserForm.objects.create(user_id=user_data, **validated_data)
return user_form
views.py
class FormSubmit(generics.ListCreateAPIView):
queryset = UserForm.objects.all()
serializer_class = UserFormSerializer
When i try to post it via Angular I get this error:
Forbidden (CSRF token missing or incorrect.): /api/form/
Am I doing something wrong?
Had the exact same problem when I wanted to upload a profile picture to my Django REST backend.
You have 2 options, basically.
The first one is disabling the CSRF checks, which are by default enforced by DRF. Have a read here. You may do this for testing purposes.
The second option would be to pass the CSRF Token inside your request header. To do that, have a look at the $cookies API.
With that, you can get the token out of the cookie and paste it into your header like so: 'X-CSRFToken': your-csrf-token-here.
You can verify this by opening your dev tools inside the browser and navigating to your site's cookies. You should see one called csrftoken there.
I hope this sent you on the right track.

Excluding Basic Authentication In A Single View - Django Rest Framework

I set basic authentication in my setting.py as follows. Now I need a view that doesn't use basic authentication. How can I do it.
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',),
}
To exclude a view from authentication, set authentication_classes and permission_classes to [].
class SignupView(APIView):
authentication_classes = []
permission_classes = []
def post(self, request):
# view code
You simply need to set the authentication_classes on your view. Have a look at http://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme for an example.
Edit:
To remove authentication, set the authentication_classes to an empty list. Don't forget to remove permissions as well since they usually rely on authentication.

Resources