DjangoFilterBackend field = null - django-rest-framework

I used DjangoFilterBackend with all fields:
class EntitiesViewSet(viewsets.ModelViewSet):
queryset = Entity.objects.all()
serializer_class = EntitiesSerializer
pagination_class = StandardResultsSetPagination
filter_backends = (DjangoFilterBackend,)
filter_fields = '__all__'
It worked perfectly for querying through the url of one or more fields with fixed or boolean values.
like this:
http://localhost:8000/api/persons/?news_by_email=True
http://localhost:8000/api/persons/?issuer=SSP-SC
But I need to also filter for fields with null value and it is not working.
I tried:
/persons/?parent=null
/persons/?parent=Null
/persons/?parent=NULL
/persons/?parent=
/persons/?parent__isnull
Any suggestions in this same simplified process?
Any suggestions that require an extension or new viewset?

You may also want to query the 'exact' value, besides isnull. In that case,
filter_fields = {'parent': ['exact', 'isnull']}
And you can make a query with '=True',
/persons/?parent__isnull=True

filter_fields = {'parent': ['isnull']}
This will add a filter with 'isnull' and you can make a query as below:
/persons/?parent__isnull=true

Related

Django rest framework multi searching

everyone. I tried understand search.
I have url path('quiz/all/', QuizListView.as_view()),
View :
class QuizListView(generics.ListAPIView):
queryset = Quiz.objects.all()
serializer_class = QuizDetailSerializer
search_fields = ('description', 'title',)
filterset_fields = ['title', 'description',]
(method1)If I use search, for example /api/v1/quiz/all/?search=QI got all instances where title or description contains 'Q'
(method2)I can search /api/v1/quiz/all/?title=Q&description=d I got a instance which has exact title and description.
(method3)But I want to get list of all instances where title contains one value and description contains other value. For example, I want to write /api/v1/quiz/all/?title=Q&description=d and get list where title contains Q and description contains d.
Quiz1(title=Q, description=d)
Quiz2(title=Test, description=dd)
Quiz3(title=NewQ, description=Test_d)
For (method1,/api/v1/quiz/all/?search=Q) I got Quiz1, Quiz2, Quiz3
For (method2,/api/v1/quiz/all/?title=Q&description=d) I got Quiz1
For (method3,/api/v1/quiz/all/?title=Q&description=d) I would like to
get Quiz1 and Quiz3 (because they contain Q for title and d for description)
Thanks.
You'll need to create a custom FilterSet class and use the contains or icontains (if you want case insensitive) because the default is using exact and that's why you don't get back the result that you want. See docs here and here
# filters.py
class QuizFilter(django_filters.FilterSet):
class Meta:
model = Quiz
fields = {
'title': ['contains'], # or icontains
'description': ['contains'], # or icontains
}
# views.py
from django_filters.rest_framework import DjangoFilterBackend
from .filters import QuizFilter
class QuizListView(generics.ListAPIView):
queryset = Quiz.objects.all()
serializer_class = QuizDetailSerializer
filter_backends = (DjangoFilterBackend, ) # add here other filters backends
filterset_class = QuizFilter

Filtering users based on date joined

I am trying to filter users based on the date joined. but it somehow it requires the time also even though i formatted it to only have the date, for example "2020-05-08".
class UserSerializer(serializers.ModelSerializer):
date_joined = serializers.DateTimeField(
format='%Y-%m-%d', input_formats=None)
class Meta:
model = User
fields = ['__all__']
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [ DjangoFilterBackend]
filterset_fields = ['date_joined']
{
"date_joined": [
"Enter a valid date/time."
]
}
You should use DateField in your serializer instead of DateTimeField:
https://www.django-rest-framework.org/api-guide/fields/#datefield
If you have the DateTimeField in your model (and I guess, you are, as it seems, that you are using standard User) you will need to use the corresponding serializer field. If you want to have the DateTimeField in your model, but filter only by the date you should customize your view/serializer/filter.
The easiest way to do it will be something like:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = UserSerializer
def get_queryset(self):
some_date = self.request.GET.get('date')
qs = User.objects.all()
if date:
#filter depends of what exactly do you want
return qs.filter(date_joined__contains=some_date)
return qs
You can check out the examples of date filters here:
How do I filter query objects by date range in Django?

DRF - in filter, use field-value instead of default pk / id

