1
0
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:
Simon Charette
2019-05-12 17:17:47 -04:00
committed by Mariusz Felisiak
parent d08e6f55e3
commit 1e38f1191d
10 changed files with 122 additions and 39 deletions

View File

@@ -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'))

View File

@@ -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'),

View File

@@ -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)

View File

@@ -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