如何将django查询集转换为字典以用作模板上下文

问题描述 投票:1回答:2

我正在重新创建Django中的宝石库存状态报告,该报告最初是在Microsoft Access中构建的。该报告的组织如下:

  • 交易(“ABC” - 表示房屋所有,委托,合伙等) 库存状态(库存,已售出,仅成本,历史FYI等) 细节线(StoneID,克拉,购买成本等) 小计(总成本,总克拉数 - 能够将其插入正确的位置是我被卡住的地方......)

以下是模型的相关部分:

class Deal(models.Model):
    deal_name = models.TextField()

class Stone(models.Model):
    stoneid = models.TextField(verbose_name='StoneID', unique=True)
    dealid = models.ForeignKey(Deal, on_delete=models.PROTECT)
    ct_in = models.DecimalField(verbose_name='Carats', max_digits=7, decimal_places=3)
    cost_purchase = models.DecimalField(verbose_name='Purchase Cost', max_digits=14, decimal_places=2, null=True, blank=True)

我通过两个查询得到数据 - 一个用于细节线,另一个用于小计。以下是查询:


def dump_stone(request):
    query = Stone.objects.filter(Q(dealid_id__deal_name='ABC') | \
                                    Q(dealid_id__deal_name='DEF') | \
                                    Q(dealid_id__deal_name='GHI')).select_related().order_by('dealid_id__deal_name', 'inventory_status', 'stoneid')
    totals = Stone.objects.values('dealid', 'inventory_status').annotate(sum_by_deal=Sum('cost_purchase'), sum_ct_in_by_deal=Sum('ct_in'))

通过交易按状态打印库存明细表的模板是:

    {% block content %}
    REPORT:
    </br>
    {% regroup context by dealid as deal_list %}
        {% for dealid in deal_list %}
        {{dealid.grouper}}
            {% regroup dealid.list by inventory_status as stone_list%}
            {% for inventory_status in stone_list %}
                {{inventory_status.grouper}}
                <table>
                    <thead>
                        <tr>
                        <th>StoneID</th>
                        <th>Ct</th>
                        <th>Cost</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for stone in inventory_status.list %}
                        <tr>
                        <td>{{ stone.stoneid }}</td>
                        <td>{{ stone.ct_in|floatformat:2 }}</td> 
                        <td>{{ stone.cost_purchase|prepend_dollars }}</td>
                        </tr>
                        {% endfor %}
                    {% endfor %}
                </tbody>
                </table>
        {% endfor %}
    {% endblock content %}

总计查询生成以下输出:

    {'dealid': 1, 'inventory_status': 'HistoricFYI', 'sum_by_deal': Decimal('1287750'), 'sum_ct_in_by_deal': Decimal('15.1500000000000')}
    {'dealid': 1, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('209138.7100000'), 'sum_ct_in_by_deal': Decimal('327.810000000000')}
    {'dealid': 2, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('338726.99000000'), 'sum_ct_in_by_deal': Decimal('56.2000000000000')}
    {'dealid': 3, 'inventory_status': 'Inventory', 'sum_by_deal': Decimal('296754.5900000'), 'sum_ct_in_by_deal': Decimal('294.970000000000')}
    {'dealid': 3, 'inventory_status': 'Memo In', 'sum_by_deal': Decimal('192948.340000000'), 'sum_ct_in_by_deal': Decimal('9.47000000000000')}
    {'dealid': 3, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('154384.57000000'), 'sum_ct_in_by_deal': Decimal('88.1200000000000')}
    {'dealid': 5, 'inventory_status': 'Inventory', 'sum_by_deal': Decimal('187000'), 'sum_ct_in_by_deal': Decimal('26.75')}
    {'dealid': 5, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('20000'), 'sum_ct_in_by_deal': Decimal('2')}
    {'dealid': 5, 'inventory_status': 'Test', 'sum_by_deal': Decimal('13700'), 'sum_ct_in_by_deal': Decimal('19')}

