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"})
Related
I'm using Wagtail as a headless CMS / API, however instead of using the PagesAPIViewSet I've gone the route of overriding the serve() method as described here to fetch pages:
https://docs.wagtail.org/en/stable/reference/contrib/routablepage.html
This has worked great since I can return a JsonResponse. The problem is that (as far as I understand), debug toolbar only works with the standard rest framework Response(), so I'm trying to find a way to return a DRF Response instead.
The error I'm getting is ".accepted_renderer not set on Response"
How would I set the accepted renderer?
This is roughly what I've got:
from rest_framework.response import Response
from wagtail.core.models import Page
class BasePage(Page, RoutablePageMixin):
....
def serve(self, request, *args, **kwargs):
context = self.get_context(request, *args, **kwargs) # Context where I serialize data to JSON etc.
return Response(data=context['data'], status=status.HTTP_200_OK) ##Throws error, with JsonResponse() it works fine.
I'm using Django Rest Framework for created a API. In this project i want to capture parameters in the URL. For example i want to capture the username and password of a user and my idea is like this:
http://localhost:8000/accounts/login/?unsername=username&password=password
But i cant, I' usin routers and django-filter, but i cant get the parameters url. My project files are there:
view.py:
class AccountsData(viewsets.ModelViewSet):
queryset = models.UserData.objects.all()
serializer_class = serializers.AccountsDataSerializer
permission_classes = (IsAuthenticated,)
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ['username', 'password']
lookup_url_kwarg = 'username'
#action(methods=['get'], detail=True, url_name='login', url_path='login')
def login(self, request, pk=None):
return Response({"Login successfully"}, 200)
urls.py:
from api import views
router = routers.SimpleRouter()
router.register(r'accounts', views.AccountsData)
Request query parameters have nothing to do with routing, they are passed with the request independently of how you configure the route. You have access to them in request.query_params, for example, request.query_params.get('username') would get the value of the username parameter.
Being said that, your idea has a terrible mistake: password or any kind of confidential data should NEVER go in query parameters, you should use an http verb that carries the data in its body (POST, for example).
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.
I'm using DRF and to return response, I used Response() which located at from rest_framework.response import Response
To make custom response, first, I copied all of the source to custom file.
And in my views, I changed Response() to my own file.
But when I running django server and access via web, it through errors.
AssertionError: .accepted_renderer not set on Response
I just copied original Repsonse() and re-use it.
Why it occur errors?
Purpose of custom response is I want to add more argument that something likes cursor for pagination.
As you know that in original Response, takes 6 arguments.
def __init__(self, data=None, status=None,
template_name=None, headers=None,
exception=False, content_type=None):
And return it in def rendered_content(self), line of ret = renderer.render(self.data, accepted_media_type, context)
So my scenario is, add cursor to __init__ and pass through it to renderer.render().
Any problem with my way?
Thanks.
I have few views in my project which are called only by ajax and I need to put in a check that the views are processed only if the request is ajax. So I thought of writing a decorator. Upon searching for similar type of decorators I hit upon few but couldn't understand anything.
I make use of class based views and will use this decorator on get/post methods. I did find few snippets but all were written for function based views which made it more difficult for me to understand as I have never worked upon function based views.
I just need to know what is idea behind decorators and how it works.
from functools import wraps
from django.core.exceptions import PermissionDenied
def require_ajax(view):
#wraps(view)
def _wrapped_view(request, *args, **kwargs):
if request.is_ajax():
return view(request, *args, **kwargs)
else:
raise PermissionDenied()
return _wrapped_view
After a google search I've found this:
from django.http import HttpResponseBadRequest
def ajax_required(f):
"""
AJAX request required decorator
use it in your views:
#ajax_required
def my_view(request):
....
"""
def wrap(request, *args, **kwargs):
if not request.is_ajax():
return HttpResponseBadRequest()
return f(request, *args, **kwargs)
wrap.__doc__=f.__doc__
wrap.__name__=f.__name__
return wrap
Didn't tried it, so you have to try it. The essential part is request.is_ajax() which checks if the request is made through AJAX. Check also the docs for more info on is_ajax() method.
EDIT
To decorate a view class in django see Decorating the class in the documentation. Basically the decorator function wraps a method of the class. So you can use the django #method_decorator() to wrap a method in your decorator function (ajax_required):
#method_decorator(ajax_required)
def method_you_want_to_get_only_AJAX_requests():
......