Django CBVs to return a JsonResponse? - django

I have been using Django for some time, but running into some issues with trying something new.
I have built API's with Django-Rest-Framework using Class Based Views, and I have also built API's using Function based views returning a JsonResponse
Now what I am tasked to do is use CBV's to return a JsonResponse without using DRF. I am trying to produce a simple get request
class BusinessDetailView(DetailView):
model = BusinessDetail
def get_queryset(self):
business = BusinessDetail.objects.get(id=self.kwargs.get('pk'))
return JsonResponse({'business': list(business)})
Using the models pk I keep running into issues with this simple request. I am getting a TypeError 'BusinessDetail' object is not iterable and if I make some small changes and override get_object I'll get the same error, or I'll even get a 'BusinessDetail' object is not callable
Does anybody have any tips with using CBVs to return Json, without using DRF?
Thank you all in advance!

i would try something like this:
class BusinessDetailView(DetailView):
model = BusinessDetail
def get_queryset(self):
business = BusinessDetail.objects.get(id=self.kwargs.get('pk'))
return business
def get(self, request, *args, **kwargs):
queryset = self.get_queryset()
data = serializers.serialize("json", queryset)
return JsonResponse(data, status=200, safe=False)

Related

Django Rest Framework query data is not json serialiable Error

This is my views:
class BillDetailView(APIView):
serializer_class = BillSerializer
def get(self, request, format=None):
bill = Bill.objects.get(flat__id='a flat id')
return Response(bill)
I know to get detailed data, we can use RetriveAPIView but I will not use it for some reasons for my business logic.
That is why I am using APIView
I am trying to Response the query data as you see but it firing following error:
Object of type 'Bill' is not JSON serializable
Can anyone help me how to Response the query data?
If I pass a dictionary in Response method, It will work great but you it is bad practice for my case.
I just Pass the queried data in Response method. Can anyone help me in this case?
Serialize the bill object using BillSerializer
#views.py
class BillDetailView(APIView):
serializer_class = BillSerializer
def get(self, request, format=None):
bill = Bill.objects.get(flat__id='a flat id')
return Response(self.serializer_class(bill).data)
from rest_framework import generics
class BillDetailView(generics.RetrieveAPIView):
serializer_class = BillSerializer
def get_object(self):
return Bill.objects.get(id=self.kwargs['pk'])
View should return a Response in json format. When you inherit APIView you will have to explicitly call the serializer class on the object.
If you use generics, you do not have to worry about that.

django rest framework: : Passing raw query

Is it possible to perform raw queries in django rest framework like django. https://docs.djangoproject.com/en/dev/topics/db/sql/#performing-raw-queries
Yes you should be able to, since you can customize the queryset that backs your view, e.g.
class MyModelViewSet(viewsets.ModelViewSet):
# The usual stuff here
model = MyModel
def list(self, request):
queryset = MyModel.objects.raw('... your SQL here...')
serializer = MyModelSerializer(queryset, many=True)
return Response(serializer.data)
Manager.raw() returns RawQuerySet which is a QuerySet, so you can see how it all fits

MemoryError in django-rest-framework CreateAPIView due to unnecessary queries

I want to create an object using the CreateAPIView from the django-rest-framework. When calling the view, I get a MemoryError. That's probably because the view tries to present all 350000 existing objects in the browseable response.
How should I prevent the view from performing the corresponding query? Defining a post or a get_queryset method does not help.
I solved the problem by using the APIView instead of the CreateAPIView. Here's the class I wrote:
class VoteCreateAPIView(views.APIView):
def post(self, request, *args, **kwargs):
vote = request.POST.get('vote', '')
# here some validation
Vote.objects.create(
user=request.user,
vote=vote)
return response.Response({'vote': vote}, status=status.HTTP_200_OK)
I would still be curious if there's a better way to do it.

Adding more views to a Router or viewset (Django-Rest-Framework)

