Skip to content

Commit

Permalink
Merge pull request #111 from uncovertruth/feature/migrate_to_complement
Browse files Browse the repository at this point in the history
Migrate to complement
  • Loading branch information
tsuyukimakoto authored Aug 7, 2018
2 parents 355307a + 3ab4f9c commit 6136459
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 16 deletions.
1 change: 1 addition & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ History

- Add support for Django 2.1
- Add support for Python 3.7
- Migrate to `complement` from `exists` and `neexists`

0.1.0
-----
Expand Down
7 changes: 3 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,10 @@ neexact is negate exact, neiexact is negate iexact, others are similar.
- neregex
- neiregex

Exists
^^^^^^
Complement
^^^^^^^^^^

- exists
- neexists
- complement

Extra regexes
^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion lookup_extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

__author__ = """UNCOVER TRUTH Inc."""
__email__ = '[email protected]'
__version__ = '0.1.1'
__version__ = '0.2.0'

default_app_config = 'lookup_extensions.apps.LookupExtensionsConfig'
1 change: 1 addition & 0 deletions lookup_extensions/lookups/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .exists import * # noqa F401,F403
from .exregex import * # noqa F401,F403
from .negate import * # noqa F401,F403
24 changes: 24 additions & 0 deletions lookup_extensions/lookups/exists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django.db.models import Exists
from django.db.models.fields import Field
from django.db.models.lookups import (
BuiltinLookup,
FieldGetDbPrepValueMixin,
)


@Field.register_lookup
class Complement(FieldGetDbPrepValueMixin, BuiltinLookup):
lookup_name = 'complement'

def process_rhs(self, compiler, connection):
if self.rhs_is_direct_value() or not isinstance(self.rhs, Exists):
raise ValueError("Exists subqueries are required")

db_rhs = getattr(self.rhs, '_db', None)
if db_rhs is not None and db_rhs != connection.alias:
raise ValueError("Subqueries aren't allowed across different databases")

return super(Complement, self).process_rhs(compiler, connection)

def as_sql(self, compiler, connection):
return self.process_rhs(compiler, connection)
9 changes: 2 additions & 7 deletions lookup_extensions/sql.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
from django.core.exceptions import FieldError
from django.db.models import Exists
from django.db.models.constants import LOOKUP_SEP
from django.db.models.sql import Query as DjangoQuery


class ExtendedQueryMixin(object):
SUBQUERY_LOOKUPS = (
'exists',
'neexists',
'complement',
)

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

def prepare_lookup_value(self, value, lookups, can_reuse, allow_joins=True):
Expand Down
8 changes: 4 additions & 4 deletions tests/test_lookup/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1168,20 +1168,20 @@ def test_negate_exregex_short_end_escapes(self):
],
)

def test_exists(self):
def test_complement(self):
tags = Tag.objects.filter(articles=OuterRef('id'), name='Tag 2')
self.assertQuerysetEqual(
Article.objects.filter(tag__exists=Exists(tags)).filter(author=self.au1),
Article.objects.filter(tag__complement=Exists(tags)).filter(author=self.au1),
[
'<Article: Article 4>',
'<Article: Article 3>',
],
)

def test_neexists(self):
def test_negate_complement(self):
tags = Tag.objects.filter(articles=OuterRef('id'), name='Tag 2')
self.assertQuerysetEqual(
Article.objects.filter(tag__neexists=Exists(tags)).filter(author=self.au1),
Article.objects.filter(tag__complement=~Exists(tags)).filter(author=self.au1),
[
'<Article: Article 2>',
'<Article: Article 1>',
Expand Down

0 comments on commit 6136459

Please sign in to comment.