Reputation: 149
I am converting a UNIX date to a string date and passing it as a custom read-only field. What would be the best way to use django-filter to be able to filter this custom field? The error I get is Cannot resolve keyword 'convert_time' into the field
. Choices are:
Models class
class AccountT(models.Model):
created_date_t = models.BigIntegerField(blank=True, null=True)
def convert_time(self):
result = time.strftime("%D", time.localtime(self.created_date_t))
return result
Serializer Class
class AccountTSerializer(serializers.ModelSerializer):
created_date = serializers.ReadOnlyField(source='convert_time')
class Meta:
model = AccountT
fields = ('othermodelfield','othermodelfield', 'created_date',)
ListAPIView
class AccountTListView(generics.ListAPIView):
serializer_class = AccountTSerializer
queryset = AccountT.objects.all()
filter_backends = (filters.DjangoFilterBackend, filters.OrderingFilter,)
filter_fields = ('othermodelfield','created_date_t')
Upvotes: 3
Views: 8846
Reputation: 1998
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_*
.
Upvotes: 5
Reputation: 995
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)
Upvotes: 0
Reputation: 7717
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
Upvotes: 0