我想要做的是将总计查询转换为字典字典,以便我可以通过交易,按状态访问单个小计,并使用标记将它们插入模板中的正确位置(不会很难) -coded如图所示,但我将继续下一步):

    {{deal_dict.1.Sold.sum_by_deal}}

我正在尝试生成一个如下所示的字典:

    {   1:
            {
                ‘HistoricFYI’:{’sum_by_deal': Decimal('1287750'), 'sum_ct_in_by_deal': Decimal('15.1500000000000’)},
                'Sold:{'sum_by_deal': Decimal('209138.7100000'), 'sum_ct_in_by_deal': Decimal('327.810000000000’)}
            },
        2:
            {
                ’Sold’:{‘sum_by_deal': Decimal('338726.99000000'), 'sum_ct_in_by_deal': Decimal('56.2000000000000’)},
            },
        3:
            {
                'Inventory’:{‘sum_by_deal': Decimal('296754.5900000'), 'sum_ct_in_by_deal': Decimal('294.970000000000’)},
                'Memo In’:{‘sum_by_deal': Decimal('192948.340000000'), 'sum_ct_in_by_deal': Decimal('9.47000000000000’)},
                'Sold’: {‘sum_by_deal': Decimal('154384.57000000'), 'sum_ct_in_by_deal': Decimal('88.1200000000000')}
            },
        5:  {
                'Inventory’:{‘sum_by_deal': Decimal('187000'), 'sum_ct_in_by_deal': Decimal('26.75’)},
                'Sold’:  {‘sum_by_deal': Decimal('20000'), 'sum_ct_in_by_deal': Decimal(‘2’)},
                'Test’:      {‘sum_by_deal': Decimal('13700'), 'sum_ct_in_by_deal': Decimal('19')}
            }
    }

我已经尝试了一些东西来获取总计查询集并使其成为嵌套字典:

deal_dict = {}
status_dict = {}
numbers_dict = {}
for things in totals:
    print(things)
    numbers_dict['sum_by_deal']=things['sum_by_deal']
    numbers_dict['sum_ct_in_by_deal']=things['sum_ct_in_by_deal']
    status_dict[things['inventory_status']]=dict(numbers_dict)
    deal_dict[things['dealid']]=dict(status_dict)

上述代码的问题在于,每笔交易的嵌套字典包括先前交易的状态,除非交易本身具有覆盖先前数据的该状态的自己的数据。换句话说,对于交易2,例如我得到

    {   2:
            {
                ‘HistoricFYI’:{’sum_by_deal': Decimal('1287750'), 'sum_ct_in_by_deal': Decimal('15.1500000000000’)},
                'Sold:{'sum_by_deal': Decimal('338726.99000000'), 'sum_ct_in_by_deal': Decimal('56.2000000000000’)}
            },

即使它没有自己的任何“HistoricFYI”数据,因为字典仍包含交易1数据。

我也尝试过这样清理字典

    status_dict.clear()

在每个循环结束时,但我最后只收到每个交易(销售或测试)按字母顺序排列的最后状态字典。

我也试过了

    deal_dict = {}
    for things in totals:
        deal_dict.update({things['dealid']:{things['inventory_status']:{'sum_by_deal': things['sum_by_deal'], 'sum_ct_in_by_deal': things['sum_ct_in_by_deal']}}})

但这只是字典中每个交易的最后状态,就像我尝试了clear()方法一样。

我无法弄清楚如何适应这个 - > Totals/Subtotals in Django template或者这个 - > Django: how to process flat queryset to nested dictionary?

我怎样才能生成这个字典词典,以便我可以在模板中插入小计,或者以某种方式将小计插入到正确的位置?我非常感谢任何帮助!

python django dictionary django-queryset
2个回答
2
投票

这似乎实现了你想要的嵌套字典:

