How to write the functionality in generics.ListAPIView which can be written in APIView in Django DRF - django-rest-framework

I have a function base view which get 2 parameters from URL
http://127.0.0.1:8000/api/v1/contest/0b36d92a-51a7-4752-9df1-e5f2733116c1/paintings/
#api_view(['GET',])
#permission_classes([AllowAny])
def Contest_detail_by_id_and_category(request, id, category_name):
if request.method == 'GET':
artcontests = Artwork.objects.filter(artcontest = id,category__name__iexact=category_name)
serializer = ArtworkSerializer(artcontests, many=True)
# serializer = ArtworkSerializer(artcontests, many=True)
return Response(serializer.data)
which give proper result , but when I try to write the same functionality in generics.ListAPIView it gives
TypeError at /api/v1/contesty/0b36d92a-51a7-4752-9df1-e5f2733116c1/paintings/
object of type 'method' has no len()
class Contest_detail_by_id_category(generics.ListAPIView):
serializer_class = ArtworkSerializer1(many=True)
permission_classes = [AllowAny]
def queryset(self):
queryset = Artwork.objects.filter(artcontest = self.kwargs['id'],category__name__iexact=self.kwargs['category_name'])
# queryset = self.get_queryset()
serializer = ArtworkSerializer1(queryset)
return Response(serializer.data)
Can anyone help me - how to write the correct view in generics.ListAPIView or viewsets.ModelViewSet

Remove many=True from serializer_class. Create get_queryset function as below, instead of your queryset function.
class Contest_detail_by_id_category(generics.ListAPIView):
serializer_class = ArtworkSerializer1
queryset = Artwork.objects.all()
permission_classes = [AllowAny]
def get_queryset(self):
return self.queryset.filter(artcontest = self.kwargs['id'],category__name__iexact=self.kwargs['category_name'])
Update: Working:
ListAPIView class inherits ListModelMixin.
class ListModelMixin:
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
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)
See here. https://github.com/encode/django-rest-framework/blob/master/rest_framework/mixins.py

Related

Django Rest API Pagination not limiting number of objects

i followed the documentation but i still get the full list of objects not limited
views.py
from rest_framework.pagination import (LimitOffsetPagination,PageNumberPagination)
class PostView(generics.ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
pagination_class = PageNumberPagination
def list(self, request, *args, **kwargs):
self.object_list = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(self.object_list, many=True)
return Response(serializer.data)
settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 2
}
By implementing the list method yourself, the pagination is no longer applied. Indeed, the list method for a ListAPIView is implemented as [GitHub]:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
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)
But you thus do not need to implement the list method yourself. Your view can look like:
from rest_framework.pagination import (LimitOffsetPagination,PageNumberPagination)
class PostView(generics.ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
pagination_class = PageNumberPagination
# no list method

Using django-filter overriding list method in drf

I'm using django-filter to filter my viewsets in drf.
When I have a ModelViewset, works fine like example bellow:
class MyExampleViewSet(viewsets.ModelViewSet):
queryset = myqueryset
model = ModelExample
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filterset_fields = {
"field_example": ["exact", "icontains"],
"another_field_example": ["exact", "range"],
}
serializer_class = MyExampleViewSet
My problem is when I override the list method using a ViewSet, like this:
class MyExampleViewSet(viewsets.ViewSet):
def list(self, request, queryset=queryset, *args, **kwargs):
return something
In this case my filters does not working. Is there a way of using django-filter in this case (overriding list)?
I know what I can do with query_params, but I would like to use django-filter.
First, you should take a look at how the list method is implemented:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
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)
Django-filter is applied at this point queryset = self.filter_queryset(self.get_queryset()).
So if you want to override the list method but keep the the filtering feature, then make sure to call self.filter_queryset() with the queryset.

django rest framework - override viewsets.ViewSet#retrieve in order to use 2 parameters for lookup resource

