Search code examples
djangopython-3.xdjango-rest-frameworkdjango-filters

DRF - django_filters -Using custom methods


I have a model like this:

class Agreement(models.Model):
    file_no = models.IntegerField(primary_key=True)
    contract_date = models.DateField()
    contract_time = models.IntegerField()

    @property
    def calculate_expiry_date(self):
        return self.contract_date + relativedelta(years=self.contract_time)

    @property
    def is_expired(self):
        return (self.contract_date + relativedelta(years=self.contract_time)) < timezone.now().date() 

is_expired function returns true or false for each agreement

and I have a simple filter like this:

class AgreementFilter(filters.FilterSet):
    file_no = filters.NumberFilter(lookup_expr='icontains')

    class Meta:
        model = Agreement
        fields = ['file_no',] 

I think that I cannot filter on the property field because Django filters operate at the database level. So how can I make it work to filter agreement model objects if it is valid or invalid or either true or false


Solution

  • I managed to write a custom filter for filtering the expired and valid agreements by using the 'method' parameter to specify a custom method - see django-filter docs

    from datetime import timedelta
    from django.db.models import F, Case, When, BooleanField
    from django_filters import rest_framework as filters
    
    class AgreementFilter(filters.FilterSet):
    
        file_no = filters.NumberFilter(lookup_expr='icontains')
        is_expired = filters.BooleanFilter(method='filter_is_expired')
    
        class Meta:
            model = Agreement
            fields = ['file_no',] 
    
          def filter_is_expired(self, queryset, name, value):
            if value is not None:
                queryset = queryset.annotate(
    
                    expired=Case(When(contract_date__lt=timezone.now().date() - (timedelta(days=365) * F('contract_time')),
                                 then=True), default=False, output_field=BooleanField())).filter(expired=value)
            return queryset
    

    Queryset will return if the object of the agreement is either true or false by calculation:

    For expired agreements

    http://127.0.0.1:8000/api/agreement/?is_expired=True
    
    

    and For valid agreements

    http://127.0.0.1:8000/api/agreement/?is_expired=False