mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-25 02:54:06 +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:
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
django: ["2.2", "3.0"]
|
||||
django: ["2.2", "3.0", "3.1"]
|
||||
python-version: ["3.6", "3.7", "3.8"]
|
||||
|
||||
steps:
|
||||
|
|
|
@ -186,3 +186,24 @@ Default: ``None``
|
|||
GRAPHENE = {
|
||||
'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 (
|
||||
ArrayField,
|
||||
HStoreField,
|
||||
JSONField,
|
||||
JSONField as PGJSONField,
|
||||
RangeField,
|
||||
)
|
||||
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.pyutils import register_description
|
||||
|
||||
from .compat import ArrayField, HStoreField, JSONField, RangeField
|
||||
from .fields import DjangoConnectionField, DjangoListField
|
||||
from .compat import ArrayField, HStoreField, JSONField, PGJSONField, RangeField
|
||||
from .fields import DjangoListField, DjangoConnectionField
|
||||
from .settings import graphene_settings
|
||||
from .utils import import_single_dispatch
|
||||
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(PGJSONField)
|
||||
@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(
|
||||
description=get_django_field_description(field), required=not field.null
|
||||
)
|
||||
|
|
|
@ -40,6 +40,10 @@ DEFAULTS = {
|
|||
"DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME": None,
|
||||
# Use a separate path for handling subscriptions.
|
||||
"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:
|
||||
|
|
|
@ -61,13 +61,15 @@
|
|||
var fetchURL = locationQuery(otherParams);
|
||||
|
||||
// Defines a GraphQL fetcher using the fetch API.
|
||||
function httpClient(graphQLParams) {
|
||||
var headers = {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
};
|
||||
function httpClient(graphQLParams, opts) {
|
||||
if (typeof opts === 'undefined') {
|
||||
opts = {};
|
||||
}
|
||||
var headers = opts.headers || {};
|
||||
headers['Accept'] = headers['Accept'] || 'application/json';
|
||||
headers['Content-Type'] = headers['Content-Type'] || 'application/json';
|
||||
if (csrftoken) {
|
||||
headers["X-CSRFToken"] = csrftoken;
|
||||
headers['X-CSRFToken'] = csrftoken
|
||||
}
|
||||
return fetch(fetchURL, {
|
||||
method: "post",
|
||||
|
@ -108,7 +110,7 @@
|
|||
var activeSubscription = null;
|
||||
|
||||
// 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);
|
||||
|
||||
// If we're about to execute a new operation, and we have an active subscription,
|
||||
|
@ -126,7 +128,7 @@
|
|||
},
|
||||
};
|
||||
} else {
|
||||
return httpClient(graphQLParams);
|
||||
return httpClient(graphQLParams, opts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +175,7 @@
|
|||
onEditQuery: onEditQuery,
|
||||
onEditVariables: onEditVariables,
|
||||
onEditOperationName: onEditOperationName,
|
||||
headerEditorEnabled: GRAPHENE_SETTINGS.graphiqlHeaderEditorEnabled,
|
||||
query: parameters.query,
|
||||
};
|
||||
if (parameters.variables) {
|
||||
|
|
|
@ -45,6 +45,7 @@ add "&raw" to the end of the URL within a browser.
|
|||
{% if subscription_path %}
|
||||
subscriptionPath: "{{subscription_path}}",
|
||||
{% endif %}
|
||||
graphiqlHeaderEditorEnabled: {{ graphiql_header_editor_enabled|yesno:"true,false" }},
|
||||
};
|
||||
</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.json import JSONString
|
||||
|
||||
from ..compat import ArrayField, HStoreField, JSONField, MissingType, RangeField
|
||||
from ..compat import (
|
||||
ArrayField,
|
||||
HStoreField,
|
||||
JSONField,
|
||||
PGJSONField,
|
||||
MissingType,
|
||||
RangeField,
|
||||
)
|
||||
from ..converter import (
|
||||
convert_django_field,
|
||||
convert_django_field_with_choices,
|
||||
|
@ -352,8 +359,13 @@ def test_should_postgres_hstore_convert_string():
|
|||
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():
|
||||
assert_conversion(PGJSONField, JSONString)
|
||||
|
||||
|
||||
@pytest.mark.skipif(JSONField is MissingType, reason="JSONField should exist")
|
||||
def test_should_json_convert_string():
|
||||
assert_conversion(JSONField, JSONString)
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from graphene import Connection, Field, Interface, ObjectType, Schema, String
|
|||
from graphene.relay import Node
|
||||
|
||||
from .. import registry
|
||||
from ..filter import DjangoFilterConnectionField
|
||||
from ..types import DjangoObjectType, DjangoObjectTypeOptions
|
||||
from .models import Article as ArticleModel
|
||||
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 = 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:
|
||||
|
|
|
@ -154,6 +154,8 @@ class GraphQLView(View):
|
|||
subscriptions_transport_ws_sri=self.subscriptions_transport_ws_sri,
|
||||
# The SUBSCRIPTION_PATH setting.
|
||||
subscription_path=self.subscription_path,
|
||||
# GraphiQL headers tab,
|
||||
graphiql_header_editor_enabled=graphene_settings.GRAPHIQL_HEADER_EDITOR_ENABLED,
|
||||
)
|
||||
|
||||
if self.batch:
|
||||
|
|
4
tox.ini
4
tox.ini
|
@ -1,6 +1,6 @@
|
|||
[tox]
|
||||
envlist =
|
||||
py{36,37,38}-django{22,30,master},
|
||||
py{36,37,38}-django{22,30,31,master},
|
||||
black,flake8
|
||||
|
||||
[gh-actions]
|
||||
|
@ -13,6 +13,7 @@ python =
|
|||
DJANGO =
|
||||
2.2: django22
|
||||
3.0: django30
|
||||
3.1: django31
|
||||
master: djangomaster
|
||||
|
||||
[testenv]
|
||||
|
@ -28,6 +29,7 @@ deps =
|
|||
django21: Django>=2.1,<2.2
|
||||
django22: Django>=2.2,<3.0
|
||||
django30: Django>=3.0a1,<3.1
|
||||
django31: Django>=3.1,<3.2
|
||||
djangomaster: https://github.com/django/django/archive/master.zip
|
||||
commands = {posargs:py.test --cov=graphene_django graphene_django examples}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user