Django Rest Framework get serializer of different model? - python

I have a method in one of my viewsets:
Endpoint: /api/game/{id}/sessions:
from .serializers import GameSerializer
from .models import Game
from gamesessions.models import GameSession
from gamesessions.serializers import GameSessionSerializer
from gamesessions.viewsets import GameSessionViewSet
#action(methods=['get'], detail=True)
def sessions(self, request, **id):
game = self.get_object()
sessions = []
for session in GameSession.objects.filter(game=game.id):
sessions.append(session)
serializer = GameSessionViewSet.get_serializer(sessions, many=True)
return Response(serializer.data)
But I'm getting an error because I can't figure out where the get_serializer method comes from and/or how to implement it externally.
I need to get the serializer of the session model. I can generate the list of sessions just fine, but it says the object is not JSON serializable, which is what DRF is supposed to handle.
So I just need to know what do I import to get the seralizer from the other class?

Maybe you can try, to use the serializer directly with the name like this:
serializer = GameSessionSerializer(sessions, many=True)

Related

Pagination in Django-Rest-Framework with API-View

It is necessary to display the configured 10 records per swagger page (book api).
The BookListView class in views.py looks like this:
class BookListView(APIView):
def get(self, request):
author = set([item.get('ID') for item in Author.objects.values('ID').all()])
saler = set([item.get('name') for item in Saler.objects.values('name').all()])
if author:
salers = Saler.objects.filter(name__in=list(saler - author))
else:
salers = Saler.objects.all()
serializer = SalerListSerializer(salers, many = True)
return Response(serializer.data)
Now all records are displayed at once, I would like to add pangination and display 10 records on one page.
I added 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.DESIRED_PAGINATION_STYLE', 'PAGE_SIZE': 100 to the settings.py file, but since I am using APIView, this does not work.
What is the way to implement the idea?
You need to call your paginator's paginate_queryset method. Since you're using a APIView, it does not have many of the built-in functions to do this for you, but the process is as follows:
Instantiate a paginator:
from rest_framework.settings import api_settings
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
paginator = pagination_class()
Get a page from your queryset and serialize it:
page = paginator.paginate_queryset(queryset, request, view=self)
serializer = self.get_serializer(page, many=True)
Return a paginated response:
return paginator.get_paginated_response(serializer.data)
This is how django-rest does it with its ListAPIView class. You can go though the code easily by checking out the ListAPIView's list method here.
However, I suggest using a ListAPIView instead of an APIView since it already handles
pagination for you.

i'm not able to understand why i have to restart the server to see the changes made by a Put request (django rest framework)

The put request is working, but if i want to see the post updated i have to restart the server.
This is the view function:
from rest_framework import status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from blog.models import Post
from .serializers import PostSerializer
from django.contrib.auth.models import User
#api_view(['PUT'])
def api_update_post_view(request, Slug):
try:
blog_post = Post.objects.get(slug=Slug)
except Post.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = PostSerializer(blog_post, data=request.data, partial=True)
data = {}
if serializer.is_valid():
serializer.save()
data['succes'] = 'update successful'
return Response(data=data)
return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
I didn't see any special features in your PUT method. So, I suggest you to use ModelViewSet instead of creating a basic PUT method yourself. ModelViewSet will generate basic CRUD operations for you automatically.
Here is the official docs. It should be no problem after you use the ModelViewSet.

Additional views to Django Rest Framework ViewSet

