I have a CreateAPIView class based view and I need to filter the queryset defined in the serializer at run time by something that is available to me from the request. Is there any way to do that?
The serializer has this code:
class AssignWorkItemSerializer(serializers.Serializer, WorkitemSerializerMixin):
assigneeIds = serializers.ListSerializer(
child=serializers.PrimaryKeyRelatedField(
queryset=get_user_model().objects.filter(userrole__role__name='analyst')
),
source='assignees'
)
It is failing because that query returns multiple rows.
class AssignWorkItemView(ListCreateAPIView):
permission_classes = [IsAuthenticated, IsManager]
serializer_class = AssignWorkItemSerializer
def get_queryset(self):
queryset = Post.objects.all()
name = self.request.query_params.get('name')
if name:
queryset = queryset.filter(name=name)
return queryset
Can you try like this.
Related
i am learning drf and i little confused
in the urls.py i have
path('todos/<int:pk>', views.TodoRetrieveUpdateDestroy.as_view()),
on the views.py
class TodoRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView):
serializer_class = TodoSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
user = self.request.user
return Todo.objects.filter(user=user)
by logic i would add to filter like pk = self.kwargs[‘pk’] to send only one element Todo but it works and send only that ‘id=pk’ post without adding additional filter.Can u explain why please and how works RetrieveUpdateDestroy and get_queryset)
RetrieveUpdateDestroyAPIView doesnt execuate method def get_queryset(self): in the first place
It has def get_object(self): method which gets object by lookup_fields and query from query set method returned data again
So to get/update/delte single data,
You have to do like this:
class TodoRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView):
queryset = Todo.objects.all()
serializer_class = TodoSerializer
permission_classes = [permissions.IsAuthenticated]
lookup_field = 'pk'
and your urls should be like this:
path('some-paht/<int:pk>/', TodoRetrieveUpdateDestroy.as_view(), name='some_name')
I don't know why all data is returned when I query with a field that not in FilterSet class
This is my model
class TempModel(models.Model):
md5 = models.CharField()
sha1 = models.CharField()
and following is my filterSet class
class TempFilter(filters.FilterSet):
class Meta:
model = TempModel
fields = ("md5")
and finally this is my ViewSet
class TempViewSet(viewsets.ModelViewSet):
queryset = TempModel.objects.all()
permission_classes = [IsAuthenticated]
authentication_classes = [TokenAuthentication]
serializer_class = TempSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_class = TempFilter
When I make a request
/temp/?md5=XXXX, it works very well, but /temp/?sha1=XXXX, it returns all data.
I think if request not specified fields, it returns not found.
I try to define exclude in FilterSet class, but it still not working, How will I solve that problem?
I found out in the django-filters documentation that you can override qs property in order to filter the primary queryset (the queryset = TempModel.objects.all() you declared in the viewset).
class TempFilter(filters.FilterSet):
class Meta:
model = TempModel
fields = ("md5")
#property
def qs(self):
queryset = super(TempModel, self).qs
query_params = self.request.query_params
if any(query_param not in TempModel.Meta.fields for query_param in query_params):
return queryset.none()
return queryset
So the code above would return empty queryset if any of the query parameters didn't exist.
How would I filter results based on a computed field from a Serializer? I tried treating it like any other field, but django doesn't like it.
Serializer
class ImageSerializer(serializers.ModelSerializer):
is_annotated = serializers.SerializerMethodField('has_annotation')
class Meta:
model = Image
fields = '__all__'
#staticmethod
def has_annotation(image):
return image.annotation_set.count() > 0
View
class ImageViewSet(viewsets.ModelViewSet):
serializer_class = ImageSerializer
lookup_field = 'id'
permission_classes = [
Or(IsAdmin),
IsAuthenticated
]
def get_queryset(self):
queryset = Image.objects
is_annotated_filter = self.request.query_params.get('is_annotated', None)
if is_annotated_filter is not None:
queryset = queryset.filter.annotate(
cnt=Count('annotation')).filter(cnt__gte=1)
queryset_order = get_queryset_order(self.request)
return queryset.order_by(queryset_order).all()
http://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters
http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield
I think you misunderstand: 1) serializer MethodField which major purpose is READ-ONLY and the serializer is not about to be used in filtering queryset.
I will filter it more like these:
from django.db.models import Count
queryset.filter.annotate(
cnt=Count('annotation_set')
).filter(cnt__gte=1)
but... you can go even better:
1) just annotate your queryset e.g in your ViewSet
from django.db.models import Count
queryset = Image.objects.annotate(
cnt_annotations=Count('annotation_set')
)
2) then in serializer do something like these:
#staticmethod
def has_annotation(image):
if hasattr(obj, 'has_annotation'):
return bool(obj.cnt_annotations)
else:
return None
I am working on project using django rest framework in which i have to filter different parameters given by user.I am using django Filter backend.
Here is my code:
class FilterViewSet(viewsets.ModelViewSet):
serializer_class = SearchSerializer
#Filters on specific fields
filter_backends = (DjangoFilterBackend,)
filter_fields = ('property_zipcode','property_state',
'property_county',
'property_city','property_area',)#range between 100 to 500 or area less then 500.
#range is pass by user as a property_area=300.
def filter_queryset(self, queryset):
if self.request.query_params.get('property_state', None):
queryset = super(FilterViewSet, self).filter_queryset(self.get_queryset())
return queryset
else:
queryset = self.get_queryset()
return queryset
Everything is working fine. But now i have to filter property_area based on range like 100 sqft to 500 sqft. How i can achieve this using djangoFilter backend?
Thanks #Gabriel Muj. From the django-filter documentation i solved my problem. I created my own filter class added fields which is use for filters.
class Filter(django_filters.FilterSet):
class Meta:
model = Property
fields = {
'property_state': ['exact'],
'year_built': ['lt', 'gt'],
'tax':['lt','gt'],
}
Call it in Viewset:
class FilterViewSet(viewsets.ModelViewSet):
serializer_class = SearchSerializer
#Filters on specific fields
filter_class = Filter
def filter_queryset(self, queryset):
if self.request.query_params.get('property_state', None):
queryset = super(FilterViewSet, self).filter_queryset(self.get_queryset())
return queryset
else:
queryset = self.get_queryset()
return queryset
in my views.py there is ViewSet:
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_class = OrderFilter
filter_backends = (
OrderingFilter,
DjangoFilterBackend,
)
def get_queryset(self):
...some query...
return products # return QuerySet object with all products in db.
Where and how can I manipulate with Project objects after filtering? With ability to take data from request. For example:
for product in products: # after pagination, filtering, etc.
product.price = product.price*self.request.user.discount
Thank you!
By the time you are using a ModelViewSet, you will have to override the list() method for example. The signature is list(self, request, *args, **kwargs) where as you understand, you can use the request object and manipulate your data as needed.
Let me know if you need further help!