mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-22 09:37:07 +03:00
Merge branch 'master' into v3
This commit is contained in:
commit
33c6a54414
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
|
@ -8,7 +8,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
max-parallel: 4
|
max-parallel: 4
|
||||||
matrix:
|
matrix:
|
||||||
django: ["2.2", "3.0"]
|
django: ["2.2", "3.0", "3.1"]
|
||||||
python-version: ["3.6", "3.7", "3.8"]
|
python-version: ["3.6", "3.7", "3.8"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
|
@ -186,3 +186,24 @@ Default: ``None``
|
||||||
GRAPHENE = {
|
GRAPHENE = {
|
||||||
'SUBSCRIPTION_PATH': "/ws/graphql"
|
'SUBSCRIPTION_PATH': "/ws/graphql"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
``GRAPHIQL_HEADER_EDITOR_ENABLED``
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
GraphiQL starting from version 1.0.0 allows setting custom headers in similar fashion to query variables.
|
||||||
|
|
||||||
|
Set to ``False`` if you want to disable GraphiQL headers editor tab for some reason.
|
||||||
|
|
||||||
|
This setting is passed to ``headerEditorEnabled`` GraphiQL options, for details refer to GraphiQLDocs_.
|
||||||
|
|
||||||
|
.. _GraphiQLDocs: https://github.com/graphql/graphiql/tree/main/packages/graphiql#options
|
||||||
|
|
||||||
|
|
||||||
|
Default: ``True``
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
GRAPHENE = {
|
||||||
|
'GRAPHIQL_HEADER_EDITOR_ENABLED': True,
|
||||||
|
}
|
||||||
|
|
|
@ -8,8 +8,14 @@ try:
|
||||||
from django.contrib.postgres.fields import (
|
from django.contrib.postgres.fields import (
|
||||||
ArrayField,
|
ArrayField,
|
||||||
HStoreField,
|
HStoreField,
|
||||||
JSONField,
|
JSONField as PGJSONField,
|
||||||
RangeField,
|
RangeField,
|
||||||
)
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ArrayField, HStoreField, JSONField, RangeField = (MissingType,) * 4
|
ArrayField, HStoreField, PGJSONField, RangeField = (MissingType,) * 4
|
||||||
|
|
||||||
|
try:
|
||||||
|
# JSONField is only available from Django 3.1
|
||||||
|
from django.db.models import JSONField
|
||||||
|
except ImportError:
|
||||||
|
JSONField = MissingType
|
||||||
|
|
|
@ -26,9 +26,10 @@ from graphene.utils.str_converters import to_camel_case
|
||||||
from graphql import GraphQLError, assert_valid_name
|
from graphql import GraphQLError, assert_valid_name
|
||||||
from graphql.pyutils import register_description
|
from graphql.pyutils import register_description
|
||||||
|
|
||||||
from .compat import ArrayField, HStoreField, JSONField, RangeField
|
from .compat import ArrayField, HStoreField, JSONField, PGJSONField, RangeField
|
||||||
from .fields import DjangoConnectionField, DjangoListField
|
from .fields import DjangoListField, DjangoConnectionField
|
||||||
from .settings import graphene_settings
|
from .settings import graphene_settings
|
||||||
|
from .utils import import_single_dispatch
|
||||||
from .utils.str_converters import to_const
|
from .utils.str_converters import to_const
|
||||||
|
|
||||||
|
|
||||||
|
@ -296,8 +297,9 @@ def convert_postgres_array_to_list(field, registry=None):
|
||||||
|
|
||||||
|
|
||||||
@convert_django_field.register(HStoreField)
|
@convert_django_field.register(HStoreField)
|
||||||
|
@convert_django_field.register(PGJSONField)
|
||||||
@convert_django_field.register(JSONField)
|
@convert_django_field.register(JSONField)
|
||||||
def convert_postgres_field_to_string(field, registry=None):
|
def convert_pg_and_json_field_to_string(field, registry=None):
|
||||||
return JSONString(
|
return JSONString(
|
||||||
description=get_django_field_description(field), required=not field.null
|
description=get_django_field_description(field), required=not field.null
|
||||||
)
|
)
|
||||||
|
|
|
@ -40,6 +40,10 @@ DEFAULTS = {
|
||||||
"DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME": None,
|
"DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME": None,
|
||||||
# Use a separate path for handling subscriptions.
|
# Use a separate path for handling subscriptions.
|
||||||
"SUBSCRIPTION_PATH": None,
|
"SUBSCRIPTION_PATH": None,
|
||||||
|
# By default GraphiQL headers editor tab is enabled, set to False to hide it
|
||||||
|
# This sets headerEditorEnabled GraphiQL option, for details go to
|
||||||
|
# https://github.com/graphql/graphiql/tree/main/packages/graphiql#options
|
||||||
|
"GRAPHIQL_HEADER_EDITOR_ENABLED": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|
|
@ -61,13 +61,15 @@
|
||||||
var fetchURL = locationQuery(otherParams);
|
var fetchURL = locationQuery(otherParams);
|
||||||
|
|
||||||
// Defines a GraphQL fetcher using the fetch API.
|
// Defines a GraphQL fetcher using the fetch API.
|
||||||
function httpClient(graphQLParams) {
|
function httpClient(graphQLParams, opts) {
|
||||||
var headers = {
|
if (typeof opts === 'undefined') {
|
||||||
Accept: "application/json",
|
opts = {};
|
||||||
"Content-Type": "application/json",
|
}
|
||||||
};
|
var headers = opts.headers || {};
|
||||||
|
headers['Accept'] = headers['Accept'] || 'application/json';
|
||||||
|
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
||||||
if (csrftoken) {
|
if (csrftoken) {
|
||||||
headers["X-CSRFToken"] = csrftoken;
|
headers['X-CSRFToken'] = csrftoken
|
||||||
}
|
}
|
||||||
return fetch(fetchURL, {
|
return fetch(fetchURL, {
|
||||||
method: "post",
|
method: "post",
|
||||||
|
@ -108,7 +110,7 @@
|
||||||
var activeSubscription = null;
|
var activeSubscription = null;
|
||||||
|
|
||||||
// Define a GraphQL fetcher that can intelligently route queries based on the operation type.
|
// Define a GraphQL fetcher that can intelligently route queries based on the operation type.
|
||||||
function graphQLFetcher(graphQLParams) {
|
function graphQLFetcher(graphQLParams, opts) {
|
||||||
var operationType = getOperationType(graphQLParams);
|
var operationType = getOperationType(graphQLParams);
|
||||||
|
|
||||||
// If we're about to execute a new operation, and we have an active subscription,
|
// If we're about to execute a new operation, and we have an active subscription,
|
||||||
|
@ -126,7 +128,7 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return httpClient(graphQLParams);
|
return httpClient(graphQLParams, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +175,7 @@
|
||||||
onEditQuery: onEditQuery,
|
onEditQuery: onEditQuery,
|
||||||
onEditVariables: onEditVariables,
|
onEditVariables: onEditVariables,
|
||||||
onEditOperationName: onEditOperationName,
|
onEditOperationName: onEditOperationName,
|
||||||
|
headerEditorEnabled: GRAPHENE_SETTINGS.graphiqlHeaderEditorEnabled,
|
||||||
query: parameters.query,
|
query: parameters.query,
|
||||||
};
|
};
|
||||||
if (parameters.variables) {
|
if (parameters.variables) {
|
||||||
|
|
|
@ -45,6 +45,7 @@ add "&raw" to the end of the URL within a browser.
|
||||||
{% if subscription_path %}
|
{% if subscription_path %}
|
||||||
subscriptionPath: "{{subscription_path}}",
|
subscriptionPath: "{{subscription_path}}",
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
graphiqlHeaderEditorEnabled: {{ graphiql_header_editor_enabled|yesno:"true,false" }},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="{% static 'graphene_django/graphiql.js' %}"></script>
|
<script src="{% static 'graphene_django/graphiql.js' %}"></script>
|
||||||
|
|
|
@ -11,7 +11,14 @@ from graphene.relay import ConnectionField, Node
|
||||||
from graphene.types.datetime import Date, DateTime, Time
|
from graphene.types.datetime import Date, DateTime, Time
|
||||||
from graphene.types.json import JSONString
|
from graphene.types.json import JSONString
|
||||||
|
|
||||||
from ..compat import ArrayField, HStoreField, JSONField, MissingType, RangeField
|
from ..compat import (
|
||||||
|
ArrayField,
|
||||||
|
HStoreField,
|
||||||
|
JSONField,
|
||||||
|
PGJSONField,
|
||||||
|
MissingType,
|
||||||
|
RangeField,
|
||||||
|
)
|
||||||
from ..converter import (
|
from ..converter import (
|
||||||
convert_django_field,
|
convert_django_field,
|
||||||
convert_django_field_with_choices,
|
convert_django_field_with_choices,
|
||||||
|
@ -352,8 +359,13 @@ def test_should_postgres_hstore_convert_string():
|
||||||
assert_conversion(HStoreField, JSONString)
|
assert_conversion(HStoreField, JSONString)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(JSONField is MissingType, reason="JSONField should exist")
|
@pytest.mark.skipif(PGJSONField is MissingType, reason="PGJSONField should exist")
|
||||||
def test_should_postgres_json_convert_string():
|
def test_should_postgres_json_convert_string():
|
||||||
|
assert_conversion(PGJSONField, JSONString)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(JSONField is MissingType, reason="JSONField should exist")
|
||||||
|
def test_should_json_convert_string():
|
||||||
assert_conversion(JSONField, JSONString)
|
assert_conversion(JSONField, JSONString)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ from graphene import Connection, Field, Interface, ObjectType, Schema, String
|
||||||
from graphene.relay import Node
|
from graphene.relay import Node
|
||||||
|
|
||||||
from .. import registry
|
from .. import registry
|
||||||
|
from ..filter import DjangoFilterConnectionField
|
||||||
from ..types import DjangoObjectType, DjangoObjectTypeOptions
|
from ..types import DjangoObjectType, DjangoObjectTypeOptions
|
||||||
from .models import Article as ArticleModel
|
from .models import Article as ArticleModel
|
||||||
from .models import Reporter as ReporterModel
|
from .models import Reporter as ReporterModel
|
||||||
|
@ -662,3 +663,28 @@ class TestDjangoObjectType:
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_local_registry
|
||||||
|
def test_django_objecttype_name_connection_propagation():
|
||||||
|
class Reporter(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = ReporterModel
|
||||||
|
name = "CustomReporterName"
|
||||||
|
filter_fields = ["email"]
|
||||||
|
interfaces = (Node,)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
reporter = Node.Field(Reporter)
|
||||||
|
reporters = DjangoFilterConnectionField(Reporter)
|
||||||
|
|
||||||
|
assert Reporter._meta.name == "CustomReporterName"
|
||||||
|
schema = str(Schema(query=Query))
|
||||||
|
|
||||||
|
assert "type CustomReporterName implements Node {" in schema
|
||||||
|
assert "type CustomReporterNameConnection {" in schema
|
||||||
|
assert "type CustomReporterNameEdge {" in schema
|
||||||
|
|
||||||
|
assert "type Reporter implements Node {" not in schema
|
||||||
|
assert "type ReporterConnection {" not in schema
|
||||||
|
assert "type ReporterEdge {" not in schema
|
||||||
|
|
|
@ -246,7 +246,7 @@ class DjangoObjectType(ObjectType):
|
||||||
connection_class = Connection
|
connection_class = Connection
|
||||||
|
|
||||||
connection = connection_class.create_type(
|
connection = connection_class.create_type(
|
||||||
"{}Connection".format(cls.__name__), node=cls
|
"{}Connection".format(options.get("name") or cls.__name__), node=cls
|
||||||
)
|
)
|
||||||
|
|
||||||
if connection is not None:
|
if connection is not None:
|
||||||
|
|
|
@ -154,6 +154,8 @@ class GraphQLView(View):
|
||||||
subscriptions_transport_ws_sri=self.subscriptions_transport_ws_sri,
|
subscriptions_transport_ws_sri=self.subscriptions_transport_ws_sri,
|
||||||
# The SUBSCRIPTION_PATH setting.
|
# The SUBSCRIPTION_PATH setting.
|
||||||
subscription_path=self.subscription_path,
|
subscription_path=self.subscription_path,
|
||||||
|
# GraphiQL headers tab,
|
||||||
|
graphiql_header_editor_enabled=graphene_settings.GRAPHIQL_HEADER_EDITOR_ENABLED,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.batch:
|
if self.batch:
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -1,6 +1,6 @@
|
||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
py{36,37,38}-django{22,30,master},
|
py{36,37,38}-django{22,30,31,master},
|
||||||
black,flake8
|
black,flake8
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
|
@ -13,6 +13,7 @@ python =
|
||||||
DJANGO =
|
DJANGO =
|
||||||
2.2: django22
|
2.2: django22
|
||||||
3.0: django30
|
3.0: django30
|
||||||
|
3.1: django31
|
||||||
master: djangomaster
|
master: djangomaster
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
@ -28,6 +29,7 @@ deps =
|
||||||
django21: Django>=2.1,<2.2
|
django21: Django>=2.1,<2.2
|
||||||
django22: Django>=2.2,<3.0
|
django22: Django>=2.2,<3.0
|
||||||
django30: Django>=3.0a1,<3.1
|
django30: Django>=3.0a1,<3.1
|
||||||
|
django31: Django>=3.1,<3.2
|
||||||
djangomaster: https://github.com/django/django/archive/master.zip
|
djangomaster: https://github.com/django/django/archive/master.zip
|
||||||
commands = {posargs:py.test --cov=graphene_django graphene_django examples}
|
commands = {posargs:py.test --cov=graphene_django graphene_django examples}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user