Django rest framework, request object cannot list all method by dir - django-rest-framework

In any viewset, build_absolute_uri not in dir(request) but in dir(request._request). Why request.build_absolute_uri call request._request.build_absolute_uri ?
class DefaultUrlViewSet(ModelViewSet):
...
def get_queryset(self):
request = self.request
assert 'build_absolute_uri' not in dir(request)
assert 'build_absolute_uri' in dir(request._request)
assert hasattr(request, 'build_absolute_uri')
print('???? why hasattr but cannot list it in dir?')
return super().get_queryset()

build_absolute_uri is a method of standard HTTPRequest and DRF's Request is Wrapper allowing to enhance a standard HttpRequest instance
Request puts its wrapped HTTPRequest in _request attribute.
In the source code of django-rest-framework's Request It has overridden __getattr__ method such that if any attribute does not exist it will try to get it from self._request.
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)

Related

how to use parametrized decorator in python flask

I build a decorator with parameter to limit access to the certain pages
def page_decorator(arg):
"""
Decorate routes to require access to the page.
"""
def inner_decorator(f):
def wrapped(*args, **kwargs):
if arg not in session.get("menus"):
return redirect("/")
response = f(*args, **kwargs)
return response
return wrapped
return inner_decorator
and I call here
#app.route("/receiving-performance",methods=["GET","POST"])
#login_required
#page_decorator(1)
def receiving_performance():
return render_template("01-Dashboards/01-Performance/01-receiving-performance.html")
it works for the first call function, then it returns an error, when I use it for the next function/route
AssertionError: View function mapping is overwriting an existing endpoint function: wrapped
what can be the problem, thanks.

Django rest framework XLSX renderer + Apiview

I'm setting up an endpoint on my API which should return a XLSX-file. The DRF-docs mention https://github.com/wharton/drf-renderer-xlsx as the main external rendering package, aside from the pandas which also seem to be able to render XLSX.
In their code example they use a ReadOnlyViewset paired with a mixin, but there is no mention of how it's used with APIViews. Still, I would like to use an APIView as shown by this https://harshahegde.dev/rendering-xlsx-files-in-django-rest-framework-ckagk293p00eumks1bf4dlhie
However..
This works great when using CURL or Postman, but when done through a browser I get this error:
'Request' object has no attribute 'accepted_renderer'
From what I understand this is because there is no Accept header set (e.g 'Accept':'application/xlsx')
I fixed this by removing the Mixin from the renderer_classes, so it simply returns a file called "xlsx" but I can't figure out how to set the filename without the mixin. How do I set the filename using an APIView trying to access the URL from a browser?
My view:
class SomedataXlsx(APIView):
renderer_classes = [XLSXRenderer, JSONRenderer]
def get(self, request):
queryset = Somedata.objects.all()
serializer = SomeDataSerializer(queryset, many=True)
return Response(serializer.data)
Looking at the mixin code it became clear they change the content-disposition header, and so since the DRF Response() takes a header argument I tried changing it, and it worked perfectly.
class SomedataXlsx(APIView):
renderer_classes = [XLSXRenderer, JSONRenderer]
def get(self, request):
user_sub_fund_data = Somedata.objects.all()
serializer = SomeDataSerializer(queryset, many=True)
return Response(serializer.data, headers={"content-disposition":"attachment; filename=mynewfilename.xlsx"})

Is there a way to use inheritance in django viewsets or do I have to rewrite the functions?

