Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Graphene 3 #774

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,6 @@ after_success:
matrix:
fast_finish: true
include:
- python: 2.7
env: DJANGO=1.11

- python: 3.5
env: DJANGO=1.11
- python: 3.5
env: DJANGO=2.0
- python: 3.5
env: DJANGO=2.1
- python: 3.5
env: DJANGO=2.2

- python: 3.6
env: DJANGO=1.11
- python: 3.6
Expand Down
4 changes: 2 additions & 2 deletions docs/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ To restrict users from accessing the GraphQL API page the standard Django LoginR

After this, you can use the new ``PrivateGraphQLView`` in the project's URL Configuration file ``url.py``:

For Django 1.9 and below:
For Django 1.11:

.. code:: python

Expand All @@ -184,4 +184,4 @@ For Django 2.0 and above:
path('graphql', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
]

.. _LoginRequiredMixin: https://docs.djangoproject.com/en/1.10/topics/auth/default/#the-loginrequired-mixin
.. _LoginRequiredMixin: https://docs.djangoproject.com/en/1.11/topics/auth/default/#the-loginrequired-mixin
5 changes: 2 additions & 3 deletions docs/filtering.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ Filtering
=========

Graphene integrates with
`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>`__
`django-filter <https://django-filter.readthedocs.io/en/master/>`__ 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``.
Expand Down
15 changes: 10 additions & 5 deletions graphene_django/converter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from collections import OrderedDict
from django.db import models
from django.utils.encoding import force_text
from django.utils.functional import Promise
from functools import singledispatch

from graphene import (
ID,
Expand All @@ -20,20 +22,18 @@
)
from graphene.types.json import JSONString
from graphene.utils.str_converters import to_camel_case, to_const
from graphql import assert_valid_name
from graphql import assert_valid_name, GraphQLError
from graphql.pyutils import register_description

from .compat import ArrayField, HStoreField, JSONField, RangeField
from .fields import DjangoListField, DjangoConnectionField
from .utils import import_single_dispatch

singledispatch = import_single_dispatch()


def convert_choice_name(name):
name = to_const(force_text(name))
try:
assert_valid_name(name)
except AssertionError:
except GraphQLError:
name = "A_%s" % name
return name

Expand Down Expand Up @@ -252,3 +252,8 @@ def convert_postgres_range_to_string(field, registry=None):
if not isinstance(inner_type, (List, NonNull)):
inner_type = type(inner_type)
return List(inner_type, description=field.help_text, required=not field.null)


# Register Django lazy()-wrapped values as GraphQL description/help_text.
# This is needed for using lazy translations, see https://github.com/graphql-python/graphql-core-next/issues/58.
register_description(Promise)
38 changes: 19 additions & 19 deletions graphene_django/debug/tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class context(object):

# from examples.starwars_django.models import Character

pytestmark = pytest.mark.django_db
pytestmark = [pytest.mark.django_db, pytest.mark.asyncio]


