Post request handling in Django Rest framework - django

I am using Django Rest Framework, currently to pull some data from the backend we are using Get request, but due to URL limit going high we are planning to implement a Post request. To do this firstly the backend Django Rest API has to be made available to serve a post request.
I am new to Django and I don't find a post or get method in the code, all i can say is we are using viewsets, I tried using "#detail_route(methods=['post'])" but this didn't work, what am I doing wrong here?
class XViewSet(viewsets.ViewSet):
renderer_classes = ''
def retrieve(self, request, pk=None):
try:
pk = int(pk)
except ValueError:
raise InvalidParameterError(parameter_name='id', invalid_value=pk)
queryset = models.X.objects.all()
x = get_object_or_404(queryset, pk=pk)
pipelines = request.query_params.getlist('pipeline[]')
callsets =
callset_ids =
serializer = serializers.XSerializer(x, context={'request': request})
requested_samples = [z[1:] for z in request.query_params.getlist('s')]
filtered_calls = []
serialized_data = serializer.data
unfiltered_calls = serialized_data.get('calls')
if unfiltered_calls:
for serialized_calls in unfiltered_calls:
if serialized_calls['callset'] in callset_ids:
unfiltered_calls = serialized_calls['data']
for call in unfiltered_calls:
if call['y'] in requested_y:
filtered_calls.append(call)
break
serialized_data['calls'] = filtered_calls
return Response(serialized_data, status=status.HTTP_200_OK)
def list(self, request):
qp = self.request.query_params
validated_qp =
# generate the query
query_object =
query =
# execute the query
cursor = connections['default'].cursor()
cursor.execute(query)
qs = utils.dictfetchall(cursor)
# sanitize query results
if 't' in validated_qp:
return_data =
else:
for x in qs:
if 'calls' in x:
x['calls'] =
else:
x['calls'] = {}
return_data =
resp = Response(return_data, status=status.HTTP_200_OK)
if validated_qp.get(''):
resp['content-disposition'] =
return resp

You can use class-based views to handle the requirements,
from rest_framework.views import APIView
class MyAPI(APIView):
def get(selfself, request):
# do stuff with get
return Response(data="return msg or data")
def post(self, request):
post_data = request.data
# do something with `post_data`
return Response(data="return msg or data")
UPDATE : Using ViewSet
ViewSet class provide us create() methode to create new model instances. So we can override that to handle post data coming to the view. Just add a create() under your view class as below
class XViewSet(viewsets.ViewSet):
renderer_classes = ''
def create(self, request): # Here is the new update comes <<<<
post_data = request.data
# do something with post data
return Response(data="return data")
def retrieve(self, request, pk=None):
# your code
return Response(serialized_data, status=status.HTTP_200_OK)
def list(self, request):
# your code
return resp

Related

Django - How to change the default queryset in a view if a filter is used? [duplicate]

In ListView, I can easily use def post(self, request) method to make a post request from a list view. But I want to make the post request from def get_queryset(self) which I am not yet able to do. When I try to do it it shows "method 405 not allowed!" even though post method is allowed through http_method_names.
How can I access POST request inside get_queryset function?
class ZonListView(SearchMixin, SingleTableMixin, ListView):
template_name = 'cadmin/list.html'
model = Zon
table_class = ZonTable
search_fields = {
'title': 'icontains',
'description': 'icontains',
}
def post(self, request): # ***** this one works! ******
try:
toggle_status = request.POST.get('toggle-status')
pk = int(request.POST.get('pk'))
....
return HttpResponseRedirect(reverse('cadmin:zon_list'))
def get_queryset(self):
qs = super(ZonListView, self).get_queryset()
if self.request.POST: #***** Not working. 405 Error *****#
try:
toggle_status = self.request.POST.get('toggle-status')
pk = int(self.request.POST.get('pk'))
......
if self.request.GET:
try:
status = self.request.GET.get('status')
qs = qs.filter(status=status)
except Exception:
pass
return qs.distinct()
def get_context_data(self, **kwargs):
....
To make method allowed you need to implement function named same as method, post in your case. So to use request.POST in get queryset you also need to define post() method like this:
def post(self, request): # ***** this method required! ******
self.object_list = self.get_queryset()
return HttpResponseRedirect(reverse('cadmin:zon_list'))
def get_queryset(self):
qs = super(ZonListView, self).get_queryset()
if self.request.POST: #***** Now allowed *****#
try:
toggle_status = self.request.POST.get('toggle-status')
pk = int(self.request.POST.get('pk'))
......
Look Django's View source to check how allowed methods defined.