I am writting an API in Django Rest Framework. I am using viewsets however regular methods create() and update() do not do it for me and I have to rewrite them.
Suppose that I need to do just one check to test if creation of an instance is legit, do I still have to rewrite the whole create function?
Currently I have this:
class LocationViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LocationSerializer
def create(self, request, *args, **kwargs):
user = request.user
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
if user.can_create_location():
serializer.save()
return Response(serializer.data)
else:
raise exceptions.PermissionDenied('You dont have permission to create this.')
Instead of rewritting the whole thing, is it possible to somehow use inheritance and do something like this?
class LocationViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LocationSerializer
def create(self, request, *args, **kwargs):
user = request.user
if user.can_create_location():
return super().create(self, request, *args, **kwargs)
else:
raise exceptions.PermissionDenied('You dont have permission to create this.')
The above does not work as I get this error. I feel like this should be possible to do, but I don't know how. Thanks for your help!
AttributeError at /api/0/location
LocationViewSet' object has no attribute 'data'
Request Method: POST
Request URL: http://127.0.0.1:8000/api/0/location
Django Version: 2.1.7
Yes, it does work, and you're doing it almost correctly... as per your error: the function is trying to access LocationViewSet.data as you're passing self in the first argument, change that call to:
super().create(request, *args, **kwargs)
Python standard docs has some info on super() and a link to a more in depth explanation.

Overriding generic.ListView methods for AJAX requests DJANGO

I recently started using django's inbuilt generic views (Create, Update, etc) So I'm updating most of my old views to use them, one of them is the ListView, with pagination. So now, it works right,when i GET that page, it displays the objects as directed, and the pagination works fine. But i want to use AJAX on the pagination so that i just click a "More" button and it gets the next page's objects via ajax and are appended onto the end of the . So i've modified some generic views before to incorporate AJAX like the:
class Delete(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
login_url = LOGIN_URL
model = Items
success_url = reverse_lazy('web:member-area')
def test_func(self):
return not self.request.user.is_superuser and self.get_object().created_by == self.request.user
def delete(self, request, *args, **kwargs):
response = super().delete(request)
if self.request.is_ajax():
return JsonResponse({'success': 1}, status=200)
else:
return response
In the above snippet you can see i included the part where it returns something different if the request is AJAX
The current View that i'm working on is as follows:
class Items(ListView):
model = Items
paginate_by = 5
context_object_name = 'items'
template_name = 'web/items/index.html'
which works fine on normal GET requests, so the problem is i dont know which super() method(s) to override and return a different response if its AJAX on that ListView
Use dispatch
class Items(ListView):
def dispatch(request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
if request.is_ajax():
return JsonResponse({'success': 1}, status=200)
else:
return response

login_required decorator on ajax views to return 401 instead of 302

While writing some views to respond to ajax requests i find it somewhat strange that the login_required decorator always returns a 302 status code for not authenticated users. As these views are ajax views, this seems somewhat inappropriate. I do not want the user to log in in such a case, but i want Django to tell the client that authentication is required to access such a view (a 401 should be the right status code, i think).
To achieve this, i started to write my own decorator login_required_ajax, but somehow this is beyond my skills. This is what i have come up with so far:
def login_required_ajax(function=None,redirect_field_name=None):
"""
Just make sure the user is authenticated to access a certain ajax view
Otherwise return a HttpResponse 401 - authentication required
instead of the 302 redirect of the original Django decorator
"""
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
else:
return HttpResponse(status=401)
if function is None:
return _decorator
else:
return _decorator(function)
When using this decorator on a view, i get a ViewDoesNotExist exception as soon as i try to access any page on the site.
I first thought that the problem could be the direct return of an HttpResponse when a user is not authenticated, because a response object is not a callable. But then the decorator should work as long as i do not try to access the view in question, shouldn't it? And if this really is the crux, how can i write a decorator that returns a HttpResponse with a status code of 401?
That's a pretty good attempt. Here's a couple of problems I spotted:
Your _decorator function should return _wrapped_view.
The indentation for your if function is None block is a bit off -- the login_required_ajax function needs to return the decorated function.
Here's the decorator with those changes made:
def login_required_ajax(function=None,redirect_field_name=None):
"""
Just make sure the user is authenticated to access a certain ajax view
Otherwise return a HttpResponse 401 - authentication required
instead of the 302 redirect of the original Django decorator
"""
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
else:
return HttpResponse(status=401)
return _wrapped_view
if function is None:
return _decorator
else:
return _decorator(function)

Resources