I'm trying to use DRF's filters so that the URL query is like so:
/roadname/?road=M5
not like so
/roadinfo/?road=1
I can't seem to do it when I've got a ForeignKey relationship.
I've tried using lookup_field with no luck (although not sure how this would work for multiple filter fields anyway - I don't think that's the answer). I've tried using a get_queryset() method in views as in the second example in the documentation. A comment I came across suggested that this is bad RESTApi practice - is it? How would a user know to type in '1' to get results for 'M5' in a front-end client?
I've set up two really simple models (and serializers, views, etc.) to try these out as below.
If I use RoadName, I have to type the name into the filter search box (rather than having a dropdown), but the url query is how I want it.
If I use RoadInfo (which has a ForeignField to RoadName), I get a drop down in the filter box, but the url query uses the ForeignKey pk.
My question: How can I set it so that when I use RoadInfo, the query uses the field value rather than the id/pk?
Models
from django.db import models
class RoadName(models.Model):
road = models.CharField(max_length=50)
def __str__(self):
return str(self.road)
class RoadInfo(models.Model):
road = models.ForeignKey(RoadName, on_delete='CASCADE')
# other data
def __str__(self):
return str(self.road)
Serializers
from traffic.models import *
from rest_framework import serializers
class RoadNameSerializer(serializers.ModelSerializer):
road = serializers.CharField()
class Meta:
model = RoadName
exclude = ('id',)
class RoadInfoSerializer(serializers.ModelSerializer):
road = RoadNameSerializer()
class Meta:
model = RoadInfo
exclude = ('id',)
Views
from traffic.serializers import *
from traffic.models import *
from django_filters import rest_framework as filters
from rest_framework import viewsets
class RoadNameViewSet(viewsets.ReadOnlyModelViewSet):
""" List of all traffic count Counts """
queryset = RoadName.objects.all()
serializer_class = RoadNameSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = '__all__'
class RoadInfoViewSet(viewsets.ReadOnlyModelViewSet):
""" List of all traffic count Counts """
queryset = RoadInfo.objects.all()
serializer_class = RoadInfoSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = '__all__'
The data M5 on the road attribute of RoadName model. It can be filtered by road__road from RoadInfo model.
So, Try /roadname/?road__road=M5

django rest framework ordering non model field(serializer field)

i want to ordering on my fields like this:
class DealerBackOfficeViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.CreateModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
filter_backends = (filters.OrderingFilter,
)
ordering_fields = ('online',...)
this way of ordering work only on model's fields but online field defined in my serializer and while test in postman not work.
i want to done it like this :
class CustomOrdering(filters.OrderingFilter):
def filter_queryset(self, request, queryset, view):
params = request.query_params.get(self.ordering_param)
if params == 'online':
... my serializer codes
return super(CustomOrdering, self).filter_queryset(request, queryset, view)
this problem is other fields ordering not work!! is there a way to solve it any way?
if related docs help me please give me the link .
thanks for your site
after struggle in this challenge i undrestand that exist a way to some how indicate this fields as model field and not need to CustomOrdering and any extra codes!
in my get_queryset function i change the code:
queryset = Dealer.objects.all()
to:
queryset = Dealer.objects.all().annotate(bids_count=Count('bid'), device_count=Count('device'))
note that this two fields in my serializer not in my model.
in my serilizer change this two field from SerializerMethodField to IntegerField and clean the defs.
then in my api file add this:
filter_backends = (filters.OrderingFilter,)
ordering_fields = ('bids_count', 'device_count')
this my last serializer:
class DealerListSerializer(serializers.ModelSerializer):
device_count = serializers.IntegerField()
bids_count = serializers.IntegerField()
class Meta:
model = Dealer
fields = ('id', 'last_name', 'first_name', 'username', 'person_trust', 'is_active',
'work_type', 'address', 'mobile', 'device_count', 'online', 'bids_count')
by this way my code is very clear and my CustomOrdering and all elif statements also clean!
It doesn't work because the fields defined in your serializer aren't part of the model. The ordering attribute only works for model fields. You'd probably have to introduce a work around like creating a dynamic field using annotations and then order using that field but this depends on whether or not your online field logic can be annotated.

How do I specify multiple column name in a Django search field?

i use django filter backend
I define multiple columns in a search field, as follows:
class tableViewSet(ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
queryset = table.objects.all()
serializer_class = WebSerializer
pagination_class = StandardResultsSetPagination
filter_backends = (DjangoFilterBackend,SearchFilter)
search_fields = ('name','family','tel',)
i want to make an api that handle some query like this:
select * from table1 where name like ('tom') and family like ('%anderson%') and tel like ('%0223654%')
Is there any way to specify the column name in the API?
For example:
http://127.0.0.1/api-user/table/search?name=tom&family=andeson&tel=0223455
You should rather use DjangoFilterBackend instead of SearchFilter if you want to filter per specific attribute.
As you are not looking for exact search, you will have to create a FilterSet and use filter_class in you view.
Something like:
class TableFilter(django_filters.FilterSet):
name = django_filters.CharFilter(name='name', lookup_expr='icontains')
family = django_filters.CharFilter(name='family', lookup_expr='icontains')
tel = django_filters.CharFilter(name='tel', lookup_expr='icontains')
class Meta:
model = table
fields = ['name', 'family', 'tel']
class tableViewSet(ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
queryset = table.objects.all()
serializer_class = WebSerializer
pagination_class = StandardResultsSetPagination
filter_backends = (DjangoFilterBackend,SearchFilter)
filter_class = TableFilter
Specifying the field in the query string is unnecessary. The following request should work:
http://127.0.0.1/api-user/table?search=something
relevant docs

Resources