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
Related
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.
I would like to create a route that returns some model properties as a JSON with the Django REST framework and filter them based on some properties. But I don't get the serializer to working properly.
class MaxQuantPathSerializer(serializers.ModelSerializer):
class Meta():
model = MaxQuantRun
fields = '__all__'
depth=0
class MaxQuantPathsAPI(generics.ListAPIView):
filter_fields = ['project', 'setup']
filter_backends = [DjangoFilterBackend]
def get(self, request, format=None):
get_data = request.query_params
queryset = MaxQuantRun.objects.filter(project=get_data['project'])
serializer = MaxQuantPathSerializer(queryset, many=True)
data = serializer.data
return JsonResponse(data, status=201, safe=False)
The queryset returns some values but the serializer returns an empty dictionary.
I updated the code based on the comments below. Now, a json file is returned.
I have a serializer that has a nested serializer field. I had set up eager loading and everything was working great.
However, I had do add some custom filtering to the nested field, which required a SerializerMethodField.
After that change, the prefetch_related eager loading is no longer working. How can I optimize a serializer with SerializerMethodField?
Here was my initial, working setup:
# views.py
class MyView(generics.ListAPIView):
serializer_class = WorkingSerializer
def get_queryset(self):
queryset = MyModel.objects.all()
queryset = self.get_serializer_class().setup_eager_loading(queryset)
return queryset
# serializers.py
class WorkingSerializer(serializers.ModelSerializer):
related_field_name = CustomSerializer(many=True)
#staticmethod
def setup_eager_loading(queryset):
queryset = queryset.prefetch_related('related_field_name')
return queryset
And here is my changed serializer that doesn't work:
# serializers.py
class NotWorkingSerializer(serializers.ModelSerializer):
related_field_name = serializers.SerializerMethodField('get_related_field')
def get_related_field(self, instance):
queryset = instance.related_field_name.all()
# some filtering done here
return queryset
#staticmethod
def setup_eager_loading(queryset):
queryset = queryset.prefetch_related('related_field_name')
return queryset
Instead of doing the prefetch request in the serializer, you can do that in your actual queryset coming from the View. You can override get_queryset method with the custom query. Not sure what is your actual query but for an example you can do something like:
def get_queryset(self):
queryset = Model.objects.preftech_related("related_field")
return queryset
and this queryset will already have your related field in it. You won't have to write custom logic in the serializer.
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'm currently overriding the list method of the ModelViewSet and using filter_fields but I realized that the filter is applied before the list, so my list method is not being filtered by the query param. Is it possible to apply this filter after the list method?
class AccountViewSet(viewsets.ModelViewSet):
serializer_class = AccountSerializer
filter_fields = ('country__name')
filter_backends = (django_filters.rest_framework.DjangoFilterBackend,)
queryset = Account.objects.all()
def list(self, request):
if request.user.is_superuser:
queryset = Account.objects.all()
else:
bank_user = BankUser.objects.get(user=request.user)
queryset = Account.objects.filter(bank=bank_user.bank)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
When I do request using this URL http://localhost:8000/api/account/?country__name=Germany, it returns all the accounts filtered by bank but not by country.
I fixed the issue. I had to apply the filter_queryset in the queryset of the ViewSet and use it. queryset = self.filter_queryset(self.get_queryset())