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

graphql 3.0 and graphene 3.0 final rebase #951

Merged
merged 13 commits into from
May 9, 2020
Merged
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
max-parallel: 4
matrix:
django: ["1.11", "2.2", "3.0"]
django: ["2.2", "3.0"]
python-version: ["3.6", "3.7", "3.8"]

steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ A [Django](https://www.djangoproject.com/) integration for [Graphene](http://gra
For installing graphene, just run this command in your shell

```bash
pip install "graphene-django>=2.0"
pip install "graphene-django>=3"
```

### Settings
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ For installing graphene, just run this command in your shell

.. code:: bash

pip install "graphene-django>=2.0"
pip install "graphene-django>=3"

Settings
~~~~~~~~
Expand Down
11 changes: 1 addition & 10 deletions docs/authorization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +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.11:

.. code:: python

urlpatterns = [
# some other urls
url(r'^graphql$', PrivateGraphQLView.as_view(graphiql=True, schema=schema)),
]

For Django 2.0 and above:
For Django 2.2 and above:

.. code:: python

Expand Down
4 changes: 2 additions & 2 deletions docs/filtering.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ Filtering
=========

Graphene integrates with
`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>`__
`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
16 changes: 2 additions & 14 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Requirements

Graphene-Django currently supports the following versions of Django:

* >= Django 1.11
* >= Django 2.2

Installation
------------
Expand All @@ -32,19 +32,7 @@ Add ``graphene_django`` to the ``INSTALLED_APPS`` in the ``settings.py`` file of

We need to add a ``graphql`` URL to the ``urls.py`` of your Django project:

For Django 1.11:

.. code:: python

from django.conf.urls import url
from graphene_django.views import GraphQLView

urlpatterns = [
# ...
url(r"graphql", GraphQLView.as_view(graphiql=True)),
]

For Django 2.0 and above:
For Django 2.2 and above:

.. code:: python

Expand Down
28 changes: 18 additions & 10 deletions graphene_django/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@

from django.db import models
from django.utils.encoding import force_str
from django.utils.functional import Promise
from django.utils.module_loading import import_string

from graphene import (
ID,
UUID,
Boolean,
Date,
DateTime,
Dynamic,
Enum,
Field,
Expand All @@ -16,25 +19,23 @@
List,
NonNull,
String,
UUID,
DateTime,
Date,
Time,
)
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 GraphQLError, assert_valid_name
from graphql.pyutils import register_description

from .settings import graphene_settings
from .compat import ArrayField, HStoreField, JSONField, RangeField
from .fields import DjangoListField, DjangoConnectionField
from .fields import DjangoConnectionField, DjangoListField
from .settings import graphene_settings


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

Expand All @@ -52,7 +53,9 @@ def get_choices(choices):
while name in converted_names:
name += "_" + str(len(converted_names))
converted_names.append(name)
description = help_text
description = str(
help_text
) # TODO: translatable description: https://github.com/graphql-python/graphql-core-next/issues/58
yield name, value, description


Expand All @@ -64,7 +67,7 @@ def convert_choices_to_named_enum_with_descriptions(name, choices):
class EnumWithDescriptionsType(object):
@property
def description(self):
return named_choices_descriptions[self.name]
return str(named_choices_descriptions[self.name])

return Enum(name, list(named_choices), type=EnumWithDescriptionsType)

Expand Down Expand Up @@ -276,3 +279,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)
2 changes: 1 addition & 1 deletion graphene_django/debug/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get_debug_promise(self):
if not self.debug_promise:
self.debug_promise = Promise.all(self.promises)
self.promises = []
return self.debug_promise.then(self.on_resolve_all_promises)
return self.debug_promise.then(self.on_resolve_all_promises).get()

def on_resolve_all_promises(self, values):
if self.promises:
Expand Down
16 changes: 8 additions & 8 deletions graphene_django/debug/tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,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 @@ -82,7 +82,7 @@ def resolve_reporter(self, info, **args):
pets { edges { node { lastName } } }
} } }
}
__debug {
_debug {
sql {
rawSql
}
Expand Down Expand Up @@ -110,12 +110,12 @@ def resolve_reporter(self, info, **args):
)
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"]

Expand Down
15 changes: 8 additions & 7 deletions graphene_django/fields.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from functools import partial

from django.db.models.query import QuerySet
from graphql_relay.connection.arrayconnection import connection_from_list_slice
from graphql_relay.connection.arrayconnection import connection_from_array_slice
from promise import Promise

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

from .settings import graphene_settings
Expand Down Expand Up @@ -122,15 +123,15 @@ def resolve_connection(cls, connection, args, iterable):
_len = iterable.count()
else:
_len = len(iterable)
connection = connection_from_list_slice(
connection = connection_from_array_slice(
iterable,
args,
slice_start=0,
list_length=_len,
list_slice_length=_len,
connection_type=connection,
array_length=_len,
array_slice_length=_len,
connection_type=partial(connection_adapter, connection),
edge_type=connection.Edge,
pageinfo_type=PageInfo,
page_info_type=page_info_adapter,
)
connection.iterable = iterable
connection.length = _len
Expand Down
106 changes: 71 additions & 35 deletions graphene_django/filter/tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,38 +806,56 @@ class Query(ObjectType):

assert str(schema) == dedent(
"""\
schema {
query: Query
type Query {
pets(before: String = null, after: String = null, first: Int = null, last: Int = null, age: Int = null): PetTypeConnection
}

interface Node {
id: ID!
type PetTypeConnection {
\"""Pagination data for this connection.\"""
pageInfo: PageInfo!

\"""Contains the nodes in this connection.\"""
edges: [PetTypeEdge]!
}

\"""
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
\"""
type PageInfo {
\"""When paginating forwards, are there more items?\"""
hasNextPage: Boolean!

\"""When paginating backwards, are there more items?\"""
hasPreviousPage: Boolean!

\"""When paginating backwards, the cursor to continue.\"""
startCursor: String

\"""When paginating forwards, the cursor to continue.\"""
endCursor: String
}

type PetType implements Node {
age: Int!
id: ID!
}

type PetTypeConnection {
pageInfo: PageInfo!
edges: [PetTypeEdge]!
}


\"""A Relay edge containing a `PetType` and its cursor.\"""
type PetTypeEdge {
\"""The item at the end of the edge\"""
node: PetType

\"""A cursor for use in pagination\"""
cursor: String!
}

type Query {
pets(before: String, after: String, first: Int, last: Int, age: Int): PetTypeConnection

type PetType implements Node {
\"""\"""
age: Int!

\"""The ID of the object\"""
id: ID!
}

\"""An object with an ID\"""
interface Node {
\"""The ID of the object\"""
id: ID!
}
"""
)
Expand All @@ -858,40 +876,58 @@ class Query(ObjectType):

assert str(schema) == dedent(
"""\
schema {
query: Query
type Query {
pets(before: String = null, after: String = null, first: Int = null, last: Int = null, age: Int = null, age_Isnull: Boolean = null, age_Lt: Int = null): PetTypeConnection
}

interface Node {
id: ID!
type PetTypeConnection {
\"""Pagination data for this connection.\"""
pageInfo: PageInfo!

\"""Contains the nodes in this connection.\"""
edges: [PetTypeEdge]!
}

\"""
The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
\"""
type PageInfo {
\"""When paginating forwards, are there more items?\"""
hasNextPage: Boolean!

\"""When paginating backwards, are there more items?\"""
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}

type PetType implements Node {
age: Int!
id: ID!
}
\"""When paginating backwards, the cursor to continue.\"""
startCursor: String

type PetTypeConnection {
pageInfo: PageInfo!
edges: [PetTypeEdge]!
\"""When paginating forwards, the cursor to continue.\"""
endCursor: String
}

\"""A Relay edge containing a `PetType` and its cursor.\"""
type PetTypeEdge {
\"""The item at the end of the edge\"""
node: PetType

\"""A cursor for use in pagination\"""
cursor: String!
}

type Query {
pets(before: String, after: String, first: Int, last: Int, age: Int, age_Isnull: Boolean, age_Lt: Int): PetTypeConnection
type PetType implements Node {
\"""\"""
age: Int!

\"""The ID of the object\"""
id: ID!
}
"""

\"""An object with an ID\"""
interface Node {
\"""The ID of the object\"""
id: ID!
}
"""
)


Expand Down
Loading