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'
})
Related
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 have a generic class based view:
class ProjectDetails(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
generics.GenericAPIView):
queryset = Project.objects.all()
# Rest of definition
And in my urls.py, I have:
urlpatterns = [
url(r'^(?P<pk>[0-9]+)/$', views.ProjectDetails.as_view())
]
When the API is called with a non-existent id, it returns HTTP 404 response with the content:
{
"detail": "Not found."
}
Is it possible to modify this response?
I need to customize error message for this view only.
This solution affect all views:
Surely you can supply your custom exception handler: Custom exception handling
from rest_framework.views import exception_handler
from rest_framework import status
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response.status_code == status.HTTP_404_NOT_FOUND:
response.data['custom_field'] = 'some_custom_value'
return response
Sure you can skip default rest_framework.views.exception_handler and make it completely raw.
Note: remember to mention your handler in django.conf.settings.REST_FRAMEWORK['EXCEPTION_HANDLER']
Solution for specific view:
from rest_framework.response import Response
# rest of the imports
class ProjectDetails(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
generics.GenericAPIView):
queryset = Project.objects.all()
def handle_exception(self, exc):
if isinstance(exc, Http404):
return Response({'data': 'your custom response'},
status=status.HTTP_404_NOT_FOUND)
return super(ProjectDetails, self).handle_exception(exc)
It's possible by overriding specific methods like update, retrieve as:
from django.http import Http404
from rest_framework.response import Response
class ProjectDetails(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
generics.GenericAPIView):
queryset = Project.objects.all()
def retrieve(self, request, *args, **kwargs):
try:
return super().retrieve(request, *args, **kwargs)
except Http404:
return Response(data={"cusom": "message"})
def update(self, request, *args, **kwargs):
try:
return super().update(request, *args, **kwargs)
except Http404:
return Response(data={"cusom": "message"})
I am using the token based Authentication in Django and need to add User object in addition to token being returned.
How do I override this class view ? Where do I need add this class and make the changes ? Currently this is found in the rest_framework package and I don't want to modify the library .
from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.response import Response
from rest_framework.views import APIView
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
print "dasdsa"
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
obtain_auth_token = ObtainAuthToken.as_view()
From docs.
First that you need is to extend the ObtainAuthToken class.
# views.py
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})
And after this add the CustomAuthToken class to your urls.py like view
# urls.py
from django.urls import path
from . import views
urlpatterns += [
path(r'api-token-auth/', views.CustomAuthToken.as_view())
]
You should extend your CustomClass from AuthToken, the route default url to your CustomClass:
from rest_framework_jwt.views import ObtainJSONWebToken
class JSONWebTokenAPIOverride(ObtainJSONWebToken):
"""
Override JWT
"""
def post(self, request):
# Do whatever you want
Then in your urls.py:
url(
r'^api-auth$',
cache_page(0)(views.JSONWebTokenAPIOverride.as_view())
)
I hope it helps
I wanted to override some default CRSF functionality and used the following approach:
from rest_framework.authentication import SessionAuthentication
class SessionCsrfExemptAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
# Do not perform a csrf check
return False
Then in my settings file I referenced it in the following way:
'DEFAULT_AUTHENTICATION_CLASSES': (
'myapp.utils.authenticate.SessionCsrfExemptAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'oauth2_provider.ext.rest_framework.OAuth2Authentication',
'rest_framework_social_oauth2.authentication.SocialAuthentication',
),
This allowed me to import the existing functionality, override it and reference it in the settings file. I think you can use a similar approach here.
I use the option JWT_RESPONSE_PAYLOAD_HANDLER.
In the response I include the token, expiration timestamp and the user.
In settings.py add:
JWT_AUTH = {
...
'JWT_RESPONSE_PAYLOAD_HANDLER':'<app_name>.functions.custom_jwt_response',
}
Then in functions.py add the following
def custom_jwt_response(token, user=None, request=None):
import jwt
jwt = jwt.decode(token, verify=False)
return {
'token': token,
'token_exp': jwt['exp'],
'user': UserSerializer(user, context={'request': request}).data
}
The answers here are good but in my opinion they don't make full use of inheritance. When we inherit a class, we shouldn't just try to reinvent the wheel and instead make use of the super() keyword. Here is my code example, where I want to turn the username argument into lowercase before performing the authentication request:
class GetAuthToken(ObtainAuthToken):
"""
Override Django's ObtainAuthToken to provide custom way of authenticating user for token
"""
def post(self, request, *args, **kwargs):
#-- turn username to lowercase
if ('username' in request.data):
request.data['username'] = request.data['username'].lower()
#-- perform normal function
return super().post(request, *args, **kwargs)
I am very new in the python world and now I building an application with Django 1.8 with the Rest Framework and I want to create a class view to DRY my code.
For example I want to have a class view for the students in my system
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class SnippetList(APIView):
def getWorstStudents(self, request, format=None):
# Logic here
How can I assign a specified URL in urls.py to hit this method?
And also I have implemented REST framework JWT Auth
http://getblimp.github.io/django-rest-framework-jwt/ for token auth.
How can I restrict the access to allow only authenticated users can access to this url?
Thank you in advance!
Use routers with viewsets.
First, subclass your view from ModelViewSet instead of from APIView. Second, use the #list_route decorator in your getWorstStudents method. Third, tie everything up in your urls.py with a router.
It should look like (I have not tested the code):
views.py
class StudentsViewSet(viewsets.ViewSet):
#list_route(methods=['get'], permission_classes=(IsAuthenticated,)) # you can define who can access this view here
def getWorstStudents(self, request, format=None):
# Logic here
# routers.py
router = routers.DefaultRouter()
router.register(r'students', views.StudentsViewSet, base_name='student')
# urls.py
import .routers import router
urlpatterns = [
url(r'^', include(router.urls)),
]
The router will generate a view with the name student-getWorstStudents accessible from the students/getWorstStudents url.
You can set urls like any other Django app, documented here
# urls.py
from django.conf.urls import url
from somewhere import SnippetList
urlpatterns = [
url(r'^your/url/$', SnippetList.as_view()),
]
About DRY with your method, you can define the method you want to response to, and call the getWorstStudents (btw by convention I would call it get_worst_students). Let's say you want to response the post method:
# views.py
from rest_framework.response import Response
def getWorstStudents(params)
class SnippetList(APIView):
def post(self, request, *args, **kwargs):
# call getWorstStudents method here and response a Response Object
You can define getWorstStudents inside SnippetList class or in other file to import wherever you need it.
Finally, about authentication, DRF provides classes for this, documented here.
From docs, you need define this in your settings.py file:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
And use it in your views:
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
content = {
'user': unicode(request.user), # `django.contrib.auth.User` instance.
'auth': unicode(request.auth), # None
}
return Response(content)
You can also define your own authentication class and set it in authentication_classes tuple. Custom authentication classes documented here.
I have developed a simple webservice, but failed to use post with Django Rest Framework as it complains about CSRF:
"detail": "CSRF Failed: CSRF cookie not set."
Removing the api_view decorator does stop the message from appearing but then I won't be able to access the request.data. I think that the api_view does check CSRF although I added the csrf_exempt decorator.
This is my view:
#permission_classes((IsAuthenticated, ))
#csrf_exempt
#api_view(['POST'])
def get_stats(request):
"""
Returns the stats available.
"""
user = request.user
if request.method == 'POST':
serializer = StatsRequestSerializer(data=request.data)
stats_request = serializer.data
return JSONResponse(stats_request)
#serializer = QuizSerializer(user.quizes.all(), many=True)
#return JSONResponse(serializer.data)
response = ActionResponse(status='error', error='Invalid request')
serializer = ActionResponseSerializer(response)
return JSONResponse(serializer.data, status=400)
This is my model:
class StatsRequest(models.Model):
"""
A model which describes a request for some stats for specific users.
"""
start_date = models.DateField()
end_date = models.DateField()
and this is my request POST:
{"start_date" : "1992-01-15", "end_date" : "1992-01-15" }
Any ideas?
More info:
AUTHENTICATION_BACKENDS = (
'social.backends.facebook.FacebookOAuth2',
'social.backends.google.GoogleOAuth2',
'django.contrib.auth.backends.ModelBackend'
)
So, after trying to figure this out for a couple of hours I finally did it.
Tracing the source code of DRF and Django lead me to believe that I need to find a workaround for this as the CSRF verification is made explicitly even if turned off, probably the CSRF check is being made at the api_view decorator. So I simply created my own decorator:
from functools import wraps
from django.utils.decorators import available_attrs, decorator_from_middleware
def csrf_clear(view_func):
"""
Skips the CSRF checks by setting the 'csrf_processing_done' to true.
"""
def wrapped_view(*args, **kwargs):
request = args[0]
request.csrf_processing_done = True
return view_func(*args, **kwargs)
return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)
and my view with the new decorator:
#csrf_clear
#api_view(['POST'])
#permission_classes((IsAuthenticated, ))
def get_stats(request):
"""
Returns the stats available.
"""
user = request.user
if request.method == 'POST':
serializer = StatsRequestSerializer(data=request.data)
if serializer.is_valid():
stats_request = serializer.data
return JSONResponse(stats_request)
#serializer = QuizSerializer(user.quizes.all(), many=True)
#return JSONResponse(serializer.data)
response = ActionResponse(status='error', error='Invalid request')
serializer = ActionResponseSerializer(response)
return JSONResponse(serializer.data, status=400)
urls.py
from django.views.decorators.csrf import csrf_exempt
urlpatterns = [
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', csrf_exempt(views.SnippetDetail.as_view())),
]
views.py
from django.views.decorators.csrf import csrf_exempt
from rest_framework.views import APIView
class SnippetList(APIView):
#csrf_exempt
#need_post_parameters([PARAM_MESSAGE_OBJ])
def post(self, request, *args, **kwargs):
data = request.POST.get(PARAM_MESSAGE_OBJ)
try:
message_obj = json.loads(data)
except Exception as e:
return HttpResponseBadRequest(error_json("Could not parse JSON"))
http://www.chenxm.cc/post/509.html