Pagination DRF not working well

I have a little problem with pagination when I put the default pagination in my project, somethings pages work in others pages not working for example:
this is my file settings.py for all my project
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apicolonybit.notification_clbt.NotificationPagination.PaginationList'}
this is my Configuration app inside my project: myproject / Configuration
class ConfigurationsList(generics.ListAPIView):
"""
list configuration with current user authenticated.
"""
queryset = Configuration.objects.all()
serializer_class = ConfigurationSerializer
when I run this part in my postman, working well, but when I try to run in another module like this:
class TransactionListHistory(generics.ListAPIView):
# queryset = TransactionHistory.objects.all()
serializer_class = TransactionHistorySerializer
pagination_class = PaginationList
page_size = 2
page = 1
def get_object(self, current_user):
# User.objects.get(id=pk)
return TransactionHistory.objects.filter(agent_id=current_user.id).order_by('-id')
#classmethod
def get_object_client(cls, current_user, trans):
# User.objects.get(id=pk)
return TransactionHistory.objects.filter(user_id=current_user.id).order_by('-id')
def get(self, request, format=None):
current_user = request.user
status_trans = 6
agent_user = 2
client_user = 1
trans = {
'status': status_trans
}
typeusers = Profile.objects.get(user_id=current_user.id)
# actions agent user = show all transaction from all client users
if typeusers.type_user == agent_user:
list_trans_init = self.get_object(current_user)
serializer = TransactionHistorySerializer(list_trans_init, many=True)
get_data = serializer.data
# actions normal user (client user) = just see transactions from self user
if typeusers.type_user == client_user:
list_trans_init = self.get_object_client(current_user, trans)
serializer = TransactionHistorySerializer(list_trans_init, many=True)
get_data = serializer.data
# if not PaginationList.get_next_link(self):
# return JsonResponse({'data': get_data}, safe=False, status=status.HTTP_200_OK)
return self.get_paginated_response(get_data)
my custom file pagination like this
class PaginationList(PageNumberPagination):
page_size = 2 # when show me an error I added
offset = 1 # when show me an error I added
limit = 10 # when show me an error I added
count = 10 # when show me an error I added
page = 1 # when show me an error I added
def get_paginated_response(self, data):
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'results': data
})
the variables page_size and etc, then show me an error like PaginationList is not object page, I added this page_size and pass other error like PaginationList is not object offset and again added var.
well the last error show me is like this 'int' object has no attribute 'has_next'
please help me, how to add my custom pagination in my class TransactionListHistory
thanks for your attention.
You are using self.get_paginated_response() in wrong way.
from rest_framework.response import Response
class TransactionListHistory(generics.ListAPIView):
# Your code
def get(self, request, *args, **kwargs):
queryset = do_something_and_return_QuerySet() # do some logical things here and
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
the do_something_and_return_QuerySet() is fucntion or logic of you, which return a QuerySet.
Example
class TransactionListHistory(generics.ListAPIView):
# Your code
def get(self, request, *args, **kwargs):
queryset = TransactionHistory.objects.filter(user_id=request.user.id)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)

How to use django-el-pagination with filtering?

