Django rest framework APIView register route - django

I am not able to register an APIView to my url routes.
Code from views :
class PayOrderViewSet(APIView):
queryset = PayOrder.objects.all()
Code from urls :
router = routers.DefaultRouter()
router.register(r'document/payorder', PayOrderViewSet)
This newly created url doesn't exist at all.
What is solution for this?

Routers and APIViews (generic or otherwise) are two different ways to create API endpoints. Routers work with viewsets only.
In your code, you are although trying to create a viewset for a router your code is extending APIView class.
Your problem will be taken care by what #linovia has suggested in his asnwer. I would suggest it will be good idea to understand the difference between those two.
GenericViewSet inherits from GenericAPIView but does not provide any implementations of basic actions. Just only get_object, get_queryset.
ModelViewSet inherits from GenericAPIView and includes implementations for various actions. In other words you dont need implement basic actions as list, retrieve, create, update or destroy. Of course you can override them and implement your own list or your own create methods.
Read More about viewsets and Generic Class Based APIViews :

Routers won't work with APIView. They only work with ViewSets and their derivatives.
You likely want:
class PayOrderViewSet(ModelViewSet):

For ViewSet you use router for url register and for APIView you need to add path to urlpatterns. Next example should help:
from post.api.views import UniquePostViewSet
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from post.api.views import FileUploadView
router = DefaultRouter()
router.register('UniquePost', UniquePostViewSet, base_name='uniquepostitem')
urlpatterns = [
path('demo', FileUploadView.as_view(), name='demo'),
]
urlpatterns += router.urls

Related

Django rest_framework DefaultRouter() class vs normal url

I'm using REST in Django, And I couldn't understand what is the main difference between classic URL and instantiating DefaultRouter() for registering URL by ViewSet.
I have a model:
class Article(models.Model):
title = models.CharField()
body = models.TextField()
author = models.ForeignKey()
Serializing model like this:
from blog.models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['title', 'body', 'author']
View Class:
from blog.models import Article
from rest_framework import viewsets
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
serializer_class = ArticleSerializer
queryset = Article.objects.all()
and URLS:
router = DefaultRouter()
router.register(r'articles', ArticleViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Is it possible to use classic URL in URLS.py instead of instantiating the object for a ViewSet like this:
urlpatterns = [
path('api/', 'views.someAPI'),
]
I just know HTTP method in ViewSet translate methods to retrieve, list and etc...
The Question is can we use traditional(Classic) URL style in this situation, Should we ?
Thanks for your help.
Well, in a nutshell as a django developer it is notorious how it is hard to deal with normal urls in django in some cases. Every now and again we get confused with the id type of the detail page that in some case are strings or integers with its regex, and so on.
For example:
urlpatterns = [
url(r'^(?P<content_type_name>[a-zA-z-_]+)$', views.content_type, name = 'content_type'),
]
# or
urlpatterns = [
url(r'^(?P<content_type_name>comics|articles|videos)$', views.content_type, name='content_type'),
]
Not mentioning that in almost every case its needed to have two urls like:
URL pattern: ^users/$ Name: 'user-list'
URL pattern: ^users/{pk}/$ Name: 'user-detail'
THE MAIN DIFFERENCE
However, using DRF routers the example above is done automatically:
# using routers -- myapp/urls.py
router.register(r"store", StoreViewSet, basename="store")
How django will understand it:
^store/$ [name='store-list']
^store\.(?P<format>[a-z0-9]+)/?$ [name='store-list']
^store/(?P<pk>[^/.]+)/$ [name='store-detail']
^store/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='store-detail']
See how much job and headache you have saved with a line of code only?
To contrast, according to DRF documentation the routers is a type of standard to make it easy to declare urls. A pattern brought from ruby-on-rails.
Here is what the documentation details:
Resource routing allows you to quickly declare all of the common
routes for a given resourceful controller. Instead of declaring
separate routes for your index... a resourceful route declares them in
a single line of code.
— Ruby on Rails Documentation
Django rest framework documentation:
Some Web frameworks such as Rails provide functionality for
automatically determining how the URLs for an application should be
mapped to the logic that deals with handling incoming requests.
REST framework adds support for automatic URL routing to Django, and
provides you with a simple, quick and consistent way of wiring your
view logic to a set of URLs.
For more details follow the django rest framework documentation.