def regroup_inventory(totals_qset):
    for dealid, row_group in groupby(totals_qset, key=itemgetter('dealid')):
        yield dealid, {
            row['inventory_status']: {
                key: val
                for key, val in row.items()
                if key not in ('dealid', 'inventory_status')
            }
            for row in row_group
        }

注意:这是一个生成器,因此您需要像在dict.items()上一样迭代它,或者在结果上调用dict()。试试你的例子,我得到:

> from decimal import Decimal
> from pprint import pprint
> foo = [
    {'dealid': 1, 'inventory_status': 'HistoricFYI', 'sum_by_deal': Decimal('1287750'), 'sum_ct_in_by_deal': Decimal('15.1500000000000')},
    {'dealid': 1, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('209138.7100000'), 'sum_ct_in_by_deal': Decimal('327.810000000000')},
    {'dealid': 2, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('338726.99000000'), 'sum_ct_in_by_deal': Decimal('56.2000000000000')},
    {'dealid': 3, 'inventory_status': 'Inventory', 'sum_by_deal': Decimal('296754.5900000'), 'sum_ct_in_by_deal': Decimal('294.970000000000')},
    {'dealid': 3, 'inventory_status': 'Memo In', 'sum_by_deal': Decimal('192948.340000000'), 'sum_ct_in_by_deal': Decimal('9.47000000000000')},
    {'dealid': 3, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('154384.57000000'), 'sum_ct_in_by_deal': Decimal('88.1200000000000')},
    {'dealid': 5, 'inventory_status': 'Inventory', 'sum_by_deal': Decimal('187000'), 'sum_ct_in_by_deal': Decimal('26.75')},
    {'dealid': 5, 'inventory_status': 'Sold', 'sum_by_deal': Decimal('20000'), 'sum_ct_in_by_deal': Decimal('2')},
    {'dealid': 5, 'inventory_status': 'Test', 'sum_by_deal': Decimal('13700'), 'sum_ct_in_by_deal': Decimal('19')},
]
> pprint(dict(regroup_inventory(foo)))
{1: {'HistoricFYI': {'sum_by_deal': Decimal('1287750'),
                     'sum_ct_in_by_deal': Decimal('15.1500000000000')},
     'Sold': {'sum_by_deal': Decimal('209138.7100000'),
              'sum_ct_in_by_deal': Decimal('327.810000000000')}},
 2: {'Sold': {'sum_by_deal': Decimal('338726.99000000'),
              'sum_ct_in_by_deal': Decimal('56.2000000000000')}},
 3: {'Inventory': {'sum_by_deal': Decimal('296754.5900000'),
                   'sum_ct_in_by_deal': Decimal('294.970000000000')},
     'Memo In': {'sum_by_deal': Decimal('192948.340000000'),
                 'sum_ct_in_by_deal': Decimal('9.47000000000000')},
     'Sold': {'sum_by_deal': Decimal('154384.57000000'),
              'sum_ct_in_by_deal': Decimal('88.1200000000000')}},
 5: {'Inventory': {'sum_by_deal': Decimal('187000'),
                   'sum_ct_in_by_deal': Decimal('26.75')},
     'Sold': {'sum_by_deal': Decimal('20000'),
              'sum_ct_in_by_deal': Decimal('2')},
     'Test': {'sum_by_deal': Decimal('13700'),
              'sum_ct_in_by_deal': Decimal('19')}}}

1
投票

我没有测试过这个,但我想你希望你的deal_dict是一个defaultdict。然后从thing弹出dealid和inventory_status,并使用它们将deal_dict填充为嵌套字典。

from collections import defaultdict
deal_dict = defaultdict(dict)
for thing in totals:
    dealid = thing.pop('dealid')
    status = thing.pop('inventory_status')
    deal_dict[dealid][status] = dict(thing)

编辑:我应该添加一个警告,这将改变totals,如果你再去其他地方使用它将会有问题。

© www.soinside.com 2019 - 2024. All rights reserved.