Django Эффективное использование Queryset - PullRequest
0 голосов
/ 02 августа 2011

У меня есть следующий вид:

from itertools import chain, groupby
from django.db.models.aggregates import Sum
from django.shortcuts import render_to_response, get_object_or_404
from django.template.context import RequestContext
import operator
from accounts.models import GeneralLedger, Account, Journal
from receivables.models import Item as ReceivableItem
from payables.models import Item as PayableItem

ACCOUNT_TYPES = {
    'INC':1,
    'COGS':2,
    'EXP':3,
    'NCA':4,
    'CA':5,
    'NCL':6,
    'CL':7,
    'EQ':8,
}

def income_statement(request):
    """ General income statement"""

    # Get a aggregated total per account type
    debit_ledger = GeneralLedger.objects.values('debit_account','amount')
    credit_ledger = GeneralLedger.objects.values('credit_account','amount')
    debit_journal = Journal.objects.values('debit_account','amount')
    credit_journal = Journal.objects.values('credit_account','amount')
    debit_receivables = ReceivableItem.objects.values('debit_account','amount')
    credit_receivables = ReceivableItem.objects.values('credit_account','amount')
    debit_payables = PayableItem.objects.values('debit_account','amount')
    credit_payables = PayableItem.objects.values('credit_account','amount')
    general_ledger = chain(debit_ledger, credit_ledger, debit_journal, credit_journal, debit_receivables,
                     credit_receivables, debit_payables, credit_payables)
    generalledger = list(general_ledger)

    gross_total = 0
    net_profit = 0

    # For each general ledger item
    # get the account name (because value query set only returns ID)
    # get the account type so we're able to set the order for the template display
    for e in generalledger:
        if "debit_account" in e:
            account_detail = get_object_or_404(Account, pk=e["debit_account"])
            e['account'] = e['debit_account']
            e['amount'] = -e['amount']
        if "credit_account" in e:
            account_detail = get_object_or_404(Account, pk=e["credit_account"])
            e['account'] = e['credit_account']
        e["account_name"] = account_detail.name
        e["account_type"] = account_detail.type
        if account_detail.type == 'INC':
            e["order"] = ACCOUNT_TYPES['INC']
            # add to the gross total
            gross_total += e['amount']
        elif account_detail.type == 'COGS':
            # subtract from the gross total
            gross_total -= e['amount']
            e["order"] = ACCOUNT_TYPES['COGS']
        elif account_detail.type == 'EXP':
            # net profit is gross total minus the expenses
            net_profit = gross_total - e['amount']
            e["order"] = ACCOUNT_TYPES['EXP']

    generalledger = sorted(generalledger, key=operator.itemgetter('account'))

    groups = []
    uniquekeys = []
    for k, g in groupby(generalledger, operator.itemgetter('account')):
        groups.append(list(g))      # Store group iterator as a list
        uniquekeys.append(k)

    for group in groups:
        group_total = 0
        for subgroup in group:
            group_total += subgroup['amount']
            subgroup['total'] = group_total




    context_dict = {
        'GeneralLedger': groups,
        'Gross': gross_total,
        'Net': net_profit,
    }

    return render_to_response('accounts/income-statement.html', context_dict, RequestContext(request))

Это работает, однако я не думаю, что это очень эффективно, так как на каждой итерации он вызывает базу данных. Я пытался использовать objects.only (...) вместо objects.values ​​(...), однако тогда я не могу добавить нужные мне элементы. Есть ли более эффективный способ сделать это?

1 Ответ

1 голос
/ 02 августа 2011

Ну, это может быть сильно переработано и уменьшено до доли того, что есть сейчас, но это не суть вашего вопроса.

Исключение запросов на каждую итерацию действительно является самым слабым плодом для улучшения этого. Вам следует просто включить соответствующие поля сведений об учетной записи в запросы, а не запрашивать их отдельно на каждой итерации.

debit_ledger = GeneralLedger.objects.values('debit_account', 'debit_account__name', 'debit_account__type', 'amount')

Тогда в результатах вы получите его уже как e['debit__account__name'] и т. Д., Поэтому вы можете удалить запросы на каждую итерацию Account

...