mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-02-24 15:47:31 +03:00
Merge pull request #492 from jayhale/django-filter-2
Make GrapheneFilterSetMixin compatible with django-filter 2
This commit is contained in:
commit
e45708b44e
15
.travis.yml
15
.travis.yml
|
@ -11,9 +11,6 @@ install:
|
|||
pip install -e .[test]
|
||||
pip install psycopg2 # Required for Django postgres fields testing
|
||||
pip install django==$DJANGO_VERSION
|
||||
if (($(echo "$DJANGO_VERSION <= 1.9" | bc -l))); then # DRF dropped 1.8 and 1.9 support at 3.7.0
|
||||
pip install djangorestframework==3.6.4
|
||||
fi
|
||||
python setup.py develop
|
||||
elif [ "$TEST_TYPE" = lint ]; then
|
||||
pip install flake8
|
||||
|
@ -44,13 +41,13 @@ matrix:
|
|||
env: TEST_TYPE=build DJANGO_VERSION=2.0
|
||||
- python: '3.6'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=2.0
|
||||
- python: '3.5'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=2.1
|
||||
- python: '3.6'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=2.1
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.8
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.9
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.10
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=lint
|
||||
- python: '3.6'
|
||||
env: TEST_TYPE=lint
|
||||
deploy:
|
||||
provider: pypi
|
||||
|
|
|
@ -2,11 +2,9 @@ Filtering
|
|||
=========
|
||||
|
||||
Graphene integrates with
|
||||
`django-filter <https://django-filter.readthedocs.io/en/1.1.0/>`__ (< 2.0.0) to provide
|
||||
filtering of results (this also means filtering is only compatible with Django < 2.0).
|
||||
|
||||
See the `usage
|
||||
documentation <https://django-filter.readthedocs.io/en/1.1.0/guide/usage.html#the-filter>`__
|
||||
`django-filter <https://django-filter.readthedocs.io/en/master/>`__ (2.x for
|
||||
Python 3 or 1.x for Python 2) to provide filtering of results. See the `usage
|
||||
documentation <https://django-filter.readthedocs.io/en/master/guide/usage.html#the-filter>`__
|
||||
for details on the format for ``filter_fields``.
|
||||
|
||||
This filtering is automatically available when implementing a ``relay.Node``.
|
||||
|
@ -17,7 +15,7 @@ You will need to install it manually, which can be done as follows:
|
|||
.. code:: bash
|
||||
|
||||
# You'll need to django-filter
|
||||
pip install django-filter==1.1.0
|
||||
pip install django-filter>=2
|
||||
|
||||
Note: The techniques below are demoed in the `cookbook example
|
||||
app <https://github.com/graphql-python/graphene-django/tree/master/examples/cookbook>`__.
|
||||
|
@ -28,7 +26,7 @@ Filterable fields
|
|||
The ``filter_fields`` parameter is used to specify the fields which can
|
||||
be filtered upon. The value specified here is passed directly to
|
||||
``django-filter``, so see the `filtering
|
||||
documentation <https://django-filter.readthedocs.io/en/1.1.0/guide/usage.html#the-filter>`__
|
||||
documentation <https://django-filter.readthedocs.io/en/master/guide/usage.html#the-filter>`__
|
||||
for full details on the range of options available.
|
||||
|
||||
For example:
|
||||
|
@ -129,7 +127,7 @@ create your own ``Filterset`` as follows:
|
|||
all_animals = DjangoFilterConnectionField(AnimalNode,
|
||||
filterset_class=AnimalFilter)
|
||||
|
||||
The context argument is passed on as the `request argument <http://django-filter.readthedocs.io/en/1.1.0/guide/usage.html#request-based-filtering>`__
|
||||
The context argument is passed on as the `request argument <http://django-filter.readthedocs.io/en/master/guide/usage.html#request-based-filtering>`__
|
||||
in a ``django_filters.FilterSet`` instance. You can use this to customize your
|
||||
filters to be context-dependent. We could modify the ``AnimalFilter`` above to
|
||||
pre-filter animals owned by the authenticated user (set in ``context.user``).
|
||||
|
|
|
@ -2,4 +2,4 @@ graphene
|
|||
graphene-django
|
||||
graphql-core>=2.1rc1
|
||||
django==1.9
|
||||
django-filter<2
|
||||
django-filter>=2
|
||||
|
|
|
@ -5,13 +5,7 @@ class MissingType(object):
|
|||
try:
|
||||
# Postgres fields are only available in Django with psycopg2 installed
|
||||
# and we cannot have psycopg2 on PyPy
|
||||
from django.contrib.postgres.fields import ArrayField, HStoreField, RangeField
|
||||
from django.contrib.postgres.fields import (ArrayField, HStoreField,
|
||||
JSONField, RangeField)
|
||||
except ImportError:
|
||||
ArrayField, HStoreField, JSONField, RangeField = (MissingType,) * 4
|
||||
|
||||
|
||||
try:
|
||||
# Postgres fields are only available in Django 1.9+
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
except ImportError:
|
||||
JSONField = MissingType
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import itertools
|
||||
|
||||
from django.db import models
|
||||
from django.utils.text import capfirst
|
||||
from django_filters import Filter, MultipleChoiceFilter
|
||||
from django_filters import Filter, MultipleChoiceFilter, VERSION
|
||||
from django_filters.filterset import BaseFilterSet, FilterSet
|
||||
from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS
|
||||
|
||||
|
@ -15,7 +14,10 @@ class GlobalIDFilter(Filter):
|
|||
field_class = GlobalIDFormField
|
||||
|
||||
def filter(self, qs, value):
|
||||
_type, _id = from_global_id(value)
|
||||
""" Convert the filter value to a primary key before filtering """
|
||||
_id = None
|
||||
if value is not None:
|
||||
_, _id = from_global_id(value)
|
||||
return super(GlobalIDFilter, self).filter(qs, _id)
|
||||
|
||||
|
||||
|
@ -32,36 +34,52 @@ GRAPHENE_FILTER_SET_OVERRIDES = {
|
|||
models.OneToOneField: {"filter_class": GlobalIDFilter},
|
||||
models.ForeignKey: {"filter_class": GlobalIDFilter},
|
||||
models.ManyToManyField: {"filter_class": GlobalIDMultipleChoiceFilter},
|
||||
models.ManyToOneRel: {"filter_class": GlobalIDMultipleChoiceFilter},
|
||||
models.ManyToManyRel: {"filter_class": GlobalIDMultipleChoiceFilter},
|
||||
}
|
||||
|
||||
|
||||
class GrapheneFilterSetMixin(BaseFilterSet):
|
||||
""" A django_filters.filterset.BaseFilterSet with default filter overrides
|
||||
to handle global IDs """
|
||||
|
||||
FILTER_DEFAULTS = dict(
|
||||
itertools.chain(
|
||||
FILTER_FOR_DBFIELD_DEFAULTS.items(), GRAPHENE_FILTER_SET_OVERRIDES.items()
|
||||
FILTER_FOR_DBFIELD_DEFAULTS.items(),
|
||||
GRAPHENE_FILTER_SET_OVERRIDES.items()
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def filter_for_reverse_field(cls, f, name):
|
||||
"""Handles retrieving filters for reverse relationships
|
||||
|
||||
We override the default implementation so that we can handle
|
||||
Global IDs (the default implementation expects database
|
||||
primary keys)
|
||||
"""
|
||||
try:
|
||||
rel = f.field.remote_field
|
||||
except AttributeError:
|
||||
rel = f.field.rel
|
||||
# To support a Django 1.11 + Python 2.7 combination django-filter must be
|
||||
# < 2.x.x. To support the earlier version of django-filter, the
|
||||
# filter_for_reverse_field method must be present on GrapheneFilterSetMixin and
|
||||
# must not be present for later versions of django-filter.
|
||||
if VERSION[0] < 2:
|
||||
from django.utils.text import capfirst
|
||||
|
||||
default = {"name": name, "label": capfirst(rel.related_name)}
|
||||
if rel.multiple:
|
||||
# For to-many relationships
|
||||
return GlobalIDMultipleChoiceFilter(**default)
|
||||
else:
|
||||
# For to-one relationships
|
||||
return GlobalIDFilter(**default)
|
||||
class GrapheneFilterSetMixinPython2(GrapheneFilterSetMixin):
|
||||
|
||||
@classmethod
|
||||
def filter_for_reverse_field(cls, f, name):
|
||||
"""Handles retrieving filters for reverse relationships
|
||||
We override the default implementation so that we can handle
|
||||
Global IDs (the default implementation expects database
|
||||
primary keys)
|
||||
"""
|
||||
try:
|
||||
rel = f.field.remote_field
|
||||
except AttributeError:
|
||||
rel = f.field.rel
|
||||
default = {"name": name, "label": capfirst(rel.related_name)}
|
||||
if rel.multiple:
|
||||
# For to-many relationships
|
||||
return GlobalIDMultipleChoiceFilter(**default)
|
||||
else:
|
||||
# For to-one relationships
|
||||
return GlobalIDFilter(**default)
|
||||
|
||||
GrapheneFilterSetMixin = GrapheneFilterSetMixinPython2
|
||||
|
||||
|
||||
def setup_filterset(filterset_class):
|
||||
|
|
|
@ -237,16 +237,12 @@ def test_should_manytomany_convert_connectionorlist_connection():
|
|||
|
||||
|
||||
def test_should_manytoone_convert_connectionorlist():
|
||||
# Django 1.9 uses 'rel', <1.9 uses 'related
|
||||
related = getattr(Reporter.articles, "rel", None) or getattr(
|
||||
Reporter.articles, "related"
|
||||
)
|
||||
|
||||
class A(DjangoObjectType):
|
||||
class Meta:
|
||||
model = Article
|
||||
|
||||
graphene_field = convert_django_field(related, A._meta.registry)
|
||||
graphene_field = convert_django_field(Reporter.articles.rel,
|
||||
A._meta.registry)
|
||||
assert isinstance(graphene_field, graphene.Dynamic)
|
||||
dynamic_field = graphene_field.get_type()
|
||||
assert isinstance(dynamic_field, graphene.Field)
|
||||
|
@ -255,14 +251,12 @@ def test_should_manytoone_convert_connectionorlist():
|
|||
|
||||
|
||||
def test_should_onetoone_reverse_convert_model():
|
||||
# Django 1.9 uses 'rel', <1.9 uses 'related
|
||||
related = getattr(Film.details, "rel", None) or getattr(Film.details, "related")
|
||||
|
||||
class A(DjangoObjectType):
|
||||
class Meta:
|
||||
model = FilmDetails
|
||||
|
||||
graphene_field = convert_django_field(related, A._meta.registry)
|
||||
graphene_field = convert_django_field(Film.details.related,
|
||||
A._meta.registry)
|
||||
assert isinstance(graphene_field, graphene.Dynamic)
|
||||
dynamic_field = graphene_field.get_type()
|
||||
assert isinstance(dynamic_field, graphene.Field)
|
||||
|
|
7
setup.py
7
setup.py
|
@ -19,7 +19,8 @@ tests_require = [
|
|||
"coveralls",
|
||||
"mock",
|
||||
"pytz",
|
||||
"django-filter<2",
|
||||
"django-filter<2;python_version<'3'",
|
||||
"django-filter>=2;python_version>='3'",
|
||||
"pytest-django>=3.3.2",
|
||||
] + rest_framework_require
|
||||
|
||||
|
@ -39,9 +40,9 @@ setup(
|
|||
"Programming Language :: Python :: 2",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
],
|
||||
keywords="api graphql protocol rest relay graphene",
|
||||
|
@ -50,7 +51,7 @@ setup(
|
|||
"six>=1.10.0",
|
||||
"graphene>=2.1,<3",
|
||||
"graphql-core>=2.1rc1",
|
||||
"Django>=1.8.0",
|
||||
"Django>=1.11",
|
||||
"iso8601",
|
||||
"singledispatch>=3.4.0.3",
|
||||
"promise>=2.1",
|
||||
|
|
Loading…
Reference in New Issue
Block a user