[Django]-Django rest filter custom fields

5👍

The filterset_fields option is a shortcut that inspects model fields (not serializer fields) in order to generate filters. Since created_date isn’t a model field, you’ll need to manually declare a filter on a filterset_class. Declared filters can take advantage of the method argument, which will allow you to transform the incoming date into your timestamp. Something like…

# filters.py
from django_filters import rest_framework as filters

class AccountTFilter(filters.FilterSet):
    # could alternatively use IsoDateTimeFilter instead of assuming local time.
    created_date = filters.DateTimeFilter(name='created_date_t', method='filter_timestamp')

    class Meta:
        model = models.AccountT
        # 'filterset_fields' simply proxies the 'Meta.fields' option
        # Also, it isn't necessary to include declared fields here
        fields = ['othermodelfield']

    def filter_timestamp(self, queryset, name, value):
        # transform datetime into timestamp
        value = ...

        return queryset.filter(**{name: value})

# views.py
class AccountTListView(generics.ListAPIView):
    filterset_class = filters.AccountTFilter
    ...

Note: The old filter_* options have since been renamed to filtserset_*.

👤Sherpa

0👍

class AccountT(models.Model):
    created_date_t = models.BigIntegerField(blank=True, null=True)
    created_date = models.DateField(null=True)

    def save(self, *args, **kwargs):
        self.created_date = self.convert_time()
        return super(IncomeExpense, self).save(*args, **kwargs)

    def convert_time(self):
        result = time.strftime("%D", time.localtime(self.created_date_t))
        return result


class AccountTListView(generics.ListAPIView):
    serializer_class = AccountTSerializer
    queryset = AccountT.objects.all()
    filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter,)
    filter_fields = ('othermodelfield','created_date')

class AccountTFilter(FilterSet):
    class Meta:
        model = AccountT
        fields = {
            'created_date': ['gte', 'lte'],
        }

class AccountTListView(generics.ListAPIView):
    serializer_class = AccountTSerializer
    queryset = AccountT.objects.all()
    filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter,)
    filter_class = AccountTFilter
👤Ykh

0👍

To achieve this I would create a custom field on the serializer (ConvertTimeField):

import time

from .models import AccountT
from rest_framework import serializers


# Custom serializer field
# https://www.django-rest-framework.org/api-guide/fields/#custom-fields
class ConvertTimeField(serializers.ReadOnlyField):
    """
    Convert time Field.
    """

    def to_representation(self, value):
        return time.strftime("%D", time.localtime(value))

    def to_internal_value(self, data):
        """
        Here is where you could convert the incoming value
        (It's only needed if you want to perform modification before writing to the database)
        """


# Serializer
class AccountTSerializer(serializers.ModelSerializer):
    created_date = ConvertTimeField(source='created_date_t')

    class Meta:
        model = AccountT
        fields = ('othermodelfield', 'othermodelfield', 'created_date')

NOTE: You still need to pass your filter argument in the same format i.e UNIX epoch when you filter. If you need to change that you can convert your filter query param as suggested here: https://www.django-rest-framework.org/api-guide/filtering/#filtering-against-query-parameters. (Although, there are also other ways you could try accomplish that)

👤Lance

Leave a comment