I have a django Listview and i am using django-el-pagination's ajax endless pagination to paginate my results and it works well. The problem is I need to apply some filters to it and have no clue on how to do it.
Can i send parameters through GET to the view? Have searched a lot and seems like no one had this problem before.
Data is always returned in the get_queryset() function.
If you want to simply filter from the database, you can just return objects.
class IndexView(AjaxListView):
template_name = '****'
context_object_name = '****'
page_template = '****'
def get_queryset(self):
return your_model.objects.filter(title='***').order_by('**')
Else if you want to get data from non-database, you need to implement a proxy accordding to this answer.
If not,the pagination ajax will request all data, then slice it.The proxy make your data sliced while querying.
This is my filter that getting data from ElasticSearch.
class IndexView(AjaxListView):
template_name = '****'
context_object_name = '****'
page_template = '****'
def get_queryset(self):
params = {}
# get query params
for item in self.request.GET.items():
if item[0] == 'page' or item[0] == 'querystring_key':
continue
params[item[0]] = item[1]
# no filter
if len(params) == 0:
return ****.objects.filter().order_by('-date')
else:
return ESResult(params)
class ESResult(object):
def __init__(self, params):
self.params = params
def __len__(self):
s = self.search_es()
if s:
s = s[:1]
r = s.execute()
return r['hits']['total']
else:
return 0
def __getitem__(self, item):
assert isinstance(item, slice)
result = []
s = self.search_es()
if s:
s = s[item.start:item.stop] # slice before querying
r = s.execute()
for a in r.to_dict()['hits']['hits']:
one = a['_source']
one['id'] = int(a['_id'])
result.append(one)
return result
def search_es():
...
# filter request here
...
The list object used by AjaxListView is defined by get_queryset() method. To filter the queryset based on the users input, you may refer to POST method:
from app.forms import BlogFilterForm
class Blog(LoginRequiredMixin, AjaxListView):
context_object_name = "posts"
template_name = 'blog/blog.html'
page_template = 'blog/post_list.html'
success_url = '/blog'
def get_queryset(self): # define queryset
queryset = Post.objects.all() # default queryset
if self.request.method == 'POST': # check if the request method is POST
form = BlogFilterForm(self.request.POST) # define form
if form.is_valid():
name = form.cleaned_data['name'] # retrieve data from the form
if name:
queryset = queryset.filter(name=name) # filter queryset
else:
queryset = queryset
return queryset
def get_context_data(self, **kwargs):
context = super(Blog, self).get_context_data(**kwargs)
context['form'] = BlogFilterForm() # define context to render the form on GET method
return context
def post(self, request, *args, **kwargs): # define post method
return super(Blog, self).get(request, args, kwargs)
The endless pagination should work fine.
using filters with django-endless-pagination

AttributeError: '' object has no attribute 'page' DRF Pagination

