django rest permission called many times - django-rest-framework

So I'm building a simple blog app and made custom permission so only owner of post can update or delete the post, so I added print statment to check if it's working but found out it's called 6 times every time calling the view!
here's the code
class PostUpdateDeletePermission(IsAuthenticated):
def has_object_permission(self, request, view, obj):
print("called")
if request.user.is_superuser:
return True
if (request.method == 'PUT' or request.method == 'DELETE') and request.user != obj.user:
return False
return True
class PostView(ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = (PostUpdateDeletePermission,)
def create(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
obj = serializer.save(user=request.user)
if request.data.get('files_length'):
PostFile.objects.bulk_create([PostFile(img = request.data.get(f"file-{x}"), post=obj) for x in range(request.data.get('files_length'))])
return Response({'posts' : serializer.data}, status=201)
that's how many times called printed when refreshing the drf browsable api
called
called
called
called
called
called

Related

django - pass request.user to ModelForm

i am having a tricky issue. In my views.py i am passing a form in a DetailView. But i am not using a FormMixin. That has the reason that i only want my template to render the form. In the template i use that form to trigger an UpdateView.
class UpdateDetailView(LoginRequiredMixin, DetailView):
model = Balance
def get_context_data(self, **kwargs):
context = super(UpdateDetailView, self).get_context_data(**kwargs)
context['form'] = ManagerForm
return context
def get_queryset(self, *args, **kwargs):
request = self.request
pk = self.kwargs.get('pk')
select = self.kwargs.get('select')
queryset = Balance.objects.filter(pk=pk).filter(owner = request.user).filter(select = select)
return queryset
class BalanceUpdateView(LoginRequiredMixin, UpdateView):
form_class = ManagerForm
model = Balance
def get_success_url(self):
return reverse('url-year', kwargs={'year': self.object.year, 'select': self.object.select})
So far, so good. The issue is that the form in the UpdateDetailView the ChoiceFields are showing select option of other users which one is not supposed to see. Thus i need to override the queryset of the Modelform. Usually i could use the get_form_kwargs method to pass the request.user to the form. But since the UpdateDetailView is not a FormView it doesnt have that (i know, coz i tried desperately). I also tried context['form'] = ManagerForm(initial = {'user': self.request.user}) in the get_context_data method. But i was unable to access user properly with the __init__ method in the forms.py. I got a key error. If i passed it as arg then i get an attribute error. Does anyone have a solution to that problem? Do i need to use a FormMixin?
Thank you for your replies!

How to update another field when using partial_update?

I'm using partial_updates on my user model, and I wish to change the is_active to True on the user model instance when a partial_update happens - even though is_active is not exposed to the endpoint. My class looks like this:
class UserInvitationUpdate(mixins.UpdateModelMixin, generics.GenericAPIView):
serializer_class = serializers.UserSerializer
queryset = User.objects.all()
def get(request, *args, **kwargs):
username = kwargs.get('username')
token = kwargs.get('token')
return activated_user(username, token)
def get_object(self):
username = self.kwargs.get('username')
user = User.objects.get(username=username)
return user
def put(self, request, *args, **kwargs):
username = self.kwargs.get('username')
token = self.kwargs.get('token')
if my_if_statement_is_true:
# TODO set user to active
# how do I set is_active = True for the user model instance?
return self.partial_update(request, *args, **kwargs)
You have multiple way to deal with that. You could either change your serializer .save() method and set manually the field is_active to true, or set it in the view by updating the perform_update() method of your view :
def perform_update(self, serializer):
serializer.save(is_active=True)
More info here

clean django api in serilizer

i write a django api i would like to know if reminder field changed then the Appointment model object save current user.
i used this link
See object changes in post_save in django rest framework
and write this code
class AppointmentBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
model = Appointment
read_serializer_class = AppointmentSerializer
write_serializer_class = AppointmentCreateSerializer
reminder_change = False
def perform_update(self, serializer):
if 'reminder' in serializer.validated_data:
self.reminder_change = True
serializer.save()
def update(self, request, *args, **kwargs):
super(AppointmentBackOfficeViewSet, self).update(request, *args, **kwargs)
instance = self.get_object()
instance.user = request.user
if self.reminder_change:
instance.reminder_user = request.user
instance.save()
res = self.write_serializer_class(instance).data
return Response(res)
class AppointmentCreateSerializer(serializers.ModelSerializer):
class Meta:
model = Appointment
exclude = ['created_time', 'modified_time']
is there a way to write this code clearer than this :
dont use self.reminder_change class field is there better way?
may be move this lines to serializer??(in serializer dont access to request.user)
Removed unecessary fields and update method:
class AppointmentBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
model = Appointment
read_serializer_class = AppointmentSerializer
write_serializer_class = AppointmentCreateSerializer
def perform_update(self, serializer):
# Here you can set attributes directly to the serializer update method
# It's like setting attribute directly to the updating model
# Check that reminder has been changed
reminder = serializer.validated_data.get('reminder')
if reminder and reminder != instance.reminder: # Reminder is different
serializer.save(user=self.request.user,
reminder_user=self.request.user)
else:
serializer.save(user=self.request.user)
This solution will work depending on what type of reminder field is.
If it's String or Integer it will be ok. Problem is that if it's object. Viewset will raise error because serializer reminder field would be integer but instance.reminder would be instance of the reminder object so keep this in mind.

Getting "AttributeError: 'QuerySet' object has no attribute '_meta'" on django rest "PUT" method

I am trying to update the record with PUT method, Getting AttributeError: 'QuerySet' object has no attribute '_meta'.
My models.py:
class TableInfo(models.Model):
table_name = models.CharField(max_length=10)
columns = JSONField(null=False)
serializer.py:
class TableInfoSerializer(serializers.ModelSerializer):
class Meta:
model = TableInfo
fields = '__all__'
views.py :
#api_view(['GET','PUT'])
def table_info(request):
try:
queryset = TableInfo.objects.all()
print("1")
except TableInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
print("2")
serializer_class = TableInfoSerializer(queryset, many=True)
return Response(serializer_class.data)
elif request.method == 'PUT':
print(request.data)
serializer = TableInfoSerializer(queryset, data=request.data)
if serializer.is_valid():
serializer.save()
print("4")
return HttpResponse(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
code is breaking at if serializer.is_valid():
On "GET" I am getting the result. Please help me with "PUT" method.
This error happens with PUT because the serializer tries to access the Meta class on the model instance it is updating, but fails because you are not passing a model instance - you're passing a queryset as indicated in the comments.
So you need to pass an instance, and to specify which instance you would normally pass the instance id via the URL. For that you would be best to separate out your views, and create a table_detail view for retrieving and updating a specific instance.
#api_view(['GET','PUT'])
def table_detail(request, pk):
try:
table_info = TableInfo.objects.get(pk=pk) # Lookup a specific object
except TableInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer_class = TableInfoSerializer(table_info)
return Response(serializer_class.data)
elif request.method == 'PUT':
serializer = TableInfoSerializer(table_info, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Your table_info view can then just handle the list operation.
#api_view(['GET'])
def table_info(request):
if request.method == 'GET':
queryset = TableInfo.objects.all()
serializer_class = TableInfoSerializer(queryset, many=True)
return Response(serializer_class.data)

updating a user using APIVIEW fails. User object either already exists or creates new user

I am using django rest framework and I have created a view to update a user.
Here is my view:
def put(self, request, *args, **kwargs):
authorization =UserSuitsPermissions().superuser(request.user)
userpk = kwargs.get('pk', 0)
user = get_object_or_404(STUser, pk=userpk)
if not authorization:
if request.user['id'] == user.id:
authorization = True
if authorization:
serializeddata = UserSerializer(data=request.data)
if serializeddata.is_valid(raise_exception=True):
data = serializeddata.validated_data
user.__dict__.update(**data)
user.save()
serialzed = UserSerializer(user)
return Response(serialzed.data)
return Response(status=status.HTTP_401_UNAUTHORIZED)
now in this linked question they are using a generic view and using the serializer to update the user instance :
Django DRF Update User
with the accepted answer being:
def update(self, request, *args, **kwargs):
serializer = self.serializer_class(request.user, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
I am not using a generic view and in truth Im over them. I dont value it. so the question is, how do we update a user object via the api view?
my biggest question is does this line in the accepted answer have relevance to my code?
serializer = self.serializer_class(request.user, data=request.data, partial=True)
soooo
I figured it out.
the correct way is to pass an instance to a serializer then call save with the partial flag set to true.
the code looks like this:
serializeddata = UserSerializer(user, data=request.data, partial=True)
if serializeddata.is_valid(raise_exception=True):
serializeddata.save()
return Response(serializeddata.data)
with the full view looking like:
def put(self, request, *args, **kwargs):
authorization =UserSuitsPermissions().superuser(request.user)
userpk = kwargs.get('pk', 0)
user = get_object_or_404(STUser, pk=userpk)
if not authorization:
if request.user['id'] == user.id:
authorization = True
if authorization:
serializeddata = UserSerializer(user, data=request.data, partial=True)
if serializeddata.is_valid(raise_exception=True):
serializeddata.save()
return Response(serializeddata.data)
return Response(status=status.HTTP_401_UNAUTHORIZED)
so thats cool

Resources