DRF Post to ViewSet without writing into Model - python

I've always written data into database when posting via Django Rest Framework endpoints. This time I would like to process received data and send it somewhere else without writing into DB. I switched from ModelViewSet to ViewSet, I can issue GET request OK but receiving Bad Request 400 when I curl or POST via DRF URL. Here's a working minimal code (removed need for authentication etc):
urls.py
from django.urls import path, include
from .views import ContactView
from rest_framework import routers
router = routers.DefaultRouter()
router.register('message', ContactView, basename='message')
urlpatterns = [
path('', include(router.urls)),
]
serializers.py
from rest_framework import serializers
class ContactSerializer(serializers.Serializer):
text = serializers.CharField(max_length=250)
views.py
from rest_framework.response import Response
from .serializers import ContactSerializer
from rest_framework import viewsets
class ContactView(viewsets.ViewSet):
def list(self, request):
return Response('Got it')
def create(self, request):
serializer = ContactSerializer(data=request.data)
if serializer.is_valid():
return Response(serializer.data)
else:
return Response('Invalid')
Would greatly appreciate your suggestions.

You can use GenericAPIView for get or post request and do some logic in validate method, for example do something with signals or edit something. Also u can use #detailt_route or #list_route for any ModelViewSet for write special url for instance, example for edit extra data.
how i did rarely:
in urls.py
urlpatterns = [
url('v1/message', ContactAPIView.as_view(), name='message'),
]
in view.py
class ContactAPIView(GenericAPIView):
serializer_class = ContactSerializer
permission_classes = ()
def post(self, request, *args, **kwargs):
serializer_class = self.get_serializer_class()
serializer = serializer_class(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
data = {"status": True}
return Response(data)
in serializers.py
class ContactSerializer(serializers.Serializer):
text = serializers.TextField()
def validate(self, attrs):
write some logic

you are getting this error because you are using Viewsets which uses DefaultRouter to register routers for you. What it does is that it creates 2 urls for your viewset
message
message/id
so in your current case; i.e. viewset you need to send some dummy number in your url to access this post function (which is not a good approach).
So, you should use any class which parent doesn't include ViewSetMixin (which gives functionality of Router Registration) like in your case inherit your view from these classes
ListModelMixin
CreateModelMixin
GenericAPIView

Related

Empty `request.user.username` while handling a GET request created

I was trying out logging all URLs accessed by user along with user id and date time when it was accessed using django middleware as explained here.
For some URLs it was not logging user id. I checked and found that the request.user.username was empty string. I checked views corresponding to those URL and found that those views did not have desired decorators. For example, I changed this:
def getXyz_forListView(request):
# view body ...
to this:
#api_view(['GET'])
#authentication_classes([TokenAuthentication,])
def getXyz_forListView(request):
# view body ...
and it started working.
However some views are created from classes:
class XyzView(View):
def get(self, request):
# view body ...
I added same decorators:
class XyzView(View):
#api_view(['GET'])
#authentication_classes([TokenAuthentication,])
def get(self, request):
# view body ...
But it is still not working. What I am missing?
PS:
It is added to urls.py as follows:
urlpatterns = [
# ...
url(r'^xyz/', XyzView.as_view(), name="xyz"),
]
I think you should try to inherit from APIView class:
from rest_framework.views import APIView

Return custom response to successful POST request in django rest framework

I want to return a custom response to the user when they hit the API with a POST request and it's a success. Here are the code snippets :
views.py
class BlogPostAPIView(mixins.CreateModelMixin,generics.ListAPIView):
# lookup_field = 'pk'
serializer_class = BlogPostSerializer
def get_queryset(self):
return BlogPost.objects.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
def post(self,request,*args,**kwargs):
return self.create(request,*args,**kwargs)
urls.py
app_name = 'postings'
urlpatterns = [
re_path('^$', BlogPostAPIView.as_view(),name='post-create'),
re_path('^(?P<pk>\d+)/$', BlogPostRudView.as_view(),name='post-rud'),
]
Right now it's returning the details of the post request as successful response, is there any way I can return some other response based on my own custom queryset?
You can write custom api on views.py. I want to for example;
from rest_framework.views import APIView
from rest_framework.response import Response
class Hello(APIView):
#csrf_exempt
def post(self, request):
content = "Hi"
type = "message"
return Reponse({"content":content,"type":type})
and than define url.
app_name = 'postings'
urlpatterns = [
re_path('^$', BlogPostAPIView.as_view(),name='post-create'),
re_path('^(?P<pk>\d+)/$', BlogPostRudView.as_view(),name='post-rud'),
re_path('^hello/$', Hello.as_view(),name='Hello'),
]
That's it.
Also you can manage permessions : https://www.django-rest-framework.org/api-guide/permissions/#setting-the-permission-policy
and you can use serializer on views : https://www.django-rest-framework.org/api-guide/serializers/#saving-instances

Django Rest Framework: Proper retrieve view doesn't handle request when I slightly change parameter

I have this weird problem where when I try to retrieve one object from my database I receive the message {"detail":"Not found."}. I know the object is there because I can see it in my django admin. And when I try to retrieve another object it locates it fine.
urls.py
router = routers.DefaultRouter()
router.register(r'recalls', views.Recalls)
admin.autodiscover()
from rest_framework import generics, permissions, serializers
from oauth2_provider.contrib.rest_framework import TokenHasReadWriteScope, TokenHasScope
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(router.urls)),
path('api/v1/recalls/rundate/<str:run_date>/', views.Recalls.as_view({'get': 'retrieve'}), name='retrieve_by_rundate'),
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),,
]
views.py
class Recalls(viewsets.ModelViewSet):
'''
This view will be fore retrieving a recall for a car from the database
'''
queryset = CarFax.objects.all()
serializer_class = RecallsSerializer
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
def list(self, request, **kwargs):
queryset = GetRecalls.objects.all()
serializer = RecallsSerializer(queryset, many=True)
print('LIST')
return Response(serializer.data)
def retrieve(self, request, pk=None, *args, **kwargs):
queryset = CarFax.objects.all()
#record = get_list_or_404(queryset, self.kwargs)
record = get_list_or_404(queryset, vin__exact=pk)
serializer = RecallsSerializer(record, many=True)
print('RETRIEVE')
return Response(serializer.data)
def retrieve_by_rundate(self, request, run_date=None):
queryset = CarFax.objects.all()
#record = get_list_or_404(queryset, self.kwargs)
record = get_list_or_404(queryset, run_date__exact=run_date)
serializer = RecallsSerializer(record, many=True)
print('RETRIEVE RUNDATE')
return Response(serializer.data)
I am using the URL 'http://127.0.0.1:8000/api/v1/recalls/(?P<pk>[\w-]+)/$/' (which is automatically created by DefaultRouter(). The issue is that when I look up with one parameter, let's say test1 so
'http://127.0.0.1:8000/api/v1/recalls/test1' it works, and in my console I can see the message "RETRIEVE" from my "retrieve" method being printed. However, when I replace that with another object I created and I know exists, it isn't located and that "RETRIEVE" message isn't printed. So that view obviously isn't even being reached, why is that?
Thanks in advance

Python Django Rest Post API without storage

I would like to create a web api with Python and the Django Rest framework. The tutorials that I have read so far incorporate models and serializers to process and store data. I was wondering if there's a simpler way to process data that is post-ed to my api and then return a JSON response without storing any data.
Currently, this is my urls.py
from django.conf.urls import url
from rest_framework import routers
from core.views import StudentViewSet, UniversityViewSet, TestViewSet
router = routers.DefaultRouter()
router.register(r'students', StudentViewSet)
router.register(r'universities', UniversityViewSet)
router.register(r'other', TestViewSet,"other")
urlpatterns = router.urls
and this is my views.py
from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import University, Student
from .serializers import UniversitySerializer, StudentSerializer
import json
from django.http import HttpResponse
class StudentViewSet(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
class UniversityViewSet(viewsets.ModelViewSet):
queryset = University.objects.all()
serializer_class = UniversitySerializer
class TestViewSet(viewsets.ModelViewSet):
def retrieve(self, request, *args, **kwargs):
return Response({'something': 'my custom JSON'})
The first two parts regarding Students and Universities were created after following a tutorial on Django setup. I don't need the functionality that it provides for creating, editing and removing objects. I tried playing around with the TestViewSet which I created.
I am currently stuck trying to receive JSON data that gets posted to the url ending with "other" and processing that JSON before responding with some custom JSON.
Edit
These two links were helpful in addition to the solution provided:
Django REST framework: non-model serializer
http://jsatt.com/blog/abusing-django-rest-framework-part-1-non-model-endpoints/
You can use their generic APIView class (which doesn't have any attachment to Models or Serializers) and then handle the request yourself based on the HTTP request type. For example:
class RetrieveMessages(APIView):
def post(self, request, *args, **kwargs):
posted_data = self.request.data
city = posted_data['city']
return_data = [
{"echo": city}
]
return Response(status=200, data=return_data)
def get....

POST JSON request data dropped/disappear when sending to djangorestframework

I get stuck on making a simple RESTful test using django-rest-framework. I am new beginner to this. Please help. Thanks in advance!
version:
django >= 1.9
djangorestframework 3.3.3
python 3.4.3
POST request from terminal
curl -H "Content-Type: application/json" -X POST -d '{"title":"xyz","desc":"xyz"}' http://localhost:3000/api/test/
django's settings.py
INSTALLED_APP = {
'app',
'rest_framework',
#....skip to keep it short
}
# did not set anything
<!-- language-all: lang-python -->
REST_FRAMEWORK = {
}
# models.py
class Test(object):
def __init__(self, title, desc):
self.title = title
self.desc = desc
serializers.py
from rest_framework import serializers
class TestSerializer(serializers.Serializer):
title = serializers.CharField()
desc = serializers.CharField(max_length=200)
class Meta:
model = Test
fields = ('title', 'desc')
views.py
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
class TestView(APIView):
def get(self, request, format=None):
# 1. we use NoSQL, is the following line still work?
# testItems = Test.objects.all()
serializer = SnippetSerializer(testItems, many=True)
return Response(serializer.data)
def post(self, request, format=None):
# 2. Unable to get any json string in request object or request.data at all!
# 3. The entire json seems disappear and get dropped
serializer = TestSerializer(data=request.data)
if serializer.is_valid():
# 4. can save() be overrided and do custom implementation? How?
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py
from django.conf.urls import patterns, url
urlpatterns = patterns(
'app',
url(r'^api/business/$', app.views.TestView.as_view()),
)
My Questions:
we use NoSQL, is the following line still work?
testItems = Test.objects.all()
empty request.data in POST JSON request.
Unable to get any json string in request object
or request.data at all! The entire json seems disappear and get
dropped. Tried to use Fiddler/Postman capture and ensure JSON did sent out
Can save() be overrided and do custom implementation? How?
I don't know about NOSQL but you can test it on shell. I'm guessing probably it works.
About request.data error; request.data is only used for file upload with django rest framework. When you post a json, it's in the body of the request. What you should do is something like this in your view:
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
import json
#csrf_exempt
class TestView(APIView):
# your get request here.
def post(self, request, format=None):
body_unicode = request.body.decode('utf-8')
data = json.loads(body_unicode)
# Now, your json content is stores in data as a dictionary.
serializer = TestSerializer(data=request.data)
if serializer.is_valid():
# 4. can save() be overrided and do custom implementation? How?
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
FYI,I use serializers for just creating json, I don't use it to create object. Therefore, I'm not sure if this version %100 works or not. But I'm pretty sure that 2 line I've added turns JSON to dictionary. If serializer accepts dictionary as a data, it'll work.

Categories