This is my question asked 2 days back. I used Louis Barranqueiro's answer to solve my problem.
Now I want to add current page number as well as page_size in the serialized data. I know I have to customize the get_paginated_response method in PageNumberPagination class, but when I do that I get this error:
My code
def get_paginated_response(self, data, request):
# import pdb
# pdb.set_trace()
return Response(OrderedDict([
('next', self.get_next_link()),
('current', self.get_current_link()),
('previous', self.get_previous_link()),
('results', data)
]))
def get_queryset(self, request):
product_sync_ts = self.request.GET.get('product_sync_ts', None)
if product_sync_ts:
product = Product.objects.filter(....)
)
# return self.get_paginated_response(product, self.request)
return Response(product)
else:
content = {'details': "Bad Request"}
raise APIException400(request, content)
def get(self, request, format=None):
products = self.get_queryset(request)
serializer = SyncedProductSerializer(instance={'products': products})
# product = self.paginate_queryset(serializer, request)
return self.get_paginated_response(serializer, request)
# return self.get_paginated_response(serializer.data, request)
Error:
File "/Users/Documents/content-api/venv/lib/python2.7/site-packages/rest_framework/pagination.py", line 242, in get_next_link
if not self.page.has_next()
AttributeError: 'PaginatedProductList' object has no attribute 'page'
Some one might wanna try:
REST_FRAMEWORK = {
'PAGE_SIZE': 20,
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',}
on settings.py
with a view like
class GeneralManagementAPIView(generics.ListAPIView):
queryset = GeneralManagements.objects.all()
permission_classes = (IsAuthenticatedOrReadOnly,)
renderer_classes = (GeneralManagementJSONRenderer,)
serializer_class = GeneralManagementSerializer
def get_queryset(self):
return GeneralManagements.objects.all()
def list(self, request):
queryset = self.get_queryset()
page = self.paginate_queryset(queryset)
print("request ", request)
serializer_context = {'request': request}
serializer = self.serializer_class(
page, context=serializer_context, many=True
)
print("serializer ", serializer, "serializer.data", serializer.data )
return self.get_paginated_response(serializer.data)
You should call paginate_queryset before calling get_paginated_response
Note that the paginate_queryset method may set state on the pagination instance, that may later be used by the get_paginated_response method.
https://www.django-rest-framework.org/api-guide/pagination/
I am just adding my answer to help other users with the same problem they are facing.
Adding to the above statement, it also has a solution to your question in a simpler way.
Just pass PageNumberPagination and add request in self.paginate_queryset method.
class PaginatedProductList(APIView, PageNumberPagination):
def get(self, request):
products = Product.objects.filter(....)
return self.paginate_queryset(products, request)
So finally I found out how to solve this problem..
Below is the code(Simple and silly mistakes that I was doing)
class PaginatedProductList(APIView, PageNumberPagination):
page_size = 1000 #---crucial line
max_page_size = 1000
def get_paginated_response(self, data, page, page_num):
return Response(OrderedDict([
('count', self.page.paginator.count),
('current', page),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('page_size', page_num),
('results', data)
]))
def get_queryset(self, request):
product_sync_ts = self.request.GET.get('product_sync_ts', None)
if product_sync_ts:
product = Product.objects.filter(...)
)
return self.paginate_queryset(product, self.request)
raise APIException400(request, {'details': "Bad Request"})
def get(self, request):
page = self.request.GET.get('page', 1)
page_size = self.request.GET.get('page_size', 1000)
products = self.get_queryset(request)
serializer = SyncedProductSerializer(instance={'products': products})
return self.get_paginated_response(serializer.data, page, page_size)
Other than inheriting PageNumberPagination class in view, try defining your pagination class outside your view(as a separate class) inheriting from PageNumberPagination. and mention that class name as pagination class = YourClassName. inside your view
class PaginationClass(PageNumberPagination):
page_size = 2
and in view,
class GeneralManagementAPIView(generics.ListAPIView):
pagination_class = PaginationClass

Populating request.DATA for testing api in Django using APIRequestFactory

I am using restframeworks test modules to write test. I need to test a get request for which I created a test as follows
class BlogCommentTest(APISimpleTestCase):
def test_3002_getting_reviewcomment(self):
factory = APIRequestFactory()
request = factory.get(reverse('get-review-comments'), data= {"article": 1})
request.user = self.user
view = ReviewCommentViewSet.as_view({'get': 'list'})
force_authenticate(request, self.user)
response = view(request)
however the way I wrote populates the request.GET with {'article':1} when I want to populate request.DATA. What is the correct way to make the request object whose request.DATA is populated the way I want ?
EDIT
The code for viewset is
class ReviewCommentViewSet(viewsets.ModelViewSet):
"""
viewset to get recursive serialized results
"""
model = models.ReviewComments
serializer_class = serializers.ReviewCommentSerializer
queryset = models.ReviewComments.objects.filter(is_deleted=False)
def serialize_tree(self, queryset):
"""
:return: A helper function to get list of next set of childrens
"""
for obj in queryset:
data = self.get_serializer(obj).data
data['parent'] = self.serialize_tree(obj.children.all().exclude(is_deleted=True))
yield data
def list(self, request):
queryset = self.get_queryset().filter(parent=None, article=request.DATA['article'])
data = self.serialize_tree(queryset)
return Response(data)
def retrieve(self, request, pk=None):
self.object = self.get_object()
data = self.serialize_tree([self.object])
return Response(data)