mirror of
https://github.com/django/django.git
synced 2025-10-24 22:26:08 +00:00
Fixed #30446 -- Resolved Value.output_field for stdlib types.
This required implementing a limited form of dynamic dispatch to combine expressions with numerical output. Refs #26355 should eventually provide a better interface for that.
This commit is contained in:
committed by
Mariusz Felisiak
parent
d08e6f55e3
commit
1e38f1191d
@@ -848,10 +848,6 @@ class AggregateTestCase(TestCase):
|
||||
book = Book.objects.annotate(val=Max(2, output_field=IntegerField())).first()
|
||||
self.assertEqual(book.val, 2)
|
||||
|
||||
def test_missing_output_field_raises_error(self):
|
||||
with self.assertRaisesMessage(FieldError, 'Cannot resolve expression type, unknown output_field'):
|
||||
Book.objects.annotate(val=Max(2)).first()
|
||||
|
||||
def test_annotation_expressions(self):
|
||||
authors = Author.objects.annotate(combined_ages=Sum(F('age') + F('friends__age'))).order_by('name')
|
||||
authors2 = Author.objects.annotate(combined_ages=Sum('age') + Sum('friends__age')).order_by('name')
|
||||
@@ -893,7 +889,7 @@ class AggregateTestCase(TestCase):
|
||||
|
||||
def test_combine_different_types(self):
|
||||
msg = (
|
||||
'Expression contains mixed types: FloatField, IntegerField. '
|
||||
'Expression contains mixed types: FloatField, DecimalField. '
|
||||
'You must set output_field.'
|
||||
)
|
||||
qs = Book.objects.annotate(sums=Sum('rating') + Sum('pages') + Sum('price'))
|
||||
|
||||
@@ -388,7 +388,7 @@ class AggregationTests(TestCase):
|
||||
)
|
||||
|
||||
def test_annotated_conditional_aggregate(self):
|
||||
annotated_qs = Book.objects.annotate(discount_price=F('price') * 0.75)
|
||||
annotated_qs = Book.objects.annotate(discount_price=F('price') * Decimal('0.75'))
|
||||
self.assertAlmostEqual(
|
||||
annotated_qs.aggregate(test=Avg(Case(
|
||||
When(pages__lt=400, then='discount_price'),
|
||||
|
||||
@@ -3,15 +3,17 @@ import pickle
|
||||
import unittest
|
||||
import uuid
|
||||
from copy import deepcopy
|
||||
from decimal import Decimal
|
||||
from unittest import mock
|
||||
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db import DatabaseError, NotSupportedError, connection
|
||||
from django.db.models import (
|
||||
Avg, BooleanField, Case, CharField, Count, DateField, DateTimeField,
|
||||
DurationField, Exists, Expression, ExpressionList, ExpressionWrapper, F,
|
||||
Func, IntegerField, Max, Min, Model, OrderBy, OuterRef, Q, StdDev,
|
||||
Subquery, Sum, TimeField, UUIDField, Value, Variance, When,
|
||||
Avg, BinaryField, BooleanField, Case, CharField, Count, DateField,
|
||||
DateTimeField, DecimalField, DurationField, Exists, Expression,
|
||||
ExpressionList, ExpressionWrapper, F, FloatField, Func, IntegerField, Max,
|
||||
Min, Model, OrderBy, OuterRef, Q, StdDev, Subquery, Sum, TimeField,
|
||||
UUIDField, Value, Variance, When,
|
||||
)
|
||||
from django.db.models.expressions import Col, Combinable, Random, RawSQL, Ref
|
||||
from django.db.models.functions import (
|
||||
@@ -1711,6 +1713,30 @@ class ValueTests(TestCase):
|
||||
value = Value('foo', output_field=CharField())
|
||||
self.assertEqual(value.as_sql(compiler, connection), ('%s', ['foo']))
|
||||
|
||||
def test_resolve_output_field(self):
|
||||
value_types = [
|
||||
('str', CharField),
|
||||
(True, BooleanField),
|
||||
(42, IntegerField),
|
||||
(3.14, FloatField),
|
||||
(datetime.date(2019, 5, 15), DateField),
|
||||
(datetime.datetime(2019, 5, 15), DateTimeField),
|
||||
(datetime.time(3, 16), TimeField),
|
||||
(datetime.timedelta(1), DurationField),
|
||||
(Decimal('3.14'), DecimalField),
|
||||
(b'', BinaryField),
|
||||
(uuid.uuid4(), UUIDField),
|
||||
]
|
||||
for value, ouput_field_type in value_types:
|
||||
with self.subTest(type=type(value)):
|
||||
expr = Value(value)
|
||||
self.assertIsInstance(expr.output_field, ouput_field_type)
|
||||
|
||||
def test_resolve_output_field_failure(self):
|
||||
msg = 'Cannot resolve expression type, unknown output_field'
|
||||
with self.assertRaisesMessage(FieldError, msg):
|
||||
Value(object()).output_field
|
||||
|
||||
|
||||
class FieldTransformTests(TestCase):
|
||||
|
||||
@@ -1848,7 +1874,9 @@ class ExpressionWrapperTests(SimpleTestCase):
|
||||
self.assertEqual(expr.get_group_by_cols(alias=None), [])
|
||||
|
||||
def test_non_empty_group_by(self):
|
||||
expr = ExpressionWrapper(Lower(Value('f')), output_field=IntegerField())
|
||||
value = Value('f')
|
||||
value.output_field = None
|
||||
expr = ExpressionWrapper(Lower(value), output_field=IntegerField())
|
||||
group_by_cols = expr.get_group_by_cols(alias=None)
|
||||
self.assertEqual(group_by_cols, [expr.expression])
|
||||
self.assertEqual(group_by_cols[0].output_field, expr.output_field)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from datetime import datetime
|
||||
from operator import attrgetter
|
||||
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db.models import (
|
||||
CharField, DateTimeField, F, Max, OuterRef, Subquery, Value,
|
||||
)
|
||||
@@ -439,17 +438,6 @@ class OrderingTests(TestCase):
|
||||
qs = Article.objects.order_by(Value('1', output_field=CharField()), '-headline')
|
||||
self.assertSequenceEqual(qs, [self.a4, self.a3, self.a2, self.a1])
|
||||
|
||||
def test_order_by_constant_value_without_output_field(self):
|
||||
msg = 'Cannot resolve expression type, unknown output_field'
|
||||
qs = Article.objects.annotate(constant=Value('1')).order_by('constant')
|
||||
for ordered_qs in (
|
||||
qs,
|
||||
qs.values('headline'),
|
||||
Article.objects.order_by(Value('1')),
|
||||
):
|
||||
with self.subTest(ordered_qs=ordered_qs), self.assertRaisesMessage(FieldError, msg):
|
||||
ordered_qs.first()
|
||||
|
||||
def test_related_ordering_duplicate_table_reference(self):
|
||||
"""
|
||||
An ordering referencing a model with an ordering referencing a model
|
||||
|
||||
Reference in New Issue
Block a user