paginate_queryset() got an unexpected keyword argument 'view' - python

while running the below code getting the "paginate_queryset() got an unexpected keyword argument 'view'" error after adding pagination
views.py
class UsersList(ListAPIView,LimitOffsetPagination):
permission_classes = (permissions.IsAuthenticated, IsVerified,permissions.IsAdminUser)
#swagger_auto_schema(
query_serializer=PaginationSerializer,
responses={status.HTTP_200_OK: UserOutputSerializer(many=True)},
operation_id="list_users",
)
def get(self, request, *args, **kwargs):
qs = User.objects.filter(is_verified=True, is_active=True).order_by('user_name')
results = self.paginate_queryset(qs, request, view=self)
users = UserOutputSerializer(results, many=True)
return self.get_paginated_response(users.data)
urls.py
path('list_users/',UsersList.as_view(),name='list_users'),

You are discarding all the good stuff about ListAPIView by overriding the get method like that. If you want to user swagger_auto_schema, then try like this:
class UsersList(ListAPIView,LimitOffsetPagination):
permission_classes = (permissions.IsAuthenticated, IsVerified,permissions.IsAdminUser)
serializer_class = UserOutputSerializer
paginate_by = 10
queryset = User.objects.filter(is_verified=True, is_active=True).order_by('user_name')
#swagger_auto_schema(
query_serializer=PaginationSerializer,
responses={status.HTTP_200_OK: UserOutputSerializer(many=True)},
operation_id="list_users",
)
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)

Related

pass a parameter into serilazer under ListModelMixin

I am passing a parameter to a serilaizer like this:
serializer = AttractionTicketSerializer(attraction, context={'api_consumer':request.auth.application})
I have a view which inherits from ListModelMixin, I need to pass this context param to the serilizer as well.
here is a summarized view:
class AttractionView(mixins.ListModelMixin, generics.GenericAPIView):
authentication_classes = AUTHENTICATION_CLASSES
permission_classes = [IsAuthenticatedOrTokenHasReadWriteScope]
queryset = Attraction.objects.all()
serializer_class = AttractionSerializer
def get(self, request: Request, *args, **kwargs):
attractions: Dict[str, Any] = self.list(request, *args, **kwargs)
return attractions
Is there a way to do it?
Thanx in advance
You can override the get_serializer_context() function to add additional context to be passed to the serializer.
class AttractionView(mixins.ListModelMixin, generics.GenericAPIView):
<...already_defined_attributes...>
def get_serializer_context(self):
# you can access self.request here
# if you need to get some data from your request
context = super().get_serializer_context()
context.update({
'new_key': <new_value>
})
return context
You can also override the list function to achieve it.
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
context = self.get_serializer_context()
context.update({
'new_key': 'new_value'
})
if page is not None:
serializer = self.get_serializer(page, context=context, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, context=context, many=True)
return Response(serializer.data)
Based on your specific requirement you can choose any of the above approaches.

Django CBVs: reuse query from get_queryset in get_context_data method in ListView

I need to pass a Tag object to the template, but without an additional query to the database in get_context_data method.
Can you please tell me if there is a more elegant way to get the value from the get_queryset method in the get_context_data.
And if there is a more elegant way, is it correct to declare your own fields in Django views
class PostListView(ListView):
model = Post
paginate_by = 3
context_object_name = 'posts'
template_name = 'blog/post/list.html'
tag = None
def get_queryset(self):
data = super().get_queryset()
if tag_slug := self.kwargs.get('tag_slug'):
self.tag = get_object_or_404(Tag, slug=tag_slug)
data = data.filter(tags__in=[self.tag])
return data
def get_context_data(self, *, object_list=None, **kwargs):
data = super().get_context_data(**kwargs)
data['tag'] = self.tag
return data
get queryset should only prepare queryset without any hit in database. In your case you do many not needed things:
def get_queryset(self):
query = Q()
if self.kwargs.get('tag_slug'):
query = Q(tags__slug=self.kwargs['tag_slug'])
return super().get_queryset().filter(query).select_related('tag')
in reality function above is One-liner.
You don't need any tag in context, but if you want:
def get_context_data(self, *args, **kwargs):
response = super().get_context_data(*args, **kwargs)
if len(response['object_list']) : # only one hit in database
response['tag'] = response['object_list'][0].tag
else:
raise Http404
return response
len made ask in database, and get all objects.
Based on reply I edit my code for the following:
because I need tag name and it can be different from tug slug I override get method to get tag object and not execute uselees code later
removed query from get_queryset
replaced "object_list" with "posts" due to iterating through posts in my template
class PostListView(ListView):
model = Post
paginate_by = 3
context_object_name = 'posts'
template_name = 'blog/post/list.html'
tag = None
def get(self, request, *args, **kwargs):
if tag_slug := self.kwargs.get('tag_slug'):
self.tag = get_object_or_404(Tag, slug=tag_slug)
return super(PostListView, self).get(request, *args, **kwargs)
def get_queryset(self):
query = Q()
if self.tag:
query = Q(tags__slug=self.tag)
return super(PostListView, self).get_queryset().filter(query)
def get_context_data(self, *args, **kwargs):
response = super().get_context_data(*args, **kwargs)
if len(response['posts']):
response['tag'] = self.tag
else:
raise Http404
return response

