Working on an Api I wanted to give class based views in Django a go.
This is what I got so far:
urls.py
from django.conf.urls import url
from .api import Api
urlpatterns = [
url(r'^', Api.as_view())
]
api.py
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View
class Api(View):
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
super(Api, self).dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return HttpResponse("result")
def get(self, request):
return HttpResponse("result")
When calling this code from Postman I keep getting 2 issues:
Post data
In postman I have set a value in the Post headers but when I use the debugger to check the POST data it just isn't there.
Return HttpResponse
I keep getting the error The view api.api.Api didn't return an HttpResponse object. It returned None instead.
When I change the post method to a get method and send a get request with Postman I get the same result.
You need to do:
return super(Api, self).dispatch(request, *args, **kwargs)
You're not returning anything, and a function returns None by default.
Also, you probably want your url as r'^$'
Related
Consider that I have a simple view as,
# serializers.py
class EmptyPayloadResponseSerializer(serializers.Serializer):
detail = serializers.CharField()
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema
from .serializers import EmptyPayloadResponseSerializer
class EmptyPayloadAPI(APIView):
#extend_schema(responses=EmptyPayloadResponseSerializer)
def post(self, request, *args, **kwargs):
# some actions
return Response(data={"detail": "Success"})
When I render the schema, I have got the following error response,
Error #0: EmptyPayloadAPI: unable to guess serializer. This is graceful fallback handling for APIViews. Consider using GenericAPIView as view base class, if view is under your control. Ignoring view for now.
So, how can I tell to #extend_schema decorator that I'm intended to pass nothing as payload?
Settings request=None in the #extend_schema(...) decorator will do the trick!!!
class EmptyPayloadAPI(APIView):
#extend_schema(request=None, responses=EmptyPayloadResponseSerializer)
def post(self, request, *args, **kwargs):
# some actions
return Response(data={"detail": "Success"})
My views.py have a mix of def and ClassViews:
#login_required(login_url='login')
#allowed_users(allowed_roles=['Admin', 'Staff', 'Lite Scan'])
def litescan(request):
filteredOutput = Stock.objects.all()
val = {}...
#method_decorator(login_required(login_url='login'), name='dispatch')
class HomeView(ListView):
model = Post
template_name = 'community.html'
ordering = ['-id']
And here's my decorators.py if that is helpful:
from django.shortcuts import redirect
from django.http import HttpResponseRedirect
def unauthenticated_user(view_func):
def wrapper_func(request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('home')
else:
return view_func(request, *args, **kwargs)
return wrapper_func
def allowed_users(allowed_roles=[]):
def decorator(view_func):
def wrapper_func(request, *args, **kwargs):
group = None
if request.user.groups.exists():
group = request.user.groups.all()[0].name
if group in allowed_roles:
return view_func(request, *args, **kwargs)
else:
url = ('/forbidden')
return HttpResponseRedirect(url)
return wrapper_func
return decorator
I found out that #login_required and #allowed_users give out an error when used with ClassView. So i used #method_decorator which brings me to the login page before redirecting to the page. However, I can not find a way to restrict access to only certain groups like Admin, Staff, Lite Scan with my ClassView.
Little help will be appreciated. Thanks!
You can use AccessMixin for your class views.
Example I found:
from django.contrib.auth.mixins import AccessMixin
from django.http import HttpResponseRedirect
class FinanceOverview(AccessMixin, TemplateMixin):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
# This will redirect to the login view
return self.handle_no_permission()
if not self.request.user.groups.filter(name="FinanceGrp").exists():
# Redirect the user to somewhere else - add your URL here
return HttpResponseRedirect(...)
# Checks pass, let http method handlers process the request
return super().dispatch(request, *args, **kwargs)
More info found here: Use LoginRequiredMixin and UserPassesTestMixin at the same time
Relying on Django Permissions may be a far simpler approach to giving access to such a view. Rather than checking for a specific list of groups, you can assign permissions to those groups and give access to the view based on whether the user's groups have the appropriate permissions.
views.py
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionsRequiredMixin
#permission_required('foo.view_bar')
def my_view(request):
...
class MyView(PermissionRequiredMixin, DetailView):
permission_required = ('foo.view_bar', )
...
I have a Django app using djangorestframework. I want to return an empty response or nothing if the root URL is called. Below is my code.
url(r'^$', HttpResponse(''), name="redirect_to_somepage")
Expected output: [] or {}
Write a get function in your view.
view.py
from django.views.generic import View
from django.http import JsonResponse
class MyView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({}, status=204)
urls.py
url(regex=r'^$', view=MyView.as_view(), name='index'),
I am trying to set csrf_exempt on class-based view in Django I've tried this one:
Now, my class is look like this:
class UserView(View):
# Check csrf here
def get(self, request, pk):
#code here
# I want to exempt csrf checking only on this method
def post(self, request):
#code here
Meanwhile, if I use #method_decorator(csrf_exempt, name='dispatch') it will be applied to every method in the class. What's the best approach to exempt only for a specific method in a class-based view in Django?
You can't dacorate only the post()/put() because as_view function in base.py doesn't carry __dict__ methods from other methods than dispatch(). Source.
You can only decorate a class or override the dispatch() method as documentation says.
You can try this -
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
class UserView(View):
# Check csrf here
def get(self, request, pk):
#code here
# exempt csrf will affect only this method
#method_decorator(csrf_exempt)
def post(self, request):
#code here
You can also write it this way -
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
#method_decorator(csrf_exempt, name='post')
class UserView(View):
# Check csrf here
def get(self, request, pk):
#code here
# exempt csrf will affect only post method
def post(self, request):
#code here
Read the documentation here.
I'm trying to register a simple method into a DRF and I'm having some problems. The route is not showing in API Explorer.
It's probably something simple that I'm missing..
How can I get the register route to show in the API?
Resuts (empty)
GET /api/
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{}
Urls
from django.conf.urls import url, include
from django.contrib import admin
from rest_framework import routers
from rest_framework_jwt.views import obtain_jwt_token
from reservations.views.homepage import HomepageView
from users.views import UserViewSet
""" API routes
"""
router = routers.DefaultRouter()
router.register(r'test', UserViewSet, base_name='users')
""" Route patterns
"""
urlpatterns = [
url(r'^$', HomepageView.as_view(), name='homepage'),
url(r'^api/', include(router.urls)),
url(r'^api-token-auth/', obtain_jwt_token),
url(r'^admin/', admin.site.urls),
]
Viewset
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
def register(self, request):
return Response({
'status': 'User registered'
})
ViewSet has some specific methods for every method (GET, POST, PUT etc) like list, detail, create, update etc. You should use that methods. Api explorer decides on the basis of these methods that which method is allowed by your view. You can see these methods here.
In your case, I suppose you want to create new user. So you should use create method like this.
class UserViewSet(viewsets.ViewSet):
def create(self, request, *args, **kwargs):
return Response({
'status': 'User registered'
})
If you want to work only with then list, create method you must have in it. Please check below example of my code. please check this link for more information http://www.django-rest-framework.org/api-guide/viewsets/
class UserViewSet(viewsets.ViewSet):
def list(self, request):
queryset = crm_models.EmployeeLeaveApp.objects.all()
serializer = serializers.EmployeeVisitSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = crm_models.EmployeeLeaveApp.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = serializers.EmployeeVisitSerializer(user)
return Response(serializer.data)
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
Use #list_route for this. marking extra actions for routing
...
#list_route(methods=['post'])
def register(self, request):
return Response({
'status': 'User registered'
})