Essentially, I'm trying to find a good way to attach more views to a Router without creating a custom Router. What's a good way to accomplish this?
Here is something sort of equivalent to what I'm trying to accomplish. Variable names have been changed and the example method I want to introduce is extremely simplified for the sake of this question.
Router:
router = routers.SimpleRouter(trailing_slash=False)
router.register(r'myobjects', MyObjectViewSet, base_name='myobjects')
urlpatterns = router.urls
ViewSet
class MyObjectsViewSet(viewsets.ViewSet):
""" Provides API Methods to manage MyObjects. """
def list(self, request):
""" Returns a list of MyObjects. """
data = get_list_of_myobjects()
return Response(data)
def retrieve(self, request, pk):
""" Returns a single MyObject. """
data = fetch_my_object(pk)
return Response(data)
def destroy(self, request, pk):
""" Deletes a single MyObject. """
fetch_my_object_and_delete(pk)
return Response()
One example of another method type I need to include. (There are many of these):
def get_locations(self, request):
""" Returns a list of location objects somehow related to MyObject """
locations = calculate_something()
return Response(locations)
The end-result is that the following URL would work correctly and be implemented 'cleanly'.
GET example.com/myobjects/123/locations
The answer given by mariodev above is correct, as long as you're only looking to make GET requests.
If you want to POST to a function you're appending to a ViewSet, you need to use the action decorator:
from rest_framework.decorators import action, link
from rest_framework.response import Response
class MyObjectsViewSet(viewsets.ViewSet):
# For GET Requests
#link()
def get_locations(self, request):
""" Returns a list of location objects somehow related to MyObject """
locations = calculate_something()
return Response(locations)
# For POST Requests
#action()
def update_location(self, request, pk):
""" Updates the object identified by the pk """
location = self.get_object()
location.field = update_location_field() # your custom code
location.save()
# ...create a serializer and return with updated data...
Then you would POST to a URL formatted like:
/myobjects/123/update_location/
http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing has more information if you're interested!
You can now do this with the list_route and detail_route decorators: http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
For example:
from rest_framework.decorators import list_route
from rest_framework.response import Response
...
class MyObjectsViewSet(viewsets.ViewSet):
...
#list_route()
def locations(self, request):
queryset = get_locations()
serializer = LocationSerializer(queryset, many=True)
return Response(serializer.data)
You define method like you do now, but you need to use the same url as method name and add link decorator, so for
/myobjects/123/locations/
You add method like this
#link(permission_classes=[...])
def locations(self, request, pk=None):
...
and router will pick it automatically.
From Routing to extra methods on a ViewSet:
I think you may need to route the method by hand, i.e. The Old-Fashioned Way™.
First pull the method out as a separate view:
set_password_view = UserViewSet.as_view({'post': 'set_password'})
(or such)
Then assign your URL:
url(r'^users/username_available/$', set_password_view, name-=...)
(Or such)
There's a related question on SO.
If you want to extend a viewset with a view that is or should not directly be written inside your viewset, you can write a “wrapper” action to pass the data through.
For example, with class based views:
from somewhere import YourExternalClassView
class SomeViewSet(viewsets.ReadOnlyModelViewSet):
# ...
#action(detail=True)
def your_action(self, request, pk):
return YourExternalClassView.as_view()(request, pk=pk)
How does it work?
On class based views, the as_view method returns a view function, to which we will pass the data we received from the action. The view will then hand over to process further.
For non-class based view, the views can be called/wrapped directly without .as_view(...)(...).

Django Rest Framework bulk updates inserting instead of updating

I'm trying to build out a bulk update view for a specific model using Django Rest Framework. In the short term, it only needs to update one field (toggling an invite from submitted=False to submitted=True), but I'd like it to be able to provide more functionality in the future. Whenever I test the view, however, a new object is being created instead of the current one being modified.
I feel like this must be a simple mistake on my part, but I can't figure out what's going on. The serializer object appears to be ignoring the value for "id" passed in through JSON, which may be contributing to the issue. Current code is:
class InviteBulkUpdateView(generics.UpdateAPIView):
def get_queryset(self):
order = self.kwargs['order']
invite = get_objects_for_user(self.request.user, 'sourcing.view_invite')
return invite.filter(order=order)
serializer_class = InviteInputSerializer
def put(self, request, *args, **kwargs):
data = request.DATA
serializer = InviteInputSerializer(data=data, many=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
class InviteInputSerializer(serializers.ModelSerializer):
class Meta:
model = Invite
fields = ('id', 'order', 'team', 'submitted')
Can anybody shed some light onto what I might be doing wrong?
Just in case somebody is looking for a library to handle this, I wrote a Django-REST-Framework-bulk which allows to do that in a couple of lines (the example only does bulk update but the library also allows bulk create and delete):
from rest_framework_bulk import ListCreateBulkUpdateAPIView
class FooView(ListCreateBulkUpdateAPIView):
model = FooModel
You're not passing object instances to your serializer. (Thus it will create new instances rather than update.) See the docs on dealing with multiple objects in serializers where you'll see your QuerySet passed in.
Django has update method to handle that. You may want to read full info from django documentation.
Here is a sample code where you can use to update given field for multiple records:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.exceptions import APIException
class Room_Update_ViewSet(APIView):
def put(self, request,*args, **kwargs):
hotel_id = self.kwargs.get('hotel_id')
room_ids = self.request.query_params.get('room_ids')
room_ids = list(map(int, room_ids.split(',')))
try:
Room.objects.filter(hotel_id=hotel_id,id__in=room_ids).update(booked_status=False)
instances = Room.objects.filter(hotel_id=hotel_id,id__in=room_ids)
serializer = RoomSerializer(instance=instances, many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
except Exception as e:
print("Error udating rooms-->",e)
raise APIException