I'd like to use two model's attributes to lookup a Route's record
models.py
class Route(DateTimeModel):
start_poi = models.ForeignKey(Poi, related_name="start_pois", on_delete=models.CASCADE)
end_poi = models.ForeignKey(Poi, related_name="end_pois", on_delete=models.CASCADE)
...
def __str__(self):
return 'From %s to %s' % (self.start_poi.slug, self.end_poi.slug)
views.py
class RouteViewSet(viewsets.ViewSet):
http_method_names = ['get']
#staticmethod
def list(request):
queryset = ...
serializer = RouteSerializer(queryset, many=True)
return Response(serializer.data)
#staticmethod
def retrieve(request, from_poi_slug, to_poi_slug):
queryset = ...
route = get_object_or_404(queryset, from_poi_slug=from_poi_slug, to_poi_slug=to_poi_slug)
serializer = RouteSerializer(route)
return Response(serializer.data)
urls.py
urlpatterns.extend([
path(rf'{BASE_API_PATH}/routes/(?P<from_poi_slug>[-\w]+)/(?P<to_poi_slug>[-\w]+)', RouteViewSet),
])
I get
Not Found: /api/v1/routes/xyz/abc
[29/Apr/2019 10:07:01] "GET /api/v1/routes/molo-santa-maria/kennedy-ne HTTP/1.1" 404 13191
What am I missing?
How can I properly override #retrieve and correctly configure urls?
Since you are only using read operations, you could use ReadOnlyModelViewSet.
#views.py
class RouteViewSet(viewsets.ReadOnlyModelViewSet):
http_method_names = ['get']
def list(self, request, *args, **kwargs):
queryset = ...
serializer = RouteSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, *args, **kwargs):
from_poi_slug = kwargs['from_poi_slug']
to_poi_slug = kwargs['to_poi_slug']
queryset = ...
route = get_object_or_404(queryset, from_poi_slug=from_poi_slug, to_poi_slug=to_poi_slug)
serializer = RouteSerializer(route)
return Response(serializer.data)
and in your urls.py
urlpatterns.extend([
path(rf'{BASE_API_PATH}/routes/(<from_poi_slug>)/(<to_poi_slug>)/', RouteViewSet.as_view({"get": "retrieve"})),
])

where the serializer in perform_createt comes from?

Although serializer as parameter passed in perform_create method, it is not clear where it pass from. Can anyone explain where the serializer parameter pass from and how it works?
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
It comes from the CreateModelMixin:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

Where to add default value in CreateAPIView?

I have a ListCreateAPIView, on which I want to populate a field with a default value in case of not provided by request.DATA.
Problem is: where should I do that ?
I can't modify the request.DATA because it is immutable and I don't want to copy/paste the CreateMixin implementation.
Here is my code:
class ObjectiveList(generics.ListCreateAPIView):
model = Objective
serializer_class = ObjectiveSerializer
permission_classes = (permissions.IsAuthenticated,)
def create(self, request, *args, **kwargs):
# provide a default value
objective_definition_id = request.DATA.get('objective_definition',-1)
data = request.DATA.copy()
if objective_definition_id == -1:
# support for 0.9.1 version of iOS and below
logger.info(str(self.request.DATA))
mission_url = request.DATA["mission"]
objectivedefinition_pk = self.default_objectivedefinition_id(mission_url)
data["objective_definition"]=objectivedefinition_pk
# I would want to do something like this but I can't
# request.DATA = data
# super(ObjectiveList,self).create(request, *args, **kwargs)
# copy/paste of the super class implementation
serializer = self.get_serializer(data=data, files=request.FILES)
if serializer.is_valid():
self.pre_save(serializer.object)
self.object = serializer.save(force_insert=True)
self.post_save(self.object, created=True)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Here is my serializer:
class ObjectiveSerializer(serializers.HyperlinkedModelSerializer):
objective_definition = serializers.PrimaryKeyRelatedField(many=False, read_only=False, required=False, default=toto)
class Meta:
model = Objective
fields = (
'url',
'objective_definition',
)
You can use the default= argument on the field, don't know if that meets your use case?

Resources