I've got simple DRF ViewSet for a model, located at /gen_req/
class GenerationRequestViewSet(viewsets.ModelViewSet):
queryset = GenerationRequest.objects
serializer_class = GenerationRequestSerializer
It has default POST/GET/etc. handlers. However, I want to add another one for GET as well for different url patter (/gen_req/created_list:
class GenerationRequestViewSet(viewsets.ModelViewSet):
queryset = GenerationRequest.objects
serializer_class = GenerationRequestSerializer
#action(methods=['get'])
def special_get_handler(self, request):
queryset = GenerationRequest.filter(...) # Some extra filtering here
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Is there a way to create such view within the ViewSet or another APIView should be created?
You can do it, just add such record to your urls.py file.
path('/gen_req/created_list',
GenerationRequestViewSet.as_view({'get': 'special_get_handler'}),),
You can do that, but you need to name your method accordingly. So with code, you've posted you will be able to get this method by requesting /gen_req/special_get_handler.
Of course, it should be registered in url.py. Smth like:
api_router = DefaultRouter()
api_router.register("gen_req", GenerationRequestViewSet)

Wagtail: Serializing page model

I am using wagtail as a REST backend for a website. The website is built using react and fetches data via wagtails API v2.
The SPA website needs to be able to show previews of pages in wagtail. My thought was to override serve_preview on the page model and simply seralize the new page as JSON and write it to a cache which could be accessed by my frontend. But im having trouble serializing my page to json. All attempts made feel very "hackish"
I've made several attempts using extentions of wagtails built in serializers but without success:
Atempt 1:
def serve_preview(self, request, mode_name):
from wagtail.api.v2.endpoints import PagesAPIEndpoint
endpoint = PagesAPIEndpoint()
setattr(request, 'wagtailapi_router',
WagtailAPIRouter('wagtailapi_v2'))
endpoint.request = request
endpoint.action = None
endpoint.kwargs = {'slug': self.slug, 'pk': self.pk}
endpoint.lookup_field = 'pk'
serializer = endpoint.get_serializer(self)
Feels very ugly to use router here and set a bunch of attrs
Attempt 2:
def serve_preview(self, request, mode_name):
from wagtail.api.v2.endpoints import PagesAPIEndpoint
fields = PagesAPIEndpoint.get_available_fields(self)
if hasattr(self, 'api_fields'):
fields.extend(self.api_fields)
serializer_class = get_serializer_class(
type(self), fields, meta_fields=[PagesAPIEndpoint.meta_fields], base=PageSerializer)
serializer = serializer_class(self)
Better but i get context issues:
Traceback (most recent call last):
...
File "/usr/local/lib/python3.5/site-packages/wagtail/api/v2/serializers.py", line 92, in to_representation
self.context['view'].seen_types[name] = page.specific_class
KeyError: 'view'
Any toughts?
Solved it by diving through the source code.
First define an empty dummy view:
class DummyView(GenericViewSet):
def __init__(self, *args, **kwargs):
super(DummyView, self).__init__(*args, **kwargs)
# seen_types is a mapping of type name strings (format: "app_label.ModelName")
# to model classes. When an object is serialised in the API, its model
# is added to this mapping. This is used by the Admin API which appends a
# summary of the used types to the response.
self.seen_types = OrderedDict()
Then use this view and set the context of your serializer manually. Im also using the same router as in my api in my context. It has methods which are called by the PageSerializer to resolve some fields. Kinda strange it is so tightly coupled with the wagtail api but at least this works:
def serve_preview(self, request, mode_name):
import starrepublic.api as StarApi
fields = StarApi.PagesAPIEndpoint.get_available_fields(self)
if hasattr(self, 'api_fields'):
fields.extend(self.api_fields)
serializer_class = get_serializer_class(
type(self), fields, meta_fields=[StarApi.PagesAPIEndpoint.meta_fields], base=PageSerializer)
serializer = serializer_class(
self, context={'request': request, 'view': DummyView(), 'router': StarApi.api_router})
Dont forget to import:
from wagtail.api.v2.serializers import get_serializer_class
from rest_framework.viewsets import GenericViewSet
from rest_framework import status
from rest_framework.response import Response
from django.http import JsonResponse
from django.http import HttpResponse
Possibly a non-answer answer, but I too have had challenges in the area of DRF, Wagtail's layering on top of DRF, and the need to cache json results (DRF has no built-in caching as far as I can tell, so that's an additional challenge). In a recent project, I ended up just building a list of dictionaries in a view and sending them back out with HttpResponse(), bypassing DRF and Wagtail API altogether. The code ended up simple, readable, and was easy to cache:
import json
from django.http import HttpResponse
from django.core.cache import cache
data = cache.get('mydata')
if not data:
datalist = []
for foo in bar:
somedata = {}
# Populate somedata, "serializing" fields manually...
datalist.append(somedata)
# Cache for a week.
data = datalist
cache.set('mydata', datalist, 60 * 60 * 24 * 7)
return HttpResponse(json.dumps(data), content_type='application/json')
Not as elegant as using the pre-built REST framework, but sometimes the simpler approach is just more productive...

Django rest framework and python3.5 OrderedDict mutated during iteration

I use Django rest framework and python3.5. Earlier I had another version of python and everything was going right. When I want to get some information from server with URL for example like:
"http://127.0.0.1:8000/api/companies"
I'm getting error:
"OrderedDict mutated during iteration"
.
In views.py I have:
from django.shortcuts import render
from companies.models import Companies
from companies.serializers import CompaniesSerializer
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer
from rest_framework import status
class CompaniesList(generics.ListCreateAPIView):
queryset = Companies.objects.all()
serializer_class = CompaniesSerializer
class CompaniesDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Companies.objects.all()
serializer_class = CompaniesSerializer
What should I do to make it working? Where is something mutating the dict?
I don't know why using ListCreateApiView is mutating dict, but I changed class into function like :
#api_view(['GET'])
def CompaniesList(request):
if request.method == 'GET':
companies = Companies.objects.all()
serializer = CompaniesSerializer(companies, many=True)
return Response(serializer.data)
and now it's working...

Categories