mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-11-04 09:57:53 +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