def test_should_query_field():
async def test_should_query_field():
r1 = Reporter(last_name="ABA")
r1.save()
r2 = Reporter(last_name="Griffin")
Expand Down Expand Up @@ -53,14 +53,14 @@ def resolve_reporter(self, info, **args):
"_debug": {"sql": [{"rawSql": str(Reporter.objects.order_by("pk")[:1].query)}]},
}
schema = graphene.Schema(query=Query)
result = schema.execute(
result = await schema.execute_async(
query, context_value=context(), middleware=[DjangoDebugMiddleware()]
)
assert not result.errors
assert result.data == expected


def test_should_query_nested_field():
async def test_should_query_nested_field():
r1 = Reporter(last_name="ABA")
r1.save()
r2 = Reporter(last_name="Griffin")
Expand All @@ -75,7 +75,7 @@ class Meta:

class Query(graphene.ObjectType):
reporter = graphene.Field(ReporterType)
debug = graphene.Field(DjangoDebug, name="__debug")
debug = graphene.Field(DjangoDebug, name="_debug")

def resolve_reporter(self, info, **args):
return Reporter.objects.first()
Expand All @@ -89,7 +89,7 @@ def resolve_reporter(self, info, **args):
pets { edges { node { lastName } } }
} } }
}
__debug {
_debug {
sql {
rawSql
}
Expand All @@ -112,22 +112,22 @@ def resolve_reporter(self, info, **args):
}
}
schema = graphene.Schema(query=Query)
result = schema.execute(
result = await schema.execute_async(
query, context_value=context(), middleware=[DjangoDebugMiddleware()]
)
assert not result.errors
query = str(Reporter.objects.order_by("pk")[:1].query)
assert result.data["__debug"]["sql"][0]["rawSql"] == query
assert "COUNT" in result.data["__debug"]["sql"][1]["rawSql"]
assert "tests_reporter_pets" in result.data["__debug"]["sql"][2]["rawSql"]
assert "COUNT" in result.data["__debug"]["sql"][3]["rawSql"]
assert "tests_reporter_pets" in result.data["__debug"]["sql"][4]["rawSql"]
assert len(result.data["__debug"]["sql"]) == 5
assert result.data["_debug"]["sql"][0]["rawSql"] == query
assert "COUNT" in result.data["_debug"]["sql"][1]["rawSql"]
assert "tests_reporter_pets" in result.data["_debug"]["sql"][2]["rawSql"]
assert "COUNT" in result.data["_debug"]["sql"][3]["rawSql"]
assert "tests_reporter_pets" in result.data["_debug"]["sql"][4]["rawSql"]
assert len(result.data["_debug"]["sql"]) == 5

assert result.data["reporter"] == expected["reporter"]


def test_should_query_list():
async def test_should_query_list():
r1 = Reporter(last_name="ABA")
r1.save()
r2 = Reporter(last_name="Griffin")
Expand Down Expand Up @@ -162,14 +162,14 @@ def resolve_all_reporters(self, info, **args):
"_debug": {"sql": [{"rawSql": str(Reporter.objects.all().query)}]},
}
schema = graphene.Schema(query=Query)
result = schema.execute(
result = await schema.execute_async(
query, context_value=context(), middleware=[DjangoDebugMiddleware()]
)
assert not result.errors
assert result.data == expected


def test_should_query_connection():
async def test_should_query_connection():
r1 = Reporter(last_name="ABA")
r1.save()
r2 = Reporter(last_name="Griffin")
Expand Down Expand Up @@ -205,7 +205,7 @@ def resolve_all_reporters(self, info, **args):
"""
expected = {"allReporters": {"edges": [{"node": {"lastName": "ABA"}}]}}
schema = graphene.Schema(query=Query)
result = schema.execute(
result = await schema.execute_async(
query, context_value=context(), middleware=[DjangoDebugMiddleware()]
)
assert not result.errors
Expand All @@ -215,7 +215,7 @@ def resolve_all_reporters(self, info, **args):
assert result.data["_debug"]["sql"][1]["rawSql"] == query


def test_should_query_connectionfilter():
async def test_should_query_connectionfilter():
from ...filter import DjangoFilterConnectionField

r1 = Reporter(last_name="ABA")
Expand Down Expand Up @@ -254,7 +254,7 @@ def resolve_all_reporters(self, info, **args):
"""
expected = {"allReporters": {"edges": [{"node": {"lastName": "ABA"}}]}}
schema = graphene.Schema(query=Query)
result = schema.execute(
result = await schema.execute_async(
query, context_value=context(), middleware=[DjangoDebugMiddleware()]
)
assert not result.errors
Expand Down
8 changes: 5 additions & 3 deletions graphene_django/fields.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from functools import partial

from django.db.models.query import QuerySet
from graphene.relay.connection import page_info_adapter, connection_adapter

from graphql_relay.connection.arrayconnection import connection_from_list_slice
from promise import Promise

from graphene import NonNull
from graphene.relay import ConnectionField, PageInfo
from graphene.relay import ConnectionField
from graphene.types import Field, List

from .settings import graphene_settings
Expand Down Expand Up @@ -136,9 +138,9 @@ def resolve_connection(cls, connection, default_manager, args, iterable):
slice_start=0,
list_length=_len,
list_slice_length=_len,
connection_type=connection,
connection_type=partial(connection_adapter, connection),
edge_type=connection.Edge,
pageinfo_type=PageInfo,
pageinfo_type=page_info_adapter,
)
connection.iterable = iterable
connection.length = _len
Expand Down
32 changes: 1 addition & 31 deletions graphene_django/filter/filterset.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import itertools

from django.db import models
from django_filters import Filter, MultipleChoiceFilter, VERSION
from django_filters import Filter, MultipleChoiceFilter
from django_filters.filterset import BaseFilterSet, FilterSet
from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS

Expand Down Expand Up @@ -50,36 +50,6 @@ class GrapheneFilterSetMixin(BaseFilterSet):
)


# 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

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):
""" Wrap a provided filterset in Graphene-specific functionality
"""
Expand Down
Loading