Django Rest Framework filtering calculated model property

Viewed 9348

Sorry for a newbie question. I have a following model:

class WeightSlip(models.Model):

    grossdate = models.DateTimeField(auto_now=False, auto_now_add=False)
    grossweight = models.DecimalField(max_digits=6, decimal_places=2, default=0)
    taredate = models.DateTimeField(auto_now=False, auto_now_add=False)
    tareweight = models.DecimalField(max_digits=6, decimal_places=2, default=0)
    vehicle = models.CharField(max_length=12)

    @property
    def netweight(self):
        return self.grossweight - self.tareweight

    @property
    def slipdate(self):
        if self.grossdate > self.taredate:
           return grossdate.date()
        else:
           return taredate.date()

Serializer:

class WeightSlipSerializer(serializers.ModelSerializer):

   class Meta:
      model = models.WeightSlip
      fields = ('grossdate', 'grossweight', 'taredate', 'tareweight', 'slipdate', 'netweight', 'vehicle')
      read_only_fields = ('slipdate', 'netweight')

I am trying to use the django-rest-framework-filters to filter on the calculated 'netweight' and 'slipdate' properties:

class WeightSlipFilter(FilterSet):

   class Meta:
       model = WeightSlip
       fields = ('slipdate', 'netweight', 'vehicle')

This gives me an error:

TypeError: 'Meta.fields' contains fields that are not defined on this FilterSet: slipdate, netweight

Is there a workaround to this problem other than adding the calculated fields to the database ?

Thanks in advance.

3 Answers

Extending the answer further. With the latest django and drf version one might experience an exception like this.

Exception

File "/home/USER/.env/drf3/lib64/python3.7/site-packages/django/db/models/query.py", line 76, in iter setattr(obj, attr_name, row[col_pos]) AttributeError: can't set attribute

Versions Used: Django==3.1 djangorestframework==3.11.1 django-filter==2.3.0

The solution that worked for me is this where slipdate and netweight requires model name to be used in the queryset...

def filter_slipdate(self, queryset, name, value):
    if value:
       queryset = queryset.annotate(WeightSlip__slipdate=Case(When(grossdate__gt=F('taredate'), then=F('grossdate')), default=F('taredate'))).filter(WeightSlip__slipdate=value)
    return queryset

def filter_netweight(self, queryset, name, value):
    if value:
        queryset = queryset.annotate(WeightSlip__netweight=F('grossweight') - F('tareweight')).filter(WeightSlip__netweight=value)
    return queryset
    return queryset 
Related