How to pass data to the model field when saving the form? django form models save

get the form and process it in post. It is necessary to save the unique uuid of the record to the model, I do it like this: formOne.save(related_uuid=related_uuid)
but doesn't work, the error is - save() got an unexpected keyword argument 'related_uuid'
models
class Orders(models.Model):
device = models.CharField(max_length=150)
uuid = models.CharField(max_length=22, blank=True)
views
class OrderAddView(TemplateView):
template_name = 'orders/order_add.html'
def get(self, request, *args, **kwargs):
context = super().get_context_data(**kwargs)
... some work code
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
formOne = SimpleOrderAddForm(self.request.POST, prefix='one_form')
if formOne.is_valid():
related_uuid = shortuuid.uuid()
formOne.save(related_uuid=related_uuid)
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
return self.form_invalid(formOne, **kwargs)
def form_invalid(self, formOne,, **kwargs):
context = self.get_context_data()
... some work code
return self.render_to_response(context)
assuming that "shortuuid.uuid()" return correct uuid; try
def post(self, request, *args, **kwargs):
formOne = SimpleOrderAddForm(self.request.POST, prefix='one_form')
if formOne.is_valid():
a=formOne.save(commit=False)
a.related_uuid=shortuuid.uuid()
a.save()
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
return self.form_invalid(formOne, **kwargs)

How to simplify the code and is it necessary?

I work on the API in the Django REST Framework. And now there is such a problem: there is a ModelViewSet and in its functions the same request to the database, the same check in the if block. Is it possible to somehow move this matter into a separate function and how to do it?
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
queryset = Link.objects.all()
def retrieve(self, request, *args, **kwargs):
instance = Link.objects.filter(Q(user_id=self.request.user.id) & Q(id=kwargs["pk"])).first()
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return super().retrieve(request, *args, **kwargs)
def partial_update(self, request, *args, **kwargs):
instance = Link.objects.filter(Q(user_id=self.request.user.id) & Q(id=kwargs["pk"])).first()
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return super().partial_update(request, *args, **kwargs)
If I understand the code correctly, the intention is to limit queryset to allow access only to links owned by currently logged in user. For that, you can just override get_queryset method and that is it. DRF will take care of getting object from the queryset by id and throwing 404 if the object is not found.
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
def get_queryset(self):
return Link.objects.filter(user_id=self.request.user.id)
You could do some of the implementation in a private method
def _link_objects_filter(self, pk):
return Link.objects.filter(Q(user_id=self.request.user.id)
& Q(pk)).first()
Or, taking it a step further, have a common implementation that uses getattr to decide which base implementation to use.
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
queryset = Link.objects.all()
def _retrieve_op(self, method, request, *args, **kwargs):
instance = self._link_objects_filter(kwargs["pk"])
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return getattr(super(), method)(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
return self._retrieve_op("retrieve", request, *args, **kw)
def partial_update(self, request, *args, **kwargs):
return self._retrieve_op("partial_update", request, *args, **kw)
def _link_objects_filter(self, pk):
return Link.objects.filter(Q(user_id=self.request.user.id)
& Q(pk)).first()
That could be further reduced with partial methods
import functools
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
queryset = Link.objects.all()
def _retrieve_op(self, request, method, *args, **kwargs):
instance = self._link_objects_filter(kwargs["pk"])
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return getattr(super(), method)(request, *args, **kwargs)
retrieve = functools.partialmethod(_retrieve_op, "retrieve")
partial_update = functools.partialmethod(_retrieve_op, "partial_update")
def _link_objects_filter(self, pk):
return Link.objects.filter(Q(user_id=self.request.user.id) & Q(pk)).first()
I'm not sure whether django meta programming will mess this up.

Django generics.ListAPIView accepting POST method

I have a Django view which extends generics.ListAPIView. It works fine with get requests, however since the char limits of the URL, now I need to send the request via POST. It is the same request, the only thing I need to change is the method to POST.
My current code is pretty simple:
class MyClass(generics.ListAPIView):
serializer_class = MySerializer
paginate_by = 1
def get_queryset(self):
queryset = SomeClass.objects.all()
# do some filtering
How could I add POST support to this class?
Try this:
class MyClass(generics.ListAPIView):
serializer_class = MySerializer
paginate_by = 1
def get_queryset(self):
queryset = SomeClass.objects.all()
# do some filtering
def post(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
You could use the ListCreatAPIView and overriede the create method to do same as your list method. Maybe you can just do something like the following:
class MyClass(generics.ListCreateAPIView):
serializer_class = MySerializer
paginate_by = 1
def get_queryset(self):
queryset = SomeClass.objects.all()
# do some filtering
def create(self, request, *args, **kwargs):
# maybe replace request.method with 'GET'
self.list(request, *args, **kwargs)
But I would suggest to use the methods as specified.

Categories