Django聚合在ModelAdmin查询集中的多个表中

时间:2013-08-20 05:47:53

标签: python django django-models django-admin

Django Code&参考Django Bug报告

鉴于以下三个模型(过度简化演示......实际上不是相同的相关模型)

class derp(models.Model):
    ...
class derp_related_1(models.Model):
    fk = models.ForeignKey(derp)
    amount = models.DecimalField(max_digits=15, decimal_places=2)
class derp_related_2(models.Model):
    fk = models.ForeignKey(derp)
    amount = models.DecimalField(max_digits=15, decimal_places=2)

并覆盖模型管理员中的查询集,如下所示。 (由于this django bug,它无效。)

class DerpAdmin(admin.ModelAdmin):
    ...
    list_display = ['derp_r1_sum', 'derp_r2_sum']
    ...
    def queryset(self, request):
        qs = super(DerpAdmin, self).queryset(request)
        qs = qs.annotate(derp_r1_sum=models.Sum('derp_r1__amount', distinct=True))
        qs = qs.annotate(derp_r2_sum=models.Sum('derp_r2__amount', distinct=True))
    def derp_r1_sum(self, obj):
        return u'%s' % obj.derp_r1_sum
    def derp_r2_sum(self, obj):
        return u'%s' % obj.derp_r2_sum

意外数据库结果示例

单独运行注释会呈现类似的内容(删除分组和总和)

+---------+--------+
| derp.id | r1_sum |
+---------+--------+
|       2 | 500.00 |
|       2 | 100.00 |
+---------+--------+
r1_sum would be 600.00
and
+---------+--------+
| derp.id | r1_sum |
+---------+--------+
|       2 | 100.00 |
|       2 | 250.00 |
+---------+--------+
r2_sum would be 350.00

如果你带两个注释的qs.query并删除总和和分组,那么问题是显而易见的。在这种情况下,我们计算两次。获得更多关系,我们在两个总和列中的增长越来越难看。

+---------+--------+--------+
| derp.id | r1_sum | r2_sum |
+---------+--------+--------+
|       2 | 500.00 | 100.00 |
|       2 | 500.00 | 250.00 |
|       2 | 100.00 | 100.00 |
|       2 | 100.00 | 250.00 |
+---------+--------+--------+
r1_sum would incorrectly be 1200.00
r2_sum would incorrectly be 700.00

问题,是否有自定义SQL以外的路由?

我可以很容易地编写查询,但是如果有人有一个建议可以避免编写那些非常棒的自定义SQL。

感谢您的帮助。

编辑:这是Django文档的注释部分的链接。一位评论者提到了不同的选择。这不起作用,我相信它是django documentation on annotation中注释部分底部的警告。

Edit2:原始的SQL构思可能比我想象的更难,因为derp.objects.raw('sql here')不会返回管理员使用它所需的queryset对象。有没有办法使用两个查询(真正的查询集加上一个自定义的查询)并从两者中填充listview?我找到的一个建议(现在我再也找不到了:S)建议创建一个映射到模型定义的视图,然后将其设置为不受django管理(对于syncdb)。然后我可以编写自定义代码,并引用它以包含在原始查询中。这听起来很乱。想法?

2 个答案:

答案 0 :(得分:0)

如果你想留在Django的查询集中,我会考虑创建一个模型超类,它共享相关和公共字段以及子类以进一步区分。否则,您需要编写自定义SQL或完全退出数据库ORM并使用Queryset.valuesQueryset.values_list

在python中操作数据

答案 1 :(得分:0)

我发现返回正确结果的最佳方法是使用queryset.extra()

derp_r1_sum_select = """
    select sum(`derp_related_1`.`amount`)
    from `derp_related_1` 
    where `derp_related_1`.`fk` = `derp`.`pk`
    """
derp_r2_sum_select = """
    select sum(`derp_related_2`.`amount`)
    from `derp_related_2`
    where `derp_related_2`.`fk` = `derp`.`pk`"
    """

def queryset(self, request):
        qs = super(DerpAdmin, self).queryset(request)
        qs = qs.extra(select={'derp_r1_sum': derp_r1_sum_select,
                              'derp_r2_sum': derp_r2_sum_select})
        return qs