Call a Django Rest Framework view within another view - python-3.x

Here calling a view within another view but I can not use its result. When attempting to use that data which gives the following result in the terminal,
Response status_code=200, "text/html; charset=utf-8">
view
class CourseFilterView(ResponseViewMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
serializer_class = CourseFilterSerializer
queryset = Course.objects.all()
class CourseFilterListView(ResponseViewMixin, APIView):
def get(self, request, *args, **kwargs):
response_data = {}
a = CourseFilterView.as_view({'get': 'list'})(request._request, *args, **kwargs)
response_data['a'] = a
print('a', a)
Am I following the right path? If not, give me a solution to make it better

Related

DRF's not calling post() method when receiving POST request

I have a viewset like this:
class MyViewSet(CreateAPIView, RetrieveModelMixin, ListModelMixin, GenericViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
def post(self, request, *args, **kwargs):
import pdb; pdb.set_trace()
class MySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = MyModel
fields = ['id', 'field1', 'field2'] #only field1 is required in the model
The GET requests for list, and retrieve works perfectly. When I make a POST request, sending the field1 I get a status 201 and a new record is added to the database, so it works too.
But my method MyViewSet.post() that should overwrite the same one from generics.CreateAPIView never gets called.
Not only that, but I've tried to add the pdb.set_trace(), literally inside the generics.CreateAPIView.post() and in the CreateModelMixin.create() functions and neither stopped once I made the POST request.
So something else is handling this POST request and inserting into the DB, I just don't know what. And how can I overwrite it, so I can customize what should be done with a post request?
PS.: Also, I don't think it's a routing problem, my urls.py:
from rest_framework import routers
from myapp.views import MyViewSet, AnotherViewSet
router = routers.DefaultRouter()
router.register(r'route_one', MyViewSet)
router.register(r'route_two', AnotherViewSet)
I think you need to use the exact class in order to use POST api.
class MyView(CreateModelMixin, ListModelMixin, generics.GenericAPIView):
queryset = MyModel.objects.all()
serializer_class = MySerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
In urls.py
from django.urls import path
from .views import MyView
urlpatterns = [
path('route_one', MyView.as_view(), name="my_view_detail")
]

How to add pagination : super()

I am trying to add pagination using super().list() method in modelviewset
class RecentlyViewedVideosViewSet(ResponseViewMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = RecentlyViewedVideos.objects.all()
serializer_class = RecentlyViewedVideosSerializer
def list(self, request, **kwargs):
print('list')
try:
if 'learner_id' in self.kwargs:
learner_id = self.kwargs['learner_id']
else:
learner_id = self.request.learner.id
response_data = super().get_queryset().filter(learner_id=learner_id)
print(response_data)
serializer = RecentlyViewedVideosSerializer(response_data, many=True)
return self.jp_response(s_code='HTTP_200_OK', data=serializer.data)
in output, it displays all the documents in the table, but I only need those details in the "details", give me a way to get the exact output.
It might be better to do this in the .get_queryset(…) method:
def get_queryset(self):
if 'learner_id' in self.kwargs:
learner_id = self.kwargs['learner_id']
else:
learner_id = self.request.learner.id
return super().get_queryset().filter(learner_id=learner_id)
then the boilerplate code to filter, paginate, etc. the view are still implemented by the .list(…) method of the ListModelMixin.

DRF permissions best practise DRY

Whats the best way to do view permissions in DRF based on user type currently?
In my structure there are several user_types and for example TEAM_LEADER cant create a team object but can see the list of teams. Which means for the same class view i want to use different permissions for POST and GET for example.
I'm looking to do this as dry as possible and i'm trying to follow the skinny view fat models design principle(also wondering if that's good practice to follow in 2021).
models.py for the user model
class User(AbstractBaseUser):
...fields here
objects = UserManager()
USERNAME_FIELD = "email"
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
if perm.Meta.verbose_name=="worksite" and perm.request.method =="POST":
if self.user_type <= self.DEPARTMENT_MANAGER:
return True
else:
return False
return True
views.py
class DashboardPermissions(BasePermission):
message="You dont have permission for this action"
def has_permission(self, request, view):
return request.user.has_perm(view.Meta.verbose_name)
class ViewName(CreateAPIView):
permission_classes = (IsAuthenticated,DashboardPermissions)
authentication_classes = ()
serializer_class = WorksiteSerializer
queryset = Worksite.objects.all()
class Meta:
verbose_name="view_name"
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
Bonus question would my solution create any performance issues?
Creating your custom Permission class is good practice. So that part looks OK to me. We could debate on whether the logic should be in the Permission or the User (like you did), but that's not a big deal.
If you want to have different permissions for different endpoints within your view, simply override the get_permissions method.
# Inherited method from APIView
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
return [permission() for permission in self.permission_classes]
As you can see, for ALL services, it will fetch the permissions from self.permission_classes.
To use different permissions between GET/CREATE, you could create a dict of endpoint: [...permissions] and override get_permissions to fetch the one matching the current action
permissions = {
"create": [P1, P2,],
"get": [P1,]
}
def get_permissions(self):
action = self.it_is_somewhere_in_there
return [permission() for permissions in self.permissions[action]]
#JordanKowal's answer is correct, but as mentioned in the comments,
Also then i'd be repeating the permissions dict a lot ? in order to do it for every class of view right
For this you can create a mixin class. What it essentially allows you to do is move some code/feature that is to duplicated in multiple views to a standalone class and just inherit from it as per your convenience.
To extend on Jordan's answer, here's what a mixin class would look like:
class DefaultPermissionsMixin(object):
permissions = {
"create": [IsAuthenticated, DashboardPermissions],
"get": [DashboardPermissions]
}
def get_permissions(self):
# default `get_permissions` method
# reads `self.permission_classes`
perms = super().get_permissions()
if self.action in self.permissions.keys():
return perms + [p() for p in self.permissions[self.action]]
else:
return perms
class View1(CreateAPIView, DefaultPermissionsMixin):
# ...snip...
class View2(CreateAPIView, DefaultPermissionsMixin):
# i can overwrite here per my convenience
permissions = {
"create": [DashboardPermissions],
"delete": [],
}
# i can also define permissions the default way
# that will be enabled on all actions
permission_classes = [IsAuthenticated]
# ...snip...

Add variable to Django request object in Middleware (once)

class CustomMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
request.variable = 1
response = self.get_response(request)
return response
This works but it it processing the request twice. I am unsure of how to set this variable after the view has been processed (for every view), only once. process_template_response is not a valid option because it will not work with every view. Is there a better way to do this?

Django: How to fetch id in class based views?

views.py
from paitent.mixini import HttpResponseMixin,SerializeMixin
from paitent.models import Paitent_Details
class PaitentCRUDCBV(View, HttpResponseMixin, SerializeMixin):
def get(self, id, *args, **kwargs):
paitent = Paitent_Details.objects.get(id=id)
json_data = self.render_to_serialize([paitent,])
return self.render_to_http_response(json_data)
urls.py
urlpatterns = [
url(r'^api/json',views.PaitentCRUDCBV.as_view()),
]
My requirement is based on id i have to fetch particular row form data base,from Frontend side i am passing id in url like:(url: "http://127.0.0.1:8000/api/json/?id=2").But i am not finding id inside my class ,don't know where i am doing wrong. please help me.
You can use query_params in view:
def get(self, request):
id = self.request.query_params.get('id')
....
You can get more information from documentation
1)I have edited your following code, you have to pass *request* in your function,
2)Inside the function calling request.query_params.get, you will get parameters value from the url
from paitent.mixini import HttpResponseMixin,SerializeMixin
from paitent.models import Paitent_Details
class PaitentCRUDCBV(View, HttpResponseMixin, SerializeMixin):
def get(self, request, *args, **kwargs):
id=self.request.query_params.get('id')
paitent = Paitent_Details.objects.get(id=id)
json_data = self.render_to_serialize([paitent,])
return self.render_to_http_response(json_data)

Resources