2016-11-05 21:12:12 +08:00
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
from django.core.exceptions import FieldError
|
2019-11-15 13:08:36 +08:00
|
|
|
from django.db.models import BooleanField, CharField, F, Q
|
|
|
|
from django.db.models.expressions import Col, Func
|
2016-11-05 21:12:12 +08:00
|
|
|
from django.db.models.fields.related_lookups import RelatedIsNull
|
|
|
|
from django.db.models.functions import Lower
|
|
|
|
from django.db.models.lookups import Exact, GreaterThan, IsNull, LessThan
|
|
|
|
from django.db.models.sql.query import Query
|
|
|
|
from django.db.models.sql.where import OR
|
2018-11-27 03:05:02 +08:00
|
|
|
from django.test import SimpleTestCase
|
2018-08-22 00:17:46 +08:00
|
|
|
from django.test.utils import register_lookup
|
2016-11-05 21:12:12 +08:00
|
|
|
|
|
|
|
from .models import Author, Item, ObjectC, Ranking
|
|
|
|
|
|
|
|
|
2018-11-27 03:05:02 +08:00
|
|
|
class TestQuery(SimpleTestCase):
|
2016-11-05 21:12:12 +08:00
|
|
|
def test_simple_query(self):
|
|
|
|
query = Query(Author)
|
|
|
|
where = query.build_where(Q(num__gt=2))
|
|
|
|
lookup = where.children[0]
|
|
|
|
self.assertIsInstance(lookup, GreaterThan)
|
|
|
|
self.assertEqual(lookup.rhs, 2)
|
|
|
|
self.assertEqual(lookup.lhs.target, Author._meta.get_field('num'))
|
|
|
|
|
2019-09-10 11:58:29 +08:00
|
|
|
def test_non_alias_cols_query(self):
|
|
|
|
query = Query(Author, alias_cols=False)
|
2019-04-30 16:20:41 +08:00
|
|
|
where = query.build_where(Q(num__gt=2, name__isnull=False) | Q(num__lt=F('id')))
|
|
|
|
|
|
|
|
name_isnull_lookup, num_gt_lookup = where.children[0].children
|
|
|
|
self.assertIsInstance(num_gt_lookup, GreaterThan)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(num_gt_lookup.lhs, Col)
|
|
|
|
self.assertIsNone(num_gt_lookup.lhs.alias)
|
2019-04-30 16:20:41 +08:00
|
|
|
self.assertIsInstance(name_isnull_lookup, IsNull)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(name_isnull_lookup.lhs, Col)
|
|
|
|
self.assertIsNone(name_isnull_lookup.lhs.alias)
|
2019-04-30 16:20:41 +08:00
|
|
|
|
|
|
|
num_lt_lookup = where.children[1]
|
|
|
|
self.assertIsInstance(num_lt_lookup, LessThan)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(num_lt_lookup.rhs, Col)
|
|
|
|
self.assertIsNone(num_lt_lookup.rhs.alias)
|
|
|
|
self.assertIsInstance(num_lt_lookup.lhs, Col)
|
|
|
|
self.assertIsNone(num_lt_lookup.lhs.alias)
|
2019-04-30 16:20:41 +08:00
|
|
|
|
2016-11-05 21:12:12 +08:00
|
|
|
def test_complex_query(self):
|
|
|
|
query = Query(Author)
|
|
|
|
where = query.build_where(Q(num__gt=2) | Q(num__lt=0))
|
|
|
|
self.assertEqual(where.connector, OR)
|
|
|
|
|
|
|
|
lookup = where.children[0]
|
|
|
|
self.assertIsInstance(lookup, GreaterThan)
|
|
|
|
self.assertEqual(lookup.rhs, 2)
|
|
|
|
self.assertEqual(lookup.lhs.target, Author._meta.get_field('num'))
|
|
|
|
|
|
|
|
lookup = where.children[1]
|
|
|
|
self.assertIsInstance(lookup, LessThan)
|
|
|
|
self.assertEqual(lookup.rhs, 0)
|
|
|
|
self.assertEqual(lookup.lhs.target, Author._meta.get_field('num'))
|
|
|
|
|
|
|
|
def test_multiple_fields(self):
|
2019-09-10 11:58:29 +08:00
|
|
|
query = Query(Item, alias_cols=False)
|
2016-11-05 21:12:12 +08:00
|
|
|
where = query.build_where(Q(modified__gt=F('created')))
|
|
|
|
lookup = where.children[0]
|
|
|
|
self.assertIsInstance(lookup, GreaterThan)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(lookup.rhs, Col)
|
|
|
|
self.assertIsNone(lookup.rhs.alias)
|
|
|
|
self.assertIsInstance(lookup.lhs, Col)
|
|
|
|
self.assertIsNone(lookup.lhs.alias)
|
2016-11-05 21:12:12 +08:00
|
|
|
self.assertEqual(lookup.rhs.target, Item._meta.get_field('created'))
|
|
|
|
self.assertEqual(lookup.lhs.target, Item._meta.get_field('modified'))
|
|
|
|
|
|
|
|
def test_transform(self):
|
2019-09-10 11:58:29 +08:00
|
|
|
query = Query(Author, alias_cols=False)
|
2018-08-22 00:17:46 +08:00
|
|
|
with register_lookup(CharField, Lower):
|
2016-11-05 21:12:12 +08:00
|
|
|
where = query.build_where(~Q(name__lower='foo'))
|
|
|
|
lookup = where.children[0]
|
|
|
|
self.assertIsInstance(lookup, Exact)
|
|
|
|
self.assertIsInstance(lookup.lhs, Lower)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(lookup.lhs.lhs, Col)
|
|
|
|
self.assertIsNone(lookup.lhs.lhs.alias)
|
2016-11-05 21:12:12 +08:00
|
|
|
self.assertEqual(lookup.lhs.lhs.target, Author._meta.get_field('name'))
|
|
|
|
|
|
|
|
def test_negated_nullable(self):
|
|
|
|
query = Query(Item)
|
|
|
|
where = query.build_where(~Q(modified__lt=datetime(2017, 1, 1)))
|
|
|
|
self.assertTrue(where.negated)
|
|
|
|
lookup = where.children[0]
|
|
|
|
self.assertIsInstance(lookup, LessThan)
|
|
|
|
self.assertEqual(lookup.lhs.target, Item._meta.get_field('modified'))
|
|
|
|
lookup = where.children[1]
|
|
|
|
self.assertIsInstance(lookup, IsNull)
|
|
|
|
self.assertEqual(lookup.lhs.target, Item._meta.get_field('modified'))
|
|
|
|
|
|
|
|
def test_foreign_key(self):
|
|
|
|
query = Query(Item)
|
|
|
|
msg = 'Joined field references are not permitted in this query'
|
|
|
|
with self.assertRaisesMessage(FieldError, msg):
|
|
|
|
query.build_where(Q(creator__num__gt=2))
|
|
|
|
|
|
|
|
def test_foreign_key_f(self):
|
|
|
|
query = Query(Ranking)
|
|
|
|
with self.assertRaises(FieldError):
|
|
|
|
query.build_where(Q(rank__gt=F('author__num')))
|
|
|
|
|
|
|
|
def test_foreign_key_exclusive(self):
|
2019-09-10 11:58:29 +08:00
|
|
|
query = Query(ObjectC, alias_cols=False)
|
2016-11-05 21:12:12 +08:00
|
|
|
where = query.build_where(Q(objecta=None) | Q(objectb=None))
|
|
|
|
a_isnull = where.children[0]
|
|
|
|
self.assertIsInstance(a_isnull, RelatedIsNull)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(a_isnull.lhs, Col)
|
|
|
|
self.assertIsNone(a_isnull.lhs.alias)
|
2016-11-05 21:12:12 +08:00
|
|
|
self.assertEqual(a_isnull.lhs.target, ObjectC._meta.get_field('objecta'))
|
|
|
|
b_isnull = where.children[1]
|
|
|
|
self.assertIsInstance(b_isnull, RelatedIsNull)
|
2019-09-10 11:58:29 +08:00
|
|
|
self.assertIsInstance(b_isnull.lhs, Col)
|
|
|
|
self.assertIsNone(b_isnull.lhs.alias)
|
2016-11-05 21:12:12 +08:00
|
|
|
self.assertEqual(b_isnull.lhs.target, ObjectC._meta.get_field('objectb'))
|
2019-09-24 03:51:43 +08:00
|
|
|
|
|
|
|
def test_clone_select_related(self):
|
|
|
|
query = Query(Item)
|
|
|
|
query.add_select_related(['creator'])
|
|
|
|
clone = query.clone()
|
|
|
|
clone.add_select_related(['note', 'creator__extra'])
|
|
|
|
self.assertEqual(query.select_related, {'creator': {}})
|
2019-11-10 16:48:36 +08:00
|
|
|
|
|
|
|
def test_iterable_lookup_value(self):
|
|
|
|
query = Query(Item)
|
|
|
|
where = query.build_where(Q(name=['a', 'b']))
|
|
|
|
name_exact = where.children[0]
|
|
|
|
self.assertIsInstance(name_exact, Exact)
|
|
|
|
self.assertEqual(name_exact.rhs, "['a', 'b']")
|
2019-11-15 13:08:36 +08:00
|
|
|
|
|
|
|
def test_filter_conditional(self):
|
|
|
|
query = Query(Item)
|
|
|
|
where = query.build_where(Func(output_field=BooleanField()))
|
|
|
|
exact = where.children[0]
|
|
|
|
self.assertIsInstance(exact, Exact)
|
|
|
|
self.assertIsInstance(exact.lhs, Func)
|
|
|
|
self.assertIs(exact.rhs, True)
|
|
|
|
|
|
|
|
def test_filter_conditional_join(self):
|
|
|
|
query = Query(Item)
|
|
|
|
filter_expr = Func('note__note', output_field=BooleanField())
|
|
|
|
msg = 'Joined field references are not permitted in this query'
|
|
|
|
with self.assertRaisesMessage(FieldError, msg):
|
|
|
|
query.build_where(filter_expr)
|
2019-11-15 13:24:21 +08:00
|
|
|
|
|
|
|
def test_filter_non_conditional(self):
|
|
|
|
query = Query(Item)
|
|
|
|
msg = 'Cannot filter against a non-conditional expression.'
|
|
|
|
with self.assertRaisesMessage(TypeError, msg):
|
|
|
|
query.build_where(Func(output_field=CharField()))
|