【问题标题】:Filter output by model property按模型属性过滤输出
【发布时间】:2017-05-05 05:13:09
【问题描述】:

在我的模型中,我计算了属性 current_tobe_payed

我想为我的属性 current_tobe_payed 小于零的所有行生成 CSV 报告

请看下面我的观点:

def export_leaseterm_csv(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="leaseterm.csv"'
    writer = csv.writer(response)
    leaseterms = serializers.serialize( "python", LeaseTerm.objects.all())
    [obj for obj in leaseterms if obj.current_tobe_payed > 0]

    for leaseterm in  obj:
        writer.writerow(leaseterm['fields'].values())
    return response

但是,我收到一个错误:

'dict' object has no attribute 'current_tobe_payed'

我该如何解决这个问题?
(我也只想将某些字段输入 CSV 而不是所有表格。)

更新:

在下面查看我的模型:

class LeaseTerm(CommonInfo):
    version = IntegerVersionField( )
    start_period = models.ForeignKey(Period, related_name='start_period' )
    end_period = models.ForeignKey(Period, related_name='end_period')
    lease = models.ForeignKey(Lease)
    increase  = models.DecimalField(max_digits=7, decimal_places=2)
    amount  = models.DecimalField(max_digits=7, decimal_places=2)
    is_terminated = models.BooleanField(default=False)

    # _total = None
    _current_period  = None
    _total_current = None

    _total_payment = None
    _total_current_payment = None

    _total_discount = None
    _total_current_discount = None

    _current_tobe_payed = None
    _current_balance = None


    def _get_total(self):
        from payment.models import LeasePayment
        from conditions.models import LeaseDiscount
        total_payment_dict = LeasePayment.objects.filter(leaseterm_id=self.id, is_active = True ).aggregate(Sum('amount'))

        if total_payment_dict ['amount__sum']:
            total_payment =  total_payment_dict['amount__sum'] 
        else:  
            total_payment =  0

        total_discount_dict = LeaseDiscount.objects.filter(leaseterm_id=self.id, is_active = True ).aggregate(Sum('amount'))

        if total_discount_dict ['amount__sum']:
            total_discount =  total_discount_dict['amount__sum'] 
        else:  
            total_discount =  0

        # current = Period.objects.filter( is_active = True, _is_current = True )

        current_date=datetime.datetime.now().date()
        current_period_dict = Period.objects.filter(start_date__lte=current_date,end_date__gte=current_date, is_active = True ).aggregate(Max('order_value'))

        #self._current_period = current_period
        if current_period_dict['order_value__max']:
            current_period =  current_period_dict['order_value__max'] 
        else:  
            current_period =  0

        current_discount_dict = LeaseDiscount.objects.filter(leaseterm_id=self.id, 
            is_active = True, period_date__gte=self.start_period,
             period_date__lte=current_period).aggregate(Sum('amount'))

        if current_discount_dict ['amount__sum']:
            current_discount =  current_discount_dict['amount__sum'] 
        else:  
            current_discount =  0

        current_periods_number = current_period - self.start_period.order_value + 1

        current_tobe_payed =  current_periods_number * self.amount -  current_discount

        current_balance =  total_payment - current_tobe_payed

        self._current_period = current_period
        self._total_payment = total_payment
        self._total_discount = total_discount
        self._current_tobe_payed  = current_tobe_payed 
        self._current_balance = current_balance


    @property
    def current_tobe_payed(self):
        if self._current_tobe_payed is None:
            self._get_total()
        return self._current_tobe_payed

    @property
    def current_balance(self):
        if self._current_balance is None:
            self._get_total()
        return self._current_balance

    @property
    def current_period(self):
        if self._current_period is None:
            self._get_total()
        return self._current_period   

    @property
    def total_payment(self):
        if self._total_payment is None:
            self._get_total()
        return self._total_payment

    @property
    def total_discount(self):
        if self._total_discount is None:
            self._get_total()
        return self._total_discount

    def clean(self):
            model = self.__class__

            if self.lease_id and (self.is_terminated == False) and (self.is_active == True) and model.objects.filter(lease=self.lease, is_active=True ).exclude(id=self.id).count() == 1:
                raise  ValidationError('!Lease has a active condition already, Terminate prior to creation of new one'.format(self.lease))

    def save(self, *args, **kwargs):
        self.full_clean()
        return super(LeaseTerm, self).save(*args, **kwargs)

    def __unicode__(self):
        return u'%s %i %s %s ' % ("term:",self.id, self.start_period, self.end_period)

【问题讨论】:

  • 嗨,鲍里斯。可以发一下你的模型吗。有时可以在数据库函数的帮助下过滤属性
  • 错误显示dict object has no attribute current_tobe_payed。序列化程序返回一个字典列表。所以很明显,当您尝试遍历该列表时,每个 dict 项目都不会具有上述属性,而是一个键。
  • 它不是模型字段,因此无法开箱即用地序列化。模型属性需要在其自己的类中序列化。看到这个stackoverflow.com/questions/17066074/…

标签: python django csv


【解决方案1】:

这是您在 get_total 方法中进行的相当冗长的计算。我计算了其中的五个查询,以下代码将导致对表中的每一行执行这五个查询。

[obj for obj in leaseterms if obj.current_tobe_payed > 0]

这意味着如果您的表中只有 1000 行,您将执行 5000 次查询。对于 10,000 行,此列表解析需要很长时间才能运行。

解决方案。将您的属性转换为模型字段。

to_be_payed  = models.DecimalField(max_digits=7, decimal_places=2)

我经常告诉开发人员不要将简单计算的结果保存到 db 列中。但你的计算不是一个简单的计算,而是一个复杂的计算,所以它值得一个字段。您可以在 save 方法中更新此字段

def save(self, *args, **kwargs):
    self.to_be_payed = self.get_total()
    super(LeaseTerm, self).save(*args, **kwargs)

如果如您所说,要支付的金额取决于 Payment 实例的更改,您可以做的是在 Payment 模型上设置一个 post_save 信号,以触发相关的 LeaseTerm 对象被更新。进行这样的更新仍然比计算 5000 次要便宜

【讨论】:

  • 它的问题是这个值依赖于其他表。因此,如果付款是在 Payment 表中执行的,余额应该会增加,但 Lease Term 模型不会知道它必须更新。
【解决方案2】:

您正在使用返回 python 字典对象的序列化程序。它不是模型的实例。我建议如下:

编辑解决方案

def export_leaseterm_csv(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="leaseterm.csv"'

    writer = csv.writer(response)

    # get all the LeaseTerm instances
    leaseterms = LeaseTerm.objects.all()

    # filter based on current_tobe_payed
    tobe_payed_terms = [obj for obj in leaseterms if obj.current_tobe_payed > 0]
    tobe_payed_dict = serializers.serialize( "python", tobe_payed_terms)
    # serialize these objects and write to values to the csv
    for term in tobe_payed_dict:
         writer.writerow(term['fields'].values())



    return response

【讨论】:

  • 谢谢,但是从行 term_dict = serializers.serialize("python", term) 获取 'LeaseTerm' 对象是不可迭代的
  • @BorisTheAnimal 对此解决方案的修改。无需循环 tobe_payed_terms,只需将列表 tobe_payed_terms 本身传递给 serializers.serialize()。
  • 谢谢 Rajesh,我根据您的评论更新了解决方案。
  • 谢谢 Rajesh 和 nmushel,它确实可以通过这种方式进行过滤,但计算值不会填充到 csv 中。只有实际的 db 字段是。
  • @BorisTheAnimal 调试它,尝试在将值写入 csv 之前打印它们。验证计算值是否存在。如果它不存在,请编写一个自定义序列化程序并确保它序列化您的自定义属性。
【解决方案3】:

最后我没有信号也没有消毒器

此表中的记录数量永远不会超过第 100 条。此报告仅由公司中的一个人每周执行一次。在测试过程中,如果性能不足,我会去规范化,然后我更愿意尽可能地对其进行规范化。

 def export_leaseterm_csv(request):

    response = HttpResponse(content_type='text/csv')

    response['Content-Disposition'] = 'attachment; filename="leaseterm.csv"'
    writer = csv.writer(response)

    writer.writerow([
        "lease",
        "tenant",
        "amount",
        "current balance",
    ])


    leaseterms = LeaseTerm.objects.filter(is_terminated = False, is_active = True )



    tobe_payed_terms = [obj for obj in leaseterms if obj.current_balance < 0]

    for term in tobe_payed_terms:



             writer.writerow([
                term.lease,
                term.tenant,
                term.amount,
                term.current_balance,

            ])

    return response

【讨论】:

    猜你喜欢
    • 2019-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    相关资源
    最近更新 更多