mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-13 17:52:19 +03:00
Compare commits
No commits in common. "main" and "v3.2.1" have entirely different histories.
4
.github/workflows/deploy.yml
vendored
4
.github/workflows/deploy.yml
vendored
|
@ -15,9 +15,9 @@ jobs:
|
||||||
needs: [lint, tests]
|
needs: [lint, tests]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python 3.11
|
- name: Set up Python 3.11
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- name: Build wheel and source tarball
|
- name: Build wheel and source tarball
|
||||||
|
|
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
|
@ -11,9 +11,9 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python 3.11
|
- name: Set up Python 3.11
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|
14
.github/workflows/tests.yml
vendored
14
.github/workflows/tests.yml
vendored
|
@ -12,7 +12,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
max-parallel: 4
|
max-parallel: 4
|
||||||
matrix:
|
matrix:
|
||||||
django: ["3.2", "4.2", "5.0", "5.1", "5.2"]
|
django: ["3.2", "4.2", "5.0"]
|
||||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
||||||
exclude:
|
exclude:
|
||||||
- django: "3.2"
|
- django: "3.2"
|
||||||
|
@ -23,18 +23,10 @@ jobs:
|
||||||
python-version: "3.8"
|
python-version: "3.8"
|
||||||
- django: "5.0"
|
- django: "5.0"
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- django: "5.1"
|
|
||||||
python-version: "3.8"
|
|
||||||
- django: "5.1"
|
|
||||||
python-version: "3.9"
|
|
||||||
- django: "5.2"
|
|
||||||
python-version: "3.8"
|
|
||||||
- django: "5.2"
|
|
||||||
python-version: "3.9"
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
|
|
|
@ -25,6 +25,7 @@ target-version = "py38"
|
||||||
[per-file-ignores]
|
[per-file-ignores]
|
||||||
# Ignore unused imports (F401) in these files
|
# Ignore unused imports (F401) in these files
|
||||||
"__init__.py" = ["F401"]
|
"__init__.py" = ["F401"]
|
||||||
|
"graphene_django/compat.py" = ["F401"]
|
||||||
|
|
||||||
[isort]
|
[isort]
|
||||||
known-first-party = ["graphene", "graphene-django"]
|
known-first-party = ["graphene", "graphene-django"]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
graphene>=2.1,<3
|
graphene>=2.1,<3
|
||||||
graphene-django>=2.1,<3
|
graphene-django>=2.1,<3
|
||||||
graphql-core>=2.1,<3
|
graphql-core>=2.1,<3
|
||||||
django==4.2.18
|
django==3.2.24
|
||||||
django-filter>=2
|
django-filter>=2
|
||||||
|
|
|
@ -28,5 +28,3 @@ TEMPLATES = [
|
||||||
GRAPHENE = {"SCHEMA": "graphene_django.tests.schema_view.schema"}
|
GRAPHENE = {"SCHEMA": "graphene_django.tests.schema_view.schema"}
|
||||||
|
|
||||||
ROOT_URLCONF = "graphene_django.tests.urls"
|
ROOT_URLCONF = "graphene_django.tests.urls"
|
||||||
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
|
@ -24,6 +24,9 @@ class Faction(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Ship(models.Model):
|
class Ship(models.Model):
|
||||||
|
class Meta:
|
||||||
|
ordering = ["pk"]
|
||||||
|
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
faction = models.ForeignKey(Faction, on_delete=models.CASCADE, related_name="ships")
|
faction = models.ForeignKey(Faction, on_delete=models.CASCADE, related_name="ships")
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import graphene
|
import graphene
|
||||||
from graphene import Schema, relay
|
from graphene import Schema, relay, resolve_only_args
|
||||||
from graphene_django import DjangoConnectionField, DjangoObjectType
|
from graphene_django import DjangoConnectionField, DjangoObjectType
|
||||||
|
|
||||||
from .data import create_ship, get_empire, get_faction, get_rebels, get_ship, get_ships
|
from .data import create_ship, get_empire, get_faction, get_rebels, get_ship, get_ships
|
||||||
|
@ -62,13 +62,16 @@ class Query(graphene.ObjectType):
|
||||||
node = relay.Node.Field()
|
node = relay.Node.Field()
|
||||||
ships = DjangoConnectionField(Ship, description="All the ships.")
|
ships = DjangoConnectionField(Ship, description="All the ships.")
|
||||||
|
|
||||||
def resolve_ships(self, info):
|
@resolve_only_args
|
||||||
|
def resolve_ships(self):
|
||||||
return get_ships()
|
return get_ships()
|
||||||
|
|
||||||
def resolve_rebels(self, info):
|
@resolve_only_args
|
||||||
|
def resolve_rebels(self):
|
||||||
return get_rebels()
|
return get_rebels()
|
||||||
|
|
||||||
def resolve_empire(self, info):
|
@resolve_only_args
|
||||||
|
def resolve_empire(self):
|
||||||
return get_empire()
|
return get_empire()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from .fields import DjangoConnectionField, DjangoListField
|
||||||
from .types import DjangoObjectType
|
from .types import DjangoObjectType
|
||||||
from .utils import bypass_get_queryset
|
from .utils import bypass_get_queryset
|
||||||
|
|
||||||
__version__ = "3.2.3"
|
__version__ = "3.2.1"
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"__version__",
|
"__version__",
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Callable
|
|
||||||
from pathlib import PurePath
|
from pathlib import PurePath
|
||||||
|
|
||||||
# For backwards compatibility, we import JSONField to have it available for import via
|
# For backwards compatibility, we import JSONField to have it available for import via
|
||||||
# this compat module (https://github.com/graphql-python/graphene-django/issues/1428).
|
# this compat module (https://github.com/graphql-python/graphene-django/issues/1428).
|
||||||
# Django's JSONField is available in Django 3.2+ (the minimum version we support)
|
# Django's JSONField is available in Django 3.2+ (the minimum version we support)
|
||||||
from django.db.models import Choices, JSONField
|
from django.db.models import JSONField
|
||||||
|
|
||||||
|
|
||||||
class MissingType:
|
class MissingType:
|
||||||
|
@ -43,23 +42,3 @@ except ImportError:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ArrayField = MissingType
|
ArrayField = MissingType
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
from django.utils.choices import normalize_choices
|
|
||||||
except ImportError:
|
|
||||||
|
|
||||||
def normalize_choices(choices):
|
|
||||||
if isinstance(choices, type) and issubclass(choices, Choices):
|
|
||||||
choices = choices.choices
|
|
||||||
|
|
||||||
if isinstance(choices, Callable):
|
|
||||||
choices = choices()
|
|
||||||
|
|
||||||
# In restframework==3.15.0, choices are not passed
|
|
||||||
# as OrderedDict anymore, so it's safer to check
|
|
||||||
# for a dict
|
|
||||||
if isinstance(choices, dict):
|
|
||||||
choices = choices.items()
|
|
||||||
|
|
||||||
return choices
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import inspect
|
import inspect
|
||||||
|
from collections.abc import Callable
|
||||||
from functools import partial, singledispatch, wraps
|
from functools import partial, singledispatch, wraps
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -36,7 +37,7 @@ except ImportError:
|
||||||
from graphql import assert_valid_name as assert_name
|
from graphql import assert_valid_name as assert_name
|
||||||
from graphql.pyutils import register_description
|
from graphql.pyutils import register_description
|
||||||
|
|
||||||
from .compat import ArrayField, HStoreField, RangeField, normalize_choices
|
from .compat import ArrayField, HStoreField, RangeField
|
||||||
from .fields import DjangoConnectionField, DjangoListField
|
from .fields import DjangoConnectionField, DjangoListField
|
||||||
from .settings import graphene_settings
|
from .settings import graphene_settings
|
||||||
from .utils.str_converters import to_const
|
from .utils.str_converters import to_const
|
||||||
|
@ -60,24 +61,6 @@ class BlankValueField(Field):
|
||||||
return blank_field_wrapper(resolver)
|
return blank_field_wrapper(resolver)
|
||||||
|
|
||||||
|
|
||||||
class EnumValueField(BlankValueField):
|
|
||||||
def wrap_resolve(self, parent_resolver):
|
|
||||||
resolver = super().wrap_resolve(parent_resolver)
|
|
||||||
|
|
||||||
# create custom resolver
|
|
||||||
def enum_field_wrapper(func):
|
|
||||||
@wraps(func)
|
|
||||||
def wrapped_resolver(*args, **kwargs):
|
|
||||||
return_value = func(*args, **kwargs)
|
|
||||||
if isinstance(return_value, models.Choices):
|
|
||||||
return_value = return_value.value
|
|
||||||
return return_value
|
|
||||||
|
|
||||||
return wrapped_resolver
|
|
||||||
|
|
||||||
return enum_field_wrapper(resolver)
|
|
||||||
|
|
||||||
|
|
||||||
def convert_choice_name(name):
|
def convert_choice_name(name):
|
||||||
name = to_const(force_str(name))
|
name = to_const(force_str(name))
|
||||||
try:
|
try:
|
||||||
|
@ -89,7 +72,15 @@ def convert_choice_name(name):
|
||||||
|
|
||||||
def get_choices(choices):
|
def get_choices(choices):
|
||||||
converted_names = []
|
converted_names = []
|
||||||
choices = normalize_choices(choices)
|
if isinstance(choices, Callable):
|
||||||
|
choices = choices()
|
||||||
|
|
||||||
|
# In restframework==3.15.0, choices are not passed
|
||||||
|
# as OrderedDict anymore, so it's safer to check
|
||||||
|
# for a dict
|
||||||
|
if isinstance(choices, dict):
|
||||||
|
choices = choices.items()
|
||||||
|
|
||||||
for value, help_text in choices:
|
for value, help_text in choices:
|
||||||
if isinstance(help_text, (tuple, list)):
|
if isinstance(help_text, (tuple, list)):
|
||||||
yield from get_choices(help_text)
|
yield from get_choices(help_text)
|
||||||
|
@ -166,7 +157,7 @@ def convert_django_field_with_choices(
|
||||||
|
|
||||||
converted = EnumCls(
|
converted = EnumCls(
|
||||||
description=get_django_field_description(field), required=required
|
description=get_django_field_description(field), required=required
|
||||||
).mount_as(EnumValueField)
|
).mount_as(BlankValueField)
|
||||||
else:
|
else:
|
||||||
converted = convert_django_field(field, registry)
|
converted = convert_django_field(field, registry)
|
||||||
if registry is not None:
|
if registry is not None:
|
||||||
|
@ -199,13 +190,19 @@ def convert_field_to_string(field, registry=None):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@convert_django_field.register(models.AutoField)
|
|
||||||
@convert_django_field.register(models.BigAutoField)
|
@convert_django_field.register(models.BigAutoField)
|
||||||
@convert_django_field.register(models.SmallAutoField)
|
@convert_django_field.register(models.AutoField)
|
||||||
def convert_field_to_id(field, registry=None):
|
def convert_field_to_id(field, registry=None):
|
||||||
return ID(description=get_django_field_description(field), required=not field.null)
|
return ID(description=get_django_field_description(field), required=not field.null)
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(models, "SmallAutoField"):
|
||||||
|
|
||||||
|
@convert_django_field.register(models.SmallAutoField)
|
||||||
|
def convert_field_small_to_id(field, registry=None):
|
||||||
|
return convert_field_to_id(field, registry)
|
||||||
|
|
||||||
|
|
||||||
@convert_django_field.register(models.UUIDField)
|
@convert_django_field.register(models.UUIDField)
|
||||||
def convert_field_to_uuid(field, registry=None):
|
def convert_field_to_uuid(field, registry=None):
|
||||||
return UUID(
|
return UUID(
|
||||||
|
|
|
@ -101,13 +101,19 @@ class DjangoConnectionField(ConnectionField):
|
||||||
non_null = True
|
non_null = True
|
||||||
assert issubclass(
|
assert issubclass(
|
||||||
_type, DjangoObjectType
|
_type, DjangoObjectType
|
||||||
), "DjangoConnectionField only accepts DjangoObjectType types"
|
), "DjangoConnectionField only accepts DjangoObjectType types as underlying type"
|
||||||
assert _type._meta.connection, "The type {} doesn't have a connection".format(
|
assert _type._meta.connection, "The type {} doesn't have a connection".format(
|
||||||
_type.__name__
|
_type.__name__
|
||||||
)
|
)
|
||||||
connection_type = _type._meta.connection
|
connection_type = _type._meta.connection
|
||||||
if non_null:
|
if non_null:
|
||||||
return NonNull(connection_type)
|
return NonNull(connection_type)
|
||||||
|
# Since Relay Connections require to have a predictible ordering for pagination,
|
||||||
|
# check on init that the Django model provided has a default ordering declared.
|
||||||
|
model = connection_type._meta.node._meta.model
|
||||||
|
assert (
|
||||||
|
len(getattr(model._meta, "ordering", [])) > 0
|
||||||
|
), f"Django model {model._meta.app_label}.{model.__name__} has to have a default ordering to be used in a Connection."
|
||||||
return connection_type
|
return connection_type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -247,7 +253,7 @@ class DjangoConnectionField(ConnectionField):
|
||||||
def wrap_resolve(self, parent_resolver):
|
def wrap_resolve(self, parent_resolver):
|
||||||
return partial(
|
return partial(
|
||||||
self.connection_resolver,
|
self.connection_resolver,
|
||||||
self.resolver or parent_resolver,
|
parent_resolver,
|
||||||
self.connection_type,
|
self.connection_type,
|
||||||
self.get_manager(),
|
self.get_manager(),
|
||||||
self.get_queryset_resolver(),
|
self.get_queryset_resolver(),
|
||||||
|
|
|
@ -26,6 +26,9 @@ else:
|
||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
|
class Meta:
|
||||||
|
ordering = ["pk"]
|
||||||
|
|
||||||
name = models.CharField(max_length=50)
|
name = models.CharField(max_length=50)
|
||||||
tags = ArrayField(models.CharField(max_length=50))
|
tags = ArrayField(models.CharField(max_length=50))
|
||||||
tag_ids = ArrayField(models.IntegerField())
|
tag_ids = ArrayField(models.IntegerField())
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django import VERSION as DJANGO_VERSION, forms
|
from django import forms
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
|
||||||
from graphene import (
|
from graphene import (
|
||||||
|
@ -19,16 +19,12 @@ from graphene import (
|
||||||
from ..converter import convert_form_field
|
from ..converter import convert_form_field
|
||||||
|
|
||||||
|
|
||||||
def assert_conversion(django_field, graphene_field, *args, **kwargs):
|
def assert_conversion(django_field, graphene_field, *args):
|
||||||
# Arrange
|
field = django_field(*args, help_text="Custom Help Text")
|
||||||
help_text = kwargs.setdefault("help_text", "Custom Help Text")
|
|
||||||
field = django_field(*args, **kwargs)
|
|
||||||
# Act
|
|
||||||
graphene_type = convert_form_field(field)
|
graphene_type = convert_form_field(field)
|
||||||
# Assert
|
|
||||||
assert isinstance(graphene_type, graphene_field)
|
assert isinstance(graphene_type, graphene_field)
|
||||||
field = graphene_type.Field()
|
field = graphene_type.Field()
|
||||||
assert field.description == help_text
|
assert field.description == "Custom Help Text"
|
||||||
return field
|
return field
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,12 +59,7 @@ def test_should_slug_convert_string():
|
||||||
|
|
||||||
|
|
||||||
def test_should_url_convert_string():
|
def test_should_url_convert_string():
|
||||||
kwargs = {}
|
assert_conversion(forms.URLField, String)
|
||||||
if DJANGO_VERSION >= (5, 0):
|
|
||||||
# silence RemovedInDjango60Warning
|
|
||||||
kwargs["assume_scheme"] = "https"
|
|
||||||
|
|
||||||
assert_conversion(forms.URLField, String, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_choice_convert_string():
|
def test_should_choice_convert_string():
|
||||||
|
@ -84,7 +75,8 @@ def test_should_regex_convert_string():
|
||||||
|
|
||||||
|
|
||||||
def test_should_uuid_convert_string():
|
def test_should_uuid_convert_string():
|
||||||
assert_conversion(forms.UUIDField, UUID)
|
if hasattr(forms, "UUIDField"):
|
||||||
|
assert_conversion(forms.UUIDField, UUID)
|
||||||
|
|
||||||
|
|
||||||
def test_should_integer_convert_int():
|
def test_should_integer_convert_int():
|
||||||
|
|
|
@ -3,7 +3,7 @@ from graphene import ID
|
||||||
from graphene.types.inputobjecttype import InputObjectType
|
from graphene.types.inputobjecttype import InputObjectType
|
||||||
from graphene.utils.str_converters import to_camel_case
|
from graphene.utils.str_converters import to_camel_case
|
||||||
|
|
||||||
from ..converter import EnumValueField
|
from ..converter import BlankValueField
|
||||||
from ..types import ErrorType # noqa Import ErrorType for backwards compatibility
|
from ..types import ErrorType # noqa Import ErrorType for backwards compatibility
|
||||||
from .mutation import fields_for_form
|
from .mutation import fields_for_form
|
||||||
|
|
||||||
|
@ -57,10 +57,11 @@ class DjangoFormInputObjectType(InputObjectType):
|
||||||
if (
|
if (
|
||||||
object_type
|
object_type
|
||||||
and name in object_type._meta.fields
|
and name in object_type._meta.fields
|
||||||
and isinstance(object_type._meta.fields[name], EnumValueField)
|
and isinstance(object_type._meta.fields[name], BlankValueField)
|
||||||
):
|
):
|
||||||
# Field type EnumValueField here means that field
|
# Field type BlankValueField here means that field
|
||||||
# with choices have been converted to enum
|
# with choices have been converted to enum
|
||||||
|
# (BlankValueField is using only for that task ?)
|
||||||
setattr(cls, name, cls.get_enum_cnv_cls_instance(name, object_type))
|
setattr(cls, name, cls.get_enum_cnv_cls_instance(name, object_type))
|
||||||
elif (
|
elif (
|
||||||
object_type
|
object_type
|
||||||
|
|
|
@ -96,7 +96,8 @@ def test_should_regex_convert_string():
|
||||||
|
|
||||||
|
|
||||||
def test_should_uuid_convert_string():
|
def test_should_uuid_convert_string():
|
||||||
assert_conversion(serializers.UUIDField, graphene.String)
|
if hasattr(serializers, "UUIDField"):
|
||||||
|
assert_conversion(serializers.UUIDField, graphene.String)
|
||||||
|
|
||||||
|
|
||||||
def test_should_model_convert_field():
|
def test_should_model_convert_field():
|
||||||
|
|
|
@ -1,39 +1,13 @@
|
||||||
import django
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
CHOICES = ((1, "this"), (2, _("that")))
|
CHOICES = ((1, "this"), (2, _("that")))
|
||||||
|
|
||||||
|
|
||||||
def get_choices_as_class(choices_class):
|
|
||||||
if django.VERSION >= (5, 0):
|
|
||||||
return choices_class
|
|
||||||
else:
|
|
||||||
return choices_class.choices
|
|
||||||
|
|
||||||
|
|
||||||
def get_choices_as_callable(choices_class):
|
|
||||||
if django.VERSION >= (5, 0):
|
|
||||||
|
|
||||||
def choices():
|
|
||||||
return choices_class.choices
|
|
||||||
|
|
||||||
return choices
|
|
||||||
else:
|
|
||||||
return choices_class.choices
|
|
||||||
|
|
||||||
|
|
||||||
class TypedIntChoice(models.IntegerChoices):
|
|
||||||
CHOICE_THIS = 1
|
|
||||||
CHOICE_THAT = 2
|
|
||||||
|
|
||||||
|
|
||||||
class TypedStrChoice(models.TextChoices):
|
|
||||||
CHOICE_THIS = "this"
|
|
||||||
CHOICE_THAT = "that"
|
|
||||||
|
|
||||||
|
|
||||||
class Person(models.Model):
|
class Person(models.Model):
|
||||||
|
class Meta:
|
||||||
|
ordering = ["pk"]
|
||||||
|
|
||||||
name = models.CharField(max_length=30)
|
name = models.CharField(max_length=30)
|
||||||
parent = models.ForeignKey(
|
parent = models.ForeignKey(
|
||||||
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
|
"self", on_delete=models.CASCADE, null=True, blank=True, related_name="children"
|
||||||
|
@ -41,6 +15,9 @@ class Person(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Pet(models.Model):
|
class Pet(models.Model):
|
||||||
|
class Meta:
|
||||||
|
ordering = ["pk"]
|
||||||
|
|
||||||
name = models.CharField(max_length=30)
|
name = models.CharField(max_length=30)
|
||||||
age = models.PositiveIntegerField()
|
age = models.PositiveIntegerField()
|
||||||
owner = models.ForeignKey(
|
owner = models.ForeignKey(
|
||||||
|
@ -60,6 +37,9 @@ class FilmDetails(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Film(models.Model):
|
class Film(models.Model):
|
||||||
|
class Meta:
|
||||||
|
ordering = ["pk"]
|
||||||
|
|
||||||
genre = models.CharField(
|
genre = models.CharField(
|
||||||
max_length=2,
|
max_length=2,
|
||||||
help_text="Genre",
|
help_text="Genre",
|
||||||
|
@ -75,26 +55,14 @@ class DoeReporterManager(models.Manager):
|
||||||
|
|
||||||
|
|
||||||
class Reporter(models.Model):
|
class Reporter(models.Model):
|
||||||
|
class Meta:
|
||||||
|
ordering = ["pk"]
|
||||||
|
|
||||||
first_name = models.CharField(max_length=30)
|
first_name = models.CharField(max_length=30)
|
||||||
last_name = models.CharField(max_length=30)
|
last_name = models.CharField(max_length=30)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
pets = models.ManyToManyField("self")
|
pets = models.ManyToManyField("self")
|
||||||
a_choice = models.IntegerField(choices=CHOICES, null=True, blank=True)
|
a_choice = models.IntegerField(choices=CHOICES, null=True, blank=True)
|
||||||
typed_choice = models.IntegerField(
|
|
||||||
choices=TypedIntChoice.choices,
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
class_choice = models.IntegerField(
|
|
||||||
choices=get_choices_as_class(TypedIntChoice),
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
callable_choice = models.IntegerField(
|
|
||||||
choices=get_choices_as_callable(TypedStrChoice),
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
doe_objects = DoeReporterManager()
|
doe_objects = DoeReporterManager()
|
||||||
fans = models.ManyToManyField(Person)
|
fans = models.ManyToManyField(Person)
|
||||||
|
|
|
@ -25,7 +25,7 @@ from ..converter import (
|
||||||
)
|
)
|
||||||
from ..registry import Registry
|
from ..registry import Registry
|
||||||
from ..types import DjangoObjectType
|
from ..types import DjangoObjectType
|
||||||
from .models import Article, Film, FilmDetails, Reporter, TypedIntChoice, TypedStrChoice
|
from .models import Article, Film, FilmDetails, Reporter
|
||||||
|
|
||||||
# from graphene.core.types.custom_scalars import DateTime, Time, JSONString
|
# from graphene.core.types.custom_scalars import DateTime, Time, JSONString
|
||||||
|
|
||||||
|
@ -53,8 +53,9 @@ def assert_conversion(django_field, graphene_field, *args, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def test_should_unknown_django_field_raise_exception():
|
def test_should_unknown_django_field_raise_exception():
|
||||||
with raises(Exception, match="Don't know how to convert the Django field"):
|
with raises(Exception) as excinfo:
|
||||||
convert_django_field(None)
|
convert_django_field(None)
|
||||||
|
assert "Don't know how to convert the Django field" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_should_date_time_convert_string():
|
def test_should_date_time_convert_string():
|
||||||
|
@ -114,7 +115,8 @@ def test_should_big_auto_convert_id():
|
||||||
|
|
||||||
|
|
||||||
def test_should_small_auto_convert_id():
|
def test_should_small_auto_convert_id():
|
||||||
assert_conversion(models.SmallAutoField, graphene.ID, primary_key=True)
|
if hasattr(models, "SmallAutoField"):
|
||||||
|
assert_conversion(models.SmallAutoField, graphene.ID, primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
def test_should_uuid_convert_id():
|
def test_should_uuid_convert_id():
|
||||||
|
@ -164,14 +166,14 @@ def test_field_with_choices_convert_enum():
|
||||||
help_text="Language", choices=(("es", "Spanish"), ("en", "English"))
|
help_text="Language", choices=(("es", "Spanish"), ("en", "English"))
|
||||||
)
|
)
|
||||||
|
|
||||||
class ChoicesModel(models.Model):
|
class TranslatedModel(models.Model):
|
||||||
language = field
|
language = field
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = "test"
|
app_label = "test"
|
||||||
|
|
||||||
graphene_type = convert_django_field_with_choices(field).type.of_type
|
graphene_type = convert_django_field_with_choices(field).type.of_type
|
||||||
assert graphene_type._meta.name == "TestChoicesModelLanguageChoices"
|
assert graphene_type._meta.name == "TestTranslatedModelLanguageChoices"
|
||||||
assert graphene_type._meta.enum.__members__["ES"].value == "es"
|
assert graphene_type._meta.enum.__members__["ES"].value == "es"
|
||||||
assert graphene_type._meta.enum.__members__["ES"].description == "Spanish"
|
assert graphene_type._meta.enum.__members__["ES"].description == "Spanish"
|
||||||
assert graphene_type._meta.enum.__members__["EN"].value == "en"
|
assert graphene_type._meta.enum.__members__["EN"].value == "en"
|
||||||
|
@ -184,14 +186,14 @@ def test_field_with_callable_choices_convert_enum():
|
||||||
|
|
||||||
field = models.CharField(help_text="Language", choices=get_choices)
|
field = models.CharField(help_text="Language", choices=get_choices)
|
||||||
|
|
||||||
class CallableChoicesModel(models.Model):
|
class TranslatedModel(models.Model):
|
||||||
language = field
|
language = field
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
app_label = "test"
|
app_label = "test"
|
||||||
|
|
||||||
graphene_type = convert_django_field_with_choices(field).type.of_type
|
graphene_type = convert_django_field_with_choices(field).type.of_type
|
||||||
assert graphene_type._meta.name == "TestCallableChoicesModelLanguageChoices"
|
assert graphene_type._meta.name == "TestTranslatedModelLanguageChoices"
|
||||||
assert graphene_type._meta.enum.__members__["ES"].value == "es"
|
assert graphene_type._meta.enum.__members__["ES"].value == "es"
|
||||||
assert graphene_type._meta.enum.__members__["ES"].description == "Spanish"
|
assert graphene_type._meta.enum.__members__["ES"].description == "Spanish"
|
||||||
assert graphene_type._meta.enum.__members__["EN"].value == "en"
|
assert graphene_type._meta.enum.__members__["EN"].value == "en"
|
||||||
|
@ -441,102 +443,35 @@ def test_choice_enum_blank_value():
|
||||||
class ReporterType(DjangoObjectType):
|
class ReporterType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
fields = ("callable_choice",)
|
fields = (
|
||||||
|
"first_name",
|
||||||
|
"a_choice",
|
||||||
|
)
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
reporter = graphene.Field(ReporterType)
|
reporter = graphene.Field(ReporterType)
|
||||||
|
|
||||||
def resolve_reporter(root, info):
|
def resolve_reporter(root, info):
|
||||||
# return a model instance with blank choice field value
|
return Reporter.objects.first()
|
||||||
return Reporter(callable_choice="")
|
|
||||||
|
|
||||||
schema = graphene.Schema(query=Query)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
||||||
|
# Create model with empty choice option
|
||||||
|
Reporter.objects.create(
|
||||||
|
first_name="Bridget", last_name="Jones", email="bridget@example.com"
|
||||||
|
)
|
||||||
|
|
||||||
result = schema.execute(
|
result = schema.execute(
|
||||||
"""
|
"""
|
||||||
query {
|
query {
|
||||||
reporter {
|
reporter {
|
||||||
callableChoice
|
firstName
|
||||||
|
aChoice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
assert not result.errors
|
assert not result.errors
|
||||||
assert result.data == {
|
assert result.data == {
|
||||||
"reporter": {"callableChoice": None},
|
"reporter": {"firstName": "Bridget", "aChoice": None},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_typed_choice_value():
|
|
||||||
"""Test that typed choices fields are resolved correctly to the enum values"""
|
|
||||||
|
|
||||||
class ReporterType(DjangoObjectType):
|
|
||||||
class Meta:
|
|
||||||
model = Reporter
|
|
||||||
fields = ("typed_choice", "class_choice", "callable_choice")
|
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
|
||||||
reporter = graphene.Field(ReporterType)
|
|
||||||
|
|
||||||
def resolve_reporter(root, info):
|
|
||||||
# assign choice values to the fields instead of their str or int values
|
|
||||||
return Reporter(
|
|
||||||
typed_choice=TypedIntChoice.CHOICE_THIS,
|
|
||||||
class_choice=TypedIntChoice.CHOICE_THAT,
|
|
||||||
callable_choice=TypedStrChoice.CHOICE_THIS,
|
|
||||||
)
|
|
||||||
|
|
||||||
class CreateReporter(graphene.Mutation):
|
|
||||||
reporter = graphene.Field(ReporterType)
|
|
||||||
|
|
||||||
def mutate(root, info, **kwargs):
|
|
||||||
return CreateReporter(
|
|
||||||
reporter=Reporter(
|
|
||||||
typed_choice=TypedIntChoice.CHOICE_THIS,
|
|
||||||
class_choice=TypedIntChoice.CHOICE_THAT,
|
|
||||||
callable_choice=TypedStrChoice.CHOICE_THIS,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Mutation(graphene.ObjectType):
|
|
||||||
create_reporter = CreateReporter.Field()
|
|
||||||
|
|
||||||
schema = graphene.Schema(query=Query, mutation=Mutation)
|
|
||||||
|
|
||||||
reporter_fragment = """
|
|
||||||
fragment reporter on ReporterType {
|
|
||||||
typedChoice
|
|
||||||
classChoice
|
|
||||||
callableChoice
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
expected_reporter = {
|
|
||||||
"typedChoice": "A_1",
|
|
||||||
"classChoice": "A_2",
|
|
||||||
"callableChoice": "THIS",
|
|
||||||
}
|
|
||||||
|
|
||||||
result = schema.execute(
|
|
||||||
reporter_fragment
|
|
||||||
+ """
|
|
||||||
query {
|
|
||||||
reporter { ...reporter }
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
assert not result.errors
|
|
||||||
assert result.data["reporter"] == expected_reporter
|
|
||||||
|
|
||||||
result = schema.execute(
|
|
||||||
reporter_fragment
|
|
||||||
+ """
|
|
||||||
mutation {
|
|
||||||
createReporter {
|
|
||||||
reporter { ...reporter }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
assert not result.errors
|
|
||||||
assert result.data["createReporter"]["reporter"] == expected_reporter
|
|
||||||
|
|
|
@ -2,11 +2,12 @@ import datetime
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db.models import Count, Prefetch
|
from django.db.models import Count, Model, Prefetch
|
||||||
|
|
||||||
from graphene import List, NonNull, ObjectType, Schema, String
|
from graphene import List, NonNull, ObjectType, Schema, String
|
||||||
|
from graphene.relay import Node
|
||||||
|
|
||||||
from ..fields import DjangoListField
|
from ..fields import DjangoConnectionField, DjangoListField
|
||||||
from ..types import DjangoObjectType
|
from ..types import DjangoObjectType
|
||||||
from .models import (
|
from .models import (
|
||||||
Article as ArticleModel,
|
Article as ArticleModel,
|
||||||
|
@ -716,3 +717,34 @@ class TestDjangoListField:
|
||||||
r'SELECT .* FROM "tests_film" INNER JOIN "tests_film_reporters" .* LEFT OUTER JOIN "tests_filmdetails"',
|
r'SELECT .* FROM "tests_film" INNER JOIN "tests_film_reporters" .* LEFT OUTER JOIN "tests_filmdetails"',
|
||||||
captured.captured_queries[1]["sql"],
|
captured.captured_queries[1]["sql"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDjangoConnectionField:
|
||||||
|
def test_model_ordering_assertion(self):
|
||||||
|
class Chaos(Model):
|
||||||
|
class Meta:
|
||||||
|
app_label = "test"
|
||||||
|
|
||||||
|
class ChaosType(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = Chaos
|
||||||
|
interfaces = (Node,)
|
||||||
|
|
||||||
|
class Query(ObjectType):
|
||||||
|
chaos = DjangoConnectionField(ChaosType)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
TypeError,
|
||||||
|
match=r"Django model test\.Chaos has to have a default ordering to be used in a Connection\.",
|
||||||
|
):
|
||||||
|
Schema(query=Query)
|
||||||
|
|
||||||
|
def test_only_django_object_types(self):
|
||||||
|
class Query(ObjectType):
|
||||||
|
something = DjangoConnectionField(String)
|
||||||
|
|
||||||
|
with pytest.raises(
|
||||||
|
TypeError,
|
||||||
|
match="DjangoConnectionField only accepts DjangoObjectType types as underlying type",
|
||||||
|
):
|
||||||
|
Schema(query=Query)
|
||||||
|
|
|
@ -26,7 +26,6 @@ class TestShouldCallGetQuerySetOnForeignKey:
|
||||||
class ReporterType(DjangoObjectType):
|
class ReporterType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, queryset, info):
|
def get_queryset(cls, queryset, info):
|
||||||
|
@ -37,7 +36,6 @@ class TestShouldCallGetQuerySetOnForeignKey:
|
||||||
class ArticleType(DjangoObjectType):
|
class ArticleType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Article
|
model = Article
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, queryset, info):
|
def get_queryset(cls, queryset, info):
|
||||||
|
@ -202,7 +200,6 @@ class TestShouldCallGetQuerySetOnForeignKeyNode:
|
||||||
class ReporterType(DjangoObjectType):
|
class ReporterType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Reporter
|
model = Reporter
|
||||||
fields = "__all__"
|
|
||||||
interfaces = (Node,)
|
interfaces = (Node,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -214,7 +211,6 @@ class TestShouldCallGetQuerySetOnForeignKeyNode:
|
||||||
class ArticleType(DjangoObjectType):
|
class ArticleType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Article
|
model = Article
|
||||||
fields = "__all__"
|
|
||||||
interfaces = (Node,)
|
interfaces = (Node,)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -374,7 +370,6 @@ class TestShouldCallGetQuerySetOnOneToOne:
|
||||||
class FilmDetailsType(DjangoObjectType):
|
class FilmDetailsType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FilmDetails
|
model = FilmDetails
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, queryset, info):
|
def get_queryset(cls, queryset, info):
|
||||||
|
@ -385,7 +380,6 @@ class TestShouldCallGetQuerySetOnOneToOne:
|
||||||
class FilmType(DjangoObjectType):
|
class FilmType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Film
|
model = Film
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, queryset, info):
|
def get_queryset(cls, queryset, info):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import base64
|
import base64
|
||||||
import datetime
|
import datetime
|
||||||
from unittest.mock import ANY, Mock
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -2001,62 +2000,14 @@ def test_connection_should_succeed_if_last_higher_than_number_of_objects():
|
||||||
assert result.data == expected
|
assert result.data == expected
|
||||||
|
|
||||||
|
|
||||||
def test_connection_should_call_resolver_function():
|
|
||||||
resolver_mock = Mock(
|
|
||||||
name="resolver",
|
|
||||||
return_value=[
|
|
||||||
Reporter(first_name="Some", last_name="One"),
|
|
||||||
Reporter(first_name="John", last_name="Doe"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
class ReporterType(DjangoObjectType):
|
|
||||||
class Meta:
|
|
||||||
model = Reporter
|
|
||||||
fields = "__all__"
|
|
||||||
interfaces = [Node]
|
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
|
||||||
reporters = DjangoConnectionField(ReporterType, resolver=resolver_mock)
|
|
||||||
|
|
||||||
schema = graphene.Schema(query=Query)
|
|
||||||
result = schema.execute(
|
|
||||||
"""
|
|
||||||
query {
|
|
||||||
reporters {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
firstName
|
|
||||||
lastName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
resolver_mock.assert_called_once_with(None, ANY)
|
|
||||||
assert not result.errors
|
|
||||||
assert result.data == {
|
|
||||||
"reporters": {
|
|
||||||
"edges": [
|
|
||||||
{"node": {"firstName": "Some", "lastName": "One"}},
|
|
||||||
{"node": {"firstName": "John", "lastName": "Doe"}},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_should_query_nullable_foreign_key():
|
def test_should_query_nullable_foreign_key():
|
||||||
class PetType(DjangoObjectType):
|
class PetType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Pet
|
model = Pet
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
class PersonType(DjangoObjectType):
|
class PersonType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Person
|
model = Person
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
pet = graphene.Field(PetType, name=graphene.String(required=True))
|
pet = graphene.Field(PetType, name=graphene.String(required=True))
|
||||||
|
@ -2071,8 +2022,10 @@ def test_should_query_nullable_foreign_key():
|
||||||
schema = graphene.Schema(query=Query)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
||||||
person = Person.objects.create(name="Jane")
|
person = Person.objects.create(name="Jane")
|
||||||
Pet.objects.create(name="Stray dog", age=1)
|
[
|
||||||
Pet.objects.create(name="Jane's dog", owner=person, age=1)
|
Pet.objects.create(name="Stray dog", age=1),
|
||||||
|
Pet.objects.create(name="Jane's dog", owner=person, age=1),
|
||||||
|
]
|
||||||
|
|
||||||
query_pet = """
|
query_pet = """
|
||||||
query getPet($name: String!) {
|
query getPet($name: String!) {
|
||||||
|
@ -2115,7 +2068,6 @@ def test_should_query_nullable_one_to_one_relation_with_custom_resolver():
|
||||||
class FilmType(DjangoObjectType):
|
class FilmType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Film
|
model = Film
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, queryset, info):
|
def get_queryset(cls, queryset, info):
|
||||||
|
@ -2124,7 +2076,6 @@ def test_should_query_nullable_one_to_one_relation_with_custom_resolver():
|
||||||
class FilmDetailsType(DjangoObjectType):
|
class FilmDetailsType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = FilmDetails
|
model = FilmDetails
|
||||||
fields = "__all__"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_queryset(cls, queryset, info):
|
def get_queryset(cls, queryset, info):
|
||||||
|
|
|
@ -40,9 +40,6 @@ def test_should_map_fields_correctly():
|
||||||
"email",
|
"email",
|
||||||
"pets",
|
"pets",
|
||||||
"a_choice",
|
"a_choice",
|
||||||
"typed_choice",
|
|
||||||
"class_choice",
|
|
||||||
"callable_choice",
|
|
||||||
"fans",
|
"fans",
|
||||||
"reporter_type",
|
"reporter_type",
|
||||||
]
|
]
|
||||||
|
|
|
@ -77,9 +77,6 @@ def test_django_objecttype_map_correct_fields():
|
||||||
"email",
|
"email",
|
||||||
"pets",
|
"pets",
|
||||||
"a_choice",
|
"a_choice",
|
||||||
"typed_choice",
|
|
||||||
"class_choice",
|
|
||||||
"callable_choice",
|
|
||||||
"fans",
|
"fans",
|
||||||
"reporter_type",
|
"reporter_type",
|
||||||
]
|
]
|
||||||
|
@ -189,9 +186,6 @@ def test_schema_representation():
|
||||||
email: String!
|
email: String!
|
||||||
pets: [Reporter!]!
|
pets: [Reporter!]!
|
||||||
aChoice: TestsReporterAChoiceChoices
|
aChoice: TestsReporterAChoiceChoices
|
||||||
typedChoice: TestsReporterTypedChoiceChoices
|
|
||||||
classChoice: TestsReporterClassChoiceChoices
|
|
||||||
callableChoice: TestsReporterCallableChoiceChoices
|
|
||||||
reporterType: TestsReporterReporterTypeChoices
|
reporterType: TestsReporterReporterTypeChoices
|
||||||
articles(offset: Int, before: String, after: String, first: Int, last: Int): ArticleConnection!
|
articles(offset: Int, before: String, after: String, first: Int, last: Int): ArticleConnection!
|
||||||
}
|
}
|
||||||
|
@ -205,33 +199,6 @@ def test_schema_representation():
|
||||||
A_2
|
A_2
|
||||||
}
|
}
|
||||||
|
|
||||||
\"""An enumeration.\"""
|
|
||||||
enum TestsReporterTypedChoiceChoices {
|
|
||||||
\"""Choice This\"""
|
|
||||||
A_1
|
|
||||||
|
|
||||||
\"""Choice That\"""
|
|
||||||
A_2
|
|
||||||
}
|
|
||||||
|
|
||||||
\"""An enumeration.\"""
|
|
||||||
enum TestsReporterClassChoiceChoices {
|
|
||||||
\"""Choice This\"""
|
|
||||||
A_1
|
|
||||||
|
|
||||||
\"""Choice That\"""
|
|
||||||
A_2
|
|
||||||
}
|
|
||||||
|
|
||||||
\"""An enumeration.\"""
|
|
||||||
enum TestsReporterCallableChoiceChoices {
|
|
||||||
\"""Choice This\"""
|
|
||||||
THIS
|
|
||||||
|
|
||||||
\"""Choice That\"""
|
|
||||||
THAT
|
|
||||||
}
|
|
||||||
|
|
||||||
\"""An enumeration.\"""
|
\"""An enumeration.\"""
|
||||||
enum TestsReporterReporterTypeChoices {
|
enum TestsReporterReporterTypeChoices {
|
||||||
\"""Regular\"""
|
\"""Regular\"""
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import json
|
import json
|
||||||
from http import HTTPStatus
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -38,7 +37,7 @@ def jl(**kwargs):
|
||||||
|
|
||||||
def test_graphiql_is_enabled(client):
|
def test_graphiql_is_enabled(client):
|
||||||
response = client.get(url_string(), HTTP_ACCEPT="text/html")
|
response = client.get(url_string(), HTTP_ACCEPT="text/html")
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response["Content-Type"].split(";")[0] == "text/html"
|
assert response["Content-Type"].split(";")[0] == "text/html"
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ def test_qfactor_graphiql(client):
|
||||||
url_string(query="{test}"),
|
url_string(query="{test}"),
|
||||||
HTTP_ACCEPT="application/json;q=0.8, text/html;q=0.9",
|
HTTP_ACCEPT="application/json;q=0.8, text/html;q=0.9",
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response["Content-Type"].split(";")[0] == "text/html"
|
assert response["Content-Type"].split(";")[0] == "text/html"
|
||||||
|
|
||||||
|
|
||||||
|
@ -56,7 +55,7 @@ def test_qfactor_json(client):
|
||||||
url_string(query="{test}"),
|
url_string(query="{test}"),
|
||||||
HTTP_ACCEPT="text/html;q=0.8, application/json;q=0.9",
|
HTTP_ACCEPT="text/html;q=0.8, application/json;q=0.9",
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response["Content-Type"].split(";")[0] == "application/json"
|
assert response["Content-Type"].split(";")[0] == "application/json"
|
||||||
assert response_json(response) == {"data": {"test": "Hello World"}}
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ def test_qfactor_json(client):
|
||||||
def test_allows_get_with_query_param(client):
|
def test_allows_get_with_query_param(client):
|
||||||
response = client.get(url_string(query="{test}"))
|
response = client.get(url_string(query="{test}"))
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello World"}}
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ def test_allows_get_with_variable_values(client):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +94,7 @@ def test_allows_get_with_operation_name(client):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +103,7 @@ def test_allows_get_with_operation_name(client):
|
||||||
def test_reports_validation_errors(client):
|
def test_reports_validation_errors(client):
|
||||||
response = client.get(url_string(query="{ test, unknownOne, unknownTwo }"))
|
response = client.get(url_string(query="{ test, unknownOne, unknownTwo }"))
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [
|
"errors": [
|
||||||
{
|
{
|
||||||
|
@ -129,7 +128,7 @@ def test_errors_when_missing_operation_name(client):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [
|
"errors": [
|
||||||
{
|
{
|
||||||
|
@ -147,7 +146,7 @@ def test_errors_when_sending_a_mutation_via_get(client):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED
|
assert response.status_code == 405
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [
|
"errors": [
|
||||||
{"message": "Can only perform a mutation operation from a POST request."}
|
{"message": "Can only perform a mutation operation from a POST request."}
|
||||||
|
@ -166,7 +165,7 @@ def test_errors_when_selecting_a_mutation_within_a_get(client):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED
|
assert response.status_code == 405
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [
|
"errors": [
|
||||||
{"message": "Can only perform a mutation operation from a POST request."}
|
{"message": "Can only perform a mutation operation from a POST request."}
|
||||||
|
@ -185,14 +184,14 @@ def test_allows_mutation_to_exist_within_a_get(client):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello World"}}
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
||||||
|
|
||||||
|
|
||||||
def test_allows_post_with_json_encoding(client):
|
def test_allows_post_with_json_encoding(client):
|
||||||
response = client.post(url_string(), j(query="{test}"), "application/json")
|
response = client.post(url_string(), j(query="{test}"), "application/json")
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello World"}}
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -201,7 +200,7 @@ def test_batch_allows_post_with_json_encoding(client):
|
||||||
batch_url_string(), jl(id=1, query="{test}"), "application/json"
|
batch_url_string(), jl(id=1, query="{test}"), "application/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == [
|
assert response_json(response) == [
|
||||||
{"id": 1, "data": {"test": "Hello World"}, "status": 200}
|
{"id": 1, "data": {"test": "Hello World"}, "status": 200}
|
||||||
]
|
]
|
||||||
|
@ -210,7 +209,7 @@ def test_batch_allows_post_with_json_encoding(client):
|
||||||
def test_batch_fails_if_is_empty(client):
|
def test_batch_fails_if_is_empty(client):
|
||||||
response = client.post(batch_url_string(), "[]", "application/json")
|
response = client.post(batch_url_string(), "[]", "application/json")
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "Received an empty list in the batch request."}]
|
"errors": [{"message": "Received an empty list in the batch request."}]
|
||||||
}
|
}
|
||||||
|
@ -223,7 +222,7 @@ def test_allows_sending_a_mutation_via_post(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"writeTest": {"test": "Hello World"}}}
|
assert response_json(response) == {"data": {"writeTest": {"test": "Hello World"}}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +233,7 @@ def test_allows_post_with_url_encoding(client):
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello World"}}
|
assert response_json(response) == {"data": {"test": "Hello World"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -248,7 +247,7 @@ def test_supports_post_json_query_with_string_variables(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -263,7 +262,7 @@ def test_batch_supports_post_json_query_with_string_variables(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == [
|
assert response_json(response) == [
|
||||||
{"id": 1, "data": {"test": "Hello Dolly"}, "status": 200}
|
{"id": 1, "data": {"test": "Hello Dolly"}, "status": 200}
|
||||||
]
|
]
|
||||||
|
@ -279,7 +278,7 @@ def test_supports_post_json_query_with_json_variables(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -294,7 +293,7 @@ def test_batch_supports_post_json_query_with_json_variables(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == [
|
assert response_json(response) == [
|
||||||
{"id": 1, "data": {"test": "Hello Dolly"}, "status": 200}
|
{"id": 1, "data": {"test": "Hello Dolly"}, "status": 200}
|
||||||
]
|
]
|
||||||
|
@ -312,7 +311,7 @@ def test_supports_post_url_encoded_query_with_string_variables(client):
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -323,7 +322,7 @@ def test_supports_post_json_quey_with_get_variable_values(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -334,7 +333,7 @@ def test_post_url_encoded_query_with_get_variable_values(client):
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -345,7 +344,7 @@ def test_supports_post_raw_text_query_with_get_variable_values(client):
|
||||||
"application/graphql",
|
"application/graphql",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
assert response_json(response) == {"data": {"test": "Hello Dolly"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -366,7 +365,7 @@ def test_allows_post_with_operation_name(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
||||||
}
|
}
|
||||||
|
@ -390,7 +389,7 @@ def test_batch_allows_post_with_operation_name(client):
|
||||||
"application/json",
|
"application/json",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == [
|
assert response_json(response) == [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
|
@ -414,7 +413,7 @@ def test_allows_post_with_get_operation_name(client):
|
||||||
"application/graphql",
|
"application/graphql",
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
"data": {"test": "Hello World", "shared": "Hello Everyone"}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +430,7 @@ def test_inherited_class_with_attributes_works(client):
|
||||||
|
|
||||||
# Check graphiql works
|
# Check graphiql works
|
||||||
response = client.get(url_string(inherited_url), HTTP_ACCEPT="text/html")
|
response = client.get(url_string(inherited_url), HTTP_ACCEPT="text/html")
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.urls("graphene_django.tests.urls_pretty")
|
@pytest.mark.urls("graphene_django.tests.urls_pretty")
|
||||||
|
@ -453,7 +452,7 @@ def test_supports_pretty_printing_by_request(client):
|
||||||
|
|
||||||
def test_handles_field_errors_caught_by_graphql(client):
|
def test_handles_field_errors_caught_by_graphql(client):
|
||||||
response = client.get(url_string(query="{thrower}"))
|
response = client.get(url_string(query="{thrower}"))
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"data": None,
|
"data": None,
|
||||||
"errors": [
|
"errors": [
|
||||||
|
@ -468,7 +467,7 @@ def test_handles_field_errors_caught_by_graphql(client):
|
||||||
|
|
||||||
def test_handles_syntax_errors_caught_by_graphql(client):
|
def test_handles_syntax_errors_caught_by_graphql(client):
|
||||||
response = client.get(url_string(query="syntaxerror"))
|
response = client.get(url_string(query="syntaxerror"))
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [
|
"errors": [
|
||||||
{
|
{
|
||||||
|
@ -482,7 +481,7 @@ def test_handles_syntax_errors_caught_by_graphql(client):
|
||||||
def test_handles_errors_caused_by_a_lack_of_query(client):
|
def test_handles_errors_caused_by_a_lack_of_query(client):
|
||||||
response = client.get(url_string())
|
response = client.get(url_string())
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "Must provide query string."}]
|
"errors": [{"message": "Must provide query string."}]
|
||||||
}
|
}
|
||||||
|
@ -491,7 +490,7 @@ def test_handles_errors_caused_by_a_lack_of_query(client):
|
||||||
def test_handles_not_expected_json_bodies(client):
|
def test_handles_not_expected_json_bodies(client):
|
||||||
response = client.post(url_string(), "[]", "application/json")
|
response = client.post(url_string(), "[]", "application/json")
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "The received data is not a valid JSON query."}]
|
"errors": [{"message": "The received data is not a valid JSON query."}]
|
||||||
}
|
}
|
||||||
|
@ -500,7 +499,7 @@ def test_handles_not_expected_json_bodies(client):
|
||||||
def test_handles_invalid_json_bodies(client):
|
def test_handles_invalid_json_bodies(client):
|
||||||
response = client.post(url_string(), "[oh}", "application/json")
|
response = client.post(url_string(), "[oh}", "application/json")
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "POST body sent invalid JSON."}]
|
"errors": [{"message": "POST body sent invalid JSON."}]
|
||||||
}
|
}
|
||||||
|
@ -515,14 +514,14 @@ def test_handles_django_request_error(client, monkeypatch):
|
||||||
valid_json = json.dumps({"foo": "bar"})
|
valid_json = json.dumps({"foo": "bar"})
|
||||||
response = client.post(url_string(), valid_json, "application/json")
|
response = client.post(url_string(), valid_json, "application/json")
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {"errors": [{"message": "foo-bar"}]}
|
assert response_json(response) == {"errors": [{"message": "foo-bar"}]}
|
||||||
|
|
||||||
|
|
||||||
def test_handles_incomplete_json_bodies(client):
|
def test_handles_incomplete_json_bodies(client):
|
||||||
response = client.post(url_string(), '{"query":', "application/json")
|
response = client.post(url_string(), '{"query":', "application/json")
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "POST body sent invalid JSON."}]
|
"errors": [{"message": "POST body sent invalid JSON."}]
|
||||||
}
|
}
|
||||||
|
@ -534,7 +533,7 @@ def test_handles_plain_post_text(client):
|
||||||
"query helloWho($who: String){ test(who: $who) }",
|
"query helloWho($who: String){ test(who: $who) }",
|
||||||
"text/plain",
|
"text/plain",
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "Must provide query string."}]
|
"errors": [{"message": "Must provide query string."}]
|
||||||
}
|
}
|
||||||
|
@ -546,7 +545,7 @@ def test_handles_poorly_formed_variables(client):
|
||||||
query="query helloWho($who: String){ test(who: $who) }", variables="who:You"
|
query="query helloWho($who: String){ test(who: $who) }", variables="who:You"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "Variables are invalid JSON."}]
|
"errors": [{"message": "Variables are invalid JSON."}]
|
||||||
}
|
}
|
||||||
|
@ -554,7 +553,7 @@ def test_handles_poorly_formed_variables(client):
|
||||||
|
|
||||||
def test_handles_unsupported_http_methods(client):
|
def test_handles_unsupported_http_methods(client):
|
||||||
response = client.put(url_string(query="{test}"))
|
response = client.put(url_string(query="{test}"))
|
||||||
assert response.status_code == HTTPStatus.METHOD_NOT_ALLOWED
|
assert response.status_code == 405
|
||||||
assert response["Allow"] == "GET, POST"
|
assert response["Allow"] == "GET, POST"
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"errors": [{"message": "GraphQL only supports GET and POST requests."}]
|
"errors": [{"message": "GraphQL only supports GET and POST requests."}]
|
||||||
|
@ -564,7 +563,7 @@ def test_handles_unsupported_http_methods(client):
|
||||||
def test_passes_request_into_context_request(client):
|
def test_passes_request_into_context_request(client):
|
||||||
response = client.get(url_string(query="{request}", q="testing"))
|
response = client.get(url_string(query="{request}", q="testing"))
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
assert response_json(response) == {"data": {"request": "testing"}}
|
assert response_json(response) == {"data": {"request": "testing"}}
|
||||||
|
|
||||||
|
|
||||||
|
@ -858,7 +857,7 @@ def test_allow_introspection(client):
|
||||||
response = client.post(
|
response = client.post(
|
||||||
url_string("/graphql/", query="{__schema {queryType {name}}}")
|
url_string("/graphql/", query="{__schema {queryType {name}}}")
|
||||||
)
|
)
|
||||||
assert response.status_code == HTTPStatus.OK
|
assert response.status_code == 200
|
||||||
|
|
||||||
assert response_json(response) == {
|
assert response_json(response) == {
|
||||||
"data": {"__schema": {"queryType": {"name": "QueryRoot"}}}
|
"data": {"__schema": {"queryType": {"name": "QueryRoot"}}}
|
||||||
|
@ -870,7 +869,7 @@ def test_allow_introspection(client):
|
||||||
def test_validation_disallow_introspection(client, url):
|
def test_validation_disallow_introspection(client, url):
|
||||||
response = client.post(url_string(url, query="{__schema {queryType {name}}}"))
|
response = client.post(url_string(url, query="{__schema {queryType {name}}}"))
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
|
|
||||||
json_response = response_json(response)
|
json_response = response_json(response)
|
||||||
assert "data" not in json_response
|
assert "data" not in json_response
|
||||||
|
@ -889,7 +888,7 @@ def test_validation_disallow_introspection(client, url):
|
||||||
def test_within_max_validation_errors(client, url):
|
def test_within_max_validation_errors(client, url):
|
||||||
response = client.post(url_string(url, query=QUERY_WITH_TWO_INTROSPECTIONS))
|
response = client.post(url_string(url, query=QUERY_WITH_TWO_INTROSPECTIONS))
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
|
|
||||||
json_response = response_json(response)
|
json_response = response_json(response)
|
||||||
assert "data" not in json_response
|
assert "data" not in json_response
|
||||||
|
@ -914,7 +913,7 @@ def test_within_max_validation_errors(client, url):
|
||||||
def test_exceeds_max_validation_errors(client, url):
|
def test_exceeds_max_validation_errors(client, url):
|
||||||
response = client.post(url_string(url, query=QUERY_WITH_TWO_INTROSPECTIONS))
|
response = client.post(url_string(url, query=QUERY_WITH_TWO_INTROSPECTIONS))
|
||||||
|
|
||||||
assert response.status_code == HTTPStatus.BAD_REQUEST
|
assert response.status_code == 400
|
||||||
|
|
||||||
json_response = response_json(response)
|
json_response = response_json(response)
|
||||||
assert "data" not in json_response
|
assert "data" not in json_response
|
||||||
|
|
|
@ -111,7 +111,24 @@ def is_valid_django_model(model):
|
||||||
|
|
||||||
|
|
||||||
def import_single_dispatch():
|
def import_single_dispatch():
|
||||||
from functools import singledispatch
|
try:
|
||||||
|
from functools import singledispatch
|
||||||
|
except ImportError:
|
||||||
|
singledispatch = None
|
||||||
|
|
||||||
|
if not singledispatch:
|
||||||
|
try:
|
||||||
|
from singledispatch import singledispatch
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not singledispatch:
|
||||||
|
raise Exception(
|
||||||
|
"It seems your python version does not include "
|
||||||
|
"functools.singledispatch. Please install the 'singledispatch' "
|
||||||
|
"package. More information here: "
|
||||||
|
"https://pypi.python.org/pypi/singledispatch"
|
||||||
|
)
|
||||||
|
|
||||||
return singledispatch
|
return singledispatch
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,3 @@ omit = */tests/*
|
||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
DJANGO_SETTINGS_MODULE = examples.django_test_settings
|
DJANGO_SETTINGS_MODULE = examples.django_test_settings
|
||||||
addopts = --random-order
|
addopts = --random-order
|
||||||
filterwarnings =
|
|
||||||
error
|
|
||||||
# we can't do anything about the DeprecationWarning about typing.ByteString in graphql
|
|
||||||
default:'typing\.ByteString' is deprecated:DeprecationWarning:graphql\.pyutils\.is_iterable
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -55,8 +55,6 @@ setup(
|
||||||
"Framework :: Django :: 3.2",
|
"Framework :: Django :: 3.2",
|
||||||
"Framework :: Django :: 4.1",
|
"Framework :: Django :: 4.1",
|
||||||
"Framework :: Django :: 4.2",
|
"Framework :: Django :: 4.2",
|
||||||
"Framework :: Django :: 5.1",
|
|
||||||
"Framework :: Django :: 5.2",
|
|
||||||
],
|
],
|
||||||
keywords="api graphql protocol rest relay graphene",
|
keywords="api graphql protocol rest relay graphene",
|
||||||
packages=find_packages(exclude=["tests", "examples", "examples.*"]),
|
packages=find_packages(exclude=["tests", "examples", "examples.*"]),
|
||||||
|
|
7
tox.ini
7
tox.ini
|
@ -2,7 +2,8 @@
|
||||||
envlist =
|
envlist =
|
||||||
py{38,39,310}-django32
|
py{38,39,310}-django32
|
||||||
py{38,39}-django42
|
py{38,39}-django42
|
||||||
py{310,311,312}-django{42,50,51,main}
|
py{310,311}-django{42,50,main}
|
||||||
|
py312-django{42,50,main}
|
||||||
pre-commit
|
pre-commit
|
||||||
|
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
|
@ -18,8 +19,6 @@ DJANGO =
|
||||||
3.2: django32
|
3.2: django32
|
||||||
4.2: django42
|
4.2: django42
|
||||||
5.0: django50
|
5.0: django50
|
||||||
5.1: django51
|
|
||||||
5.2: django52
|
|
||||||
main: djangomain
|
main: djangomain
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
|
@ -34,8 +33,6 @@ deps =
|
||||||
django32: Django>=3.2,<4.0
|
django32: Django>=3.2,<4.0
|
||||||
django42: Django>=4.2,<4.3
|
django42: Django>=4.2,<4.3
|
||||||
django50: Django>=5.0,<5.1
|
django50: Django>=5.0,<5.1
|
||||||
django51: Django>=5.1,<5.2
|
|
||||||
django52: Django>=5.2,<6.0
|
|
||||||
djangomain: https://github.com/django/django/archive/main.zip
|
djangomain: https://github.com/django/django/archive/main.zip
|
||||||
commands = {posargs:pytest --cov=graphene_django graphene_django examples}
|
commands = {posargs:pytest --cov=graphene_django graphene_django examples}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user