Django Rest Framework Custom Endpoints

I have recently inherited an API built with Django and DRF. I need to add some endpoints to the API but have never worked with Django or DRF before so I am trying to come up to speed as quickly as possible.
I am wondering how to do custom endpoints that don't just translate data too/from the backend database. A for instance might be an endpoint that reads data from the DB then compiles a report and returns it to the caller in JSON. But I suppose that right now the simplest method would be one that when the endpoint is hit just prints 'Hello World' to the log and returns a blank page.
I apologize if this seems basic. I've been reading through the docs and so far all I can see is stuff about serializers when what I really need is to be able to call a custom block of code.
Thanks.
if you want your REST endpoint to have all: GET, POST, PUT, DELETE etc. functionality then you have to register a route in your urls.py:
urls.py:
from rest_framework import routers
from django.urls import path, include
from . import views
router = routers.DefaultRouter()
router.register(r'hello', views.HelloWorldViewSet)
urlpatterns = [
# Wire up our API using automatic URL routing.
# rest_framework api routing
path('api/', include(router.urls)),
# This requires login for put/update while allowing get (read-only) for everyone.
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
now the url: /hello/ points to the HelloWorldViewSet.
in your views.py add the HelloWorldViewSet that will inherits from the rest_framework.viewsets.ViewSet class. You can override the ViewSet default class behavior by defining the following "actions": list(), create(), retrieve(), update(), partial_update(), destroy(). For displaying "hello world" on GET you only need to override list():
so in your views.py:
from rest_framework import viewsets
from rest_framework.response import Response
class HelloWorldViewSet(viewsets.ViewSet):
def list(self, response):
return Response('Hello World')
So, in your more advanced list() function you have to interact with the database, to retrieve the data you want, process it and create the report as a json serializable dictionary and return it as a Response object.
If you don't want to override the standard list action, you could instead add a new action to the HelloWorldViewSet let's call it report:
so in your views.py:
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.decorators import action
class HelloWorldViewSet(viewsets.ViewSet):
#action(detail=False)
def report(self, request, **kwargs):
return Response('Hello World')
I hope this is what you were looking for.
Note that you don't need django-rest-framework if you are not interested in POST, PUT, PATCH, DELETE, etc... you can simply add a path to your urls.py that points to a Django view function that returns a Django JsonResponse object containing your report.
One good option for you would be DRF actions. Docs here
The action allows you to choose a relevant view for what you wanna do, so you can just pop things into the API you inherited. No additional setup needed, they show up next to the regular routes.

JWT authentication doesn't work for custom controller in Django

I am using the Django Rest Framework in my Python app, and am using JSON Web Token Authentication (DRF JWT) for the api authentication.
My problem comes when I am building a custom controller. I pointed a specific URL to a function in my calculations.py file that I created. Following are how they look.
urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from rest_framework import routers
from app.serializers import xxxViewSet, yyyViewSet
from app.calculations import getReturns
router = routers.DefaultRouter()
router.register(r"xxx", xxxViewSet)
router.register(r"yyy", yyyViewSet)
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^api/auth/token/$', 'rest_framework_jwt.views.obtain_jwt_token'),
url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api-token-verify/', 'rest_framework_jwt.views.verify_jwt_token'),
url(r'^api/', include(router.urls)),
**url(r'^getReturns/', getReturns),**
)
calculations.py
from django.http import HttpResponse
from .models import xxx, yyy, zzz, aaa
def getReturns(request):
data = request.GET('data')
**running calculations here on data and giving out response**
return HttpResponse(response)
serializers.py
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework import routers, serializers, viewsets, permissions
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from .models import xxx, yyy, zzz, aaa
class xxxSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = xxx
fields = ('id', 'name')
class xxxViewSet(viewsets.ModelViewSet):
authentication_classes = [SessionAuthentication, BasicAuthentication, JSONWebTokenAuthentication]
permission_classes = [permissions.IsAuthenticated, permissions.IsAdminUser]
queryset = xxx.objects.all()
serializer_class = xxxSerializer
The above serializers.py file contains serializer classes for all my models, and also viewsets for the same. I haven't yet transferred the viewsets into views.py, so that file is empty for now.
Anyway, my calculations.pyis separate from these files, and the function defined in this file is directly being called by the '/getReturns/' URL without going through a view. How do I incorporate the functions defined in my calculations file into a viewset so that my authorization classes are called before the function gets executed?
I started doing this in comments and it was too long. Generally, you haven't really provided enough code to assist properly, but here's my crack anyway. It's not obvious which version/implementation of Django JWT you're using (there are a few), how you're authorising your views, or whether your calculations.py file is a view or something else. (If it's something else, I'd authorise in the view, and call it from there.)
Why are you unable to send a POST? Generally, Once you have the token in your front end, you can use from rest_framework.decorators import authentication_classes and #authentication_classes([JSONWebTokenAuthentication,]) wrapper on any function that needs authorisation.
That looks like this:
#authentication_classes([JSONWebTokenAuthentication,])
def function_here(arguments):
#function does stuff
How are you passing/trying to send the web token back to the application?
Presumably in CURL your initial auth to get a token looks something like:
curl -X POST -d "username=admin&password=abc123
after that you get (if you're using rest_framework_jwt) the token back:
{JWTAuthorization: YourTokenHere}.
After that, to return it to DRF protected pages (assuming they're wrapped, as above, or have a similar protection) - you haven't outlined how you're authorisation - then from the docs you do:
curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/
If you're generating the call in Angular or similar, then it's the same - you need to pass it in the headers.
Edit: I'd also note you've drastically increased the amount of code here since my original answer. Fundamentally though, you need to check the auth classes you're declaring; the easiest way is as specified above.

The smartest way to redirect in django without creating view

The task is simple:
If user visits site root then:
if user is authenticated then:
redirect to /dashboard/
else:
redirect to settings.LOGIN_URL
There are many ways to implement that, but I wonder if there is such way in which I do need to use only urls.py.
I found a solution with RedirectView login_required(RedirectView.as_view(url=my_url)), however then I can only write static my_url instead of reverse(), which is not flexible.
You could use reverse_lazy (Django 1.4) in you url configuration, like so:
from django.conf.urls.defaults import url, patterns
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
urlpatterns = patterns('',
url(r'^/$', lambda request: return redirect(reverse_lazy('url_name')),
)
Another possibility is to define LOGIN_URL using reverse_lazy, so you could continue to use settings.LOGIN_URL in your redirects.
Code is untested, might have a typo somewhere.
You just need to mixin LoginRequired to your view. You can find an example of the mixin here:
http://djangosnippets.org/snippets/2442/
Then where you define that view, you just do:
class RedirectView(LoginRequiredMixin, DetailView):
....
Or whatever Class Based View you're inheriting from. Hope that helps!

How to get current application in Django

I'm using the Django 1.3 alpha to create a project with two applications. I use 1.3 because of the class-based views. For these two applications I have a set of common base view-classes, that are inherited by the actual views in the applications. In the base class, is there a way to find out what application the view is "called" from? E.g. can I use the URL to get the "current" application?
If you are inheriting from the generic list and detail views that django provides you could access self.model to gain access to the model that the view displays information about, otherwise you will probably have use django's resolve(): resolve(self.request.path).
You also could make your own View subclass that you call with a keyword of your choice:
# views.py
from django.views.generic.base import View
class MyView(View):
app_name = None
# urls.py
from django.conf.urls.defaults import *
from some_app.views import MyView
urlpatterns = patterns('',
(r'^myview/', MyView.as_view(app_name='app_name')),
)
Then you should be able to access it through self.app_name.