Django Rest Framework фильтрует вычисляемое свойство модели

Извините за вопрос новичка. У меня есть следующая модель:

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()

Сериализатор:

class WeightSlipSerializer(serializers.ModelSerializer):

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

Я пытаюсь использовать django-rest-framework-filters для фильтрации рассчитанные свойства «нетто-вес» и «слипдата»:

class WeightSlipFilter(FilterSet):

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

Это дает мне ошибку:

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

Есть ли решение этой проблемы, кроме добавления вычисляемых полей в базу данных?

Заранее спасибо.


person Aman Sandhu    schedule 08.07.2017    source источник


Ответы (3)


Вы можете создать собственный фильтр для slipdate, netweight, который будет оценивать и фильтровать эти поля в БД. Для этого вы можете использовать условные выражения и Выражение F

from django.db.models import F, Case, When

class WeightSlipFilter(FilterSet):
    slipdate = DateTimeFilter(method='filter_slipdate')
    netweight = NumberFilter(method='filter_netweight')

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

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

    def filter_slipdate(self, queryset, value):
        if value:
            queryset = queryset.annotate(slipdate=Case(When(grossdate__gt=F('taredate'), then=F('grossdate')), default=F('taredate')).filter(slipdate=value)
        return queryset
person Dima Kudosh    schedule 08.07.2017
comment
Большое спасибо, отличная помощь, так как большая часть моего проекта содержит фильтрацию по вычисляемым полям. - person Aman Sandhu; 11.07.2017
comment
из импорта django_filters.rest_framework (NumberFilter, DateTimeFilter,) - person gek; 22.10.2019

Обратите внимание, что если вы используете последнюю версию django-filter, Filter.method принимает 4 аргумента, например:

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

```

person Clarity    schedule 23.03.2018

Расширение ответа дальше. В последних версиях django и drf может возникнуть подобное исключение.

Исключение

Файл /home/USER/.env/drf3/lib64/python3.7/site-packages/django/db/models/query.py, строка 76, в iter setattr(obj, attr_name, row [col_pos]) AttributeError: невозможно установить атрибут

Используемые версии: Django==3.1 djangorestframework==3.11.1 django-filter==2.3.0

решение, которое сработало для меня, заключается в том, что проскальзывающая дата и вес нетто требуют, чтобы имя модели использовалось в наборе запросов...

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 
person MaNKuR    schedule 25.08.2020