Django rest framework and python3.5 OrderedDict mutated during iteration - python

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...

Related

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.

DRF Post to ViewSet without writing into Model

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

Django Rest Framework get serializer of different model?

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)

Serializing User model

I have the following code that should return me all the Users that are registered in my site, but for some reason it just returned me the last signed user, I need all the users in my JSON.
from django.shortcuts import render, redirect
import json
from django.http import HttpResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from profiles.serializers import ProfileSerializer
from django.contrib.auth.models import User
from rest_framework.decorators import api_view
class ProfilesAPI(APIView):
serializer = ProfileSerializer
def get(self, request, format=None):
users = User.objects.all()
response = self.serializer(users, many=True)
for j in range(0,len(response.data)):
dictionary = response.data[j]
myresponse = ""
for i, (val, v) in enumerate(dictionary.items()):
myresponse = [{"text":v} for v in dictionary.values()]
print(myresponse)
return HttpResponse(json.dumps({'messages': myresponse}), content_type='application/json')
and throws me this, even I have more than one user registered.
My JSON
This is where your problem lies:
dictionary = response.data[j]
You declare the dictionary and at the same time, you the existing value with the latest value in the loop. By the time the loop exits, The dictionary would contain only one result (The last one in the loop)
you should declare it outside the loop and then call dict.update something like this:
dictionary = {}
for j in range(0,len(response.data)):
dictionary.update(response.data[j])
That solves your problem.
But there's no point generating a this new dictionary. You can just iterate once on response.data if you need to customize the response.
since response.data is a list of users you can do this instead
dictionary = {'messages': []}
for user in response.data:
dictionary['messages'].append({'text': user['id']})
dictionary['messages'].append({'text': user['username']})
Also since you're using django rest framework and the APIView you don't need json.dumps
django framework has a Response class that takes care of that and it also sets the appropriate headers.
from rest_framework.response import Response
and then change your return statement to:
return Response(dictionary)

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....

Categories