You can write your search criteria in short code !!! When I learned this, I thought it was revolutionary.
django-filter official documentation
As a sample, let's define such a model and serializer.
models.py
class Book(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    is_deleted = models.CharField(max_length=1, default='0')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    title = models.CharField(max_length=128)
    sub_title = models.CharField(max_length=128)
    price = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
    author = models.ForeignKey(Author, on_delete=models.PROTECT, blank=True, null=True)
    publisher = models.ForeignKey(Publisher, on_delete=models.PROTECT)
serializer.py
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
First of all, with django-filter Try to get the list without narrowing down by search conditions.
view.py
from django_filters import rest_framework as filters
class FilterBook(filters.FilterSet):
    class Meta:
        model = Book
        fields = '__all__'
class ListBook(APIView):
    def get(self, request):
        filterset = FilterBook(request.query_params, queryset=Book.objects.all())
        serializer = BookSerializer(instance=filterset.qs, many=True)
        return Response(serializer.data)
Create a class that inherits FilterSet and specify the model there. It's similar to Django's Form and Serializer.
And the URL of this created ListBook API
/ api / v1 / book / list / (omitted)
When you make a request from a browser, such data will be returned.
Of course the data is a sample lol

Then, specify the search conditions and make a request.
The URL you requested earlier was / api / v1 / book / list /, but specify the query parameters there.
If you change to / api / v1 / book / list /? title = test and request, the response will change.

No response was returned this time.
This title = test returns an object whose Book model title matches test.
Also change title to sub_title to / api / v1 / book / list /? sub_title = test
When I make a request, no response is returned.
However, if you specify a query parameter that does not exist in the Book model Data cannot be narrowed down and data is returned in the response.
Earlier, narrow down by title and sub_title
The data was extracted.
However, this time, title can be a search condition, but sub_title is not required as a search condition!
I wonder what happened.
In such a case, you can specify only applicable in fields of Filter class.
views.py
class FilterBook(filters.FilterSet):
    class Meta:
        model = Book
        fields = ['title']
By doing this, I was able to narrow down by sub_title earlier, but now I can narrow down only by title.
If you specified fields with __all__ earlier, it will be the default search method.
I couldn't use the search method such as numerical search or partial match.
(For example, even if you request / api / v1 / book / list /? Price = test, it will not be narrowed down.)
Therefore, we will customize the search method.
views.py
class FilterBook(filters.FilterSet):
    # UUID
    id = filters.UUIDFilter()
    #Partial Match
    title = filters.CharFilter(lookup_expr='icontains')
    #Find out the amount
    price = filters.NumberFilter()
    price__gt = filters.NumberFilter(field_name='price', lookup_expr='gt')
    price__lt = filters.NumberFilter(field_name='price', lookup_expr='lt')
    class Meta:
        model = Book
        fields = []
The content of customization.
filters.UUIDFilter () is a UUID support. Even if you search by ID, it will be caught.
With filters.CharFilter (lookup_expr ='icontains')
I made a partial match. The argument for this lookup_expr is Django's field lookups
You can specify the same as.
filters.NumberFilter(field_name='price', lookup_expr='gt')
Specifies the correspondence and range of numbers.
field_name specifies the target field of the model.
Normally, if the field name of the filter and the field name of the model match, there is no problem even if you do not specify it.
Let's make a request after customization.
ʻApi / v1 / book / list /? title = sqlandapi/v1/book/list/?price_gt=3200&price_lt=4000`
Data is now responded to when you make a request to.

If you want to use the model linked by Foreign Key as a search condition,
Specify in field_name.
You can specify it in django's lookup expression.
views.py
class FilterBook(filters.FilterSet):
    author_last_name = filters.CharFilter(field_name='author__last_name',lookup_expr='icontains')
With the default filter, CharFilter had to be customized for each field, such as an exact match. Instead of customizing on a field-by-field basis, we're trying to customize the CharFilter defaults.
views.py
class FilterBook(filters.FilterSet):
    class Meta:
        model = Book
        fields = ['title']
        filter_overrides = {
            models.CharField: {
                'filter_class': filters.CharFilter,
                'extra': lambda f: {
                    'lookup_expr': 'icontains',
                },
            },
        }
In class Meta:, describe filter_overrrides and the content you want to set as the default.
Now all fields using CharFiled in the model are partially matched by default.
You can do your own search when narrowing down. I created an example that removes hyphens and makes a partial match.
views.py
class FilterBook(filters.FilterSet):
    title = filters.CharFilter(method='title_custom_filter')
    class Meta:
        model = Book
        fields = []
    @staticmethod
    def title_custom_filter(queryset, name, value):
        #name is the field name
        #value is the query parameter
        # name__icontains
        lookup = '__'.join([name, 'icontains'])
        #Erase hyphens
        replaced_value = value.replace('-', '')
        return queryset.filter(**{
            lookup: replaced_value,
        })
If you pass the name of the method as a string in the argument of CharFilter, the method will be executed at the time of search.
The field name and value search parameters are passed to name.
So if you change that, you can freely create search conditions.
This time, for example
Same as / api / v1 / book / list /? title = g
/api/v1/book/list/?title=g---
You can now get a response even if you send a request to.

For more information, see By all means official documentation I hope you can check it !!!!!!!!!!!!!!!!!
Recommended Posts