Skip to content

Commit 6136459

Browse files
Merge pull request #111 from uncovertruth/feature/migrate_to_complement
Migrate to complement
2 parents 355307a + 3ab4f9c commit 6136459

File tree

7 files changed

+36
-16
lines changed

7 files changed

+36
-16
lines changed

HISTORY.rst

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ History
77

88
- Add support for Django 2.1
99
- Add support for Python 3.7
10+
- Migrate to `complement` from `exists` and `neexists`
1011

1112
0.1.0
1213
-----

README.rst

+3-4
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,10 @@ neexact is negate exact, neiexact is negate iexact, others are similar.
102102
- neregex
103103
- neiregex
104104

105-
Exists
106-
^^^^^^
105+
Complement
106+
^^^^^^^^^^
107107

108-
- exists
109-
- neexists
108+
- complement
110109

111110
Extra regexes
112111
^^^^^^^^^^^^^

lookup_extensions/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
__author__ = """UNCOVER TRUTH Inc."""
44
__email__ = '[email protected]'
5-
__version__ = '0.1.1'
5+
__version__ = '0.2.0'
66

77
default_app_config = 'lookup_extensions.apps.LookupExtensionsConfig'

lookup_extensions/lookups/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
from .exists import * # noqa F401,F403
12
from .exregex import * # noqa F401,F403
23
from .negate import * # noqa F401,F403

lookup_extensions/lookups/exists.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from django.db.models import Exists
2+
from django.db.models.fields import Field
3+
from django.db.models.lookups import (
4+
BuiltinLookup,
5+
FieldGetDbPrepValueMixin,
6+
)
7+
8+
9+
@Field.register_lookup
10+
class Complement(FieldGetDbPrepValueMixin, BuiltinLookup):
11+
lookup_name = 'complement'
12+
13+
def process_rhs(self, compiler, connection):
14+
if self.rhs_is_direct_value() or not isinstance(self.rhs, Exists):
15+
raise ValueError("Exists subqueries are required")
16+
17+
db_rhs = getattr(self.rhs, '_db', None)
18+
if db_rhs is not None and db_rhs != connection.alias:
19+
raise ValueError("Subqueries aren't allowed across different databases")
20+
21+
return super(Complement, self).process_rhs(compiler, connection)
22+
23+
def as_sql(self, compiler, connection):
24+
return self.process_rhs(compiler, connection)

lookup_extensions/sql.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
1-
from django.core.exceptions import FieldError
2-
from django.db.models import Exists
31
from django.db.models.constants import LOOKUP_SEP
42
from django.db.models.sql import Query as DjangoQuery
53

64

75
class ExtendedQueryMixin(object):
86
SUBQUERY_LOOKUPS = (
9-
'exists',
10-
'neexists',
7+
'complement',
118
)
129

1310
def build_lookup(self, lookups, lhs, rhs):
1411
if lookups and lookups[-1] in self.SUBQUERY_LOOKUPS:
15-
if not isinstance(rhs, Exists):
16-
raise FieldError("Value is not Subquery instance.")
17-
return super(ExtendedQueryMixin, self).build_lookup(['exact'], rhs, not lookups[-1].startswith('ne'))
12+
return super(ExtendedQueryMixin, self).build_lookup(lookups[-1:], rhs, rhs)
1813
return super(ExtendedQueryMixin, self).build_lookup(lookups, lhs, rhs)
1914

2015
def prepare_lookup_value(self, value, lookups, can_reuse, allow_joins=True):

tests/test_lookup/tests.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1168,20 +1168,20 @@ def test_negate_exregex_short_end_escapes(self):
11681168
],
11691169
)
11701170

1171-
def test_exists(self):
1171+
def test_complement(self):
11721172
tags = Tag.objects.filter(articles=OuterRef('id'), name='Tag 2')
11731173
self.assertQuerysetEqual(
1174-
Article.objects.filter(tag__exists=Exists(tags)).filter(author=self.au1),
1174+
Article.objects.filter(tag__complement=Exists(tags)).filter(author=self.au1),
11751175
[
11761176
'<Article: Article 4>',
11771177
'<Article: Article 3>',
11781178
],
11791179
)
11801180

1181-
def test_neexists(self):
1181+
def test_negate_complement(self):
11821182
tags = Tag.objects.filter(articles=OuterRef('id'), name='Tag 2')
11831183
self.assertQuerysetEqual(
1184-
Article.objects.filter(tag__neexists=Exists(tags)).filter(author=self.au1),
1184+
Article.objects.filter(tag__complement=~Exists(tags)).filter(author=self.au1),
11851185
[
11861186
'<Article: Article 2>',
11871187
'<Article: Article 1>',

0 commit comments

Comments
 (0)