Object update by PATCH for Django REST Framework - python

I am using viewsets.ModelViewSet
from rest_framework import viewsets
class ProjectViewSet(viewsets.ModelViewSet):
serializer_class = s.ProjectSerializer
queryset = m.Project.objects.all()
def patch(self,request,*args,**kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance,data = request.data)
if serializer.is_valid():
self.perform_update(serializer)
return Response(serializer.data)
return Response()
Then I test to update the object via django restframework UI.
Then this error occurs.
My basic idea that changing object data via PATCH is correct?
How can I update the data via Django REST Framework
Expected view ProjectViewSet to be called with a URL keyword argument named "pk". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.

First, the problem is directly related to URL setup, in any case I would suggest using DRF routers:
from django.urls import path, include
from .views import ProjectViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'projects', ProjectViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
Secondly, the actions provided by ModelViewSet are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy().
The action related to PATCH is partial_update, so:
class ProjectViewSet(ModelViewSet):
serializer_class = ProjectSerializer
queryset = Project.objects.all()
def partial_update(self, request, *args, **kwargs):
partial = True
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
Original code of UpdateModelMixin
.partial_update:
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
As you can see it still calls .update which does almost exactly the same:
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)
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)

Related

How to check for user authentication in Django's class-based view with get_context_data

When using function based views in Django, I normally would check for user authentication with:
if not self.request.user.is_authenticated:
return HttpResponseRedirect('/path/to/redirect')
Now I am trying to use Class based views instead but I don't know how to implement the authentication if I am using the get_context_data() method instead of just get().
class MyGenericView(generic.ListView):
model = models.MyModel
template_name = 'path/to/template.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['foo'] = 'bar'
return context
You want to override the dispatch method.
class MyView(ListView):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseRedirect('/path/to/redirect')
return super().dispatch(request, *args, **kwargs)
More details in the doc.
You can use LoginRequiredMixin in the view to check if the user is authenitcated:
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin , ListView):
login_url = “login_url_name”
# rest of the code
You can also use user_passes_test, for example, in urls:
def user_authenticated(user):
return user.is_authenticated
urlpatterns = [
path(
'',
user_passes_test(user_authenticated, login_url='login')(
MyView.as_view()
),
)
]

django post method create record using ListApiView

I am a beginner to django rest-framework and trying to create new record using POST method in ListAPIView.
Here's my serializer:
from scheme.models import ProjectScheme, ProjectSchemeMaster
from rest_framework import serializers
class SchemeDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectScheme
fields = ('id', 'name', 'parent_scheme_id', 'rule', 'created_on', 'created_by', 'updated_on','updated_by')
depth=1
And view:
class ProjectSchemeList(ListAPIView):
"""
List all Schemes
"""
serializer_class = SchemeDetailSerializer
# pagination_class = ProjectLimitOffsetPagination
def get_queryset(self, *args, **kwargs):
comp_logger.info('invoked scheme list all')
schemes = ProjectScheme.objects.all().order_by('-id')
return schemes
def post(self, request, *args, **kwargs):
if serializer_class.is_valid():
serializer_class.save()
return Response(serializer_class.data, status=status.HTTP_201_CREATED)
return Response(serializer_class.errors, status=status.HTTP_400_BAD_REQUEST)
I get this error:
NameError at /scheme/schemes/
name 'serializer_class' is not defined
How do I pass request data to serializer_class?
Created functioanlity is included by default in CreateAPIView generic view, or if you want to provide list and create functionality, you can use ListCreateAPIView which provides both. More details on DRF's generic views here.
class ProjectSchemeList(ListCreateAPIView):
serializer_class = SchemeDetailSerializer
def get_queryset(self, *args, **kwargs):
comp_logger.info('invoked scheme list all')
schemes = ProjectScheme.objects.all().order_by('-id')
return schemes
With this definition, you won't need to manually write a post method.
If you want to manually define a post methdod, you can investiage how it is written in generic CreateAPIView and copy it, it's slighly different from how you want to write it. Finally, following is your version of the post method with errors fixed:
class ProjectSchemeList(ListAPIView):
serializer_class = SchemeDetailSerializer
def get_queryset(self, *args, **kwargs):
comp_logger.info('invoked scheme list all')
schemes = ProjectScheme.objects.all().order_by('-id')
return schemes
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Notice how we use self.serializer_class(data=request.data) instead of just serializer_class

