Override list method in Django rest ViewSet - django

In a Django Rest Framework ViewSet I have an overrided list()
class TicketViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
def_list():
make_my_checks()
things_copied_from_parent()
Since the list() has many lines of code and I must make checks in every ViewSet, how can I make this checks and return the overrided method? A thing like:
def_list():
make_my_checks()
super(list())

You are almost there!!!
class TicketViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
def list(self, request, *args, **kwargs):
make_my_checks() # your custom checks
return super().list(request, *args, **kwargs) # you should return them

Related

Django rest framework creating Orders and order items

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

Defining two api end points with same generic class view Django Rest Framework?

I am new to Django. I was trying to use django generic views, class based.
How do I implement following situation, I have two api end points login/ and logout/ and the same view class handles it ?
# Login and logout
class SignInActivity(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
request = None
def get_queryset(self):
return User.objects.filter(Q(username=self.request.data["username"])
& Q(userpassword=self.request.data["userpassword"]))
def update(self, request, *args, **kwargs):
self.request = request
query_set = self.get_queryset()
if bool(query_set):
query_set.update_or_create(username=self.request.data["username"],
userpassword=self.request.data["userpassword"],
defaults={
"lastlogin": timezone.now()
})
return Response(data={"message": "User logged in successfully.", "response_code": 222}, status=201)
else:
return Response(data={"message": "User not found.", "response_code": 444}, status=201)
And my urls.py is
path('login/', SignInActivity.as_view())
This implementation hadles login/ with this class, PUT method. Now can I use same class with another method to handle logout/ PUT method?
By default generics.RetrieveUpdateAPIView provides 3 HTTP methods, which are GET,PUT,PATCH. You could override any of the following methods to logout procedure.
class SignInActivity(generics.RetrieveUpdateAPIView):
serializer_class = UserSerializer
request = None
def get_queryset(self):
return User.objects.filter(Q(username=self.request.data["username"])
& Q(userpassword=self.request.data["userpassword"]))
def update(self, request, *args, **kwargs):
return Response(data="HTTP PUT method")
def partial_update(self, request, *args, **kwargs):
return Response(data="HTTP PATCH method")
def retrieve(self, request, *args, **kwargs):
# your log-out logic
return Response(data="HTTP GET method")
I think you can use retrieve() method to do logout procedure, because, there is no need of sending additional payloads to the end-point, I assume
I think it's not possible to points two end points to same method function (POST or PUT) of the same view class.
One of the solution could be to use inheritance, where there could be a base class and login and logout endpoint classes inheriting from this class, and later implement respective method functions inside the child classes.
So basically, the two end points will still use functions of different class, with common properties inherited.
class SignInActivity(generics.RetrieveUpdateAPIView):
should be
from rest_framework.views import APIView
class SignInActivity(APIView):
Urls.py
path('login/', SignInActivity.as_view()),
path('logout/', SignInActivity.as_view())
This should be doing the fact as when the request is recieved our domain.com/logout/ it would forward it to the SignInActivity View as well as when request over domain.com/login/ is received!

Mixin for changing the behaviour of a get method in Django class based view

I'm attempting to write a mixin for setting a translation language based on the language set in the user Profile model.
When a get request comes in, the mixin should set a language to user language, get response from the view that adds the mixin, and then set the language back to what it was before. I wrote the following mixin, which is invoked, but it's get method is not invoked. What am I doing wrong?
class SetUserLanguageMixin(object):
def get(self, request):
current_language = translation.get_language()
try:
translation.activate(request.user.profile.language)
response = super(SetUserLanguageMixin, self).get(request)
finally:
translation.activate(current_language)
return response
class SomeView(LoggingMixin, APIView, SetUserLanguageMixin):
def get(self, request):
...
return Response(data, status=status.HTTP_200_OK)
If your SomeView overrides get(), then your mixin's get() method will not be called unless you call super(). You could try overriding dispatch in your mixin instead.
Note that your view will be more robust if the overridden get/dispatch method accepts args and kwargs:
def dispatch(self, request, *args, **kwargs):
...
response = super(SetUserLanguageMixin, self).dispatch(request, *args, **kwargs)
...

Modify Django rest framework request

My problem is how to write more strict API using more generic REST API. I have working api in my application but I need to add some more strict services based on generic API and here is a problem because I can't simple override request data because it's immutable. I'm using Django rest framework 3.
Example:
My generic api for animals:
class AnimalService(APIView):
def get(self, request, *args, **kwargs):
data = request.data.copy()
if data.get('type') == 'dog':
#...do something
Now I need api only for hardcoded dogs:
class DogService(AnimalService):
def get(self, request, *args, **kwargs):
#hardcode request.data['type'] = 'dog'
return super(DogService, self).get(*args, **kwargs)
Instead of overriding the request object, you can pass the type in kwargs. You can then use these kwargs in AnimalServiceView as these modified kwargs are passed to it.
class DogService(AnimalService):
def get(self, request, *args, **kwargs):
kwargs['type'] = 'dog' # set the animal type in kwargs
return super(DogService, self).get(request, *args, **kwargs)
Then in your AnimalService view, you can do:
class AnimalService(APIView):
def get(self, request, *args, **kwargs):
if kwargs.get('type') == 'dog': # check the 'type'
#...do something
Another way is to write a custom middleware which will set the animal_type attribute on the request depending on the requested resource. Then in your views, you can just check using request.animal_type attribute.

Django: Access request object from admin's form.clean()

My question is very similar to this one: How do I access the request object or any other variable in a form's clean() method?
Except, I have the same problem with admin form. So I can't see a way to init the form myself, therefore - to pass a request to it.
Thanks beforehand.
Indeed, there is a way to solve your issue!
You will need to subclass form provided by ModelAdmin.get_form() and override it:
class BusinessDocumentCommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
# Voila, now you can access request anywhere in your form methods by using self.request!
super(BusinessDocumentCommentForm, self).__init__(*args, **kwargs)
if self.request.GET.get('document_pk', False):
#Do something
def clean(self):
# Do something with self.request
# etc.
class Meta:
model = BusinessDocumentComment
class BusinessDocumentCommentAdmin(admin.ModelAdmin):
form = BusinessDocumentCommentForm
def get_form(self, request, obj=None, **kwargs):
AdminForm = super(BusinessDocumentCommentAdmin, self).get_form(request, obj, **kwargs)
class AdminFormWithRequest(AdminForm):
def __new__(cls, *args, **kwargs):
kwargs['request'] = request
return AdminForm(*args, **kwargs)
return AdminFormWithRequest
There are a number of hooks in the ModelAdmin class to allow you to do things this - look at the code in django.contrib.admin.options.
Two methods that might help you are ModelAdmin.save_form and ModelAdmin.save_model, both of which are passed the request object. So you can override these methods in your Admin subclass and do any extra processing you need.
Edited after comment
You're quite right that this won't let you validate the form dependent on the user's privileges. Unfortunately the form instantiation is buried deep within the add_view and change_view methods of ModelAdmin.
There aren't many possibilities without duplicating a lot of existing code. You could override the *_view methods; or you could try and override the modelform_factory function to return a new class with the request object baked in already; or you could try fiddling with the form class __new__ method to do the same thing, but that's tricky because of the form metaclass.
This solution works for me. You can use self.request anywhere in the form to use it, including def clean(self)
class MyModelAdmin(admin.ModelAdmin):
form = MyForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.request = request
return form