-
Notifications
You must be signed in to change notification settings - Fork 768
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support "contains" and "overlap" filtering (v2) (#1100)
* Fix project setup * Support contains/overlap filters * Add Python 2.7 support * Adjust docstrings * Remove unused fixtures
- Loading branch information
1 parent
66c8901
commit e0a5d1c
Showing
6 changed files
with
307 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
from mock import MagicMock | ||
import pytest | ||
|
||
from django.db import models | ||
from django.db.models.query import QuerySet | ||
from django_filters import filters | ||
from django_filters import FilterSet | ||
import graphene | ||
from graphene.relay import Node | ||
from graphene_django import DjangoObjectType | ||
from graphene_django.utils import DJANGO_FILTER_INSTALLED | ||
|
||
from ...compat import ArrayField | ||
|
||
pytestmark = [] | ||
|
||
if DJANGO_FILTER_INSTALLED: | ||
from graphene_django.filter import DjangoFilterConnectionField | ||
else: | ||
pytestmark.append( | ||
pytest.mark.skipif( | ||
True, reason="django_filters not installed or not compatible" | ||
) | ||
) | ||
|
||
|
||
STORE = {"events": []} | ||
|
||
|
||
@pytest.fixture | ||
def Event(): | ||
class Event(models.Model): | ||
name = models.CharField(max_length=50) | ||
tags = ArrayField(models.CharField(max_length=50)) | ||
|
||
return Event | ||
|
||
|
||
@pytest.fixture | ||
def EventFilterSet(Event): | ||
|
||
from django.contrib.postgres.forms import SimpleArrayField | ||
|
||
class ArrayFilter(filters.Filter): | ||
base_field_class = SimpleArrayField | ||
|
||
class EventFilterSet(FilterSet): | ||
class Meta: | ||
model = Event | ||
fields = { | ||
"name": ["exact"], | ||
} | ||
|
||
tags__contains = ArrayFilter(field_name="tags", lookup_expr="contains") | ||
tags__overlap = ArrayFilter(field_name="tags", lookup_expr="overlap") | ||
|
||
return EventFilterSet | ||
|
||
|
||
@pytest.fixture | ||
def EventType(Event, EventFilterSet): | ||
class EventType(DjangoObjectType): | ||
class Meta: | ||
model = Event | ||
interfaces = (Node,) | ||
filterset_class = EventFilterSet | ||
|
||
return EventType | ||
|
||
|
||
@pytest.fixture | ||
def Query(Event, EventType): | ||
class Query(graphene.ObjectType): | ||
events = DjangoFilterConnectionField(EventType) | ||
|
||
def resolve_events(self, info, **kwargs): | ||
|
||
events = [ | ||
Event(name="Live Show", tags=["concert", "music", "rock"],), | ||
Event(name="Musical", tags=["movie", "music"],), | ||
Event(name="Ballet", tags=["concert", "dance"],), | ||
] | ||
|
||
STORE["events"] = events | ||
|
||
m_queryset = MagicMock(spec=QuerySet) | ||
m_queryset.model = Event | ||
|
||
def filter_events(**kwargs): | ||
if "tags__contains" in kwargs: | ||
STORE["events"] = list( | ||
filter( | ||
lambda e: set(kwargs["tags__contains"]).issubset( | ||
set(e.tags) | ||
), | ||
STORE["events"], | ||
) | ||
) | ||
if "tags__overlap" in kwargs: | ||
STORE["events"] = list( | ||
filter( | ||
lambda e: not set(kwargs["tags__overlap"]).isdisjoint( | ||
set(e.tags) | ||
), | ||
STORE["events"], | ||
) | ||
) | ||
|
||
def mock_queryset_filter(*args, **kwargs): | ||
filter_events(**kwargs) | ||
return m_queryset | ||
|
||
def mock_queryset_none(*args, **kwargs): | ||
STORE["events"] = [] | ||
return m_queryset | ||
|
||
def mock_queryset_count(*args, **kwargs): | ||
return len(STORE["events"]) | ||
|
||
m_queryset.all.return_value = m_queryset | ||
m_queryset.filter.side_effect = mock_queryset_filter | ||
m_queryset.none.side_effect = mock_queryset_none | ||
m_queryset.count.side_effect = mock_queryset_count | ||
m_queryset.__getitem__.side_effect = STORE["events"].__getitem__ | ||
|
||
return m_queryset | ||
|
||
return Query |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import pytest | ||
|
||
from graphene import Schema | ||
|
||
from ...compat import ArrayField, MissingType | ||
|
||
|
||
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist") | ||
def test_string_contains_multiple(Query): | ||
""" | ||
Test contains filter on a string field. | ||
""" | ||
|
||
schema = Schema(query=Query) | ||
|
||
query = """ | ||
query { | ||
events (tags_Contains: ["concert", "music"]) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
result = schema.execute(query) | ||
assert not result.errors | ||
assert result.data["events"]["edges"] == [ | ||
{"node": {"name": "Live Show"}}, | ||
] | ||
|
||
|
||
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist") | ||
def test_string_contains_one(Query): | ||
""" | ||
Test contains filter on a string field. | ||
""" | ||
|
||
schema = Schema(query=Query) | ||
|
||
query = """ | ||
query { | ||
events (tags_Contains: ["music"]) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
result = schema.execute(query) | ||
assert not result.errors | ||
assert result.data["events"]["edges"] == [ | ||
{"node": {"name": "Live Show"}}, | ||
{"node": {"name": "Musical"}}, | ||
] | ||
|
||
|
||
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist") | ||
def test_string_contains_none(Query): | ||
""" | ||
Test contains filter on a string field. | ||
""" | ||
|
||
schema = Schema(query=Query) | ||
|
||
query = """ | ||
query { | ||
events (tags_Contains: []) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
result = schema.execute(query) | ||
assert not result.errors | ||
assert result.data["events"]["edges"] == [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import pytest | ||
|
||
from graphene import Schema | ||
|
||
from ...compat import ArrayField, MissingType | ||
|
||
|
||
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist") | ||
def test_string_overlap_multiple(Query): | ||
""" | ||
Test overlap filter on a string field. | ||
""" | ||
|
||
schema = Schema(query=Query) | ||
|
||
query = """ | ||
query { | ||
events (tags_Overlap: ["concert", "music"]) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
result = schema.execute(query) | ||
assert not result.errors | ||
assert result.data["events"]["edges"] == [ | ||
{"node": {"name": "Live Show"}}, | ||
{"node": {"name": "Musical"}}, | ||
{"node": {"name": "Ballet"}}, | ||
] | ||
|
||
|
||
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist") | ||
def test_string_overlap_one(Query): | ||
""" | ||
Test overlap filter on a string field. | ||
""" | ||
|
||
schema = Schema(query=Query) | ||
|
||
query = """ | ||
query { | ||
events (tags_Overlap: ["music"]) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
result = schema.execute(query) | ||
assert not result.errors | ||
assert result.data["events"]["edges"] == [ | ||
{"node": {"name": "Live Show"}}, | ||
{"node": {"name": "Musical"}}, | ||
] | ||
|
||
|
||
@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist") | ||
def test_string_overlap_none(Query): | ||
""" | ||
Test overlap filter on a string field. | ||
""" | ||
|
||
schema = Schema(query=Query) | ||
|
||
query = """ | ||
query { | ||
events (tags_Overlap: []) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
""" | ||
result = schema.execute(query) | ||
assert not result.errors | ||
assert result.data["events"]["edges"] == [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters