我正在尝试添加我的 models.py 中应纳税的学分总和。如果我在没有taxable_credits的情况下计算余额,它就可以工作。当我将taxable_credits添加到混合中时,我收到错误。
class Account(models.Model):
name = models.CharField(max_length=32)
class Debit(models.Model):
account = models.ForeignKey(Account, related_name='debits', on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=12, decimal_places=2)
class Credit(models.Model):
account = models.ForeignKey(Account, related_name='credits', on_delete=models.CASCADE)
amount = models.DecimalField(max_digits=12, decimal_places=2)
taxable = models.BooleanField(default=False)
我的测试如下:
lass TestAccount(TestCase):
# https://mixedquantum.blogspot.com/2017/08/django-tips-3-subquery-expressions.html
def setUp(self) -> None:
self.accounts = dict()
self.accounts['fox'] = Account.objects.create(name='FOX')
self.accounts['dog'] = Account.objects.create(name='DOG')
self.accounts['snake'] = Account.objects.create(name='SNAKE')
"""
# Credits
+----------------+-----------------+-----------------+
| account_name | credit_amount | taxable |
|----------------+-----------------|-----------------+
| FOX | 100.0 | False |
| SNAKE | 50.0 | False |
| SNAKE | 20.0 | False |
| DOG | 300.0 | False |
| DOG | 100.0 | True |
+----------------+-----------------+-----------------+
"""
Credit.objects.create(account=self.accounts['fox'], amount=Decimal('100.0'))
Credit.objects.create(account=self.accounts['snake'], amount=Decimal('50.0'))
Credit.objects.create(account=self.accounts['snake'], amount=Decimal('20.0'))
Credit.objects.create(account=self.accounts['dog'], amount=Decimal('300.0'))
Credit.objects.create(account=self.accounts['dog'], amount=Decimal('100.0'), taxable=True)
"""
# Debits
+----------------+----------------+
| account_name | dedit_amount |
|----------------+----------------|
| FOX | 40.0 |
| SNAKE | 30.0 |
| DOG | 12.0 |
| DOG | 23.0 |
+----------------+----------------+
"""
Debit.objects.create(account=self.accounts['fox'], amount=Decimal('40.0'))
Debit.objects.create(account=self.accounts['snake'], amount=Decimal('30.0'))
Debit.objects.create(account=self.accounts['dog'], amount=Decimal('12.0'))
Debit.objects.create(account=self.accounts['dog'], amount=Decimal('23.0'))
def test_sum(self):
credits = Credit.objects.filter(
account=OuterRef('pk')).values('account_id').annotate(sum_credits=Sum('amount'))
taxable_credits = Credit.objects.filter(
account=OuterRef('pk'), taxable=True).values('account_id').annotate(sum_taxable_credits=Sum('amount'))
debits = Debit.objects.filter(
account=OuterRef('pk')).values('account_id').annotate(sum_debits=Sum('amount'))
balances = Account.objects.annotate(
credit_sum=Subquery(credits.values('sum_credits')),
taxable_credit_sum=Subquery(taxable_credits.values('sum_taxable_credits')),
debit_sum=Subquery(debits.values('sum_debits')),
balance= F('credit_sum') - F('debit_sum') - F('taxable_credit_sum')
).values_list('name', 'balance') # , 'taxable_credit_sum')
self.assertEqual(balances[0], ('FOX', Decimal('60.0')))
self.assertEqual(balances[2], ('SNAKE', Decimal('40.0')))
self.assertEqual(balances[1], ('DOG', Decimal('365.0')))
"""
[('FOX', Decimal('60.00')),
('SNAKE', Decimal('40.00')),
('DOG', Decimal('265.00'))]
"""
运行测试时,我得到以下错误回溯
Error
Traceback (most recent call last):
File "/opt/project/alpha_clinic/banking/tests/tests_models.py", line 65, in test_sum
self.assertEqual(balances[0], ('FOX', Decimal('60.0')))
File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 308, in __getitem__
qs._fetch_all()
File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 1242, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py", line 144, in __iter__
return compiler.results_iter(tuple_expected=True, chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1085, in results_iter
results = self.execute_sql(MULTI, chunked_fetch=chunked_fetch, chunk_size=chunk_size)
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1120, in execute_sql
sql, params = self.as_sql()
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 474, in as_sql
extra_select, order_by, group_by = self.pre_sql_setup()
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 54, in pre_sql_setup
self.setup_query()
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 45, in setup_query
self.select, self.klass_info, self.annotation_col_map = self.get_select()
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 254, in get_select
sql, params = self.compile(col, select_format=True)
File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 407, in compile
return node.output_field.select_format(self, sql, params)
File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 258, in output_field
output_field = self._resolve_output_field()
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 290, in _resolve_output_field
sources_iter = (source for source in self.get_source_fields() if source is not None)
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 344, in get_source_fields
return [e._output_field_or_none for e in self.get_source_expressions()]
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 344, in <listcomp>
return [e._output_field_or_none for e in self.get_source_expressions()]
File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 271, in _output_field_or_none
return self.output_field
File "/usr/local/lib/python3.6/site-packages/django/utils/functional.py", line 80, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 258, in output_field
output_field = self._resolve_output_field()
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 1010, in _resolve_output_field
return super()._resolve_output_field()
File "/usr/local/lib/python3.6/site-packages/django/db/models/expressions.py", line 293, in _resolve_output_field
raise FieldError('Expression contains mixed types. You must set output_field.')
django.core.exceptions.FieldError: Expression contains mixed types. You must set output_field.
我尝试将output_field添加到子查询中,这对我来说没有意义,但又出现另一个错误。
指导将不胜感激
最佳答案
您可以在不使用子查询
的情况下使用其他聚合函数来创建这些注释。这个答案中使用的所有函数都是这样导入的
from django.db.models import Sum, Case, When, F, Value
要注释相关记录的总和,您可以执行以下操作:
Account.objects.annotate(
credit_sum=Sum('credits__amount')
).values('name', 'credit_sum')
# <QuerySet [{'name': 'FOX', 'credit_sum': Decimal('100.00')}, {'name': 'DOG', 'credit_sum': Decimal('400.00')}, {'name': 'SNAKE', 'credit_sum': Decimal('70.00')}]>
这样做的一个问题是,当您对 2 个关系求和时,如果一条记录有多个相关行,您将得到重复项(此处“DOG”的所有值都加倍)
Account.objects.annotate(
credit_sum=Sum('credits__amount'),
debit_sum=Sum('debits__amount')
).values('name', 'credit_sum', 'debit_sum')
# <QuerySet [{'name': 'FOX', 'credit_sum': Decimal('100.00'), 'debit_sum': Decimal('40.00')}, {'name': 'DOG', 'credit_sum': Decimal('800.00'), 'debit_sum': Decimal('70.00')}, {'name': 'SNAKE', 'credit_sum': Decimal('70.00'), 'debit_sum': Decimal('60.00')}]>
要计算不同值,您可以将 distinct=
添加到 Sum
Account.objects.annotate(
credit_sum=Sum('credits__amount', distinct=F('credits__id')),
debit_sum=Sum('debits__amount', distinct=F('debits__id'))
)
要有条件地求和或计算行数,您可以使用 Case
and When
,对于您的 taxable_credit_sum
注释,它看起来像这样
balances = Account.objects.annotate(
credit_sum=Sum('credits__amount', distinct=F('credits__id')),
debit_sum=Sum('debits__amount', distinct=F('debits__id')),
taxable_credit_sum=Sum(Case(
When(credits__taxable=True, then=F('credits__amount')),
default=Value(0),
output_field=models.DecimalField()
), distinct=F('credits__id'))
).annotate(
balance=F('credit_sum') - F('debit_sum') - F('taxable_credit_sum')
).order_by('id').values_list('name', 'balance')
现在您应该拥有所需的查询
您不需要同时计算抵免额和非应税抵免额
由于您只是从另一个中减去一个,因此您只需为此创建一个注释,将所有不需纳税的学分相加即可
balances = Account.objects.annotate(
debit_sum=Sum('debits__amount', distinct=F('debits__id')),
non_taxable_credit_sum=Sum(Case(
When(credits__taxable=False, then=F('credits__amount')),
default=Value(0),
output_field=models.DecimalField()
), distinct=F('credits__id'))
).annotate(
balance=F('non_taxable_credit_sum') - F('debit_sum')
).order_by('id').values_list('name', 'balance')
关于python - Django 子查询表达式包含混合类型。您必须设置output_field,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59593570/