How can I return the created data when created success in Django

In my Django project, I have a APIView:
class PhysicalServerManualGenerateOrderAPIView(CreateAPIView):
serializer_class = PhysicalServerManualGenerateOrderSerialzier
permission_classes = [IsFinanceAdmin, IsSuperAdmin]
queryset = Order.objects.all()
in the PhysicalServerManualGenerateOrderSerialzier:
class PhysicalServerManualGenerateOrderSerialzier(ModelSerializer):
...
def create(self, validated_data):
try:
order = getOrder(user=user, validated_data=validated_data) # there I create the order instance
except Exception as e:
order = None
return order
But I have a requirement, I want to return the created order's id (or other data) when I access the APIView success.
Change your api as below,
from rest_framework.response import Response
from rest_framework import status
class PhysicalServerManualGenerateOrderAPIView(CreateAPIView):
# your code
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
sufficent_data = serializer.data # you will get the serialized data here, which includes the "order_id" too
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
When you call the self.perform_create(serializer) the create() method of PhysicalServerManualGenerateOrderSerialzier class will get called and return the Order instance. serializer.data will serialize that instance and makes it available at API class

Modifying a Django Rest Framework Request

I have a Django rest framework api set up, and I'm trying to insert the current time into incoming PUT requests. I currently have:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.filter(done = False).order_by('-time')
serializer_class = ItemSerializer
paginate_by = None
def list(self, request, *args, **kwargs):
self.object_list = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(self.object_list, many=True)
return Response({'results': serializer.data})
This handles partial updates, but I would like to be able to send a request setting an Item to done = True and have the api also insert a unix timestamp into the data sent to the serializer. Could I alter the request object like this, or is there a better way?
def put(self, request, *args, **kwargs):
request.data['time'] = time.time()
return self.partial_update(request, *args, **kwargs)
Instead of modifying request, override serializer's method update.
Class ItemlSerializer(serializers.ModelSerializer):
class Meta:
model = ItemModel
fields = '__all__'
read_only_fields = ('time',)
def update(self, instance, validated_data):
instance.time = time.time()
return super().update(instance, validated_data)
You make a Parent serializer mixin with a serializer method field. Then all your serializers can inherit this serializer mixin.
class TimeStampSerializerMixin(object):
timestamp = serializers.SerializerMethodField()
def get_timestamp((self, obj):
return str(timezone.now())

Retrieving current user inside Serializer Method in Django Rest API

I have a serializer for user profiles in Django Rest:
class UserProfileSerializer(serializers.ModelSerializer):
......................
......................
status = serializers.SerializerMethodField()
def get_status(self, obj):
user = self.context['request'].user
if obj.user.userprofile in user.followed_userprofiles_set.all():
return "following"
else:
return "notfollowing"
class Meta:
model = UserProfile
fields = (...., 'status',...)
And I have two views that use this serializer:
class Followers(APIView):
def get(self, request, format=None):
#user who follow current user
users = request.user.userprofile.followers.all()
userprofiles= UserProfile.objects.filter(user__in=users)
serializer = UserProfileSerializer(userprofiles, many=True)
return Response(serializer.data)
and
class Friends(mixins.ListModelMixin, generics.GenericAPIView):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
.................
.................
return queryset
One view is using APIView and other is using genericAPIView. When i request from genericAPIView, its working properly. But when i request from APIView, its giving me key error. How to retrieve the current user inside serializer method when APIView is used?
Since you are manually instantiating the UserProfileSerializer in your APIView class without passing the context, KeyError exception gets raised.
You should pass the request in context parameter when instantiating the UserProfileSerializer in your APIView.
class Followers(APIView):
def get(self, request, format=None):
#user who follow current user
users = request.user.userprofile.followers.all()
userprofiles= UserProfile.objects.filter(user__in=users)
context = {'request':request} # prepare serializer context
serializer = UserProfileSerializer(userprofiles, many=True, context=context) # pass context
return Response(serializer.data)

Categories

Resources