Suppose i have this code
class SnippetList(APIView):
"""
List all snippets, or create a new snippet.
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
Now Let us suppose that in my get, i need to modify request.data
def get(self, request, format=None):
data = modify(request.data)
request.data = data # This don't work as i can't set attribute on request
response = self.post(self, new_request, format=None) # i want to do post method now with new request
serializer = SnippetSerializer(snippets, many=True)
Is there any way to do that
You could try something like this:
def get(self, request, format=None):
response = self.post(self, request, format=None, data=data ) # i want to do post method now with new request
serializer = SnippetSerializer(snippets, many=True)
Now your method post should be:
def post(self, request, format=None, **kwargs):
original_data = request.data
additional_data = kwargs.get('data') # now you will be able to do whatever you want
# more code goes here
You should not modify your data upon get as they are usually marked as "unsafe" because they modify the data state.
You don't want to mess with the provided request either. It often creates more issues than it solves and adds some magic.
If you want the serializer to have more data than the request sent you should pass them to the serializer's save function. They will be added to the validated_data and available in the serializer's create / update methods (http://www.django-rest-framework.org/api-guide/serializers/#passing-additional-attributes-to-save)
Related
I want to create a Order and order items.
For this i am simply creating new model object in views.py using CreateApiView but i am receiving error that "Serializer_class" should be included but i don't need serializer for this.
//views.py
class CreateOrder(CreateAPIView):
def Post(self,request):
header_token = request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
access_token = header_token.split(' ')[1]
status,user = validate_token(access_token)
cart=Cart.objects.get(user=user)
print(cart)
if cart:
total=cart.total
userprofile=UserProfile.objects.get(user=user)
order,created=Order.objects.get_or_create(billing_profile=userprofile,total=total)
cart_items=CartItem.objects.get(cart=cart)
print(cart_items)
for item in cart_items:
itemid=item.item_id
qty=item.quantity
item_instance = Items.objects.get(item_id=item)
order_item,created = OrderItems.objects.get_or_create(order=order, product=item_instance,quantity=qty)
order.save()
order_item.save()
if created:
item.delete()
return Response (status=rt_status.HTTP_200_OK)
I want to understand how to achieve this with or without serializer
You are overriding the incorrect post method. If you look at the source code of CreateAPIView you will see the method named as shown below.
class CreateAPIView(mixins.CreateModelMixin, GenericAPIView):
"""
Concrete view for creating a model instance.
"""
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
NOTE: The method is all lower case.
This method calls self.create which is derived from the CreateModelMixin and this method needs a serializer.
If you need something light weight where a serializer is not needed I would suggest using APIView.
from rest_framework.views import APIView
class CreateOrder(APIView):
def post(self, request):
....
I use serializers.ModelSerializer and viewsets.ModelViewSet of Django REST Framework in my REST API.
While I was testing, I found out that the PATCH of HTTP method was running model save().
I know PATCH is for updating data, Why DRF use save() instead of update()?
Because update applies on a queryset and not a single instance and the update may alter more fields that provided by the clients which makes things hard to predict.
Basically its happening here, inside UpdateModelMixin. The code is:
class UpdateModelMixin(object):
...
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial) # <-- Partial Update
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save() # <-- here
And UpdateModelMixin is later subclassed by ModelViewSet.
Basically its using Serializer's Partial update feature.
I am using Django Rest Framework for my API project. Now i have one APIVIEW with post and get method. How can i add different endpoint only for a particular get or post.
class UserView(APIVIEW):
def get(self, request, format=None):
.....
pass
def post(self, request, format=None):
.....
pass
Now in the urls.py, I want something like this:
urlpatterns = [
url(r'^user\/?$', UserView.as_view()),
url(r'^user_content\/?$', UserView.as_view()),
]
user only accept GET-request and user_content only accept POST-request.
Do not do that. You already can handle different types of request separately in your APIView. You can create two different APIViews, or you can handle this in get or post methods. You can try something like this:
class UserView(APIView):
def get(self, request, format=None):
is_user_request = request.data.get('is_user_request', False)
if is_user_request:
# Handle your user request here and return JSOn
return JsonResponse({})
else:
# Handle your other requests here
return JsonResponse({})
def post(self, request, format=None):
is_user_content_request = request.data.get('is_user_content_request', False)
if is_user_content_request:
# Handle your user content request here and return JSOn
return JsonResponse({})
else:
# Handle your other type requests (if there is any) here
return JsonResponse({})
urlpatterns = [
url(r'^api/user$', UserView.as_view()),
]
This is just an example. If there are specific parameters for your each requests, you can identify your request's type from those parameters. You don't have to put extra boolean values like i did above. Check this way and see if that works for you.
I'm building a social app which has a "like" feature, I'm using django-redis to store "like" numbers from every post like this:
from django_redis import get_redis_connection
con = get_redis_connection("default")
con.incr("post" + ":" + id)
From the doc Raw client access
It works great, and I also use django-rest-framework to provide api, ListCreateAPIView to list all posts.Usually it will display any fields you want in the database. The problem here is now I wanna display "like" count from django-redis in this API which means the json return are from my database and django-redis.
I had look around the source code about django-rest-framework
class ListCreateAPIView(mixins.ListModelMixin,mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
and the list method:
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
It seems like I can only get data from the database queryset(which I use Mysql). Any possible way to return data from django-redis with django-rest-framework?
Solved:
Thanks answer from #Rahul Gupta, I do a little hack to work better:
def get_likes(self, obj):
post_id = obj.id
post_like = get_redis_connection("default")
likes = post_like.get("post"+":"+str(post_id))
if likes == None:
return 0
else:
likes = likes.decode('utf-8')
return likes
You can add a 'likes' SerializerMethodField in your PostSerializer to add likes in the serialized representation of the object.
class SocialPostSerializer(serializers.ModelSerializer):
likes = serializers.SerializerMethodField() # define field
class Meta:
model = SocialPost
def get_likes(self, obj):
social_post_id = obj.id
# here write the logic to get the no. of likes
# for a social post using the 'social_post_id' from redis
return likes
Now, the serialized data returned by DRF in list requests will contain a parameter likes along with other parameters.
Maybe this is not the kind of answer you're looking for, but my suggestion would be to take a look at Cacheops.
It provides an automatic caching/invalidation mechanism for your django models, so you will not have to handle different lookups for redis and db in your code, just use the default django orm and in the background it will handle the memory caching for you. If you anyway store the likes both in db and redis, this is a better solution in my opinion.
without using serializer_class and queryset properties inside viewclass(which inherits viewsets.ModelViewSet) ;
i want to make a get request to a another url and return its response as the result of django get request.
can we do that?
Thanks.
You can put any dummy model and make it work.
class SampleViewSet(APIView):
model = DummyModel
renderer_classes = (JSONRenderer,)
def get(self, request, *args, **kwargs):
if request.method == "GET":
// do some operation
return Response(<json response>, status=status.HTTP_200_OK)