djangorestframework: use kwargs instead of requests - django

My code is littered with a lot of parsing keys from request.data objects inside functions, which makes it hard to follow data flow. How can I change from
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def greet(request):
# FYI for Character.AI we use the chat and not this stateless_chat view.
payload = request.data
user = payload.get("user", "test")
friend = payload.get("friend", "bart")
num_friends = int(payload.get("num_friends", 16))
return None
to
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def greet(user:str='test', friend:str='bart', num_friends:int=16):
return None

You could take a look at DRF Serializers (https://www.django-rest-framework.org/api-guide/serializers/). In addition to cleaning up your code they provide additional advantages like validation for example.
It's not exactly what you wanted (I don't think that's even possible) but using Serializers is following best practices working with DRF.

Related

Set some views which use Django Rest Framework API

I have a very basic Django Rest API.
I don't know how to have some HTML views, in the same django project, which uses API (finally keeping API returning JSON only).
I followed this, but it seems to change the API View (in this case, curl will retrieve HTML and not JSON) :
https://www.django-rest-framework.org/api-guide/renderers/#templatehtmlrenderer
Do I need another Django App ? Another Django project ? Some JS ?
EDIT :
Ok, I've seen it's possible, thanks to rrebase.
But I can't retrieve the JSON with Curl, here my views.py
from rest_framework import generics
from rest_framework.renderers import TemplateHTMLRenderer, JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import IsAdminUser
from . import models
from . import serializers
class UserListView(generics.ListAPIView):
renderer_classes = [JSONRenderer, TemplateHTMLRenderer]
template_name = 'profile_list.html'
def get(self, request):
queryset = models.CustomUser.objects.all()
serializer_class = serializers.UserSerializer
return Response({'profiles': queryset})
My models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
def __str__(self):
return self.email
I get an error "Object of type 'CustomUser' is not JSON serializable" when I request the API (http://127.0.0.1:8000/api/v1/users/)
Sorry, it's some different that initial question...
Yes, you can have both. The link you provided to docs has the following:
You can use TemplateHTMLRenderer either to return regular HTML pages using REST framework, or to return both HTML and API responses from a single endpoint.
When making an API request, set the ACCEPT request-header accordingly to html or json.
Finally I made some conditions in my view, and it's working
class UserListView(generics.ListAPIView):
renderer_classes = [JSONRenderer, TemplateHTMLRenderer]
permission_classes = (IsAdminUser,)
def get(self, request):
queryset = CustomUser.objects.all()
if request.accepted_renderer.format == 'html':
data = {'profiles': queryset}
return Response(data, template_name='profile_list.html')
else:
serializer = UserSerializer(queryset, many=True)
data = serializer.data
return Response(data)

Coverage show test get_serializer_class as untested

Use coverage to show what have to be covered by tests drf views. And coverage show that all view is tested ok (cover by unittest + dango client API)
But coverage shows that this part need to be covered:
def get_serializer_class(self):
return self.serializer_class `
I think that that code possible to delete as it useless (it is not my code):)
Any idea how to cover that past of code in GenericAPIView? Thx for any help
There are two approaches to specify the serializer class:
First approach is to set the serializer_class attribute in your class.
Second approach is to override the get_serializer_class()method.
If you already added the serializer_class attribute in your class (first approach) then the get_serializer_class() is definitely useless.
This is the example:
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
class UserList(generics.GenericAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
In most situations you should use the first approach because usually you will need only one serializer for your API view. Second approach is useful for dynamic behavior, such as using different serializers for read and write operations, or providing different serializers to different types of users.
Example:
def get_serializer_class(self):
if self.request.user.is_staff:
return StaffSerializer
return BasicSerializer

Custom view and url for DRF

I'd like to know how to add a custom view and url using DRF.
I currently have a UserDetail(APIView) class that can display a user object using a url like /users/123/ but I'd like to also have the ability to view a users history with a url like /users/123/history/ which would likely call on a new method within the UserDetail class. Is there a way to do this?
I've tried looking through DRFs documentation and it looks like they can achieve this through ViewSets and custom Routers, but I get errors when using ViewSets likes needing to define a queryset.
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class UserDetail(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserCreateSerializer
permission_classes = (IsAuthenticated,)
#detail_route(methods=['GET'])
def history(self, request, pk):
user= self.get_object()
serializer = UserCreateSerializer(user)
return Response(serializer.data)

Django rest framework complex return info for a single api

How to return complex information in a single api under Django rest framework?
Assuming I have a model:
class Apple(models.Model):
color
size
shape
With a single api: /api/get-apples, I want to return a json like below:
{"red": {"round":[0.1,0.2,0.3],"spigold":[0.3,0.4,0.5]},
"yellow":{"round":[0.1,0.2,0.4],"spigold":[0.2,0.4,0.5]}}
What will be the best way to achieve this?
Create a serializers.py in your app's folder and add this code into it.
from rest_framework import serializers
class AppleSerializer(serializers.ModelSerializer):
class Meta:
model = Apple
fields = ('color', 'size', 'shape',)
In your views.py:
from rest_framework.generics import ListAPIView
from .serializers import AppleSerializer
class get_apples(ListAPIView):
serializer_class = AppleSerializer
def get_queryset(self):
# Here create your queryset.
queryset = Apple.objects.all()
return queryset
In your urls.py:
url(r'^api/get_apples/', views.get_apples.as_view(), name='get_apples'),
And you are good to go.
Output will be like this.
Let's say you have 2 apples.
{{"color": "red", "size": "blabla", "shape": "round"},{...(another apple json)}}
I'd edit my previous answer but I think it is a good example of using serializers with covering view,urls and serializer parts. Therefore, I didn't want to delete it :)
Here is how to return a complex json structure.
As I mentioned before, as far as I know, we can't do something like that by using rest framework's serializers class because it need comparison and grouping. I'll use rest_framework's api_view structure.
Also, I didn't understand types of size and shape in your model and what is your desired output. Therefore, this might be wrong but you'll get the idea anyway.
from rest_framework.decorators import api_view
from django.http import HttpResponse
import json
#api_view(['GET'])
def get_apples(request):
# define your queryset here as you want.
basket = Apple.objects.all()
data = {} # empty dictionary
for apple in basket:
if data.get(apple.color, None) is not None: # if same color of apple exists in basket
# check if shape exists or not.
if data.get(apple.color).get(apple.shape, None) is not None:
data.get(apple.color).get(apple.shape).append(apple.size)
else:
data.get(apple.color)[apple.shape] = [apple.size]
else:
data[apple.color] = {apple.shape: [apple.size]}
return HttpResponse(json.dumps(data), content_type='application/json; charset=utf-8')
I didn't test this code but probably this will work. Let me know if this works or not!

django-rest-framework limit the allowed_methods to GET

I Have just started with django-rest-framework.
Pretty enthousiastic about it, except for the fact there are very little examples available.
getting the api working is going great, but all the extra's is a puzzle.
(adding extra custom fields etc.)
Now I wonder how you can restrict the allowed_methods in for example a ListView or a DetailView.
Adding this to the class in the views.py like I read somewhere as an answer... does not seem to have any effect:
allowed_methods = ('GET',)
If you are using ModelViewSet and still want to restrict some methods you can add http_method_names.
Example:
class SomeModelViewSet(viewsets.ModelViewSet):
queryset = SomeModel.objects.all()
serializer_class = SomeModelSerializer
http_method_names = ['get', 'post', 'head']
Once you do this, get, post and head will be allowed. But put, patch and delete will not be allowed.
Sorry for necro, but I stumbled upon this question looking for a similar issue.
I only wanted to allow retrieve() but not to list(). What I ended up doing:
from rest_framework import viewsets
from rest_framework.exceptions import MethodNotAllowed
from myapp.models import MyModel
class MyViewSet(viewsets.ModelViewSet):
http_method_names = ["get"]
queryset = MyModel.objects.all()
serializer_class = MySerializer
def list(self, request, *args, **kwargs):
raise MethodNotAllowed("GET")
As almost everything in django-rest-framework, once you find it out, its very simple.
In the urls in stead of using ListOrCreateModelView I had to use ListModelView.
Besides the http_method_names, there's this solution that Django Rest Framework proposes in here: https://www.django-rest-framework.org/api-guide/viewsets/#custom-viewset-base-classes
It consists on inheriting from GenericViewSet instead of ModelViewSet, and from the appropiate mixin classes.
If you want a viewset that provides only the list and retrieve methods (both of them use GET), then do this:
class ListRetrieveViewSet(
Viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
)
Probably not relevant anymore based on recent years downvotes.. It was relevant in '12 tho :)
Django-rest-framework actually have very many examples..
Take a look at http://django-rest-framework.org, http://django-rest-framework.org/contents.html and http://rest.ep.io/ for some good examples and documentation.
If you are designing a REST function by yourself, not using any of the django-rest-framework magic (like rest.ep.io) to generate it for you, you should look into mixin (http://django-rest-framework.org/howto/mixin.html).
If you want to restrict to only get methods. Just use def get(...) and the mixin class.
Example from link provided:
curl -X GET http://rest.ep.io/mixin/
urls.py
from djangorestframework.compat import View
from djangorestframework.mixins import ResponseMixin
from djangorestframework.renderers import DEFAULT_RENDERERS
from djangorestframework.response import Response
from django.conf.urls.defaults import patterns, url
from django.core.urlresolvers import reverse
class ExampleView(ResponseMixin, View):
renderers = DEFAULT_RENDERERS
def get(self, request):
response = Response(200, {'description': 'Some example content',
'url': reverse('mixin-view')})
return self.render(response)
urlpatterns = patterns('',
url(r'^$', ExampleView.as_view(), name='mixin-view'),
)