【问题标题】:Django Rest Framework filtering calculated model propertyDjango Rest Framework 过滤计算模型属性
【发布时间】:2017-12-12 20:23:56
【问题描述】:

抱歉,有一个新手问题。我有以下型号:

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

除了将计算字段添加到数据库之外,还有其他解决方法吗?

提前致谢。

【问题讨论】:

    标签: django django-rest-framework django-filter django-filters


    【解决方案1】:

    您可以为 slipdate, netweight 创建自定义过滤器,它将评估和过滤 db 中的此字段。为此,您可以使用 conditional expressionsF expression

    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
    

    【讨论】:

    • 非常感谢,很大的帮助,因为我的大部分项目都包含对计算字段的过滤。
    • 从 django_filters.rest_framework 导入(NumberFilter、DateTimeFilter、)
    【解决方案2】:

    请注意,如果您使用的是最新版本的 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
    

    ```

    【讨论】:

      【解决方案3】:

      进一步扩展答案。使用最新的 django 和 drf 版本可能会遇到这样的异常。

      例外

      iter 中的文件“/home/USER/.env/drf3/lib64/python3.7/site-packages/django/db/models/query.py”,第 76 行 setattr(obj, attr_name, row[col_pos]) AttributeError: 无法设置属性

      使用的版本: Django==3.1 djangorestframework==3.11.1 django-filter==2.3.0

      对我有用的解决方案是,slipdate 和 netweight 要求在查询集中使用模型名称...

      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 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-23
        • 2020-12-21
        • 1970-01-01
        相关资源
        最近更新 更多