From 00e27d6a66687a06f2fc12fc159f504ebe5ece5f Mon Sep 17 00:00:00 2001
From: removedporn <86824510+removedporn@users.noreply.github.com>
Date: Tue, 3 Aug 2021 18:20:18 +0800
Subject: [PATCH] Delete graphene_django directory
---
graphene_django/__init__.py | 11 -
graphene_django/compat.py | 25 -
graphene_django/conftest.py | 18 -
graphene_django/constants.py | 1 -
graphene_django/converter.py | 356 ----
graphene_django/debug/__init__.py | 4 -
graphene_django/debug/exception/__init__.py | 0
graphene_django/debug/exception/formating.py | 17 -
graphene_django/debug/exception/types.py | 10 -
graphene_django/debug/middleware.py | 71 -
graphene_django/debug/sql/__init__.py | 0
graphene_django/debug/sql/tracking.py | 169 --
graphene_django/debug/sql/types.py | 41 -
graphene_django/debug/tests/__init__.py | 0
graphene_django/debug/tests/test_query.py | 313 ----
graphene_django/debug/types.py | 14 -
graphene_django/fields.py | 249 ---
graphene_django/filter/__init__.py | 29 -
graphene_django/filter/fields.py | 109 --
graphene_django/filter/filters/__init__.py | 25 -
.../filter/filters/array_filter.py | 27 -
.../filter/filters/global_id_filter.py | 28 -
graphene_django/filter/filters/list_filter.py | 26 -
.../filter/filters/range_filter.py | 24 -
.../filter/filters/typed_filter.py | 27 -
graphene_django/filter/filterset.py | 51 -
graphene_django/filter/tests/__init__.py | 0
graphene_django/filter/tests/conftest.py | 151 --
graphene_django/filter/tests/filters.py | 30 -
.../tests/test_array_field_contains_filter.py | 87 -
.../tests/test_array_field_exact_filter.py | 130 --
.../tests/test_array_field_overlap_filter.py | 84 -
.../filter/tests/test_enum_filtering.py | 160 --
graphene_django/filter/tests/test_fields.py | 1296 --------------
.../filter/tests/test_in_filter.py | 448 -----
.../filter/tests/test_range_filter.py | 115 --
.../filter/tests/test_typed_filter.py | 157 --
graphene_django/filter/utils.py | 155 --
graphene_django/forms/__init__.py | 1 -
graphene_django/forms/converter.py | 99 -
graphene_django/forms/forms.py | 40 -
graphene_django/forms/mutation.py | 192 --
graphene_django/forms/tests/__init__.py | 0
graphene_django/forms/tests/test_converter.py | 121 --
graphene_django/forms/tests/test_mutation.py | 385 ----
graphene_django/forms/types.py | 1 -
graphene_django/management/__init__.py | 0
.../management/commands/__init__.py | 0
.../management/commands/graphql_schema.py | 115 --
graphene_django/registry.py | 43 -
graphene_django/rest_framework/__init__.py | 0
graphene_django/rest_framework/models.py | 16 -
graphene_django/rest_framework/mutation.py | 175 --
.../rest_framework/serializer_converter.py | 159 --
.../rest_framework/tests/__init__.py | 0
.../tests/test_field_converter.py | 220 ---
.../tests/test_multiple_model_serializers.py | 66 -
.../rest_framework/tests/test_mutation.py | 286 ---
graphene_django/rest_framework/types.py | 7 -
graphene_django/settings.py | 140 --
.../static/graphene_django/graphiql.js | 203 ---
.../templates/graphene/graphiql.html | 53 -
graphene_django/tests/__init__.py | 0
graphene_django/tests/forms.py | 16 -
graphene_django/tests/issues/__init__.py | 0
graphene_django/tests/issues/test_520.py | 44 -
graphene_django/tests/models.py | 119 --
graphene_django/tests/mutations.py | 18 -
graphene_django/tests/schema.py | 40 -
graphene_django/tests/schema_view.py | 32 -
graphene_django/tests/test_command.py | 58 -
graphene_django/tests/test_converter.py | 468 -----
graphene_django/tests/test_fields.py | 502 ------
graphene_django/tests/test_forms.py | 40 -
graphene_django/tests/test_query.py | 1595 -----------------
graphene_django/tests/test_schema.py | 55 -
graphene_django/tests/test_types.py | 691 -------
graphene_django/tests/test_utils.py | 88 -
graphene_django/tests/test_views.py | 834 ---------
graphene_django/tests/types.py | 9 -
graphene_django/tests/urls.py | 8 -
graphene_django/tests/urls_inherited.py | 13 -
graphene_django/tests/urls_pretty.py | 6 -
graphene_django/types.py | 305 ----
graphene_django/utils/__init__.py | 19 -
graphene_django/utils/str_converters.py | 6 -
graphene_django/utils/testing.py | 153 --
graphene_django/utils/tests/__init__.py | 0
.../utils/tests/test_str_converters.py | 9 -
graphene_django/utils/tests/test_testing.py | 45 -
graphene_django/utils/utils.py | 107 --
graphene_django/views.py | 398 ----
92 files changed, 12458 deletions(-)
delete mode 100644 graphene_django/__init__.py
delete mode 100644 graphene_django/compat.py
delete mode 100644 graphene_django/conftest.py
delete mode 100644 graphene_django/constants.py
delete mode 100644 graphene_django/converter.py
delete mode 100644 graphene_django/debug/__init__.py
delete mode 100644 graphene_django/debug/exception/__init__.py
delete mode 100644 graphene_django/debug/exception/formating.py
delete mode 100644 graphene_django/debug/exception/types.py
delete mode 100644 graphene_django/debug/middleware.py
delete mode 100644 graphene_django/debug/sql/__init__.py
delete mode 100644 graphene_django/debug/sql/tracking.py
delete mode 100644 graphene_django/debug/sql/types.py
delete mode 100644 graphene_django/debug/tests/__init__.py
delete mode 100644 graphene_django/debug/tests/test_query.py
delete mode 100644 graphene_django/debug/types.py
delete mode 100644 graphene_django/fields.py
delete mode 100644 graphene_django/filter/__init__.py
delete mode 100644 graphene_django/filter/fields.py
delete mode 100644 graphene_django/filter/filters/__init__.py
delete mode 100644 graphene_django/filter/filters/array_filter.py
delete mode 100644 graphene_django/filter/filters/global_id_filter.py
delete mode 100644 graphene_django/filter/filters/list_filter.py
delete mode 100644 graphene_django/filter/filters/range_filter.py
delete mode 100644 graphene_django/filter/filters/typed_filter.py
delete mode 100644 graphene_django/filter/filterset.py
delete mode 100644 graphene_django/filter/tests/__init__.py
delete mode 100644 graphene_django/filter/tests/conftest.py
delete mode 100644 graphene_django/filter/tests/filters.py
delete mode 100644 graphene_django/filter/tests/test_array_field_contains_filter.py
delete mode 100644 graphene_django/filter/tests/test_array_field_exact_filter.py
delete mode 100644 graphene_django/filter/tests/test_array_field_overlap_filter.py
delete mode 100644 graphene_django/filter/tests/test_enum_filtering.py
delete mode 100644 graphene_django/filter/tests/test_fields.py
delete mode 100644 graphene_django/filter/tests/test_in_filter.py
delete mode 100644 graphene_django/filter/tests/test_range_filter.py
delete mode 100644 graphene_django/filter/tests/test_typed_filter.py
delete mode 100644 graphene_django/filter/utils.py
delete mode 100644 graphene_django/forms/__init__.py
delete mode 100644 graphene_django/forms/converter.py
delete mode 100644 graphene_django/forms/forms.py
delete mode 100644 graphene_django/forms/mutation.py
delete mode 100644 graphene_django/forms/tests/__init__.py
delete mode 100644 graphene_django/forms/tests/test_converter.py
delete mode 100644 graphene_django/forms/tests/test_mutation.py
delete mode 100644 graphene_django/forms/types.py
delete mode 100644 graphene_django/management/__init__.py
delete mode 100644 graphene_django/management/commands/__init__.py
delete mode 100644 graphene_django/management/commands/graphql_schema.py
delete mode 100644 graphene_django/registry.py
delete mode 100644 graphene_django/rest_framework/__init__.py
delete mode 100644 graphene_django/rest_framework/models.py
delete mode 100644 graphene_django/rest_framework/mutation.py
delete mode 100644 graphene_django/rest_framework/serializer_converter.py
delete mode 100644 graphene_django/rest_framework/tests/__init__.py
delete mode 100644 graphene_django/rest_framework/tests/test_field_converter.py
delete mode 100644 graphene_django/rest_framework/tests/test_multiple_model_serializers.py
delete mode 100644 graphene_django/rest_framework/tests/test_mutation.py
delete mode 100644 graphene_django/rest_framework/types.py
delete mode 100644 graphene_django/settings.py
delete mode 100644 graphene_django/static/graphene_django/graphiql.js
delete mode 100644 graphene_django/templates/graphene/graphiql.html
delete mode 100644 graphene_django/tests/__init__.py
delete mode 100644 graphene_django/tests/forms.py
delete mode 100644 graphene_django/tests/issues/__init__.py
delete mode 100644 graphene_django/tests/issues/test_520.py
delete mode 100644 graphene_django/tests/models.py
delete mode 100644 graphene_django/tests/mutations.py
delete mode 100644 graphene_django/tests/schema.py
delete mode 100644 graphene_django/tests/schema_view.py
delete mode 100644 graphene_django/tests/test_command.py
delete mode 100644 graphene_django/tests/test_converter.py
delete mode 100644 graphene_django/tests/test_fields.py
delete mode 100644 graphene_django/tests/test_forms.py
delete mode 100644 graphene_django/tests/test_query.py
delete mode 100644 graphene_django/tests/test_schema.py
delete mode 100644 graphene_django/tests/test_types.py
delete mode 100644 graphene_django/tests/test_utils.py
delete mode 100644 graphene_django/tests/test_views.py
delete mode 100644 graphene_django/tests/types.py
delete mode 100644 graphene_django/tests/urls.py
delete mode 100644 graphene_django/tests/urls_inherited.py
delete mode 100644 graphene_django/tests/urls_pretty.py
delete mode 100644 graphene_django/types.py
delete mode 100644 graphene_django/utils/__init__.py
delete mode 100644 graphene_django/utils/str_converters.py
delete mode 100644 graphene_django/utils/testing.py
delete mode 100644 graphene_django/utils/tests/__init__.py
delete mode 100644 graphene_django/utils/tests/test_str_converters.py
delete mode 100644 graphene_django/utils/tests/test_testing.py
delete mode 100644 graphene_django/utils/utils.py
delete mode 100644 graphene_django/views.py
diff --git a/graphene_django/__init__.py b/graphene_django/__init__.py
deleted file mode 100644
index 999f3de..0000000
--- a/graphene_django/__init__.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from .fields import DjangoConnectionField, DjangoListField
-from .types import DjangoObjectType
-
-__version__ = "3.0.0b7"
-
-__all__ = [
- "__version__",
- "DjangoObjectType",
- "DjangoListField",
- "DjangoConnectionField",
-]
diff --git a/graphene_django/compat.py b/graphene_django/compat.py
deleted file mode 100644
index 1956786..0000000
--- a/graphene_django/compat.py
+++ /dev/null
@@ -1,25 +0,0 @@
-class MissingType(object):
- def __init__(self, *args, **kwargs):
- pass
-
-
-try:
- # Postgres fields are only available in Django with psycopg2 installed
- # and we cannot have psycopg2 on PyPy
- from django.contrib.postgres.fields import (
- IntegerRangeField,
- ArrayField,
- HStoreField,
- JSONField as PGJSONField,
- RangeField,
- )
-except ImportError:
- IntegerRangeField, ArrayField, HStoreField, PGJSONField, RangeField = (
- MissingType,
- ) * 5
-
-try:
- # JSONField is only available from Django 3.1
- from django.db.models import JSONField
-except ImportError:
- JSONField = MissingType
diff --git a/graphene_django/conftest.py b/graphene_django/conftest.py
deleted file mode 100644
index 509a84c..0000000
--- a/graphene_django/conftest.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import pytest
-
-from graphene_django.settings import graphene_settings as gsettings
-
-from .registry import reset_global_registry
-
-
-@pytest.fixture(autouse=True)
-def reset_registry_fixture(db):
- yield None
- reset_global_registry()
-
-
-@pytest.fixture()
-def graphene_settings():
- settings = dict(gsettings.__dict__)
- yield gsettings
- gsettings.__dict__ = settings
diff --git a/graphene_django/constants.py b/graphene_django/constants.py
deleted file mode 100644
index e4d7ea9..0000000
--- a/graphene_django/constants.py
+++ /dev/null
@@ -1 +0,0 @@
-MUTATION_ERRORS_FLAG = "graphene_mutation_has_errors"
diff --git a/graphene_django/converter.py b/graphene_django/converter.py
deleted file mode 100644
index c243e82..0000000
--- a/graphene_django/converter.py
+++ /dev/null
@@ -1,356 +0,0 @@
-from collections import OrderedDict
-from functools import singledispatch, wraps
-
-from django.db import models
-from django.utils.encoding import force_str
-from django.utils.functional import Promise
-from django.utils.module_loading import import_string
-
-from graphene import (
- ID,
- UUID,
- Boolean,
- Date,
- DateTime,
- Dynamic,
- Enum,
- Field,
- Float,
- Int,
- List,
- NonNull,
- String,
- Time,
- Decimal,
-)
-from graphene.types.json import JSONString
-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, PGJSONField, RangeField
-from .fields import DjangoListField, DjangoConnectionField
-from .settings import graphene_settings
-from .utils.str_converters import to_const
-
-
-class BlankValueField(Field):
- def wrap_resolve(self, parent_resolver):
- resolver = self.resolver or parent_resolver
-
- # create custom resolver
- def blank_field_wrapper(func):
- @wraps(func)
- def wrapped_resolver(*args, **kwargs):
- return_value = func(*args, **kwargs)
- if return_value == "":
- return None
- return return_value
-
- return wrapped_resolver
-
- return blank_field_wrapper(resolver)
-
-
-def convert_choice_name(name):
- name = to_const(force_str(name))
- try:
- assert_valid_name(name)
- except GraphQLError:
- name = "A_%s" % name
- return name
-
-
-def get_choices(choices):
- converted_names = []
- if isinstance(choices, OrderedDict):
- choices = choices.items()
- for value, help_text in choices:
- if isinstance(help_text, (tuple, list)):
- for choice in get_choices(help_text):
- yield choice
- else:
- name = convert_choice_name(value)
- while name in converted_names:
- name += "_" + str(len(converted_names))
- converted_names.append(name)
- description = str(
- help_text
- ) # TODO: translatable description: https://github.com/graphql-python/graphql-core-next/issues/58
- yield name, value, description
-
-
-def convert_choices_to_named_enum_with_descriptions(name, choices):
- choices = list(get_choices(choices))
- named_choices = [(c[0], c[1]) for c in choices]
- named_choices_descriptions = {c[0]: c[2] for c in choices}
-
- class EnumWithDescriptionsType(object):
- @property
- def description(self):
- return str(named_choices_descriptions[self.name])
-
- return_type = Enum(name, list(named_choices), type=EnumWithDescriptionsType)
- return return_type
-
-
-def generate_enum_name(django_model_meta, field):
- if graphene_settings.DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME:
- # Try and import custom function
- custom_func = import_string(
- graphene_settings.DJANGO_CHOICE_FIELD_ENUM_CUSTOM_NAME
- )
- name = custom_func(field)
- elif graphene_settings.DJANGO_CHOICE_FIELD_ENUM_V2_NAMING is True:
- name = to_camel_case("{}_{}".format(django_model_meta.object_name, field.name))
- else:
- name = "{app_label}{object_name}{field_name}Choices".format(
- app_label=to_camel_case(django_model_meta.app_label.title()),
- object_name=django_model_meta.object_name,
- field_name=to_camel_case(field.name.title()),
- )
- return name
-
-
-def convert_choice_field_to_enum(field, name=None):
- if name is None:
- name = generate_enum_name(field.model._meta, field)
- choices = field.choices
- return convert_choices_to_named_enum_with_descriptions(name, choices)
-
-
-def convert_django_field_with_choices(
- field, registry=None, convert_choices_to_enum=True
-):
- if registry is not None:
- converted = registry.get_converted_field(field)
- if converted:
- return converted
- choices = getattr(field, "choices", None)
- if choices and convert_choices_to_enum:
- EnumCls = convert_choice_field_to_enum(field)
- required = not (field.blank or field.null)
-
- converted = EnumCls(
- description=get_django_field_description(field), required=required
- ).mount_as(BlankValueField)
- else:
- converted = convert_django_field(field, registry)
- if registry is not None:
- registry.register_converted_field(field, converted)
- return converted
-
-
-def get_django_field_description(field):
- return str(field.help_text) if field.help_text else None
-
-
-@singledispatch
-def convert_django_field(field, registry=None):
- raise Exception(
- "Don't know how to convert the Django field %s (%s)" % (field, field.__class__)
- )
-
-
-@convert_django_field.register(models.CharField)
-@convert_django_field.register(models.TextField)
-@convert_django_field.register(models.EmailField)
-@convert_django_field.register(models.SlugField)
-@convert_django_field.register(models.URLField)
-@convert_django_field.register(models.GenericIPAddressField)
-@convert_django_field.register(models.FileField)
-@convert_django_field.register(models.FilePathField)
-def convert_field_to_string(field, registry=None):
- return String(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.BigAutoField)
-@convert_django_field.register(models.AutoField)
-def convert_field_to_id(field, registry=None):
- 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)
-def convert_field_to_uuid(field, registry=None):
- return UUID(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.PositiveIntegerField)
-@convert_django_field.register(models.PositiveSmallIntegerField)
-@convert_django_field.register(models.SmallIntegerField)
-@convert_django_field.register(models.BigIntegerField)
-@convert_django_field.register(models.IntegerField)
-def convert_field_to_int(field, registry=None):
- return Int(description=get_django_field_description(field), required=not field.null)
-
-
-@convert_django_field.register(models.NullBooleanField)
-@convert_django_field.register(models.BooleanField)
-def convert_field_to_boolean(field, registry=None):
- return Boolean(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.DecimalField)
-def convert_field_to_decimal(field, registry=None):
- return Decimal(description=field.help_text, required=not field.null)
-
-
-@convert_django_field.register(models.FloatField)
-@convert_django_field.register(models.DurationField)
-def convert_field_to_float(field, registry=None):
- return Float(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.DateTimeField)
-def convert_datetime_to_string(field, registry=None):
- return DateTime(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.DateField)
-def convert_date_to_string(field, registry=None):
- return Date(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.TimeField)
-def convert_time_to_string(field, registry=None):
- return Time(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(models.OneToOneRel)
-def convert_onetoone_field_to_djangomodel(field, registry=None):
- model = field.related_model
-
- def dynamic_type():
- _type = registry.get_type_for_model(model)
- if not _type:
- return
-
- return Field(_type, required=not field.null)
-
- return Dynamic(dynamic_type)
-
-
-@convert_django_field.register(models.ManyToManyField)
-@convert_django_field.register(models.ManyToManyRel)
-@convert_django_field.register(models.ManyToOneRel)
-def convert_field_to_list_or_connection(field, registry=None):
- model = field.related_model
-
- def dynamic_type():
- _type = registry.get_type_for_model(model)
- if not _type:
- return
-
- if isinstance(field, models.ManyToManyField):
- description = get_django_field_description(field)
- else:
- description = get_django_field_description(field.field)
-
- # If there is a connection, we should transform the field
- # into a DjangoConnectionField
- if _type._meta.connection:
- # Use a DjangoFilterConnectionField if there are
- # defined filter_fields or a filterset_class in the
- # DjangoObjectType Meta
- if _type._meta.filter_fields or _type._meta.filterset_class:
- from .filter.fields import DjangoFilterConnectionField
-
- return DjangoFilterConnectionField(
- _type, required=True, description=description
- )
-
- return DjangoConnectionField(_type, required=True, description=description)
-
- return DjangoListField(
- _type,
- required=True, # A Set is always returned, never None.
- description=description,
- )
-
- return Dynamic(dynamic_type)
-
-
-@convert_django_field.register(models.OneToOneField)
-@convert_django_field.register(models.ForeignKey)
-def convert_field_to_djangomodel(field, registry=None):
- model = field.related_model
-
- def dynamic_type():
- _type = registry.get_type_for_model(model)
- if not _type:
- return
-
- return Field(
- _type,
- description=get_django_field_description(field),
- required=not field.null,
- )
-
- return Dynamic(dynamic_type)
-
-
-@convert_django_field.register(ArrayField)
-def convert_postgres_array_to_list(field, registry=None):
- inner_type = convert_django_field(field.base_field)
- if not isinstance(inner_type, (List, NonNull)):
- inner_type = (
- NonNull(type(inner_type))
- if inner_type.kwargs["required"]
- else type(inner_type)
- )
- return List(
- inner_type,
- description=get_django_field_description(field),
- required=not field.null,
- )
-
-
-@convert_django_field.register(HStoreField)
-@convert_django_field.register(PGJSONField)
-@convert_django_field.register(JSONField)
-def convert_pg_and_json_field_to_string(field, registry=None):
- return JSONString(
- description=get_django_field_description(field), required=not field.null
- )
-
-
-@convert_django_field.register(RangeField)
-def convert_postgres_range_to_string(field, registry=None):
- inner_type = convert_django_field(field.base_field)
- if not isinstance(inner_type, (List, NonNull)):
- inner_type = (
- NonNull(type(inner_type))
- if inner_type.kwargs["required"]
- else type(inner_type)
- )
- return List(
- inner_type,
- description=get_django_field_description(field),
- required=not field.null,
- )
-
-
-# Register Django lazy()-wrapped values as GraphQL description/help_text.
-# This is needed for using lazy translations, see https://github.com/graphql-python/graphql-core-next/issues/58.
-register_description(Promise)
diff --git a/graphene_django/debug/__init__.py b/graphene_django/debug/__init__.py
deleted file mode 100644
index 3e078da..0000000
--- a/graphene_django/debug/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .middleware import DjangoDebugMiddleware
-from .types import DjangoDebug
-
-__all__ = ["DjangoDebugMiddleware", "DjangoDebug"]
diff --git a/graphene_django/debug/exception/__init__.py b/graphene_django/debug/exception/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/debug/exception/formating.py b/graphene_django/debug/exception/formating.py
deleted file mode 100644
index ed7ebab..0000000
--- a/graphene_django/debug/exception/formating.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import traceback
-
-from django.utils.encoding import force_str
-
-from .types import DjangoDebugException
-
-
-def wrap_exception(exception):
- return DjangoDebugException(
- message=force_str(exception),
- exc_type=force_str(type(exception)),
- stack="".join(
- traceback.format_exception(
- etype=type(exception), value=exception, tb=exception.__traceback__
- )
- ),
- )
diff --git a/graphene_django/debug/exception/types.py b/graphene_django/debug/exception/types.py
deleted file mode 100644
index 3484ccb..0000000
--- a/graphene_django/debug/exception/types.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from graphene import ObjectType, String
-
-
-class DjangoDebugException(ObjectType):
- class Meta:
- description = "Represents a single exception raised."
-
- exc_type = String(required=True, description="The class of the exception")
- message = String(required=True, description="The message of the exception")
- stack = String(required=True, description="The stack trace")
diff --git a/graphene_django/debug/middleware.py b/graphene_django/debug/middleware.py
deleted file mode 100644
index 804e7c8..0000000
--- a/graphene_django/debug/middleware.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from django.db import connections
-
-from promise import Promise
-
-from .sql.tracking import unwrap_cursor, wrap_cursor
-from .exception.formating import wrap_exception
-from .types import DjangoDebug
-
-
-class DjangoDebugContext(object):
- def __init__(self):
- self.debug_promise = None
- self.promises = []
- self.object = DjangoDebug(sql=[], exceptions=[])
- self.enable_instrumentation()
-
- def get_debug_promise(self):
- if not self.debug_promise:
- self.debug_promise = Promise.all(self.promises)
- self.promises = []
- return self.debug_promise.then(self.on_resolve_all_promises).get()
-
- def on_resolve_error(self, value):
- if hasattr(self, "object"):
- self.object.exceptions.append(wrap_exception(value))
- return Promise.reject(value)
-
- def on_resolve_all_promises(self, values):
- if self.promises:
- self.debug_promise = None
- return self.get_debug_promise()
- self.disable_instrumentation()
- return self.object
-
- def add_promise(self, promise):
- if self.debug_promise:
- self.promises.append(promise)
-
- def enable_instrumentation(self):
- # This is thread-safe because database connections are thread-local.
- for connection in connections.all():
- wrap_cursor(connection, self)
-
- def disable_instrumentation(self):
- for connection in connections.all():
- unwrap_cursor(connection)
-
-
-class DjangoDebugMiddleware(object):
- def resolve(self, next, root, info, **args):
- context = info.context
- django_debug = getattr(context, "django_debug", None)
- if not django_debug:
- if context is None:
- raise Exception("DjangoDebug cannot be executed in None contexts")
- try:
- context.django_debug = DjangoDebugContext()
- except Exception:
- raise Exception(
- "DjangoDebug need the context to be writable, context received: {}.".format(
- context.__class__.__name__
- )
- )
- if info.schema.get_type("DjangoDebug") == info.return_type:
- return context.django_debug.get_debug_promise()
- try:
- promise = next(root, info, **args)
- except Exception as e:
- return context.django_debug.on_resolve_error(e)
- context.django_debug.add_promise(promise)
- return promise
diff --git a/graphene_django/debug/sql/__init__.py b/graphene_django/debug/sql/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/debug/sql/tracking.py b/graphene_django/debug/sql/tracking.py
deleted file mode 100644
index f7346e6..0000000
--- a/graphene_django/debug/sql/tracking.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# Code obtained from django-debug-toolbar sql panel tracking
-from __future__ import absolute_import, unicode_literals
-
-import json
-from threading import local
-from time import time
-
-from django.utils.encoding import force_str
-
-from .types import DjangoDebugSQL
-
-
-class SQLQueryTriggered(Exception):
- """Thrown when template panel triggers a query"""
-
-
-class ThreadLocalState(local):
- def __init__(self):
- self.enabled = True
-
- @property
- def Wrapper(self):
- if self.enabled:
- return NormalCursorWrapper
- return ExceptionCursorWrapper
-
- def recording(self, v):
- self.enabled = v
-
-
-state = ThreadLocalState()
-recording = state.recording # export function
-
-
-def wrap_cursor(connection, panel):
- if not hasattr(connection, "_graphene_cursor"):
- connection._graphene_cursor = connection.cursor
-
- def cursor():
- return state.Wrapper(connection._graphene_cursor(), connection, panel)
-
- connection.cursor = cursor
- return cursor
-
-
-def unwrap_cursor(connection):
- if hasattr(connection, "_graphene_cursor"):
- previous_cursor = connection._graphene_cursor
- connection.cursor = previous_cursor
- del connection._graphene_cursor
-
-
-class ExceptionCursorWrapper(object):
- """
- Wraps a cursor and raises an exception on any operation.
- Used in Templates panel.
- """
-
- def __init__(self, cursor, db, logger):
- pass
-
- def __getattr__(self, attr):
- raise SQLQueryTriggered()
-
-
-class NormalCursorWrapper(object):
- """
- Wraps a cursor and logs queries.
- """
-
- def __init__(self, cursor, db, logger):
- self.cursor = cursor
- # Instance of a BaseDatabaseWrapper subclass
- self.db = db
- # logger must implement a ``record`` method
- self.logger = logger
-
- def _quote_expr(self, element):
- if isinstance(element, str):
- return "'%s'" % force_str(element).replace("'", "''")
- else:
- return repr(element)
-
- def _quote_params(self, params):
- if not params:
- return params
- if isinstance(params, dict):
- return dict((key, self._quote_expr(value)) for key, value in params.items())
- return list(map(self._quote_expr, params))
-
- def _decode(self, param):
- try:
- return force_str(param, strings_only=True)
- except UnicodeDecodeError:
- return "(encoded string)"
-
- def _record(self, method, sql, params):
- start_time = time()
- try:
- return method(sql, params)
- finally:
- stop_time = time()
- duration = stop_time - start_time
- _params = ""
- try:
- _params = json.dumps(list(map(self._decode, params)))
- except Exception:
- pass # object not JSON serializable
-
- alias = getattr(self.db, "alias", "default")
- conn = self.db.connection
- vendor = getattr(conn, "vendor", "unknown")
-
- params = {
- "vendor": vendor,
- "alias": alias,
- "sql": self.db.ops.last_executed_query(
- self.cursor, sql, self._quote_params(params)
- ),
- "duration": duration,
- "raw_sql": sql,
- "params": _params,
- "start_time": start_time,
- "stop_time": stop_time,
- "is_slow": duration > 10,
- "is_select": sql.lower().strip().startswith("select"),
- }
-
- if vendor == "postgresql":
- # If an erroneous query was ran on the connection, it might
- # be in a state where checking isolation_level raises an
- # exception.
- try:
- iso_level = conn.isolation_level
- except conn.InternalError:
- iso_level = "unknown"
- params.update(
- {
- "trans_id": self.logger.get_transaction_id(alias),
- "trans_status": conn.get_transaction_status(),
- "iso_level": iso_level,
- "encoding": conn.encoding,
- }
- )
-
- _sql = DjangoDebugSQL(**params)
- # We keep `sql` to maintain backwards compatibility
- self.logger.object.sql.append(_sql)
-
- def callproc(self, procname, params=None):
- return self._record(self.cursor.callproc, procname, params)
-
- def execute(self, sql, params=None):
- return self._record(self.cursor.execute, sql, params)
-
- def executemany(self, sql, param_list):
- return self._record(self.cursor.executemany, sql, param_list)
-
- def __getattr__(self, attr):
- return getattr(self.cursor, attr)
-
- def __iter__(self):
- return iter(self.cursor)
-
- def __enter__(self):
- return self
-
- def __exit__(self, type, value, traceback):
- self.close()
diff --git a/graphene_django/debug/sql/types.py b/graphene_django/debug/sql/types.py
deleted file mode 100644
index eeef482..0000000
--- a/graphene_django/debug/sql/types.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from graphene import Boolean, Float, ObjectType, String
-
-
-class DjangoDebugSQL(ObjectType):
- class Meta:
- description = "Represents a single database query made to a Django managed DB."
-
- vendor = String(
- required=True,
- description=(
- "The type of database being used (e.g. postrgesql, mysql, sqlite)."
- ),
- )
- alias = String(
- required=True, description="The Django database alias (e.g. 'default')."
- )
- sql = String(description="The actual SQL sent to this database.")
- duration = Float(
- required=True, description="Duration of this database query in seconds."
- )
- raw_sql = String(
- required=True, description="The raw SQL of this query, without params."
- )
- params = String(
- required=True, description="JSON encoded database query parameters."
- )
- start_time = Float(required=True, description="Start time of this database query.")
- stop_time = Float(required=True, description="Stop time of this database query.")
- is_slow = Boolean(
- required=True,
- description="Whether this database query took more than 10 seconds.",
- )
- is_select = Boolean(
- required=True, description="Whether this database query was a SELECT."
- )
-
- # Postgres
- trans_id = String(description="Postgres transaction ID if available.")
- trans_status = String(description="Postgres transaction status if available.")
- iso_level = String(description="Postgres isolation level if available.")
- encoding = String(description="Postgres connection encoding if available.")
diff --git a/graphene_django/debug/tests/__init__.py b/graphene_django/debug/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/debug/tests/test_query.py b/graphene_django/debug/tests/test_query.py
deleted file mode 100644
index eae94dc..0000000
--- a/graphene_django/debug/tests/test_query.py
+++ /dev/null
@@ -1,313 +0,0 @@
-import graphene
-import pytest
-from graphene.relay import Node
-from graphene_django import DjangoConnectionField, DjangoObjectType
-
-from ...tests.models import Reporter
-from ..middleware import DjangoDebugMiddleware
-from ..types import DjangoDebug
-
-
-class context(object):
- pass
-
-
-def test_should_query_field():
- r1 = Reporter(last_name="ABA")
- r1.save()
- r2 = Reporter(last_name="Griffin")
- r2.save()
-
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(graphene.ObjectType):
- reporter = graphene.Field(ReporterType)
- debug = graphene.Field(DjangoDebug, name="_debug")
-
- def resolve_reporter(self, info, **args):
- return Reporter.objects.first()
-
- query = """
- query ReporterQuery {
- reporter {
- lastName
- }
- _debug {
- sql {
- rawSql
- }
- }
- }
- """
- expected = {
- "reporter": {"lastName": "ABA"},
- "_debug": {"sql": [{"rawSql": str(Reporter.objects.order_by("pk")[:1].query)}]},
- }
- schema = graphene.Schema(query=Query)
- result = schema.execute(
- query, context_value=context(), middleware=[DjangoDebugMiddleware()]
- )
- assert not result.errors
- assert result.data == expected
-
-
-@pytest.mark.parametrize("max_limit", [None, 100])
-def test_should_query_nested_field(graphene_settings, max_limit):
- graphene_settings.RELAY_CONNECTION_MAX_LIMIT = max_limit
-
- r1 = Reporter(last_name="ABA")
- r1.save()
- r2 = Reporter(last_name="Griffin")
- r2.save()
- r2.pets.add(r1)
- r1.pets.add(r2)
-
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(graphene.ObjectType):
- reporter = graphene.Field(ReporterType)
- debug = graphene.Field(DjangoDebug, name="_debug")
-
- def resolve_reporter(self, info, **args):
- return Reporter.objects.first()
-
- query = """
- query ReporterQuery {
- reporter {
- lastName
- pets { edges { node {
- lastName
- pets { edges { node { lastName } } }
- } } }
- }
- _debug {
- sql {
- rawSql
- }
- }
- }
- """
- expected = {
- "reporter": {
- "lastName": "ABA",
- "pets": {
- "edges": [
- {
- "node": {
- "lastName": "Griffin",
- "pets": {"edges": [{"node": {"lastName": "ABA"}}]},
- }
- }
- ]
- },
- }
- }
- schema = graphene.Schema(query=Query)
- result = schema.execute(
- query, context_value=context(), middleware=[DjangoDebugMiddleware()]
- )
- assert not result.errors
- query = str(Reporter.objects.order_by("pk")[:1].query)
- assert result.data["_debug"]["sql"][0]["rawSql"] == query
- assert "COUNT" in result.data["_debug"]["sql"][1]["rawSql"]
- assert "tests_reporter_pets" in result.data["_debug"]["sql"][2]["rawSql"]
- assert "COUNT" in result.data["_debug"]["sql"][3]["rawSql"]
- assert "tests_reporter_pets" in result.data["_debug"]["sql"][4]["rawSql"]
- assert len(result.data["_debug"]["sql"]) == 5
-
- assert result.data["reporter"] == expected["reporter"]
-
-
-def test_should_query_list():
- r1 = Reporter(last_name="ABA")
- r1.save()
- r2 = Reporter(last_name="Griffin")
- r2.save()
-
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(graphene.ObjectType):
- all_reporters = graphene.List(ReporterType)
- debug = graphene.Field(DjangoDebug, name="_debug")
-
- def resolve_all_reporters(self, info, **args):
- return Reporter.objects.all()
-
- query = """
- query ReporterQuery {
- allReporters {
- lastName
- }
- _debug {
- sql {
- rawSql
- }
- }
- }
- """
- expected = {
- "allReporters": [{"lastName": "ABA"}, {"lastName": "Griffin"}],
- "_debug": {"sql": [{"rawSql": str(Reporter.objects.all().query)}]},
- }
- schema = graphene.Schema(query=Query)
- result = schema.execute(
- query, context_value=context(), middleware=[DjangoDebugMiddleware()]
- )
- assert not result.errors
- assert result.data == expected
-
-
-@pytest.mark.parametrize("max_limit", [None, 100])
-def test_should_query_connection(graphene_settings, max_limit):
- graphene_settings.RELAY_CONNECTION_MAX_LIMIT = max_limit
-
- r1 = Reporter(last_name="ABA")
- r1.save()
- r2 = Reporter(last_name="Griffin")
- r2.save()
-
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(graphene.ObjectType):
- all_reporters = DjangoConnectionField(ReporterType)
- debug = graphene.Field(DjangoDebug, name="_debug")
-
- def resolve_all_reporters(self, info, **args):
- return Reporter.objects.all()
-
- query = """
- query ReporterQuery {
- allReporters(first:1) {
- edges {
- node {
- lastName
- }
- }
- }
- _debug {
- sql {
- rawSql
- }
- }
- }
- """
- expected = {"allReporters": {"edges": [{"node": {"lastName": "ABA"}}]}}
- schema = graphene.Schema(query=Query)
- result = schema.execute(
- query, context_value=context(), middleware=[DjangoDebugMiddleware()]
- )
- assert not result.errors
- assert result.data["allReporters"] == expected["allReporters"]
- assert len(result.data["_debug"]["sql"]) == 2
- assert "COUNT" in result.data["_debug"]["sql"][0]["rawSql"]
- query = str(Reporter.objects.all()[:1].query)
- assert result.data["_debug"]["sql"][1]["rawSql"] == query
-
-
-@pytest.mark.parametrize("max_limit", [None, 100])
-def test_should_query_connectionfilter(graphene_settings, max_limit):
- graphene_settings.RELAY_CONNECTION_MAX_LIMIT = max_limit
-
- from ...filter import DjangoFilterConnectionField
-
- r1 = Reporter(last_name="ABA")
- r1.save()
- r2 = Reporter(last_name="Griffin")
- r2.save()
-
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(graphene.ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterType, fields=["last_name"])
- s = graphene.String(resolver=lambda *_: "S")
- debug = graphene.Field(DjangoDebug, name="_debug")
-
- def resolve_all_reporters(self, info, **args):
- return Reporter.objects.all()
-
- query = """
- query ReporterQuery {
- allReporters(first:1) {
- edges {
- node {
- lastName
- }
- }
- }
- _debug {
- sql {
- rawSql
- }
- }
- }
- """
- expected = {"allReporters": {"edges": [{"node": {"lastName": "ABA"}}]}}
- schema = graphene.Schema(query=Query)
- result = schema.execute(
- query, context_value=context(), middleware=[DjangoDebugMiddleware()]
- )
- assert not result.errors
- assert result.data["allReporters"] == expected["allReporters"]
- assert len(result.data["_debug"]["sql"]) == 2
- assert "COUNT" in result.data["_debug"]["sql"][0]["rawSql"]
- query = str(Reporter.objects.all()[:1].query)
- assert result.data["_debug"]["sql"][1]["rawSql"] == query
-
-
-def test_should_query_stack_trace():
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(graphene.ObjectType):
- reporter = graphene.Field(ReporterType)
- debug = graphene.Field(DjangoDebug, name="_debug")
-
- def resolve_reporter(self, info, **args):
- raise Exception("caught stack trace")
-
- query = """
- query ReporterQuery {
- reporter {
- lastName
- }
- _debug {
- exceptions {
- message
- stack
- }
- }
- }
- """
- schema = graphene.Schema(query=Query)
- result = schema.execute(
- query, context_value=context(), middleware=[DjangoDebugMiddleware()]
- )
- assert result.errors
- assert len(result.data["_debug"]["exceptions"])
- debug_exception = result.data["_debug"]["exceptions"][0]
- assert debug_exception["stack"].count("\n") > 1
- assert "test_query.py" in debug_exception["stack"]
- assert debug_exception["message"] == "caught stack trace"
diff --git a/graphene_django/debug/types.py b/graphene_django/debug/types.py
deleted file mode 100644
index a523b4f..0000000
--- a/graphene_django/debug/types.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from graphene import List, ObjectType
-
-from .sql.types import DjangoDebugSQL
-from .exception.types import DjangoDebugException
-
-
-class DjangoDebug(ObjectType):
- class Meta:
- description = "Debugging information for the current query."
-
- sql = List(DjangoDebugSQL, description="Executed SQL queries for this API query.")
- exceptions = List(
- DjangoDebugException, description="Raise exceptions for this API query."
- )
diff --git a/graphene_django/fields.py b/graphene_django/fields.py
deleted file mode 100644
index e1972c7..0000000
--- a/graphene_django/fields.py
+++ /dev/null
@@ -1,249 +0,0 @@
-from functools import partial
-
-from django.db.models.query import QuerySet
-from graphql_relay.connection.arrayconnection import (
- connection_from_array_slice,
- cursor_to_offset,
- get_offset_with_default,
- offset_to_cursor,
-)
-from promise import Promise
-
-from graphene import Int, NonNull
-from graphene.relay import ConnectionField
-from graphene.relay.connection import connection_adapter, page_info_adapter
-from graphene.types import Field, List
-
-from .settings import graphene_settings
-from .utils import maybe_queryset
-
-
-class DjangoListField(Field):
- def __init__(self, _type, *args, **kwargs):
- from .types import DjangoObjectType
-
- if isinstance(_type, NonNull):
- _type = _type.of_type
-
- # Django would never return a Set of None vvvvvvv
- super(DjangoListField, self).__init__(List(NonNull(_type)), *args, **kwargs)
-
- assert issubclass(
- self._underlying_type, DjangoObjectType
- ), "DjangoListField only accepts DjangoObjectType types"
-
- @property
- def _underlying_type(self):
- _type = self._type
- while hasattr(_type, "of_type"):
- _type = _type.of_type
- return _type
-
- @property
- def model(self):
- return self._underlying_type._meta.model
-
- def get_manager(self):
- return self.model._default_manager
-
- @staticmethod
- def list_resolver(
- django_object_type, resolver, default_manager, root, info, **args
- ):
- queryset = maybe_queryset(resolver(root, info, **args))
- if queryset is None:
- queryset = maybe_queryset(default_manager)
-
- if isinstance(queryset, QuerySet):
- # Pass queryset to the DjangoObjectType get_queryset method
- queryset = maybe_queryset(django_object_type.get_queryset(queryset, info))
-
- return queryset
-
- def wrap_resolve(self, parent_resolver):
- resolver = super(DjangoListField, self).wrap_resolve(parent_resolver)
- _type = self.type
- if isinstance(_type, NonNull):
- _type = _type.of_type
- django_object_type = _type.of_type.of_type
- return partial(
- self.list_resolver, django_object_type, resolver, self.get_manager(),
- )
-
-
-class DjangoConnectionField(ConnectionField):
- def __init__(self, *args, **kwargs):
- self.on = kwargs.pop("on", False)
- self.max_limit = kwargs.pop(
- "max_limit", graphene_settings.RELAY_CONNECTION_MAX_LIMIT
- )
- self.enforce_first_or_last = kwargs.pop(
- "enforce_first_or_last",
- graphene_settings.RELAY_CONNECTION_ENFORCE_FIRST_OR_LAST,
- )
- kwargs.setdefault("offset", Int())
- super(DjangoConnectionField, self).__init__(*args, **kwargs)
-
- @property
- def type(self):
- from .types import DjangoObjectType
-
- _type = super(ConnectionField, self).type
- non_null = False
- if isinstance(_type, NonNull):
- _type = _type.of_type
- non_null = True
- assert issubclass(
- _type, DjangoObjectType
- ), "DjangoConnectionField only accepts DjangoObjectType types"
- assert _type._meta.connection, "The type {} doesn't have a connection".format(
- _type.__name__
- )
- connection_type = _type._meta.connection
- if non_null:
- return NonNull(connection_type)
- return connection_type
-
- @property
- def connection_type(self):
- type = self.type
- if isinstance(type, NonNull):
- return type.of_type
- return type
-
- @property
- def node_type(self):
- return self.connection_type._meta.node
-
- @property
- def model(self):
- return self.node_type._meta.model
-
- def get_manager(self):
- if self.on:
- return getattr(self.model, self.on)
- else:
- return self.model._default_manager
-
- @classmethod
- def resolve_queryset(cls, connection, queryset, info, args):
- # queryset is the resolved iterable from ObjectType
- return connection._meta.node.get_queryset(queryset, info)
-
- @classmethod
- def resolve_connection(cls, connection, args, iterable, max_limit=None):
- # Remove the offset parameter and convert it to an after cursor.
- offset = args.pop("offset", None)
- after = args.get("after")
- if offset:
- if after:
- offset += cursor_to_offset(after) + 1
- # input offset starts at 1 while the graphene offset starts at 0
- args["after"] = offset_to_cursor(offset - 1)
-
- iterable = maybe_queryset(iterable)
-
- if isinstance(iterable, QuerySet):
- list_length = iterable.count()
- else:
- list_length = len(iterable)
- list_slice_length = (
- min(max_limit, list_length) if max_limit is not None else list_length
- )
-
- # If after is higher than list_length, connection_from_list_slice
- # would try to do a negative slicing which makes django throw an
- # AssertionError
- after = min(get_offset_with_default(args.get("after"), -1) + 1, list_length)
-
- if max_limit is not None and args.get("first", None) is None:
- if args.get("last", None) is not None:
- after = list_length - args["last"]
- else:
- args["first"] = max_limit
-
- connection = connection_from_array_slice(
- iterable[after:],
- args,
- slice_start=after,
- array_length=list_length,
- array_slice_length=list_slice_length,
- connection_type=partial(connection_adapter, connection),
- edge_type=connection.Edge,
- page_info_type=page_info_adapter,
- )
- connection.iterable = iterable
- connection.length = list_length
- return connection
-
- @classmethod
- def connection_resolver(
- cls,
- resolver,
- connection,
- default_manager,
- queryset_resolver,
- max_limit,
- enforce_first_or_last,
- root,
- info,
- **args
- ):
- first = args.get("first")
- last = args.get("last")
- offset = args.get("offset")
- before = args.get("before")
-
- if enforce_first_or_last:
- assert first or last, (
- "You must provide a `first` or `last` value to properly paginate the `{}` connection."
- ).format(info.field_name)
-
- if max_limit:
- if first:
- assert first <= max_limit, (
- "Requesting {} records on the `{}` connection exceeds the `first` limit of {} records."
- ).format(first, info.field_name, max_limit)
- args["first"] = min(first, max_limit)
-
- if last:
- assert last <= max_limit, (
- "Requesting {} records on the `{}` connection exceeds the `last` limit of {} records."
- ).format(last, info.field_name, max_limit)
- args["last"] = min(last, max_limit)
-
- if offset is not None:
- assert before is None, (
- "You can't provide a `before` value at the same time as an `offset` value to properly paginate the `{}` connection."
- ).format(info.field_name)
-
- # eventually leads to DjangoObjectType's get_queryset (accepts queryset)
- # or a resolve_foo (does not accept queryset)
- iterable = resolver(root, info, **args)
- if iterable is None:
- iterable = default_manager
- # thus the iterable gets refiltered by resolve_queryset
- # but iterable might be promise
- iterable = queryset_resolver(connection, iterable, info, args)
- on_resolve = partial(
- cls.resolve_connection, connection, args, max_limit=max_limit
- )
-
- if Promise.is_thenable(iterable):
- return Promise.resolve(iterable).then(on_resolve)
-
- return on_resolve(iterable)
-
- def wrap_resolve(self, parent_resolver):
- return partial(
- self.connection_resolver,
- parent_resolver,
- self.connection_type,
- self.get_manager(),
- self.get_queryset_resolver(),
- self.max_limit,
- self.enforce_first_or_last,
- )
-
- def get_queryset_resolver(self):
- return self.resolve_queryset
diff --git a/graphene_django/filter/__init__.py b/graphene_django/filter/__init__.py
deleted file mode 100644
index f02fc6b..0000000
--- a/graphene_django/filter/__init__.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import warnings
-from ..utils import DJANGO_FILTER_INSTALLED
-
-if not DJANGO_FILTER_INSTALLED:
- warnings.warn(
- "Use of django filtering requires the django-filter package "
- "be installed. You can do so using `pip install django-filter`",
- ImportWarning,
- )
-else:
- from .fields import DjangoFilterConnectionField
- from .filters import (
- ArrayFilter,
- GlobalIDFilter,
- GlobalIDMultipleChoiceFilter,
- ListFilter,
- RangeFilter,
- TypedFilter,
- )
-
- __all__ = [
- "DjangoFilterConnectionField",
- "GlobalIDFilter",
- "GlobalIDMultipleChoiceFilter",
- "ArrayFilter",
- "ListFilter",
- "RangeFilter",
- "TypedFilter",
- ]
diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py
deleted file mode 100644
index c6dd50e..0000000
--- a/graphene_django/filter/fields.py
+++ /dev/null
@@ -1,109 +0,0 @@
-from collections import OrderedDict
-from functools import partial
-
-from django.core.exceptions import ValidationError
-
-from graphene.types.enum import EnumType
-from graphene.types.argument import to_arguments
-from graphene.utils.str_converters import to_snake_case
-
-from ..fields import DjangoConnectionField
-from .utils import get_filtering_args_from_filterset, get_filterset_class
-
-
-def convert_enum(data):
- """
- Check if the data is a enum option (or potentially nested list of enum option)
- and convert it to its value.
-
- This method is used to pre-process the data for the filters as they can take an
- graphene.Enum as argument, but filters (from django_filters) expect a simple value.
- """
- if isinstance(data, list):
- return [convert_enum(item) for item in data]
- if isinstance(type(data), EnumType):
- return data.value
- else:
- return data
-
-
-class DjangoFilterConnectionField(DjangoConnectionField):
- def __init__(
- self,
- type,
- fields=None,
- order_by=None,
- extra_filter_meta=None,
- filterset_class=None,
- *args,
- **kwargs
- ):
- self._fields = fields
- self._provided_filterset_class = filterset_class
- self._filterset_class = None
- self._filtering_args = None
- self._extra_filter_meta = extra_filter_meta
- self._base_args = None
- super(DjangoFilterConnectionField, self).__init__(type, *args, **kwargs)
-
- @property
- def args(self):
- return to_arguments(self._base_args or OrderedDict(), self.filtering_args)
-
- @args.setter
- def args(self, args):
- self._base_args = args
-
- @property
- def filterset_class(self):
- if not self._filterset_class:
- fields = self._fields or self.node_type._meta.filter_fields
- meta = dict(model=self.model, fields=fields)
- if self._extra_filter_meta:
- meta.update(self._extra_filter_meta)
-
- filterset_class = (
- self._provided_filterset_class or self.node_type._meta.filterset_class
- )
- self._filterset_class = get_filterset_class(filterset_class, **meta)
-
- return self._filterset_class
-
- @property
- def filtering_args(self):
- if not self._filtering_args:
- self._filtering_args = get_filtering_args_from_filterset(
- self.filterset_class, self.node_type
- )
- return self._filtering_args
-
- @classmethod
- def resolve_queryset(
- cls, connection, iterable, info, args, filtering_args, filterset_class
- ):
- def filter_kwargs():
- kwargs = {}
- for k, v in args.items():
- if k in filtering_args:
- if k == "order_by" and v is not None:
- v = to_snake_case(v)
- kwargs[k] = convert_enum(v)
- return kwargs
-
- qs = super(DjangoFilterConnectionField, cls).resolve_queryset(
- connection, iterable, info, args
- )
-
- filterset = filterset_class(
- data=filter_kwargs(), queryset=qs, request=info.context
- )
- if filterset.is_valid():
- return filterset.qs
- raise ValidationError(filterset.form.errors.as_json())
-
- def get_queryset_resolver(self):
- return partial(
- self.resolve_queryset,
- filterset_class=self.filterset_class,
- filtering_args=self.filtering_args,
- )
diff --git a/graphene_django/filter/filters/__init__.py b/graphene_django/filter/filters/__init__.py
deleted file mode 100644
index fcf75af..0000000
--- a/graphene_django/filter/filters/__init__.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import warnings
-from ...utils import DJANGO_FILTER_INSTALLED
-
-if not DJANGO_FILTER_INSTALLED:
- warnings.warn(
- "Use of django filtering requires the django-filter package "
- "be installed. You can do so using `pip install django-filter`",
- ImportWarning,
- )
-else:
- from .array_filter import ArrayFilter
- from .global_id_filter import GlobalIDFilter, GlobalIDMultipleChoiceFilter
- from .list_filter import ListFilter
- from .range_filter import RangeFilter
- from .typed_filter import TypedFilter
-
- __all__ = [
- "DjangoFilterConnectionField",
- "GlobalIDFilter",
- "GlobalIDMultipleChoiceFilter",
- "ArrayFilter",
- "ListFilter",
- "RangeFilter",
- "TypedFilter",
- ]
diff --git a/graphene_django/filter/filters/array_filter.py b/graphene_django/filter/filters/array_filter.py
deleted file mode 100644
index e886cff..0000000
--- a/graphene_django/filter/filters/array_filter.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from django_filters.constants import EMPTY_VALUES
-
-from .typed_filter import TypedFilter
-
-
-class ArrayFilter(TypedFilter):
- """
- Filter made for PostgreSQL ArrayField.
- """
-
- def filter(self, qs, value):
- """
- Override the default filter class to check first whether the list is
- empty or not.
- This needs to be done as in this case we expect to get the filter applied with
- an empty list since it's a valid value but django_filter consider an empty list
- to be an empty input value (see `EMPTY_VALUES`) meaning that
- the filter does not need to be applied (hence returning the original
- queryset).
- """
- if value in EMPTY_VALUES and value != []:
- return qs
- if self.distinct:
- qs = qs.distinct()
- lookup = "%s__%s" % (self.field_name, self.lookup_expr)
- qs = self.get_method(qs)(**{lookup: value})
- return qs
diff --git a/graphene_django/filter/filters/global_id_filter.py b/graphene_django/filter/filters/global_id_filter.py
deleted file mode 100644
index a612a8a..0000000
--- a/graphene_django/filter/filters/global_id_filter.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django_filters import Filter, MultipleChoiceFilter
-
-from graphql_relay.node.node import from_global_id
-
-from ...forms import GlobalIDFormField, GlobalIDMultipleChoiceField
-
-
-class GlobalIDFilter(Filter):
- """
- Filter for Relay global ID.
- """
-
- field_class = GlobalIDFormField
-
- def filter(self, qs, value):
- """ Convert the filter value to a primary key before filtering """
- _id = None
- if value is not None:
- _, _id = from_global_id(value)
- return super(GlobalIDFilter, self).filter(qs, _id)
-
-
-class GlobalIDMultipleChoiceFilter(MultipleChoiceFilter):
- field_class = GlobalIDMultipleChoiceField
-
- def filter(self, qs, value):
- gids = [from_global_id(v)[1] for v in value]
- return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids)
diff --git a/graphene_django/filter/filters/list_filter.py b/graphene_django/filter/filters/list_filter.py
deleted file mode 100644
index 9689be3..0000000
--- a/graphene_django/filter/filters/list_filter.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from .typed_filter import TypedFilter
-
-
-class ListFilter(TypedFilter):
- """
- Filter that takes a list of value as input.
- It is for example used for `__in` filters.
- """
-
- def filter(self, qs, value):
- """
- Override the default filter class to check first whether the list is
- empty or not.
- This needs to be done as in this case we expect to get an empty output
- (if not an exclude filter) but django_filter consider an empty list
- to be an empty input value (see `EMPTY_VALUES`) meaning that
- the filter does not need to be applied (hence returning the original
- queryset).
- """
- if value is not None and len(value) == 0:
- if self.exclude:
- return qs
- else:
- return qs.none()
- else:
- return super(ListFilter, self).filter(qs, value)
diff --git a/graphene_django/filter/filters/range_filter.py b/graphene_django/filter/filters/range_filter.py
deleted file mode 100644
index c2faddb..0000000
--- a/graphene_django/filter/filters/range_filter.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from django.core.exceptions import ValidationError
-from django.forms import Field
-
-from .typed_filter import TypedFilter
-
-
-def validate_range(value):
- """
- Validator for range filter input: the list of value must be of length 2.
- Note that validators are only run if the value is not empty.
- """
- if len(value) != 2:
- raise ValidationError(
- "Invalid range specified: it needs to contain 2 values.", code="invalid"
- )
-
-
-class RangeField(Field):
- default_validators = [validate_range]
- empty_values = [None]
-
-
-class RangeFilter(TypedFilter):
- field_class = RangeField
diff --git a/graphene_django/filter/filters/typed_filter.py b/graphene_django/filter/filters/typed_filter.py
deleted file mode 100644
index 2c813e4..0000000
--- a/graphene_django/filter/filters/typed_filter.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from django_filters import Filter
-
-from graphene.types.utils import get_type
-
-
-class TypedFilter(Filter):
- """
- Filter class for which the input GraphQL type can explicitly be provided.
- If it is not provided, when building the schema, it will try to guess
- it from the field.
- """
-
- def __init__(self, input_type=None, *args, **kwargs):
- self._input_type = input_type
- super(TypedFilter, self).__init__(*args, **kwargs)
-
- @property
- def input_type(self):
- input_type = get_type(self._input_type)
- if input_type is not None:
- if not callable(getattr(input_type, "get_type", None)):
- raise ValueError(
- "Wrong `input_type` for {}: it only accepts graphene types, got {}".format(
- self.__class__.__name__, input_type
- )
- )
- return input_type
diff --git a/graphene_django/filter/filterset.py b/graphene_django/filter/filterset.py
deleted file mode 100644
index b3333bf..0000000
--- a/graphene_django/filter/filterset.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import itertools
-
-from django.db import models
-from django_filters.filterset import BaseFilterSet, FilterSet
-from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS
-
-from .filters import GlobalIDFilter, GlobalIDMultipleChoiceFilter
-
-
-GRAPHENE_FILTER_SET_OVERRIDES = {
- models.AutoField: {"filter_class": GlobalIDFilter},
- models.OneToOneField: {"filter_class": GlobalIDFilter},
- models.ForeignKey: {"filter_class": GlobalIDFilter},
- models.ManyToManyField: {"filter_class": GlobalIDMultipleChoiceFilter},
- models.ManyToOneRel: {"filter_class": GlobalIDMultipleChoiceFilter},
- models.ManyToManyRel: {"filter_class": GlobalIDMultipleChoiceFilter},
-}
-
-
-class GrapheneFilterSetMixin(BaseFilterSet):
- """ A django_filters.filterset.BaseFilterSet with default filter overrides
- to handle global IDs """
-
- FILTER_DEFAULTS = dict(
- itertools.chain(
- FILTER_FOR_DBFIELD_DEFAULTS.items(), GRAPHENE_FILTER_SET_OVERRIDES.items()
- )
- )
-
-
-def setup_filterset(filterset_class):
- """ Wrap a provided filterset in Graphene-specific functionality
- """
- return type(
- "Graphene{}".format(filterset_class.__name__),
- (filterset_class, GrapheneFilterSetMixin),
- {},
- )
-
-
-def custom_filterset_factory(model, filterset_base_class=FilterSet, **meta):
- """ Create a filterset for the given model using the provided meta data
- """
- meta.update({"model": model})
- meta_class = type(str("Meta"), (object,), meta)
- filterset = type(
- str("%sFilterSet" % model._meta.object_name),
- (filterset_base_class, GrapheneFilterSetMixin),
- {"Meta": meta_class},
- )
- return filterset
diff --git a/graphene_django/filter/tests/__init__.py b/graphene_django/filter/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/filter/tests/conftest.py b/graphene_django/filter/tests/conftest.py
deleted file mode 100644
index 57924af..0000000
--- a/graphene_django/filter/tests/conftest.py
+++ /dev/null
@@ -1,151 +0,0 @@
-from mock import MagicMock
-import pytest
-
-from django.db import models
-from django.db.models.query import QuerySet
-from django_filters import filters
-from django_filters import FilterSet
-import graphene
-from graphene.relay import Node
-from graphene_django import DjangoObjectType
-from graphene_django.utils import DJANGO_FILTER_INSTALLED
-from graphene_django.filter import ArrayFilter, ListFilter
-
-from ...compat import ArrayField
-
-pytestmark = []
-
-if DJANGO_FILTER_INSTALLED:
- from graphene_django.filter import DjangoFilterConnectionField
-else:
- pytestmark.append(
- pytest.mark.skipif(
- True, reason="django_filters not installed or not compatible"
- )
- )
-
-
-STORE = {"events": []}
-
-
-class Event(models.Model):
- name = models.CharField(max_length=50)
- tags = ArrayField(models.CharField(max_length=50))
- tag_ids = ArrayField(models.IntegerField())
- random_field = ArrayField(models.BooleanField())
-
-
-@pytest.fixture
-def EventFilterSet():
- class EventFilterSet(FilterSet):
- class Meta:
- model = Event
- fields = {
- "name": ["exact", "contains"],
- }
-
- # Those are actually usable with our Query fixture bellow
- tags__contains = ArrayFilter(field_name="tags", lookup_expr="contains")
- tags__overlap = ArrayFilter(field_name="tags", lookup_expr="overlap")
- tags = ArrayFilter(field_name="tags", lookup_expr="exact")
-
- # Those are actually not usable and only to check type declarations
- tags_ids__contains = ArrayFilter(field_name="tag_ids", lookup_expr="contains")
- tags_ids__overlap = ArrayFilter(field_name="tag_ids", lookup_expr="overlap")
- tags_ids = ArrayFilter(field_name="tag_ids", lookup_expr="exact")
- random_field__contains = ArrayFilter(
- field_name="random_field", lookup_expr="contains"
- )
- random_field__overlap = ArrayFilter(
- field_name="random_field", lookup_expr="overlap"
- )
- random_field = ArrayFilter(field_name="random_field", lookup_expr="exact")
-
- return EventFilterSet
-
-
-@pytest.fixture
-def EventType(EventFilterSet):
- class EventType(DjangoObjectType):
- class Meta:
- model = Event
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = EventFilterSet
-
- return EventType
-
-
-@pytest.fixture
-def Query(EventType):
- """
- Note that we have to use a custom resolver to replicate the arrayfield filter behavior as
- we are running unit tests in sqlite which does not have ArrayFields.
- """
-
- class Query(graphene.ObjectType):
- events = DjangoFilterConnectionField(EventType)
-
- def resolve_events(self, info, **kwargs):
-
- events = [
- Event(name="Live Show", tags=["concert", "music", "rock"],),
- Event(name="Musical", tags=["movie", "music"],),
- Event(name="Ballet", tags=["concert", "dance"],),
- Event(name="Speech", tags=[],),
- ]
-
- STORE["events"] = events
-
- m_queryset = MagicMock(spec=QuerySet)
- m_queryset.model = Event
-
- def filter_events(**kwargs):
- if "tags__contains" in kwargs:
- STORE["events"] = list(
- filter(
- lambda e: set(kwargs["tags__contains"]).issubset(
- set(e.tags)
- ),
- STORE["events"],
- )
- )
- if "tags__overlap" in kwargs:
- STORE["events"] = list(
- filter(
- lambda e: not set(kwargs["tags__overlap"]).isdisjoint(
- set(e.tags)
- ),
- STORE["events"],
- )
- )
- if "tags__exact" in kwargs:
- STORE["events"] = list(
- filter(
- lambda e: set(kwargs["tags__exact"]) == set(e.tags),
- STORE["events"],
- )
- )
-
- def mock_queryset_filter(*args, **kwargs):
- filter_events(**kwargs)
- return m_queryset
-
- def mock_queryset_none(*args, **kwargs):
- STORE["events"] = []
- return m_queryset
-
- def mock_queryset_count(*args, **kwargs):
- return len(STORE["events"])
-
- m_queryset.all.return_value = m_queryset
- m_queryset.filter.side_effect = mock_queryset_filter
- m_queryset.none.side_effect = mock_queryset_none
- m_queryset.count.side_effect = mock_queryset_count
- m_queryset.__getitem__.side_effect = lambda index: STORE[
- "events"
- ].__getitem__(index)
-
- return m_queryset
-
- return Query
diff --git a/graphene_django/filter/tests/filters.py b/graphene_django/filter/tests/filters.py
deleted file mode 100644
index a7443c0..0000000
--- a/graphene_django/filter/tests/filters.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import django_filters
-from django_filters import OrderingFilter
-
-from graphene_django.tests.models import Article, Pet, Reporter
-
-
-class ArticleFilter(django_filters.FilterSet):
- class Meta:
- model = Article
- fields = {
- "headline": ["exact", "icontains"],
- "pub_date": ["gt", "lt", "exact"],
- "reporter": ["exact", "in"],
- }
-
- order_by = OrderingFilter(fields=("pub_date",))
-
-
-class ReporterFilter(django_filters.FilterSet):
- class Meta:
- model = Reporter
- fields = ["first_name", "last_name", "email", "pets"]
-
- order_by = OrderingFilter(fields=("first_name",))
-
-
-class PetFilter(django_filters.FilterSet):
- class Meta:
- model = Pet
- fields = ["name"]
diff --git a/graphene_django/filter/tests/test_array_field_contains_filter.py b/graphene_django/filter/tests/test_array_field_contains_filter.py
deleted file mode 100644
index 4144614..0000000
--- a/graphene_django/filter/tests/test_array_field_contains_filter.py
+++ /dev/null
@@ -1,87 +0,0 @@
-import pytest
-
-from graphene import Schema
-
-from ...compat import ArrayField, MissingType
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_contains_multiple(Query):
- """
- Test contains filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags_Contains: ["concert", "music"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Live Show"}},
- ]
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_contains_one(Query):
- """
- Test contains filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags_Contains: ["music"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Live Show"}},
- {"node": {"name": "Musical"}},
- ]
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_contains_empty_list(Query):
- """
- Test contains filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags_Contains: []) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Live Show"}},
- {"node": {"name": "Musical"}},
- {"node": {"name": "Ballet"}},
- {"node": {"name": "Speech"}},
- ]
diff --git a/graphene_django/filter/tests/test_array_field_exact_filter.py b/graphene_django/filter/tests/test_array_field_exact_filter.py
deleted file mode 100644
index cd72868..0000000
--- a/graphene_django/filter/tests/test_array_field_exact_filter.py
+++ /dev/null
@@ -1,130 +0,0 @@
-import pytest
-
-from graphene import Schema
-
-from ...compat import ArrayField, MissingType
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_exact_no_match(Query):
- """
- Test exact filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags: ["concert", "music"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == []
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_exact_match(Query):
- """
- Test exact filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags: ["movie", "music"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Musical"}},
- ]
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_exact_empty_list(Query):
- """
- Test exact filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags: []) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Speech"}},
- ]
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_filter_schema_type(Query):
- """
- Check that the type in the filter is an array field like on the object type.
- """
- schema = Schema(query=Query)
- schema_str = str(schema)
-
- assert (
- '''type EventType implements Node {
- """The ID of the object"""
- id: ID!
- name: String!
- tags: [String!]!
- tagIds: [Int!]!
- randomField: [Boolean!]!
-}'''
- in schema_str
- )
-
- filters = {
- "offset": "Int",
- "before": "String",
- "after": "String",
- "first": "Int",
- "last": "Int",
- "name": "String",
- "name_Contains": "String",
- "tags_Contains": "[String!]",
- "tags_Overlap": "[String!]",
- "tags": "[String!]",
- "tagsIds_Contains": "[Int!]",
- "tagsIds_Overlap": "[Int!]",
- "tagsIds": "[Int!]",
- "randomField_Contains": "[Boolean!]",
- "randomField_Overlap": "[Boolean!]",
- "randomField": "[Boolean!]",
- }
- filters_str = ", ".join(
- [
- f"{filter_field}: {gql_type} = null"
- for filter_field, gql_type in filters.items()
- ]
- )
- assert (
- f"type Query {{\n events({filters_str}): EventTypeConnection\n}}" in schema_str
- )
diff --git a/graphene_django/filter/tests/test_array_field_overlap_filter.py b/graphene_django/filter/tests/test_array_field_overlap_filter.py
deleted file mode 100644
index 5ce1576..0000000
--- a/graphene_django/filter/tests/test_array_field_overlap_filter.py
+++ /dev/null
@@ -1,84 +0,0 @@
-import pytest
-
-from graphene import Schema
-
-from ...compat import ArrayField, MissingType
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_overlap_multiple(Query):
- """
- Test overlap filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags_Overlap: ["concert", "music"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Live Show"}},
- {"node": {"name": "Musical"}},
- {"node": {"name": "Ballet"}},
- ]
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_overlap_one(Query):
- """
- Test overlap filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags_Overlap: ["music"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == [
- {"node": {"name": "Live Show"}},
- {"node": {"name": "Musical"}},
- ]
-
-
-@pytest.mark.skipif(ArrayField is MissingType, reason="ArrayField should exist")
-def test_array_field_overlap_empty_list(Query):
- """
- Test overlap filter on a array field of string.
- """
-
- schema = Schema(query=Query)
-
- query = """
- query {
- events (tags_Overlap: []) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["events"]["edges"] == []
diff --git a/graphene_django/filter/tests/test_enum_filtering.py b/graphene_django/filter/tests/test_enum_filtering.py
deleted file mode 100644
index 09c69b3..0000000
--- a/graphene_django/filter/tests/test_enum_filtering.py
+++ /dev/null
@@ -1,160 +0,0 @@
-import pytest
-
-import graphene
-from graphene.relay import Node
-
-from graphene_django import DjangoObjectType, DjangoConnectionField
-from graphene_django.tests.models import Article, Reporter
-from graphene_django.utils import DJANGO_FILTER_INSTALLED
-
-pytestmark = []
-
-if DJANGO_FILTER_INSTALLED:
- from graphene_django.filter import DjangoFilterConnectionField
-else:
- pytestmark.append(
- pytest.mark.skipif(
- True, reason="django_filters not installed or not compatible"
- )
- )
-
-
-@pytest.fixture
-def schema():
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class ArticleType(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = {
- "lang": ["exact", "in"],
- "reporter__a_choice": ["exact", "in"],
- }
-
- class Query(graphene.ObjectType):
- all_reporters = DjangoConnectionField(ReporterType)
- all_articles = DjangoFilterConnectionField(ArticleType)
-
- schema = graphene.Schema(query=Query)
- return schema
-
-
-@pytest.fixture
-def reporter_article_data():
- john = Reporter.objects.create(
- first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
- )
- jane = Reporter.objects.create(
- first_name="Jane", last_name="Doe", email="janedoe@example.com", a_choice=2
- )
- Article.objects.create(
- headline="Article Node 1", reporter=john, editor=john, lang="es",
- )
- Article.objects.create(
- headline="Article Node 2", reporter=john, editor=john, lang="en",
- )
- Article.objects.create(
- headline="Article Node 3", reporter=jane, editor=jane, lang="en",
- )
-
-
-def test_filter_enum_on_connection(schema, reporter_article_data):
- """
- Check that we can filter with enums on a connection.
- """
- query = """
- query {
- allArticles(lang: ES) {
- edges {
- node {
- headline
- }
- }
- }
- }
- """
-
- expected = {"allArticles": {"edges": [{"node": {"headline": "Article Node 1"}},]}}
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
-
-def test_filter_on_foreign_key_enum_field(schema, reporter_article_data):
- """
- Check that we can filter with enums on a field from a foreign key.
- """
- query = """
- query {
- allArticles(reporter_AChoice: A_1) {
- edges {
- node {
- headline
- }
- }
- }
- }
- """
-
- expected = {
- "allArticles": {
- "edges": [
- {"node": {"headline": "Article Node 1"}},
- {"node": {"headline": "Article Node 2"}},
- ]
- }
- }
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
-
-def test_filter_enum_field_schema_type(schema):
- """
- Check that the type in the filter is an enum like on the object type.
- """
- schema_str = str(schema)
-
- assert (
- '''type ArticleType implements Node {
- """The ID of the object"""
- id: ID!
- headline: String!
- pubDate: Date!
- pubDateTime: DateTime!
- reporter: ReporterType!
- editor: ReporterType!
-
- """Language"""
- lang: TestsArticleLangChoices!
- importance: TestsArticleImportanceChoices
-}'''
- in schema_str
- )
-
- filters = {
- "offset": "Int",
- "before": "String",
- "after": "String",
- "first": "Int",
- "last": "Int",
- "lang": "TestsArticleLangChoices",
- "lang_In": "[TestsArticleLangChoices]",
- "reporter_AChoice": "TestsReporterAChoiceChoices",
- "reporter_AChoice_In": "[TestsReporterAChoiceChoices]",
- }
- filters_str = ", ".join(
- [
- f"{filter_field}: {gql_type} = null"
- for filter_field, gql_type in filters.items()
- ]
- )
- assert f" allArticles({filters_str}): ArticleTypeConnection\n" in schema_str
diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py
deleted file mode 100644
index 17b4630..0000000
--- a/graphene_django/filter/tests/test_fields.py
+++ /dev/null
@@ -1,1296 +0,0 @@
-from datetime import datetime
-from textwrap import dedent
-
-import pytest
-from django.db.models import TextField, Value
-from django.db.models.functions import Concat
-
-from graphene import Argument, Boolean, Field, Float, ObjectType, Schema, String
-from graphene.relay import Node
-from graphene_django import DjangoObjectType
-from graphene_django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
-from graphene_django.tests.models import Article, Person, Pet, Reporter
-from graphene_django.utils import DJANGO_FILTER_INSTALLED
-
-pytestmark = []
-
-if DJANGO_FILTER_INSTALLED:
- import django_filters
- from django_filters import FilterSet, NumberFilter, OrderingFilter
-
- from graphene_django.filter import (
- GlobalIDFilter,
- DjangoFilterConnectionField,
- GlobalIDMultipleChoiceFilter,
- )
- from graphene_django.filter.tests.filters import (
- ArticleFilter,
- PetFilter,
- ReporterFilter,
- )
-else:
- pytestmark.append(
- pytest.mark.skipif(
- True, reason="django_filters not installed or not compatible"
- )
- )
-
-if DJANGO_FILTER_INSTALLED:
-
- class ArticleNode(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ("headline",)
-
- class ReporterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class PetNode(DjangoObjectType):
- class Meta:
- model = Pet
- interfaces = (Node,)
- fields = "__all__"
-
-
-def get_args(field):
- return field.args
-
-
-def assert_arguments(field, *arguments):
- ignore = ("offset", "after", "before", "first", "last", "order_by")
- args = get_args(field)
- actual = [name for name in args if name not in ignore and not name.startswith("_")]
- assert set(arguments) == set(
- actual
- ), "Expected arguments ({}) did not match actual ({})".format(arguments, actual)
-
-
-def assert_orderable(field):
- args = get_args(field)
- assert "order_by" in args, "Field cannot be ordered"
-
-
-def assert_not_orderable(field):
- args = get_args(field)
- assert "order_by" not in args, "Field can be ordered"
-
-
-def test_filter_explicit_filterset_arguments():
- field = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleFilter)
- assert_arguments(
- field,
- "headline",
- "headline__icontains",
- "pub_date",
- "pub_date__gt",
- "pub_date__lt",
- "reporter",
- "reporter__in",
- )
-
-
-def test_filter_shortcut_filterset_arguments_list():
- field = DjangoFilterConnectionField(ArticleNode, fields=["pub_date", "reporter"])
- assert_arguments(field, "pub_date", "reporter")
-
-
-def test_filter_shortcut_filterset_arguments_dict():
- field = DjangoFilterConnectionField(
- ArticleNode, fields={"headline": ["exact", "icontains"], "reporter": ["exact"]}
- )
- assert_arguments(field, "headline", "headline__icontains", "reporter")
-
-
-def test_filter_explicit_filterset_orderable():
- field = DjangoFilterConnectionField(ReporterNode, filterset_class=ReporterFilter)
- assert_orderable(field)
-
-
-# def test_filter_shortcut_filterset_orderable_true():
-# field = DjangoFilterConnectionField(ReporterNode)
-# assert_not_orderable(field)
-
-
-# def test_filter_shortcut_filterset_orderable_headline():
-# field = DjangoFilterConnectionField(ReporterNode, order_by=['headline'])
-# assert_orderable(field)
-
-
-def test_filter_explicit_filterset_not_orderable():
- field = DjangoFilterConnectionField(PetNode, filterset_class=PetFilter)
- assert_not_orderable(field)
-
-
-def test_filter_shortcut_filterset_extra_meta():
- field = DjangoFilterConnectionField(
- ArticleNode, extra_filter_meta={"exclude": ("headline",)}
- )
- assert "headline" not in field.filterset_class.get_fields()
-
-
-def test_filter_shortcut_filterset_context():
- class ArticleContextFilter(django_filters.FilterSet):
- class Meta:
- model = Article
- exclude = set()
-
- @property
- def qs(self):
- qs = super(ArticleContextFilter, self).qs
- return qs.filter(reporter=self.request.reporter)
-
- class Query(ObjectType):
- context_articles = DjangoFilterConnectionField(
- ArticleNode, filterset_class=ArticleContextFilter
- )
-
- r1 = Reporter.objects.create(first_name="r1", last_name="r1", email="r1@test.com")
- r2 = Reporter.objects.create(first_name="r2", last_name="r2", email="r2@test.com")
- Article.objects.create(
- headline="a1",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r1,
- editor=r1,
- )
- Article.objects.create(
- headline="a2",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r2,
- editor=r2,
- )
-
- class context(object):
- reporter = r2
-
- query = """
- query {
- contextArticles {
- edges {
- node {
- headline
- }
- }
- }
- }
- """
- schema = Schema(query=Query)
- result = schema.execute(query, context_value=context())
- assert not result.errors
-
- assert len(result.data["contextArticles"]["edges"]) == 1
- assert result.data["contextArticles"]["edges"][0]["node"]["headline"] == "a2"
-
-
-def test_filter_filterset_information_on_meta():
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["first_name", "articles"]
-
- field = DjangoFilterConnectionField(ReporterFilterNode)
- assert_arguments(field, "first_name", "articles")
- assert_not_orderable(field)
-
-
-def test_filter_filterset_information_on_meta_related():
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["first_name", "articles"]
-
- class ArticleFilterNode(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["headline", "reporter"]
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
- all_articles = DjangoFilterConnectionField(ArticleFilterNode)
- reporter = Field(ReporterFilterNode)
- article = Field(ArticleFilterNode)
-
- schema = Schema(query=Query)
- articles_field = ReporterFilterNode._meta.fields["articles"].get_type()
- assert_arguments(articles_field, "headline", "reporter")
- assert_not_orderable(articles_field)
-
-
-def test_filter_filterset_class_filter_fields_exception():
- with pytest.raises(Exception):
-
- class ReporterFilter(FilterSet):
- class Meta:
- model = Reporter
- fields = ["first_name", "articles"]
-
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ReporterFilter
- filter_fields = ["first_name", "articles"]
-
-
-def test_filter_filterset_class_information_on_meta():
- class ReporterFilter(FilterSet):
- class Meta:
- model = Reporter
- fields = ["first_name", "articles"]
-
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ReporterFilter
-
- field = DjangoFilterConnectionField(ReporterFilterNode)
- assert_arguments(field, "first_name", "articles")
- assert_not_orderable(field)
-
-
-def test_filter_filterset_class_information_on_meta_related():
- class ReporterFilter(FilterSet):
- class Meta:
- model = Reporter
- fields = ["first_name", "articles"]
-
- class ArticleFilter(FilterSet):
- class Meta:
- model = Article
- fields = ["headline", "reporter"]
-
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ReporterFilter
-
- class ArticleFilterNode(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ArticleFilter
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
- all_articles = DjangoFilterConnectionField(ArticleFilterNode)
- reporter = Field(ReporterFilterNode)
- article = Field(ArticleFilterNode)
-
- schema = Schema(query=Query)
- articles_field = ReporterFilterNode._meta.fields["articles"].get_type()
- assert_arguments(articles_field, "headline", "reporter")
- assert_not_orderable(articles_field)
-
-
-def test_filter_filterset_related_results():
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["first_name", "articles"]
-
- class ArticleFilterNode(DjangoObjectType):
- class Meta:
- interfaces = (Node,)
- model = Article
- fields = "__all__"
- filter_fields = ["headline", "reporter"]
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
- all_articles = DjangoFilterConnectionField(ArticleFilterNode)
- reporter = Field(ReporterFilterNode)
- article = Field(ArticleFilterNode)
-
- r1 = Reporter.objects.create(first_name="r1", last_name="r1", email="r1@test.com")
- r2 = Reporter.objects.create(first_name="r2", last_name="r2", email="r2@test.com")
- Article.objects.create(
- headline="a1",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r1,
- editor=r1,
- )
- Article.objects.create(
- headline="a2",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r2,
- editor=r2,
- )
-
- query = """
- query {
- allReporters {
- edges {
- node {
- articles {
- edges {
- node {
- headline
- }
- }
- }
- }
- }
- }
- }
- """
- schema = Schema(query=Query)
- result = schema.execute(query)
- assert not result.errors
- # We should only get back a single article for each reporter
- assert (
- len(result.data["allReporters"]["edges"][0]["node"]["articles"]["edges"]) == 1
- )
- assert (
- len(result.data["allReporters"]["edges"][1]["node"]["articles"]["edges"]) == 1
- )
-
-
-def test_global_id_field_implicit():
- field = DjangoFilterConnectionField(ArticleNode, fields=["id"])
- filterset_class = field.filterset_class
- id_filter = filterset_class.base_filters["id"]
- assert isinstance(id_filter, GlobalIDFilter)
- assert id_filter.field_class == GlobalIDFormField
-
-
-def test_global_id_field_explicit():
- class ArticleIdFilter(django_filters.FilterSet):
- class Meta:
- model = Article
- fields = ["id"]
-
- field = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleIdFilter)
- filterset_class = field.filterset_class
- id_filter = filterset_class.base_filters["id"]
- assert isinstance(id_filter, GlobalIDFilter)
- assert id_filter.field_class == GlobalIDFormField
-
-
-def test_filterset_descriptions():
- class ArticleIdFilter(django_filters.FilterSet):
- class Meta:
- model = Article
- fields = ["id"]
-
- max_time = django_filters.NumberFilter(
- method="filter_max_time", label="The maximum time"
- )
-
- field = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleIdFilter)
- max_time = field.args["max_time"]
- assert isinstance(max_time, Argument)
- assert max_time.type == Float
- assert max_time.description == "The maximum time"
-
-
-def test_global_id_field_relation():
- field = DjangoFilterConnectionField(ArticleNode, fields=["reporter"])
- filterset_class = field.filterset_class
- id_filter = filterset_class.base_filters["reporter"]
- assert isinstance(id_filter, GlobalIDFilter)
- assert id_filter.field_class == GlobalIDFormField
-
-
-def test_global_id_field_relation_with_filter():
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["first_name", "articles"]
-
- class ArticleFilterNode(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["headline", "reporter"]
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
- all_articles = DjangoFilterConnectionField(ArticleFilterNode)
- reporter = Field(ReporterFilterNode)
- article = Field(ArticleFilterNode)
-
- r1 = Reporter.objects.create(first_name="r1", last_name="r1", email="r1@test.com")
- r2 = Reporter.objects.create(first_name="r2", last_name="r2", email="r2@test.com")
- Article.objects.create(
- headline="a1",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r1,
- editor=r1,
- )
- Article.objects.create(
- headline="a2",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r2,
- editor=r2,
- )
-
- # Query articles created by the reporter `r1`
- query = """
- query {
- allArticles (reporter: "UmVwb3J0ZXJGaWx0ZXJOb2RlOjE=") {
- edges {
- node {
- id
- }
- }
- }
- }
- """
- schema = Schema(query=Query)
- result = schema.execute(query)
- assert not result.errors
- # We should only get back a single article
- assert len(result.data["allArticles"]["edges"]) == 1
-
-
-def test_global_id_field_relation_with_filter_not_valid_id():
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["first_name", "articles"]
-
- class ArticleFilterNode(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ["headline", "reporter"]
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
- all_articles = DjangoFilterConnectionField(ArticleFilterNode)
- reporter = Field(ReporterFilterNode)
- article = Field(ArticleFilterNode)
-
- r1 = Reporter.objects.create(first_name="r1", last_name="r1", email="r1@test.com")
- r2 = Reporter.objects.create(first_name="r2", last_name="r2", email="r2@test.com")
- Article.objects.create(
- headline="a1",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r1,
- editor=r1,
- )
- Article.objects.create(
- headline="a2",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r2,
- editor=r2,
- )
-
- # Filter by the global ID that does not exist
- query = """
- query {
- allArticles (reporter: "fake_global_id") {
- edges {
- node {
- id
- }
- }
- }
- }
- """
- schema = Schema(query=Query)
- result = schema.execute(query)
- assert "Invalid ID specified." in result.errors[0].message
-
-
-def test_global_id_multiple_field_implicit():
- field = DjangoFilterConnectionField(ReporterNode, fields=["pets"])
- filterset_class = field.filterset_class
- multiple_filter = filterset_class.base_filters["pets"]
- assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
- assert multiple_filter.field_class == GlobalIDMultipleChoiceField
-
-
-def test_global_id_multiple_field_explicit():
- class ReporterPetsFilter(django_filters.FilterSet):
- class Meta:
- model = Reporter
- fields = ["pets"]
-
- field = DjangoFilterConnectionField(
- ReporterNode, filterset_class=ReporterPetsFilter
- )
- filterset_class = field.filterset_class
- multiple_filter = filterset_class.base_filters["pets"]
- assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
- assert multiple_filter.field_class == GlobalIDMultipleChoiceField
-
-
-def test_global_id_multiple_field_implicit_reverse():
- field = DjangoFilterConnectionField(ReporterNode, fields=["articles"])
- filterset_class = field.filterset_class
- multiple_filter = filterset_class.base_filters["articles"]
- assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
- assert multiple_filter.field_class == GlobalIDMultipleChoiceField
-
-
-def test_global_id_multiple_field_explicit_reverse():
- class ReporterPetsFilter(django_filters.FilterSet):
- class Meta:
- model = Reporter
- fields = ["articles"]
-
- field = DjangoFilterConnectionField(
- ReporterNode, filterset_class=ReporterPetsFilter
- )
- filterset_class = field.filterset_class
- multiple_filter = filterset_class.base_filters["articles"]
- assert isinstance(multiple_filter, GlobalIDMultipleChoiceFilter)
- assert multiple_filter.field_class == GlobalIDMultipleChoiceField
-
-
-def test_filter_filterset_related_results_with_filter():
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = {"first_name": ["icontains"]}
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
-
- Reporter.objects.create(
- first_name="A test user", last_name="Last Name", email="test1@test.com"
- )
- Reporter.objects.create(
- first_name="Other test user",
- last_name="Other Last Name",
- email="test2@test.com",
- )
- Reporter.objects.create(
- first_name="Random", last_name="RandomLast", email="random@test.com"
- )
-
- query = """
- query {
- allReporters(firstName_Icontains: "test") {
- edges {
- node {
- id
- }
- }
- }
- }
- """
- schema = Schema(query=Query)
- result = schema.execute(query)
- assert not result.errors
- # We should only get two reporters
- assert len(result.data["allReporters"]["edges"]) == 2
-
-
-def test_recursive_filter_connection():
- class ReporterFilterNode(DjangoObjectType):
- child_reporters = DjangoFilterConnectionField(lambda: ReporterFilterNode)
-
- def resolve_child_reporters(self, **args):
- return []
-
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
-
- assert (
- ReporterFilterNode._meta.fields["child_reporters"].node_type
- == ReporterFilterNode
- )
-
-
-def test_should_query_filter_node_limit():
- class ReporterFilter(FilterSet):
- limit = NumberFilter(method="filter_limit")
-
- def filter_limit(self, queryset, name, value):
- return queryset[:value]
-
- class Meta:
- model = Reporter
- fields = ["first_name"]
-
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class ArticleType(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ("lang",)
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(
- ReporterType, filterset_class=ReporterFilter
- )
-
- def resolve_all_reporters(self, info, **args):
- return Reporter.objects.order_by("a_choice")
-
- Reporter.objects.create(
- first_name="Bob", last_name="Doe", email="bobdoe@example.com", a_choice=2
- )
- r = Reporter.objects.create(
- first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
- )
-
- Article.objects.create(
- headline="Article Node 1",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r,
- editor=r,
- lang="es",
- )
- Article.objects.create(
- headline="Article Node 2",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=r,
- editor=r,
- lang="en",
- )
-
- schema = Schema(query=Query)
- query = """
- query NodeFilteringQuery {
- allReporters(limit: 1) {
- edges {
- node {
- id
- firstName
- articles(lang: ES) {
- edges {
- node {
- id
- lang
- }
- }
- }
- }
- }
- }
- }
- """
-
- expected = {
- "allReporters": {
- "edges": [
- {
- "node": {
- "id": "UmVwb3J0ZXJUeXBlOjI=",
- "firstName": "John",
- "articles": {
- "edges": [
- {"node": {"id": "QXJ0aWNsZVR5cGU6MQ==", "lang": "ES"}}
- ]
- },
- }
- }
- ]
- }
- }
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
-
-def test_order_by():
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(
- ReporterType, filterset_class=ReporterFilter
- )
-
- Reporter.objects.create(first_name="b")
- Reporter.objects.create(first_name="a")
-
- schema = Schema(query=Query)
- query = """
- query NodeFilteringQuery {
- allReporters(orderBy: "-firstName") {
- edges {
- node {
- firstName
- }
- }
- }
- }
- """
- expected = {
- "allReporters": {
- "edges": [{"node": {"firstName": "b"}}, {"node": {"firstName": "a"}}]
- }
- }
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
- query = """
- query NodeFilteringQuery {
- allReporters(orderBy: "-first_name") {
- edges {
- node {
- firstName
- }
- }
- }
- }
- """
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
- query = """
- query NodeFilteringQuery {
- allReporters(orderBy: "-firtsnaMe") {
- edges {
- node {
- firstName
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert result.errors
-
-
-def test_order_by_is_perserved():
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ()
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(
- ReporterType, reverse_order=Boolean()
- )
-
- def resolve_all_reporters(self, info, reverse_order=False, **args):
- reporters = Reporter.objects.order_by("first_name")
-
- if reverse_order:
- return reporters.reverse()
-
- return reporters
-
- Reporter.objects.create(first_name="b")
- Reporter.objects.create(first_name="a")
-
- schema = Schema(query=Query)
- query = """
- query NodeFilteringQuery {
- allReporters(first: 1) {
- edges {
- node {
- firstName
- }
- }
- }
- }
- """
- expected = {"allReporters": {"edges": [{"node": {"firstName": "a"}}]}}
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
- reverse_query = """
- query NodeFilteringQuery {
- allReporters(first: 1, reverseOrder: true) {
- edges {
- node {
- firstName
- }
- }
- }
- }
- """
-
- reverse_expected = {"allReporters": {"edges": [{"node": {"firstName": "b"}}]}}
-
- reverse_result = schema.execute(reverse_query)
-
- assert not reverse_result.errors
- assert reverse_result.data == reverse_expected
-
-
-def test_annotation_is_preserved():
- class ReporterType(DjangoObjectType):
- full_name = String()
-
- def resolve_full_name(instance, info, **args):
- return instance.full_name
-
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ()
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterType)
-
- def resolve_all_reporters(self, info, **args):
- return Reporter.objects.annotate(
- full_name=Concat(
- "first_name", Value(" "), "last_name", output_field=TextField()
- )
- )
-
- Reporter.objects.create(first_name="John", last_name="Doe")
-
- schema = Schema(query=Query)
-
- query = """
- query NodeFilteringQuery {
- allReporters(first: 1) {
- edges {
- node {
- fullName
- }
- }
- }
- }
- """
- expected = {"allReporters": {"edges": [{"node": {"fullName": "John Doe"}}]}}
-
- result = schema.execute(query)
-
- assert not result.errors
- assert result.data == expected
-
-
-def test_annotation_with_only():
- class ReporterType(DjangoObjectType):
- full_name = String()
-
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ()
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(ReporterType)
-
- def resolve_all_reporters(self, info, **args):
- return Reporter.objects.only("first_name", "last_name").annotate(
- full_name=Concat(
- "first_name", Value(" "), "last_name", output_field=TextField()
- )
- )
-
- Reporter.objects.create(first_name="John", last_name="Doe")
-
- schema = Schema(query=Query)
-
- query = """
- query NodeFilteringQuery {
- allReporters(first: 1) {
- edges {
- node {
- fullName
- }
- }
- }
- }
- """
- expected = {"allReporters": {"edges": [{"node": {"fullName": "John Doe"}}]}}
-
- result = schema.execute(query)
-
- assert not result.errors
- assert result.data == expected
-
-
-def test_node_get_queryset_is_called():
- class ReporterType(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = ()
-
- @classmethod
- def get_queryset(cls, queryset, info):
- return queryset.filter(first_name="b")
-
- class Query(ObjectType):
- all_reporters = DjangoFilterConnectionField(
- ReporterType, reverse_order=Boolean()
- )
-
- Reporter.objects.create(first_name="b")
- Reporter.objects.create(first_name="a")
-
- schema = Schema(query=Query)
- query = """
- query NodeFilteringQuery {
- allReporters(first: 10) {
- edges {
- node {
- firstName
- }
- }
- }
- }
- """
- expected = {"allReporters": {"edges": [{"node": {"firstName": "b"}}]}}
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data == expected
-
-
-def test_integer_field_filter_type():
- class PetType(DjangoObjectType):
- class Meta:
- model = Pet
- interfaces = (Node,)
- filter_fields = {"age": ["exact"]}
- fields = ("age",)
-
- class Query(ObjectType):
- pets = DjangoFilterConnectionField(PetType)
-
- schema = Schema(query=Query)
-
- assert str(schema) == dedent(
- """\
- type Query {
- pets(offset: Int = null, before: String = null, after: String = null, first: Int = null, last: Int = null, age: Int = null): PetTypeConnection
- }
-
- type PetTypeConnection {
- \"""Pagination data for this connection.\"""
- pageInfo: PageInfo!
-
- \"""Contains the nodes in this connection.\"""
- edges: [PetTypeEdge]!
- }
-
- \"""
- The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
- \"""
- type PageInfo {
- \"""When paginating forwards, are there more items?\"""
- hasNextPage: Boolean!
-
- \"""When paginating backwards, are there more items?\"""
- hasPreviousPage: Boolean!
-
- \"""When paginating backwards, the cursor to continue.\"""
- startCursor: String
-
- \"""When paginating forwards, the cursor to continue.\"""
- endCursor: String
- }
-
- \"""A Relay edge containing a `PetType` and its cursor.\"""
- type PetTypeEdge {
- \"""The item at the end of the edge\"""
- node: PetType
-
- \"""A cursor for use in pagination\"""
- cursor: String!
- }
-
- type PetType implements Node {
- age: Int!
-
- \"""The ID of the object\"""
- id: ID!
- }
-
- \"""An object with an ID\"""
- interface Node {
- \"""The ID of the object\"""
- id: ID!
- }
- """
- )
-
-
-def test_other_filter_types():
- class PetType(DjangoObjectType):
- class Meta:
- model = Pet
- interfaces = (Node,)
- filter_fields = {"age": ["exact", "isnull", "lt"]}
- fields = ("age",)
-
- class Query(ObjectType):
- pets = DjangoFilterConnectionField(PetType)
-
- schema = Schema(query=Query)
-
- assert str(schema) == dedent(
- """\
- type Query {
- pets(offset: Int = null, before: String = null, after: String = null, first: Int = null, last: Int = null, age: Int = null, age_Isnull: Boolean = null, age_Lt: Int = null): PetTypeConnection
- }
-
- type PetTypeConnection {
- \"""Pagination data for this connection.\"""
- pageInfo: PageInfo!
-
- \"""Contains the nodes in this connection.\"""
- edges: [PetTypeEdge]!
- }
-
- \"""
- The Relay compliant `PageInfo` type, containing data necessary to paginate this connection.
- \"""
- type PageInfo {
- \"""When paginating forwards, are there more items?\"""
- hasNextPage: Boolean!
-
- \"""When paginating backwards, are there more items?\"""
- hasPreviousPage: Boolean!
-
- \"""When paginating backwards, the cursor to continue.\"""
- startCursor: String
-
- \"""When paginating forwards, the cursor to continue.\"""
- endCursor: String
- }
-
- \"""A Relay edge containing a `PetType` and its cursor.\"""
- type PetTypeEdge {
- \"""The item at the end of the edge\"""
- node: PetType
-
- \"""A cursor for use in pagination\"""
- cursor: String!
- }
-
- type PetType implements Node {
- age: Int!
-
- \"""The ID of the object\"""
- id: ID!
- }
-
- \"""An object with an ID\"""
- interface Node {
- \"""The ID of the object\"""
- id: ID!
- }
- """
- )
-
-
-def test_filter_filterset_based_on_mixin():
- class ArticleFilterMixin(FilterSet):
- @classmethod
- def get_filters(cls):
- filters = super(FilterSet, cls).get_filters()
- filters.update(
- {
- "viewer__email__in": django_filters.CharFilter(
- method="filter_email_in", field_name="reporter__email__in"
- )
- }
- )
-
- return filters
-
- def filter_email_in(self, queryset, name, value):
- return queryset.filter(**{name: [value]})
-
- class NewArticleFilter(ArticleFilterMixin, ArticleFilter):
- pass
-
- class NewReporterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
-
- class NewArticleFilterNode(DjangoObjectType):
- viewer = Field(NewReporterNode)
-
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = NewArticleFilter
-
- def resolve_viewer(self, info):
- return self.reporter
-
- class Query(ObjectType):
- all_articles = DjangoFilterConnectionField(NewArticleFilterNode)
-
- reporter_1 = Reporter.objects.create(
- first_name="John", last_name="Doe", email="john@doe.com"
- )
-
- article_1 = Article.objects.create(
- headline="Hello",
- reporter=reporter_1,
- editor=reporter_1,
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- )
-
- reporter_2 = Reporter.objects.create(
- first_name="Adam", last_name="Doe", email="adam@doe.com"
- )
-
- article_2 = Article.objects.create(
- headline="Good Bye",
- reporter=reporter_2,
- editor=reporter_2,
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- )
-
- schema = Schema(query=Query)
-
- query = """
- query NodeFilteringQuery ($email: String!) {
- allArticles(viewer_Email_In: $email) {
- edges {
- node {
- headline
- viewer {
- email
- }
- }
- }
- }
- }
- """
-
- expected = {
- "allArticles": {
- "edges": [
- {
- "node": {
- "headline": article_1.headline,
- "viewer": {"email": reporter_1.email},
- }
- }
- ]
- }
- }
-
- result = schema.execute(query, variable_values={"email": reporter_1.email},)
-
- assert not result.errors
- assert result.data == expected
-
-
-def test_filter_string_contains():
- class PersonType(DjangoObjectType):
- class Meta:
- model = Person
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = {"name": ["exact", "in", "contains", "icontains"]}
-
- class Query(ObjectType):
- people = DjangoFilterConnectionField(PersonType)
-
- schema = Schema(query=Query)
-
- Person.objects.bulk_create(
- [
- Person(name="Jack"),
- Person(name="Joe"),
- Person(name="Jane"),
- Person(name="Peter"),
- Person(name="Bob"),
- ]
- )
- query = """query nameContain($filter: String) {
- people(name_Contains: $filter) {
- edges {
- node {
- name
- }
- }
- }
- }"""
-
- result = schema.execute(query, variables={"filter": "Ja"})
- assert not result.errors
- assert result.data == {
- "people": {"edges": [{"node": {"name": "Jack"}}, {"node": {"name": "Jane"}},]}
- }
-
- result = schema.execute(query, variables={"filter": "o"})
- assert not result.errors
- assert result.data == {
- "people": {"edges": [{"node": {"name": "Joe"}}, {"node": {"name": "Bob"}},]}
- }
-
-
-def test_only_custom_filters():
- class ReporterFilter(FilterSet):
- class Meta:
- model = Reporter
- fields = []
-
- some_filter = OrderingFilter(fields=("name",))
-
- class ReporterFilterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ReporterFilter
-
- field = DjangoFilterConnectionField(ReporterFilterNode)
- assert_arguments(field, "some_filter")
diff --git a/graphene_django/filter/tests/test_in_filter.py b/graphene_django/filter/tests/test_in_filter.py
deleted file mode 100644
index 7ad0286..0000000
--- a/graphene_django/filter/tests/test_in_filter.py
+++ /dev/null
@@ -1,448 +0,0 @@
-from datetime import datetime
-
-import pytest
-
-from django_filters import FilterSet
-from django_filters import rest_framework as filters
-from graphene import ObjectType, Schema
-from graphene.relay import Node
-from graphene_django import DjangoObjectType
-from graphene_django.tests.models import Pet, Person, Reporter, Article, Film
-from graphene_django.filter.tests.filters import ArticleFilter
-from graphene_django.utils import DJANGO_FILTER_INSTALLED
-
-pytestmark = []
-
-if DJANGO_FILTER_INSTALLED:
- from graphene_django.filter import DjangoFilterConnectionField
-else:
- pytestmark.append(
- pytest.mark.skipif(
- True, reason="django_filters not installed or not compatible"
- )
- )
-
-
-@pytest.fixture
-def query():
- class PetNode(DjangoObjectType):
- class Meta:
- model = Pet
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = {
- "id": ["exact", "in"],
- "name": ["exact", "in"],
- "age": ["exact", "in", "range"],
- }
-
- class ReporterNode(DjangoObjectType):
- class Meta:
- model = Reporter
- interfaces = (Node,)
- fields = "__all__"
- # choice filter using enum
- filter_fields = {"reporter_type": ["exact", "in"]}
-
- class ArticleNode(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ArticleFilter
-
- class FilmNode(DjangoObjectType):
- class Meta:
- model = Film
- interfaces = (Node,)
- fields = "__all__"
- # choice filter not using enum
- filter_fields = {
- "genre": ["exact", "in"],
- }
- convert_choices_to_enum = False
-
- class PersonFilterSet(FilterSet):
- class Meta:
- model = Person
- fields = {"name": ["in"]}
-
- names = filters.BaseInFilter(method="filter_names")
-
- def filter_names(self, qs, name, value):
- """
- This custom filter take a string as input with comma separated values.
- Note that the value here is already a list as it has been transformed by the BaseInFilter class.
- """
- return qs.filter(name__in=value)
-
- class PersonNode(DjangoObjectType):
- class Meta:
- model = Person
- interfaces = (Node,)
- filterset_class = PersonFilterSet
- fields = "__all__"
-
- class Query(ObjectType):
- pets = DjangoFilterConnectionField(PetNode)
- people = DjangoFilterConnectionField(PersonNode)
- articles = DjangoFilterConnectionField(ArticleNode)
- films = DjangoFilterConnectionField(FilmNode)
- reporters = DjangoFilterConnectionField(ReporterNode)
-
- return Query
-
-
-def test_string_in_filter(query):
- """
- Test in filter on a string field.
- """
- Pet.objects.create(name="Brutus", age=12)
- Pet.objects.create(name="Mimi", age=3)
- Pet.objects.create(name="Jojo, the rabbit", age=3)
-
- schema = Schema(query=query)
-
- query = """
- query {
- pets (name_In: ["Brutus", "Jojo, the rabbit"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["pets"]["edges"] == [
- {"node": {"name": "Brutus"}},
- {"node": {"name": "Jojo, the rabbit"}},
- ]
-
-
-def test_string_in_filter_with_otjer_filter(query):
- """
- Test in filter on a string field which has also a custom filter doing a similar operation.
- """
- Person.objects.create(name="John")
- Person.objects.create(name="Michael")
- Person.objects.create(name="Angela")
-
- schema = Schema(query=query)
-
- query = """
- query {
- people (name_In: ["John", "Michael"]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["people"]["edges"] == [
- {"node": {"name": "John"}},
- {"node": {"name": "Michael"}},
- ]
-
-
-def test_string_in_filter_with_declared_filter(query):
- """
- Test in filter on a string field with a custom filterset class.
- """
- Person.objects.create(name="John")
- Person.objects.create(name="Michael")
- Person.objects.create(name="Angela")
-
- schema = Schema(query=query)
-
- query = """
- query {
- people (names: "John,Michael") {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["people"]["edges"] == [
- {"node": {"name": "John"}},
- {"node": {"name": "Michael"}},
- ]
-
-
-def test_int_in_filter(query):
- """
- Test in filter on an integer field.
- """
- Pet.objects.create(name="Brutus", age=12)
- Pet.objects.create(name="Mimi", age=3)
- Pet.objects.create(name="Jojo, the rabbit", age=3)
-
- schema = Schema(query=query)
-
- query = """
- query {
- pets (age_In: [3]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["pets"]["edges"] == [
- {"node": {"name": "Mimi"}},
- {"node": {"name": "Jojo, the rabbit"}},
- ]
-
- query = """
- query {
- pets (age_In: [3, 12]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["pets"]["edges"] == [
- {"node": {"name": "Brutus"}},
- {"node": {"name": "Mimi"}},
- {"node": {"name": "Jojo, the rabbit"}},
- ]
-
-
-def test_in_filter_with_empty_list(query):
- """
- Check that using a in filter with an empty list provided as input returns no objects.
- """
- Pet.objects.create(name="Brutus", age=12)
- Pet.objects.create(name="Mimi", age=8)
- Pet.objects.create(name="Picotin", age=5)
-
- schema = Schema(query=query)
-
- query = """
- query {
- pets (name_In: []) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert len(result.data["pets"]["edges"]) == 0
-
-
-def test_choice_in_filter_without_enum(query):
- """
- Test in filter o an choice field not using an enum (Film.genre).
- """
-
- john_doe = Reporter.objects.create(
- first_name="John", last_name="Doe", email="john@doe.com"
- )
- jean_bon = Reporter.objects.create(
- first_name="Jean", last_name="Bon", email="jean@bon.com"
- )
- documentary_film = Film.objects.create(genre="do")
- documentary_film.reporters.add(john_doe)
- action_film = Film.objects.create(genre="ac")
- action_film.reporters.add(john_doe)
- other_film = Film.objects.create(genre="ot")
- other_film.reporters.add(john_doe)
- other_film.reporters.add(jean_bon)
-
- schema = Schema(query=query)
-
- query = """
- query {
- films (genre_In: ["do", "ac"]) {
- edges {
- node {
- genre
- reporters {
- edges {
- node {
- lastName
- }
- }
- }
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["films"]["edges"] == [
- {
- "node": {
- "genre": "do",
- "reporters": {"edges": [{"node": {"lastName": "Doe"}}]},
- }
- },
- {
- "node": {
- "genre": "ac",
- "reporters": {"edges": [{"node": {"lastName": "Doe"}}]},
- }
- },
- ]
-
-
-def test_fk_id_in_filter(query):
- """
- Test in filter on an foreign key relationship.
- """
- john_doe = Reporter.objects.create(
- first_name="John", last_name="Doe", email="john@doe.com"
- )
- jean_bon = Reporter.objects.create(
- first_name="Jean", last_name="Bon", email="jean@bon.com"
- )
- sara_croche = Reporter.objects.create(
- first_name="Sara", last_name="Croche", email="sara@croche.com"
- )
- Article.objects.create(
- headline="A",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=john_doe,
- editor=john_doe,
- )
- Article.objects.create(
- headline="B",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=jean_bon,
- editor=jean_bon,
- )
- Article.objects.create(
- headline="C",
- pub_date=datetime.now(),
- pub_date_time=datetime.now(),
- reporter=sara_croche,
- editor=sara_croche,
- )
-
- schema = Schema(query=query)
-
- query = """
- query {
- articles (reporter_In: [%s, %s]) {
- edges {
- node {
- headline
- reporter {
- lastName
- }
- }
- }
- }
- }
- """ % (
- john_doe.id,
- jean_bon.id,
- )
- result = schema.execute(query)
- assert not result.errors
- assert result.data["articles"]["edges"] == [
- {"node": {"headline": "A", "reporter": {"lastName": "Doe"}}},
- {"node": {"headline": "B", "reporter": {"lastName": "Bon"}}},
- ]
-
-
-def test_enum_in_filter(query):
- """
- Test in filter on a choice field using an enum (Reporter.reporter_type).
- """
-
- Reporter.objects.create(
- first_name="John", last_name="Doe", email="john@doe.com", reporter_type=1
- )
- Reporter.objects.create(
- first_name="Jean", last_name="Bon", email="jean@bon.com", reporter_type=2
- )
- Reporter.objects.create(
- first_name="Jane", last_name="Doe", email="jane@doe.com", reporter_type=2
- )
- Reporter.objects.create(
- first_name="Jack", last_name="Black", email="jack@black.com", reporter_type=None
- )
-
- schema = Schema(query=query)
-
- query = """
- query {
- reporters (reporterType_In: [A_1]) {
- edges {
- node {
- email
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["reporters"]["edges"] == [
- {"node": {"email": "john@doe.com"}},
- ]
-
- query = """
- query {
- reporters (reporterType_In: [A_2]) {
- edges {
- node {
- email
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["reporters"]["edges"] == [
- {"node": {"email": "jean@bon.com"}},
- {"node": {"email": "jane@doe.com"}},
- ]
-
- query = """
- query {
- reporters (reporterType_In: [A_2, A_1]) {
- edges {
- node {
- email
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["reporters"]["edges"] == [
- {"node": {"email": "john@doe.com"}},
- {"node": {"email": "jean@bon.com"}},
- {"node": {"email": "jane@doe.com"}},
- ]
diff --git a/graphene_django/filter/tests/test_range_filter.py b/graphene_django/filter/tests/test_range_filter.py
deleted file mode 100644
index 6227a70..0000000
--- a/graphene_django/filter/tests/test_range_filter.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import json
-import pytest
-
-from django_filters import FilterSet
-from django_filters import rest_framework as filters
-from graphene import ObjectType, Schema
-from graphene.relay import Node
-from graphene_django import DjangoObjectType
-from graphene_django.tests.models import Pet
-from graphene_django.utils import DJANGO_FILTER_INSTALLED
-
-pytestmark = []
-
-if DJANGO_FILTER_INSTALLED:
- from graphene_django.filter import DjangoFilterConnectionField
-else:
- pytestmark.append(
- pytest.mark.skipif(
- True, reason="django_filters not installed or not compatible"
- )
- )
-
-
-class PetNode(DjangoObjectType):
- class Meta:
- model = Pet
- interfaces = (Node,)
- fields = "__all__"
- filter_fields = {
- "name": ["exact", "in"],
- "age": ["exact", "in", "range"],
- }
-
-
-class Query(ObjectType):
- pets = DjangoFilterConnectionField(PetNode)
-
-
-def test_int_range_filter():
- """
- Test range filter on an integer field.
- """
- Pet.objects.create(name="Brutus", age=12)
- Pet.objects.create(name="Mimi", age=8)
- Pet.objects.create(name="Jojo, the rabbit", age=3)
- Pet.objects.create(name="Picotin", age=5)
-
- schema = Schema(query=Query)
-
- query = """
- query {
- pets (age_Range: [4, 9]) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- result = schema.execute(query)
- assert not result.errors
- assert result.data["pets"]["edges"] == [
- {"node": {"name": "Mimi"}},
- {"node": {"name": "Picotin"}},
- ]
-
-
-def test_range_filter_with_invalid_input():
- """
- Test range filter used with invalid inputs raise an error.
- """
- Pet.objects.create(name="Brutus", age=12)
- Pet.objects.create(name="Mimi", age=8)
- Pet.objects.create(name="Jojo, the rabbit", age=3)
- Pet.objects.create(name="Picotin", age=5)
-
- schema = Schema(query=Query)
-
- query = """
- query ($rangeValue: [Int]) {
- pets (age_Range: $rangeValue) {
- edges {
- node {
- name
- }
- }
- }
- }
- """
- expected_error = json.dumps(
- {
- "age__range": [
- {
- "message": "Invalid range specified: it needs to contain 2 values.",
- "code": "invalid",
- }
- ]
- }
- )
-
- # Empty list
- result = schema.execute(query, variables={"rangeValue": []})
- assert len(result.errors) == 1
- assert result.errors[0].message == expected_error
-
- # Only one item in the list
- result = schema.execute(query, variables={"rangeValue": [1]})
- assert len(result.errors) == 1
- assert result.errors[0].message == expected_error
-
- # More than 2 items in the list
- result = schema.execute(query, variables={"rangeValue": [1, 2, 3]})
- assert len(result.errors) == 1
- assert result.errors[0].message == expected_error
diff --git a/graphene_django/filter/tests/test_typed_filter.py b/graphene_django/filter/tests/test_typed_filter.py
deleted file mode 100644
index b903b59..0000000
--- a/graphene_django/filter/tests/test_typed_filter.py
+++ /dev/null
@@ -1,157 +0,0 @@
-import pytest
-
-from django_filters import FilterSet
-
-import graphene
-from graphene.relay import Node
-
-from graphene_django import DjangoObjectType
-from graphene_django.tests.models import Article, Reporter
-from graphene_django.utils import DJANGO_FILTER_INSTALLED
-
-pytestmark = []
-
-if DJANGO_FILTER_INSTALLED:
- from graphene_django.filter import (
- DjangoFilterConnectionField,
- TypedFilter,
- ListFilter,
- )
-else:
- pytestmark.append(
- pytest.mark.skipif(
- True, reason="django_filters not installed or not compatible"
- )
- )
-
-
-@pytest.fixture
-def schema():
- class ArticleFilterSet(FilterSet):
- class Meta:
- model = Article
- fields = {
- "lang": ["exact", "in"],
- }
-
- lang__contains = TypedFilter(
- field_name="lang", lookup_expr="icontains", input_type=graphene.String
- )
- lang__in_str = ListFilter(
- field_name="lang",
- lookup_expr="in",
- input_type=graphene.List(graphene.String),
- )
- first_n = TypedFilter(input_type=graphene.Int, method="first_n_filter")
- only_first = TypedFilter(
- input_type=graphene.Boolean, method="only_first_filter"
- )
-
- def first_n_filter(self, queryset, _name, value):
- return queryset[:value]
-
- def only_first_filter(self, queryset, _name, value):
- if value:
- return queryset[:1]
- else:
- return queryset
-
- class ArticleType(DjangoObjectType):
- class Meta:
- model = Article
- interfaces = (Node,)
- fields = "__all__"
- filterset_class = ArticleFilterSet
-
- class Query(graphene.ObjectType):
- articles = DjangoFilterConnectionField(ArticleType)
-
- schema = graphene.Schema(query=Query)
- return schema
-
-
-def test_typed_filter_schema(schema):
- """
- Check that the type provided in the filter is reflected in the schema.
- """
-
- schema_str = str(schema)
-
- filters = {
- "offset": "Int",
- "before": "String",
- "after": "String",
- "first": "Int",
- "last": "Int",
- "lang": "TestsArticleLangChoices",
- "lang_In": "[TestsArticleLangChoices]",
- "lang_Contains": "String",
- "lang_InStr": "[String]",
- "firstN": "Int",
- "onlyFirst": "Boolean",
- }
-
- all_articles_filters = (
- schema_str.split(" articles(")[1]
- .split("): ArticleTypeConnection\n")[0]
- .split(", ")
- )
-
- for filter_field, gql_type in filters.items():
- assert "{}: {} = null".format(filter_field, gql_type) in all_articles_filters
-
-
-def test_typed_filters_work(schema):
- reporter = Reporter.objects.create(first_name="John", last_name="Doe", email="")
- Article.objects.create(
- headline="A", reporter=reporter, editor=reporter, lang="es",
- )
- Article.objects.create(
- headline="B", reporter=reporter, editor=reporter, lang="es",
- )
- Article.objects.create(
- headline="C", reporter=reporter, editor=reporter, lang="en",
- )
-
- query = "query { articles (lang_In: [ES]) { edges { node { headline } } } }"
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data["articles"]["edges"] == [
- {"node": {"headline": "A"}},
- {"node": {"headline": "B"}},
- ]
-
- query = 'query { articles (lang_InStr: ["es"]) { edges { node { headline } } } }'
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data["articles"]["edges"] == [
- {"node": {"headline": "A"}},
- {"node": {"headline": "B"}},
- ]
-
- query = 'query { articles (lang_Contains: "n") { edges { node { headline } } } }'
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data["articles"]["edges"] == [
- {"node": {"headline": "C"}},
- ]
-
- query = "query { articles (firstN: 2) { edges { node { headline } } } }"
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data["articles"]["edges"] == [
- {"node": {"headline": "A"}},
- {"node": {"headline": "B"}},
- ]
-
- query = "query { articles (onlyFirst: true) { edges { node { headline } } } }"
-
- result = schema.execute(query)
- assert not result.errors
- assert result.data["articles"]["edges"] == [
- {"node": {"headline": "A"}},
- ]
diff --git a/graphene_django/filter/utils.py b/graphene_django/filter/utils.py
deleted file mode 100644
index cd05a87..0000000
--- a/graphene_django/filter/utils.py
+++ /dev/null
@@ -1,155 +0,0 @@
-import graphene
-from django import forms
-from django_filters.utils import get_model_field, get_field_parts
-from django_filters.filters import Filter, BaseCSVFilter
-from .filters import ArrayFilter, ListFilter, RangeFilter, TypedFilter
-from .filterset import custom_filterset_factory, setup_filterset
-from ..forms import GlobalIDFormField, GlobalIDMultipleChoiceField
-
-
-def get_field_type(registry, model, field_name):
- """
- Try to get a model field corresponding Graphql type from the DjangoObjectType.
- """
- object_type = registry.get_type_for_model(model)
- if object_type:
- object_type_field = object_type._meta.fields.get(field_name)
- if object_type_field:
- field_type = object_type_field.type
- if isinstance(field_type, graphene.NonNull):
- field_type = field_type.of_type
- return field_type
- return None
-
-
-def get_filtering_args_from_filterset(filterset_class, type):
- """
- Inspect a FilterSet and produce the arguments to pass to a Graphene Field.
- These arguments will be available to filter against in the GraphQL API.
- """
- from ..forms.converter import convert_form_field
-
- args = {}
- model = filterset_class._meta.model
- registry = type._meta.registry
- for name, filter_field in filterset_class.base_filters.items():
- filter_type = filter_field.lookup_expr
- required = filter_field.extra.get("required", False)
- field_type = None
- form_field = None
-
- if (
- isinstance(filter_field, TypedFilter)
- and filter_field.input_type is not None
- ):
- # First check if the filter input type has been explicitely given
- field_type = filter_field.input_type
- else:
- if name not in filterset_class.declared_filters or isinstance(
- filter_field, TypedFilter
- ):
- # Get the filter field for filters that are no explicitly declared.
- if filter_type == "isnull":
- field = graphene.Boolean(required=required)
- else:
- model_field = get_model_field(model, filter_field.field_name)
-
- # Get the form field either from:
- # 1. the formfield corresponding to the model field
- # 2. the field defined on filter
- if hasattr(model_field, "formfield"):
- form_field = model_field.formfield(required=required)
- if not form_field:
- form_field = filter_field.field
-
- # First try to get the matching field type from the GraphQL DjangoObjectType
- if model_field:
- if (
- isinstance(form_field, forms.ModelChoiceField)
- or isinstance(form_field, forms.ModelMultipleChoiceField)
- or isinstance(form_field, GlobalIDMultipleChoiceField)
- or isinstance(form_field, GlobalIDFormField)
- ):
- # Foreign key have dynamic types and filtering on a foreign key actually means filtering on its ID.
- field_type = get_field_type(
- registry, model_field.related_model, "id"
- )
- else:
- field_type = get_field_type(
- registry, model_field.model, model_field.name
- )
-
- if not field_type:
- # Fallback on converting the form field either because:
- # - it's an explicitly declared filters
- # - we did not manage to get the type from the model type
- form_field = form_field or filter_field.field
- field_type = convert_form_field(form_field).get_type()
-
- if isinstance(filter_field, ListFilter) or isinstance(
- filter_field, RangeFilter
- ):
- # Replace InFilter/RangeFilter filters (`in`, `range`) argument type to be a list of
- # the same type as the field. See comments in `replace_csv_filters` method for more details.
- field_type = graphene.List(field_type)
-
- args[name] = graphene.Argument(
- field_type, description=filter_field.label, required=required,
- )
-
- return args
-
-
-def get_filterset_class(filterset_class, **meta):
- """
- Get the class to be used as the FilterSet.
- """
- if filterset_class:
- # If were given a FilterSet class, then set it up.
- graphene_filterset_class = setup_filterset(filterset_class)
- else:
- # Otherwise create one.
- graphene_filterset_class = custom_filterset_factory(**meta)
-
- replace_csv_filters(graphene_filterset_class)
- return graphene_filterset_class
-
-
-def replace_csv_filters(filterset_class):
- """
- Replace the "in" and "range" filters (that are not explicitly declared)
- to not be BaseCSVFilter (BaseInFilter, BaseRangeFilter) objects anymore
- but our custom InFilter/RangeFilter filter class that use the input
- value as filter argument on the queryset.
-
- This is because those BaseCSVFilter are expecting a string as input with
- comma separated values.
- But with GraphQl we can actually have a list as input and have a proper
- type verification of each value in the list.
-
- See issue https://github.com/graphql-python/graphene-django/issues/1068.
- """
- for name, filter_field in list(filterset_class.base_filters.items()):
- # Do not touch any declared filters
- if name in filterset_class.declared_filters:
- continue
-
- filter_type = filter_field.lookup_expr
- if filter_type == "in":
- filterset_class.base_filters[name] = ListFilter(
- field_name=filter_field.field_name,
- lookup_expr=filter_field.lookup_expr,
- label=filter_field.label,
- method=filter_field.method,
- exclude=filter_field.exclude,
- **filter_field.extra
- )
- elif filter_type == "range":
- filterset_class.base_filters[name] = RangeFilter(
- field_name=filter_field.field_name,
- lookup_expr=filter_field.lookup_expr,
- label=filter_field.label,
- method=filter_field.method,
- exclude=filter_field.exclude,
- **filter_field.extra
- )
diff --git a/graphene_django/forms/__init__.py b/graphene_django/forms/__init__.py
deleted file mode 100644
index 066eec4..0000000
--- a/graphene_django/forms/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField # noqa
diff --git a/graphene_django/forms/converter.py b/graphene_django/forms/converter.py
deleted file mode 100644
index b64e478..0000000
--- a/graphene_django/forms/converter.py
+++ /dev/null
@@ -1,99 +0,0 @@
-from functools import singledispatch
-
-from django import forms
-from django.core.exceptions import ImproperlyConfigured
-
-from graphene import ID, Boolean, Float, Int, List, String, UUID, Date, DateTime, Time
-
-from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField
-
-
-def get_form_field_description(field):
- return str(field.help_text) if field.help_text else None
-
-
-@singledispatch
-def convert_form_field(field):
- raise ImproperlyConfigured(
- "Don't know how to convert the Django form field %s (%s) "
- "to Graphene type" % (field, field.__class__)
- )
-
-
-@convert_form_field.register(forms.fields.BaseTemporalField)
-@convert_form_field.register(forms.CharField)
-@convert_form_field.register(forms.EmailField)
-@convert_form_field.register(forms.SlugField)
-@convert_form_field.register(forms.URLField)
-@convert_form_field.register(forms.ChoiceField)
-@convert_form_field.register(forms.RegexField)
-@convert_form_field.register(forms.Field)
-def convert_form_field_to_string(field):
- return String(
- description=get_form_field_description(field), required=field.required
- )
-
-
-@convert_form_field.register(forms.UUIDField)
-def convert_form_field_to_uuid(field):
- return UUID(description=get_form_field_description(field), required=field.required)
-
-
-@convert_form_field.register(forms.IntegerField)
-@convert_form_field.register(forms.NumberInput)
-def convert_form_field_to_int(field):
- return Int(description=get_form_field_description(field), required=field.required)
-
-
-@convert_form_field.register(forms.BooleanField)
-def convert_form_field_to_boolean(field):
- return Boolean(
- description=get_form_field_description(field), required=field.required
- )
-
-
-@convert_form_field.register(forms.NullBooleanField)
-def convert_form_field_to_nullboolean(field):
- return Boolean(description=get_form_field_description(field))
-
-
-@convert_form_field.register(forms.DecimalField)
-@convert_form_field.register(forms.FloatField)
-def convert_form_field_to_float(field):
- return Float(description=get_form_field_description(field), required=field.required)
-
-
-@convert_form_field.register(forms.MultipleChoiceField)
-def convert_form_field_to_string_list(field):
- return List(
- String, description=get_form_field_description(field), required=field.required
- )
-
-
-@convert_form_field.register(forms.ModelMultipleChoiceField)
-@convert_form_field.register(GlobalIDMultipleChoiceField)
-def convert_form_field_to_id_list(field):
- return List(ID, required=field.required)
-
-
-@convert_form_field.register(forms.DateField)
-def convert_form_field_to_date(field):
- return Date(description=get_form_field_description(field), required=field.required)
-
-
-@convert_form_field.register(forms.DateTimeField)
-def convert_form_field_to_datetime(field):
- return DateTime(
- description=get_form_field_description(field), required=field.required
- )
-
-
-@convert_form_field.register(forms.TimeField)
-def convert_form_field_to_time(field):
- return Time(description=get_form_field_description(field), required=field.required)
-
-
-@convert_form_field.register(forms.ModelChoiceField)
-@convert_form_field.register(GlobalIDFormField)
-def convert_form_field_to_id(field):
- return ID(required=field.required)
diff --git a/graphene_django/forms/forms.py b/graphene_django/forms/forms.py
deleted file mode 100644
index 4b81859..0000000
--- a/graphene_django/forms/forms.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import binascii
-
-from django.core.exceptions import ValidationError
-from django.forms import CharField, Field, MultipleChoiceField
-from django.utils.translation import gettext_lazy as _
-
-from graphql_relay import from_global_id
-
-
-class GlobalIDFormField(Field):
- default_error_messages = {"invalid": _("Invalid ID specified.")}
-
- def clean(self, value):
- if not value and not self.required:
- return None
-
- try:
- _type, _id = from_global_id(value)
- except (TypeError, ValueError, UnicodeDecodeError, binascii.Error):
- raise ValidationError(self.error_messages["invalid"])
-
- try:
- CharField().clean(_id)
- CharField().clean(_type)
- except ValidationError:
- raise ValidationError(self.error_messages["invalid"])
-
- return value
-
-
-class GlobalIDMultipleChoiceField(MultipleChoiceField):
- default_error_messages = {
- "invalid_choice": _("One of the specified IDs was invalid (%(value)s)."),
- "invalid_list": _("Enter a list of values."),
- }
-
- def valid_value(self, value):
- # Clean will raise a validation error if there is a problem
- GlobalIDFormField().clean(value)
- return True
diff --git a/graphene_django/forms/mutation.py b/graphene_django/forms/mutation.py
deleted file mode 100644
index 5a3d8e7..0000000
--- a/graphene_django/forms/mutation.py
+++ /dev/null
@@ -1,192 +0,0 @@
-# from django import forms
-from collections import OrderedDict
-
-import graphene
-from graphene import Field, InputField
-from graphene.relay.mutation import ClientIDMutation
-from graphene.types.mutation import MutationOptions
-
-# from graphene.types.inputobjecttype import (
-# InputObjectTypeOptions,
-# InputObjectType,
-# )
-from graphene.types.utils import yank_fields_from_attrs
-from graphene_django.constants import MUTATION_ERRORS_FLAG
-from graphene_django.registry import get_global_registry
-
-from ..types import ErrorType
-from .converter import convert_form_field
-
-
-def fields_for_form(form, only_fields, exclude_fields):
- fields = OrderedDict()
- for name, field in form.fields.items():
- is_not_in_only = only_fields and name not in only_fields
- is_excluded = (
- name
- in exclude_fields # or
- # name in already_created_fields
- )
-
- if is_not_in_only or is_excluded:
- continue
-
- fields[name] = convert_form_field(field)
- return fields
-
-
-class BaseDjangoFormMutation(ClientIDMutation):
- class Meta:
- abstract = True
-
- @classmethod
- def mutate_and_get_payload(cls, root, info, **input):
- form = cls.get_form(root, info, **input)
-
- if form.is_valid():
- return cls.perform_mutate(form, info)
- else:
- errors = ErrorType.from_errors(form.errors)
- _set_errors_flag_to_context(info)
-
- return cls(errors=errors, **form.data)
-
- @classmethod
- def get_form(cls, root, info, **input):
- form_kwargs = cls.get_form_kwargs(root, info, **input)
- return cls._meta.form_class(**form_kwargs)
-
- @classmethod
- def get_form_kwargs(cls, root, info, **input):
- kwargs = {"data": input}
-
- pk = input.pop("id", None)
- if pk:
- instance = cls._meta.model._default_manager.get(pk=pk)
- kwargs["instance"] = instance
-
- return kwargs
-
-
-class DjangoFormMutationOptions(MutationOptions):
- form_class = None
-
-
-class DjangoFormMutation(BaseDjangoFormMutation):
- class Meta:
- abstract = True
-
- errors = graphene.List(ErrorType)
-
- @classmethod
- def __init_subclass_with_meta__(
- cls, form_class=None, only_fields=(), exclude_fields=(), **options
- ):
-
- if not form_class:
- raise Exception("form_class is required for DjangoFormMutation")
-
- form = form_class()
- input_fields = fields_for_form(form, only_fields, exclude_fields)
- output_fields = fields_for_form(form, only_fields, exclude_fields)
-
- _meta = DjangoFormMutationOptions(cls)
- _meta.form_class = form_class
- _meta.fields = yank_fields_from_attrs(output_fields, _as=Field)
-
- input_fields = yank_fields_from_attrs(input_fields, _as=InputField)
- super(DjangoFormMutation, cls).__init_subclass_with_meta__(
- _meta=_meta, input_fields=input_fields, **options
- )
-
- @classmethod
- def perform_mutate(cls, form, info):
- if hasattr(form, "save"):
- # `save` method won't exist on plain Django forms, but this mutation can
- # in theory be used with `ModelForm`s as well and we do want to save them.
- form.save()
- return cls(errors=[], **form.cleaned_data)
-
-
-class DjangoModelDjangoFormMutationOptions(DjangoFormMutationOptions):
- model = None
- return_field_name = None
-
-
-class DjangoModelFormMutation(BaseDjangoFormMutation):
- class Meta:
- abstract = True
-
- errors = graphene.List(ErrorType)
-
- @classmethod
- def __init_subclass_with_meta__(
- cls,
- form_class=None,
- model=None,
- return_field_name=None,
- only_fields=(),
- exclude_fields=(),
- **options
- ):
-
- if not form_class:
- raise Exception("form_class is required for DjangoModelFormMutation")
-
- if not model:
- model = form_class._meta.model
-
- if not model:
- raise Exception("model is required for DjangoModelFormMutation")
-
- form = form_class()
- input_fields = fields_for_form(form, only_fields, exclude_fields)
- if "id" not in exclude_fields:
- input_fields["id"] = graphene.ID()
-
- registry = get_global_registry()
- model_type = registry.get_type_for_model(model)
- if not model_type:
- raise Exception("No type registered for model: {}".format(model.__name__))
-
- if not return_field_name:
- model_name = model.__name__
- return_field_name = model_name[:1].lower() + model_name[1:]
-
- output_fields = OrderedDict()
- output_fields[return_field_name] = graphene.Field(model_type)
-
- _meta = DjangoModelDjangoFormMutationOptions(cls)
- _meta.form_class = form_class
- _meta.model = model
- _meta.return_field_name = return_field_name
- _meta.fields = yank_fields_from_attrs(output_fields, _as=Field)
-
- input_fields = yank_fields_from_attrs(input_fields, _as=InputField)
- super(DjangoModelFormMutation, cls).__init_subclass_with_meta__(
- _meta=_meta, input_fields=input_fields, **options
- )
-
- @classmethod
- def mutate_and_get_payload(cls, root, info, **input):
- form = cls.get_form(root, info, **input)
-
- if form.is_valid():
- return cls.perform_mutate(form, info)
- else:
- errors = ErrorType.from_errors(form.errors)
- _set_errors_flag_to_context(info)
-
- return cls(errors=errors)
-
- @classmethod
- def perform_mutate(cls, form, info):
- obj = form.save()
- kwargs = {cls._meta.return_field_name: obj}
- return cls(errors=[], **kwargs)
-
-
-def _set_errors_flag_to_context(info):
- # This is not ideal but necessary to keep the response errors empty
- if info and info.context:
- setattr(info.context, MUTATION_ERRORS_FLAG, True)
diff --git a/graphene_django/forms/tests/__init__.py b/graphene_django/forms/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/forms/tests/test_converter.py b/graphene_django/forms/tests/test_converter.py
deleted file mode 100644
index ccf630f..0000000
--- a/graphene_django/forms/tests/test_converter.py
+++ /dev/null
@@ -1,121 +0,0 @@
-from django import forms
-from py.test import raises
-
-import graphene
-from graphene import (
- String,
- Int,
- Boolean,
- Float,
- ID,
- UUID,
- List,
- NonNull,
- DateTime,
- Date,
- Time,
-)
-
-from ..converter import convert_form_field
-
-
-def assert_conversion(django_field, graphene_field, *args):
- field = django_field(*args, help_text="Custom Help Text")
- graphene_type = convert_form_field(field)
- assert isinstance(graphene_type, graphene_field)
- field = graphene_type.Field()
- assert field.description == "Custom Help Text"
- return field
-
-
-def test_should_unknown_django_field_raise_exception():
- with raises(Exception) as excinfo:
- convert_form_field(None)
- assert "Don't know how to convert the Django form field" in str(excinfo.value)
-
-
-def test_should_date_convert_date():
- assert_conversion(forms.DateField, Date)
-
-
-def test_should_time_convert_time():
- assert_conversion(forms.TimeField, Time)
-
-
-def test_should_date_time_convert_date_time():
- assert_conversion(forms.DateTimeField, DateTime)
-
-
-def test_should_char_convert_string():
- assert_conversion(forms.CharField, String)
-
-
-def test_should_email_convert_string():
- assert_conversion(forms.EmailField, String)
-
-
-def test_should_slug_convert_string():
- assert_conversion(forms.SlugField, String)
-
-
-def test_should_url_convert_string():
- assert_conversion(forms.URLField, String)
-
-
-def test_should_choice_convert_string():
- assert_conversion(forms.ChoiceField, String)
-
-
-def test_should_base_field_convert_string():
- assert_conversion(forms.Field, String)
-
-
-def test_should_regex_convert_string():
- assert_conversion(forms.RegexField, String, "[0-9]+")
-
-
-def test_should_uuid_convert_string():
- if hasattr(forms, "UUIDField"):
- assert_conversion(forms.UUIDField, UUID)
-
-
-def test_should_integer_convert_int():
- assert_conversion(forms.IntegerField, Int)
-
-
-def test_should_boolean_convert_boolean():
- field = assert_conversion(forms.BooleanField, Boolean)
- assert isinstance(field.type, NonNull)
-
-
-def test_should_nullboolean_convert_boolean():
- field = assert_conversion(forms.NullBooleanField, Boolean)
- assert not isinstance(field.type, NonNull)
-
-
-def test_should_float_convert_float():
- assert_conversion(forms.FloatField, Float)
-
-
-def test_should_decimal_convert_float():
- assert_conversion(forms.DecimalField, Float)
-
-
-def test_should_multiple_choice_convert_list():
- field = forms.MultipleChoiceField()
- graphene_type = convert_form_field(field)
- assert isinstance(graphene_type, List)
- assert graphene_type.of_type == String
-
-
-def test_should_model_multiple_choice_convert_connectionorlist():
- field = forms.ModelMultipleChoiceField(queryset=None)
- graphene_type = convert_form_field(field)
- assert isinstance(graphene_type, List)
- assert graphene_type.of_type == ID
-
-
-def test_should_manytoone_convert_connectionorlist():
- field = forms.ModelChoiceField(queryset=None)
- graphene_type = convert_form_field(field)
- assert isinstance(graphene_type, ID)
diff --git a/graphene_django/forms/tests/test_mutation.py b/graphene_django/forms/tests/test_mutation.py
deleted file mode 100644
index 0770acb..0000000
--- a/graphene_django/forms/tests/test_mutation.py
+++ /dev/null
@@ -1,385 +0,0 @@
-import pytest
-from django import forms
-from django.core.exceptions import ValidationError
-from py.test import raises
-
-from graphene import Field, ObjectType, Schema, String
-from graphene_django import DjangoObjectType
-from graphene_django.tests.forms import PetForm
-from graphene_django.tests.models import Pet
-from graphene_django.tests.mutations import PetMutation
-
-from ..mutation import DjangoFormMutation, DjangoModelFormMutation
-
-
-class MyForm(forms.Form):
- text = forms.CharField()
-
- def clean_text(self):
- text = self.cleaned_data["text"]
- if text == "INVALID_INPUT":
- raise ValidationError("Invalid input")
- return text
-
- def save(self):
- pass
-
-
-def test_needs_form_class():
- with raises(Exception) as exc:
-
- class MyMutation(DjangoFormMutation):
- pass
-
- assert exc.value.args[0] == "form_class is required for DjangoFormMutation"
-
-
-def test_has_output_fields():
- class MyMutation(DjangoFormMutation):
- class Meta:
- form_class = MyForm
-
- assert "errors" in MyMutation._meta.fields
-
-
-def test_has_input_fields():
- class MyMutation(DjangoFormMutation):
- class Meta:
- form_class = MyForm
-
- assert "text" in MyMutation.Input._meta.fields
-
-
-def test_mutation_error_camelcased(graphene_settings):
- class ExtraPetForm(PetForm):
- test_field = forms.CharField(required=True)
-
- class PetType(DjangoObjectType):
- class Meta:
- model = Pet
- fields = "__all__"
-
- class PetMutation(DjangoModelFormMutation):
- pet = Field(PetType)
-
- class Meta:
- form_class = ExtraPetForm
-
- result = PetMutation.mutate_and_get_payload(None, None)
- assert {f.field for f in result.errors} == {"name", "age", "testField"}
- graphene_settings.CAMELCASE_ERRORS = False
- result = PetMutation.mutate_and_get_payload(None, None)
- assert {f.field for f in result.errors} == {"name", "age", "test_field"}
-
-
-class MockQuery(ObjectType):
- a = String()
-
-
-def test_form_invalid_form():
- class MyMutation(DjangoFormMutation):
- class Meta:
- form_class = MyForm
-
- class Mutation(ObjectType):
- my_mutation = MyMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- result = schema.execute(
- """ mutation MyMutation {
- myMutation(input: { text: "INVALID_INPUT" }) {
- errors {
- field
- messages
- }
- text
- }
- }
- """
- )
-
- assert result.errors is None
- assert result.data["myMutation"]["errors"] == [
- {"field": "text", "messages": ["Invalid input"]}
- ]
-
-
-def test_form_valid_input():
- class MyMutation(DjangoFormMutation):
- class Meta:
- form_class = MyForm
-
- class Mutation(ObjectType):
- my_mutation = MyMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- result = schema.execute(
- """ mutation MyMutation {
- myMutation(input: { text: "VALID_INPUT" }) {
- errors {
- field
- messages
- }
- text
- }
- }
- """
- )
-
- assert result.errors is None
- assert result.data["myMutation"]["errors"] == []
- assert result.data["myMutation"]["text"] == "VALID_INPUT"
-
-
-def test_default_meta_fields():
- assert PetMutation._meta.model is Pet
- assert PetMutation._meta.return_field_name == "pet"
- assert "pet" in PetMutation._meta.fields
-
-
-def test_default_input_meta_fields():
- assert PetMutation._meta.model is Pet
- assert PetMutation._meta.return_field_name == "pet"
- assert "name" in PetMutation.Input._meta.fields
- assert "client_mutation_id" in PetMutation.Input._meta.fields
- assert "id" in PetMutation.Input._meta.fields
-
-
-def test_exclude_fields_input_meta_fields():
- class PetType(DjangoObjectType):
- class Meta:
- model = Pet
- fields = "__all__"
-
- class PetMutation(DjangoModelFormMutation):
- pet = Field(PetType)
-
- class Meta:
- form_class = PetForm
- exclude_fields = ["id"]
-
- assert PetMutation._meta.model is Pet
- assert PetMutation._meta.return_field_name == "pet"
- assert "name" in PetMutation.Input._meta.fields
- assert "age" in PetMutation.Input._meta.fields
- assert "client_mutation_id" in PetMutation.Input._meta.fields
- assert "id" not in PetMutation.Input._meta.fields
-
-
-def test_custom_return_field_name():
- class PetType(DjangoObjectType):
- class Meta:
- model = Pet
- fields = "__all__"
-
- class PetMutation(DjangoModelFormMutation):
- pet = Field(PetType)
-
- class Meta:
- form_class = PetForm
- model = Pet
- return_field_name = "animal"
-
- assert PetMutation._meta.model is Pet
- assert PetMutation._meta.return_field_name == "animal"
- assert "animal" in PetMutation._meta.fields
-
-
-def test_model_form_mutation_mutate_existing():
- class Mutation(ObjectType):
- pet_mutation = PetMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- pet = Pet.objects.create(name="Axel", age=10)
-
- result = schema.execute(
- """ mutation PetMutation($pk: ID!) {
- petMutation(input: { id: $pk, name: "Mia", age: 10 }) {
- pet {
- name
- age
- }
- }
- }
- """,
- variable_values={"pk": pet.pk},
- )
-
- assert result.errors is None
- assert result.data["petMutation"]["pet"] == {"name": "Mia", "age": 10}
-
- assert Pet.objects.count() == 1
- pet.refresh_from_db()
- assert pet.name == "Mia"
-
-
-def test_model_form_mutation_creates_new():
- class Mutation(ObjectType):
- pet_mutation = PetMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- result = schema.execute(
- """ mutation PetMutation {
- petMutation(input: { name: "Mia", age: 10 }) {
- pet {
- name
- age
- }
- errors {
- field
- messages
- }
- }
- }
- """
- )
- assert result.errors is None
- assert result.data["petMutation"]["pet"] == {"name": "Mia", "age": 10}
-
- assert Pet.objects.count() == 1
- pet = Pet.objects.get()
- assert pet.name == "Mia"
- assert pet.age == 10
-
-
-def test_model_form_mutation_invalid_input():
- class Mutation(ObjectType):
- pet_mutation = PetMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- result = schema.execute(
- """ mutation PetMutation {
- petMutation(input: { name: "Mia", age: 99 }) {
- pet {
- name
- age
- }
- errors {
- field
- messages
- }
- }
- }
- """
- )
- assert result.errors is None
- assert result.data["petMutation"]["pet"] is None
- assert result.data["petMutation"]["errors"] == [
- {"field": "age", "messages": ["Too old"]}
- ]
-
- assert Pet.objects.count() == 0
-
-
-def test_model_form_mutation_mutate_invalid_form():
- result = PetMutation.mutate_and_get_payload(None, None)
-
- # A pet was not created
- Pet.objects.count() == 0
-
- fields_w_error = [e.field for e in result.errors]
- assert len(result.errors) == 2
- assert result.errors[0].messages == ["This field is required."]
- assert result.errors[1].messages == ["This field is required."]
- assert "age" in fields_w_error
- assert "name" in fields_w_error
-
-
-def test_model_form_mutation_multiple_creation_valid():
- class Mutation(ObjectType):
- pet_mutation = PetMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- result = schema.execute(
- """
- mutation PetMutations {
- petMutation1: petMutation(input: { name: "Mia", age: 10 }) {
- pet {
- name
- age
- }
- errors {
- field
- messages
- }
- }
- petMutation2: petMutation(input: { name: "Enzo", age: 0 }) {
- pet {
- name
- age
- }
- errors {
- field
- messages
- }
- }
- }
- """
- )
- assert result.errors is None
- assert result.data["petMutation1"]["pet"] == {"name": "Mia", "age": 10}
- assert result.data["petMutation2"]["pet"] == {"name": "Enzo", "age": 0}
-
- assert Pet.objects.count() == 2
-
- pet1 = Pet.objects.first()
- assert pet1.name == "Mia"
- assert pet1.age == 10
-
- pet2 = Pet.objects.last()
- assert pet2.name == "Enzo"
- assert pet2.age == 0
-
-
-def test_model_form_mutation_multiple_creation_invalid():
- class Mutation(ObjectType):
- pet_mutation = PetMutation.Field()
-
- schema = Schema(query=MockQuery, mutation=Mutation)
-
- result = schema.execute(
- """
- mutation PetMutations {
- petMutation1: petMutation(input: { name: "Mia", age: 99 }) {
- pet {
- name
- age
- }
- errors {
- field
- messages
- }
- }
- petMutation2: petMutation(input: { name: "Enzo", age: 0 }) {
- pet {
- name
- age
- }
- errors {
- field
- messages
- }
- }
- }
- """
- )
- assert result.errors is None
-
- assert result.data["petMutation1"]["pet"] is None
- assert result.data["petMutation1"]["errors"] == [
- {"field": "age", "messages": ["Too old"]}
- ]
-
- assert result.data["petMutation2"]["pet"] == {"name": "Enzo", "age": 0}
-
- assert Pet.objects.count() == 1
-
- pet = Pet.objects.get()
- assert pet.name == "Enzo"
- assert pet.age == 0
diff --git a/graphene_django/forms/types.py b/graphene_django/forms/types.py
deleted file mode 100644
index 74e275e..0000000
--- a/graphene_django/forms/types.py
+++ /dev/null
@@ -1 +0,0 @@
-from ..types import ErrorType # noqa Import ErrorType for backwards compatability
diff --git a/graphene_django/management/__init__.py b/graphene_django/management/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/management/commands/__init__.py b/graphene_django/management/commands/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/management/commands/graphql_schema.py b/graphene_django/management/commands/graphql_schema.py
deleted file mode 100644
index 565f5d8..0000000
--- a/graphene_django/management/commands/graphql_schema.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import os
-import importlib
-import json
-import functools
-
-from django.core.management.base import BaseCommand, CommandError
-from django.utils import autoreload
-
-from graphql import print_schema
-from graphene_django.settings import graphene_settings
-
-
-class CommandArguments(BaseCommand):
- def add_arguments(self, parser):
- parser.add_argument(
- "--schema",
- type=str,
- dest="schema",
- default=graphene_settings.SCHEMA,
- help="Django app containing schema to dump, e.g. myproject.core.schema.schema",
- )
-
- parser.add_argument(
- "--out",
- type=str,
- dest="out",
- default=graphene_settings.SCHEMA_OUTPUT,
- help="Output file, --out=- prints to stdout (default: schema.json)",
- )
-
- parser.add_argument(
- "--indent",
- type=int,
- dest="indent",
- default=graphene_settings.SCHEMA_INDENT,
- help="Output file indent (default: None)",
- )
-
- parser.add_argument(
- "--watch",
- dest="watch",
- default=False,
- action="store_true",
- help="Updates the schema on file changes (default: False)",
- )
-
-
-class Command(CommandArguments):
- help = "Dump Graphene schema as a JSON or GraphQL file"
- can_import_settings = True
- requires_system_checks = False
-
- def save_json_file(self, out, schema_dict, indent):
- with open(out, "w") as outfile:
- json.dump(schema_dict, outfile, indent=indent, sort_keys=True)
-
- def save_graphql_file(self, out, schema):
- with open(out, "w", encoding="utf-8") as outfile:
- outfile.write(print_schema(schema.graphql_schema))
-
- def get_schema(self, schema, out, indent):
- schema_dict = {"data": schema.introspect()}
- if out == "-" or out == "-.json":
- self.stdout.write(json.dumps(schema_dict, indent=indent, sort_keys=True))
- elif out == "-.graphql":
- self.stdout.write(print_schema(schema))
- else:
- # Determine format
- _, file_extension = os.path.splitext(out)
-
- if file_extension == ".graphql":
- self.save_graphql_file(out, schema)
- elif file_extension == ".json":
- self.save_json_file(out, schema_dict, indent)
- else:
- raise CommandError(
- 'Unrecognised file format "{}"'.format(file_extension)
- )
-
- style = getattr(self, "style", None)
- success = getattr(style, "SUCCESS", lambda x: x)
-
- self.stdout.write(
- success("Successfully dumped GraphQL schema to {}".format(out))
- )
-
- def handle(self, *args, **options):
- options_schema = options.get("schema")
-
- if options_schema and type(options_schema) is str:
- module_str, schema_name = options_schema.rsplit(".", 1)
- mod = importlib.import_module(module_str)
- schema = getattr(mod, schema_name)
-
- elif options_schema:
- schema = options_schema
-
- else:
- schema = graphene_settings.SCHEMA
-
- out = options.get("out") or graphene_settings.SCHEMA_OUTPUT
-
- if not schema:
- raise CommandError(
- "Specify schema on GRAPHENE.SCHEMA setting or by using --schema"
- )
-
- indent = options.get("indent")
- watch = options.get("watch")
- if watch:
- autoreload.run_with_reloader(
- functools.partial(self.get_schema, schema, out, indent)
- )
- else:
- self.get_schema(schema, out, indent)
diff --git a/graphene_django/registry.py b/graphene_django/registry.py
deleted file mode 100644
index 50a8ae5..0000000
--- a/graphene_django/registry.py
+++ /dev/null
@@ -1,43 +0,0 @@
-class Registry(object):
- def __init__(self):
- self._registry = {}
- self._field_registry = {}
-
- def register(self, cls):
- from .types import DjangoObjectType
-
- assert issubclass(
- cls, DjangoObjectType
- ), 'Only DjangoObjectTypes can be registered, received "{}"'.format(
- cls.__name__
- )
- assert cls._meta.registry == self, "Registry for a Model have to match."
- # assert self.get_type_for_model(cls._meta.model) == cls, (
- # 'Multiple DjangoObjectTypes registered for "{}"'.format(cls._meta.model)
- # )
- if not getattr(cls._meta, "skip_registry", False):
- self._registry[cls._meta.model] = cls
-
- def get_type_for_model(self, model):
- return self._registry.get(model)
-
- def register_converted_field(self, field, converted):
- self._field_registry[field] = converted
-
- def get_converted_field(self, field):
- return self._field_registry.get(field)
-
-
-registry = None
-
-
-def get_global_registry():
- global registry
- if not registry:
- registry = Registry()
- return registry
-
-
-def reset_global_registry():
- global registry
- registry = None
diff --git a/graphene_django/rest_framework/__init__.py b/graphene_django/rest_framework/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/rest_framework/models.py b/graphene_django/rest_framework/models.py
deleted file mode 100644
index bd84ce5..0000000
--- a/graphene_django/rest_framework/models.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from django.db import models
-
-
-class MyFakeModel(models.Model):
- cool_name = models.CharField(max_length=50)
- created = models.DateTimeField(auto_now_add=True)
-
-
-class MyFakeModelWithPassword(models.Model):
- cool_name = models.CharField(max_length=50)
- password = models.CharField(max_length=50)
-
-
-class MyFakeModelWithDate(models.Model):
- cool_name = models.CharField(max_length=50)
- last_edited = models.DateField()
diff --git a/graphene_django/rest_framework/mutation.py b/graphene_django/rest_framework/mutation.py
deleted file mode 100644
index 000b21e..0000000
--- a/graphene_django/rest_framework/mutation.py
+++ /dev/null
@@ -1,175 +0,0 @@
-from collections import OrderedDict
-
-from django.shortcuts import get_object_or_404
-from rest_framework import serializers
-
-import graphene
-from graphene.relay.mutation import ClientIDMutation
-from graphene.types import Field, InputField
-from graphene.types.mutation import MutationOptions
-from graphene.types.objecttype import yank_fields_from_attrs
-
-from ..types import ErrorType
-from .serializer_converter import convert_serializer_field
-
-
-class SerializerMutationOptions(MutationOptions):
- lookup_field = None
- model_class = None
- model_operations = ["create", "update"]
- serializer_class = None
-
-
-def fields_for_serializer(
- serializer,
- only_fields,
- exclude_fields,
- is_input=False,
- convert_choices_to_enum=True,
- lookup_field=None,
-):
- fields = OrderedDict()
- for name, field in serializer.fields.items():
- is_not_in_only = only_fields and name not in only_fields
- is_excluded = any(
- [
- name in exclude_fields,
- field.write_only
- and not is_input, # don't show write_only fields in Query
- field.read_only
- and is_input
- and lookup_field != name, # don't show read_only fields in Input
- ]
- )
-
- if is_not_in_only or is_excluded:
- continue
-
- fields[name] = convert_serializer_field(
- field, is_input=is_input, convert_choices_to_enum=convert_choices_to_enum
- )
- return fields
-
-
-class SerializerMutation(ClientIDMutation):
- class Meta:
- abstract = True
-
- errors = graphene.List(
- ErrorType, description="May contain more than one error for same field."
- )
-
- @classmethod
- def __init_subclass_with_meta__(
- cls,
- lookup_field=None,
- serializer_class=None,
- model_class=None,
- model_operations=("create", "update"),
- only_fields=(),
- exclude_fields=(),
- convert_choices_to_enum=True,
- _meta=None,
- **options
- ):
-
- if not serializer_class:
- raise Exception("serializer_class is required for the SerializerMutation")
-
- if "update" not in model_operations and "create" not in model_operations:
- raise Exception('model_operations must contain "create" and/or "update"')
-
- serializer = serializer_class()
- if model_class is None:
- serializer_meta = getattr(serializer_class, "Meta", None)
- if serializer_meta:
- model_class = getattr(serializer_meta, "model", None)
-
- if lookup_field is None and model_class:
- lookup_field = model_class._meta.pk.name
-
- input_fields = fields_for_serializer(
- serializer,
- only_fields,
- exclude_fields,
- is_input=True,
- convert_choices_to_enum=convert_choices_to_enum,
- lookup_field=lookup_field,
- )
- output_fields = fields_for_serializer(
- serializer,
- only_fields,
- exclude_fields,
- is_input=False,
- convert_choices_to_enum=convert_choices_to_enum,
- lookup_field=lookup_field,
- )
-
- if not _meta:
- _meta = SerializerMutationOptions(cls)
- _meta.lookup_field = lookup_field
- _meta.model_operations = model_operations
- _meta.serializer_class = serializer_class
- _meta.model_class = model_class
- _meta.fields = yank_fields_from_attrs(output_fields, _as=Field)
-
- input_fields = yank_fields_from_attrs(input_fields, _as=InputField)
- super(SerializerMutation, cls).__init_subclass_with_meta__(
- _meta=_meta, input_fields=input_fields, **options
- )
-
- @classmethod
- def get_serializer_kwargs(cls, root, info, **input):
- lookup_field = cls._meta.lookup_field
- model_class = cls._meta.model_class
-
- if model_class:
- if "update" in cls._meta.model_operations and lookup_field in input:
- instance = get_object_or_404(
- model_class, **{lookup_field: input[lookup_field]}
- )
- partial = True
- elif "create" in cls._meta.model_operations:
- instance = None
- partial = False
- else:
- raise Exception(
- 'Invalid update operation. Input parameter "{}" required.'.format(
- lookup_field
- )
- )
-
- return {
- "instance": instance,
- "data": input,
- "context": {"request": info.context},
- "partial": partial,
- }
-
- return {"data": input, "context": {"request": info.context}}
-
- @classmethod
- def mutate_and_get_payload(cls, root, info, **input):
- kwargs = cls.get_serializer_kwargs(root, info, **input)
- serializer = cls._meta.serializer_class(**kwargs)
-
- if serializer.is_valid():
- return cls.perform_mutate(serializer, info)
- else:
- errors = ErrorType.from_errors(serializer.errors)
-
- return cls(errors=errors)
-
- @classmethod
- def perform_mutate(cls, serializer, info):
- obj = serializer.save()
-
- kwargs = {}
- for f, field in serializer.fields.items():
- if not field.write_only:
- if isinstance(field, serializers.SerializerMethodField):
- kwargs[f] = field.to_representation(obj)
- else:
- kwargs[f] = field.get_attribute(obj)
-
- return cls(errors=None, **kwargs)
diff --git a/graphene_django/rest_framework/serializer_converter.py b/graphene_django/rest_framework/serializer_converter.py
deleted file mode 100644
index b26e5e6..0000000
--- a/graphene_django/rest_framework/serializer_converter.py
+++ /dev/null
@@ -1,159 +0,0 @@
-from functools import singledispatch
-
-from django.core.exceptions import ImproperlyConfigured
-from rest_framework import serializers
-
-import graphene
-
-from ..registry import get_global_registry
-from ..converter import convert_choices_to_named_enum_with_descriptions
-from .types import DictType
-
-
-@singledispatch
-def get_graphene_type_from_serializer_field(field):
- raise ImproperlyConfigured(
- "Don't know how to convert the serializer field %s (%s) "
- "to Graphene type" % (field, field.__class__)
- )
-
-
-def convert_serializer_field(field, is_input=True, convert_choices_to_enum=True):
- """
- Converts a django rest frameworks field to a graphql field
- and marks the field as required if we are creating an input type
- and the field itself is required
- """
-
- if isinstance(field, serializers.ChoiceField) and not convert_choices_to_enum:
- graphql_type = graphene.String
- else:
- graphql_type = get_graphene_type_from_serializer_field(field)
-
- args = []
- kwargs = {"description": field.help_text, "required": is_input and field.required}
-
- # if it is a tuple or a list it means that we are returning
- # the graphql type and the child type
- if isinstance(graphql_type, (list, tuple)):
- kwargs["of_type"] = graphql_type[1]
- graphql_type = graphql_type[0]
-
- if isinstance(field, serializers.ModelSerializer):
- if is_input:
- graphql_type = convert_serializer_to_input_type(field.__class__)
- else:
- global_registry = get_global_registry()
- field_model = field.Meta.model
- args = [global_registry.get_type_for_model(field_model)]
- elif isinstance(field, serializers.ListSerializer):
- field = field.child
- if is_input:
- kwargs["of_type"] = convert_serializer_to_input_type(field.__class__)
- else:
- del kwargs["of_type"]
- global_registry = get_global_registry()
- field_model = field.Meta.model
- args = [global_registry.get_type_for_model(field_model)]
-
- return graphql_type(*args, **kwargs)
-
-
-def convert_serializer_to_input_type(serializer_class):
- cached_type = convert_serializer_to_input_type.cache.get(
- serializer_class.__name__, None
- )
- if cached_type:
- return cached_type
- serializer = serializer_class()
-
- items = {
- name: convert_serializer_field(field)
- for name, field in serializer.fields.items()
- }
- ret_type = type(
- "{}Input".format(serializer.__class__.__name__),
- (graphene.InputObjectType,),
- items,
- )
- convert_serializer_to_input_type.cache[serializer_class.__name__] = ret_type
- return ret_type
-
-
-convert_serializer_to_input_type.cache = {}
-
-
-@get_graphene_type_from_serializer_field.register(serializers.Field)
-def convert_serializer_field_to_string(field):
- return graphene.String
-
-
-@get_graphene_type_from_serializer_field.register(serializers.ModelSerializer)
-def convert_serializer_to_field(field):
- return graphene.Field
-
-
-@get_graphene_type_from_serializer_field.register(serializers.ListSerializer)
-def convert_list_serializer_to_field(field):
- child_type = get_graphene_type_from_serializer_field(field.child)
- return (graphene.List, child_type)
-
-
-@get_graphene_type_from_serializer_field.register(serializers.IntegerField)
-def convert_serializer_field_to_int(field):
- return graphene.Int
-
-
-@get_graphene_type_from_serializer_field.register(serializers.BooleanField)
-def convert_serializer_field_to_bool(field):
- return graphene.Boolean
-
-
-@get_graphene_type_from_serializer_field.register(serializers.FloatField)
-@get_graphene_type_from_serializer_field.register(serializers.DecimalField)
-def convert_serializer_field_to_float(field):
- return graphene.Float
-
-
-@get_graphene_type_from_serializer_field.register(serializers.DateTimeField)
-def convert_serializer_field_to_datetime_time(field):
- return graphene.types.datetime.DateTime
-
-
-@get_graphene_type_from_serializer_field.register(serializers.DateField)
-def convert_serializer_field_to_date_time(field):
- return graphene.types.datetime.Date
-
-
-@get_graphene_type_from_serializer_field.register(serializers.TimeField)
-def convert_serializer_field_to_time(field):
- return graphene.types.datetime.Time
-
-
-@get_graphene_type_from_serializer_field.register(serializers.ListField)
-def convert_serializer_field_to_list(field, is_input=True):
- child_type = get_graphene_type_from_serializer_field(field.child)
- return (graphene.List, child_type)
-
-
-@get_graphene_type_from_serializer_field.register(serializers.DictField)
-def convert_serializer_field_to_dict(field):
- return DictType
-
-
-@get_graphene_type_from_serializer_field.register(serializers.JSONField)
-def convert_serializer_field_to_jsonstring(field):
- return graphene.types.json.JSONString
-
-
-@get_graphene_type_from_serializer_field.register(serializers.MultipleChoiceField)
-def convert_serializer_field_to_list_of_enum(field):
- child_type = convert_serializer_field_to_enum(field)
- return (graphene.List, child_type)
-
-
-@get_graphene_type_from_serializer_field.register(serializers.ChoiceField)
-def convert_serializer_field_to_enum(field):
- # enums require a name
- name = field.field_name or field.source or "Choices"
- return convert_choices_to_named_enum_with_descriptions(name, field.choices)
diff --git a/graphene_django/rest_framework/tests/__init__.py b/graphene_django/rest_framework/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphene_django/rest_framework/tests/test_field_converter.py b/graphene_django/rest_framework/tests/test_field_converter.py
deleted file mode 100644
index daa8349..0000000
--- a/graphene_django/rest_framework/tests/test_field_converter.py
+++ /dev/null
@@ -1,220 +0,0 @@
-import copy
-
-import graphene
-from django.db import models
-from graphene import InputObjectType
-from py.test import raises
-from rest_framework import serializers
-
-from ..serializer_converter import convert_serializer_field
-from ..types import DictType
-
-
-def _get_type(
- rest_framework_field, is_input=True, convert_choices_to_enum=True, **kwargs
-):
- # prevents the following error:
- # AssertionError: The `source` argument is not meaningful when applied to a `child=` field.
- # Remove `source=` from the field declaration.
- # since we are reusing the same child in when testing the required attribute
-
- if "child" in kwargs:
- kwargs["child"] = copy.deepcopy(kwargs["child"])
-
- field = rest_framework_field(**kwargs)
-
- return convert_serializer_field(
- field, is_input=is_input, convert_choices_to_enum=convert_choices_to_enum
- )
-
-
-def assert_conversion(rest_framework_field, graphene_field, **kwargs):
- graphene_type = _get_type(
- rest_framework_field, help_text="Custom Help Text", **kwargs
- )
- assert isinstance(graphene_type, graphene_field)
-
- graphene_type_required = _get_type(
- rest_framework_field, help_text="Custom Help Text", required=True, **kwargs
- )
- assert isinstance(graphene_type_required, graphene_field)
-
- return graphene_type
-
-
-def test_should_unknown_rest_framework_field_raise_exception():
- with raises(Exception) as excinfo:
- convert_serializer_field(None)
- assert "Don't know how to convert the serializer field" in str(excinfo.value)
-
-
-def test_should_char_convert_string():
- assert_conversion(serializers.CharField, graphene.String)
-
-
-def test_should_email_convert_string():
- assert_conversion(serializers.EmailField, graphene.String)
-
-
-def test_should_slug_convert_string():
- assert_conversion(serializers.SlugField, graphene.String)
-
-
-def test_should_url_convert_string():
- assert_conversion(serializers.URLField, graphene.String)
-
-
-def test_should_choice_convert_enum():
- field = assert_conversion(
- serializers.ChoiceField,
- graphene.Enum,
- choices=[("h", "Hello"), ("w", "World")],
- source="word",
- )
- assert field._meta.enum.__members__["H"].value == "h"
- assert field._meta.enum.__members__["H"].description == "Hello"
- assert field._meta.enum.__members__["W"].value == "w"
- assert field._meta.enum.__members__["W"].description == "World"
-
-
-def test_should_choice_convert_string_if_enum_disabled():
- assert_conversion(
- serializers.ChoiceField,
- graphene.String,
- choices=[("h", "Hello"), ("w", "World")],
- source="word",
- convert_choices_to_enum=False,
- )
-
-
-def test_should_base_field_convert_string():
- assert_conversion(serializers.Field, graphene.String)
-
-
-def test_should_regex_convert_string():
- assert_conversion(serializers.RegexField, graphene.String, regex="[0-9]+")
-
-
-def test_should_uuid_convert_string():
- if hasattr(serializers, "UUIDField"):
- assert_conversion(serializers.UUIDField, graphene.String)
-
-
-def test_should_model_convert_field():
- class MyModelSerializer(serializers.ModelSerializer):
- class Meta:
- model = None
- fields = "__all__"
-
- assert_conversion(MyModelSerializer, graphene.Field, is_input=False)
-
-
-def test_should_date_time_convert_datetime():
- assert_conversion(serializers.DateTimeField, graphene.types.datetime.DateTime)
-
-
-def test_should_date_convert_date():
- assert_conversion(serializers.DateField, graphene.types.datetime.Date)
-
-
-def test_should_time_convert_time():
- assert_conversion(serializers.TimeField, graphene.types.datetime.Time)
-
-
-def test_should_integer_convert_int():
- assert_conversion(serializers.IntegerField, graphene.Int)
-
-
-def test_should_boolean_convert_boolean():
- assert_conversion(serializers.BooleanField, graphene.Boolean)
-
-
-def test_should_float_convert_float():
- assert_conversion(serializers.FloatField, graphene.Float)
-
-
-def test_should_decimal_convert_float():
- assert_conversion(
- serializers.DecimalField, graphene.Float, max_digits=4, decimal_places=2
- )
-
-
-def test_should_list_convert_to_list():
- class StringListField(serializers.ListField):
- child = serializers.CharField()
-
- field_a = assert_conversion(
- serializers.ListField,
- graphene.List,
- child=serializers.IntegerField(min_value=0, max_value=100),
- )
-
- assert field_a.of_type == graphene.Int
-
- field_b = assert_conversion(StringListField, graphene.List)
-
- assert field_b.of_type == graphene.String
-
-
-def test_should_list_serializer_convert_to_list():
- class FooModel(models.Model):
- pass
-
- class ChildSerializer(serializers.ModelSerializer):
- class Meta:
- model = FooModel
- fields = "__all__"
-
- class ParentSerializer(serializers.ModelSerializer):
- child = ChildSerializer(many=True)
-
- class Meta:
- model = FooModel
- fields = "__all__"
-
- converted_type = convert_serializer_field(
- ParentSerializer().get_fields()["child"], is_input=True
- )
- assert isinstance(converted_type, graphene.List)
-
- converted_type = convert_serializer_field(
- ParentSerializer().get_fields()["child"], is_input=False
- )
- assert isinstance(converted_type, graphene.List)
- assert converted_type.of_type is None
-
-
-def test_should_dict_convert_dict():
- assert_conversion(serializers.DictField, DictType)
-
-
-def test_should_duration_convert_string():
- assert_conversion(serializers.DurationField, graphene.String)
-
-
-def test_should_file_convert_string():
- assert_conversion(serializers.FileField, graphene.String)
-
-
-def test_should_filepath_convert_string():
- assert_conversion(serializers.FilePathField, graphene.Enum, path="/")
-
-
-def test_should_ip_convert_string():
- assert_conversion(serializers.IPAddressField, graphene.String)
-
-
-def test_should_image_convert_string():
- assert_conversion(serializers.ImageField, graphene.String)
-
-
-def test_should_json_convert_jsonstring():
- assert_conversion(serializers.JSONField, graphene.types.json.JSONString)
-
-
-def test_should_multiplechoicefield_convert_to_list_of_enum():
- field = assert_conversion(
- serializers.MultipleChoiceField, graphene.List, choices=[1, 2, 3]
- )
-
- assert issubclass(field.of_type, graphene.Enum)
diff --git a/graphene_django/rest_framework/tests/test_multiple_model_serializers.py b/graphene_django/rest_framework/tests/test_multiple_model_serializers.py
deleted file mode 100644
index 0332b3c..0000000
--- a/graphene_django/rest_framework/tests/test_multiple_model_serializers.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from django.db import models
-from rest_framework import serializers
-
-import graphene
-from graphene import Schema
-from graphene_django import DjangoObjectType
-from graphene_django.rest_framework.mutation import SerializerMutation
-
-
-class MyFakeChildModel(models.Model):
- name = models.CharField(max_length=50)
- created = models.DateTimeField(auto_now_add=True)
-
-
-class MyFakeParentModel(models.Model):
- name = models.CharField(max_length=50)
- created = models.DateTimeField(auto_now_add=True)
- child1 = models.OneToOneField(
- MyFakeChildModel, related_name="parent1", on_delete=models.CASCADE
- )
- child2 = models.OneToOneField(
- MyFakeChildModel, related_name="parent2", on_delete=models.CASCADE
- )
-
-
-class ParentType(DjangoObjectType):
- class Meta:
- model = MyFakeParentModel
- interfaces = (graphene.relay.Node,)
- fields = "__all__"
-
-
-class ChildType(DjangoObjectType):
- class Meta:
- model = MyFakeChildModel
- interfaces = (graphene.relay.Node,)
- fields = "__all__"
-
-
-class MyModelChildSerializer(serializers.ModelSerializer):
- class Meta:
- model = MyFakeChildModel
- fields = "__all__"
-
-
-class MyModelParentSerializer(serializers.ModelSerializer):
- child1 = MyModelChildSerializer()
- child2 = MyModelChildSerializer()
-
- class Meta:
- model = MyFakeParentModel
- fields = "__all__"
-
-
-class MyParentModelMutation(SerializerMutation):
- class Meta:
- serializer_class = MyModelParentSerializer
-
-
-class Mutation(graphene.ObjectType):
- createParentWithChild = MyParentModelMutation.Field()
-
-
-def test_create_schema():
- schema = Schema(mutation=Mutation, types=[ParentType, ChildType])
- assert schema
diff --git a/graphene_django/rest_framework/tests/test_mutation.py b/graphene_django/rest_framework/tests/test_mutation.py
deleted file mode 100644
index e0e5602..0000000
--- a/graphene_django/rest_framework/tests/test_mutation.py
+++ /dev/null
@@ -1,286 +0,0 @@
-import datetime
-
-from py.test import raises
-from rest_framework import serializers
-
-from graphene import Field, ResolveInfo
-from graphene.types.inputobjecttype import InputObjectType
-
-from ...types import DjangoObjectType
-from ..models import MyFakeModel, MyFakeModelWithDate, MyFakeModelWithPassword
-from ..mutation import SerializerMutation
-
-
-def mock_info():
- return ResolveInfo(
- None,
- None,
- None,
- None,
- path=None,
- schema=None,
- fragments=None,
- root_value=None,
- operation=None,
- variable_values=None,
- context=None,
- is_awaitable=None,
- )
-
-
-class MyModelSerializer(serializers.ModelSerializer):
- class Meta:
- model = MyFakeModel
- fields = "__all__"
-
-
-class MyModelSerializerWithMethod(serializers.ModelSerializer):
- days_since_last_edit = serializers.SerializerMethodField()
-
- class Meta:
- model = MyFakeModelWithDate
- fields = "__all__"
-
- def get_days_since_last_edit(self, obj):
- now = datetime.date(2020, 1, 8)
- return (now - obj.last_edited).days
-
-
-class MyModelMutation(SerializerMutation):
- class Meta:
- serializer_class = MyModelSerializer
-
-
-class MySerializer(serializers.Serializer):
- text = serializers.CharField()
- model = MyModelSerializer()
-
- def create(self, validated_data):
- return validated_data
-
-
-def test_needs_serializer_class():
- with raises(Exception) as exc:
-
- class MyMutation(SerializerMutation):
- pass
-
- assert str(exc.value) == "serializer_class is required for the SerializerMutation"
-
-
-def test_has_fields():
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = MySerializer
-
- assert "text" in MyMutation._meta.fields
- assert "model" in MyMutation._meta.fields
- assert "errors" in MyMutation._meta.fields
-
-
-def test_has_input_fields():
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = MySerializer
-
- assert "text" in MyMutation.Input._meta.fields
- assert "model" in MyMutation.Input._meta.fields
-
-
-def test_exclude_fields():
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = MyModelSerializer
- exclude_fields = ["created"]
-
- assert "cool_name" in MyMutation._meta.fields
- assert "created" not in MyMutation._meta.fields
- assert "errors" in MyMutation._meta.fields
- assert "cool_name" in MyMutation.Input._meta.fields
- assert "created" not in MyMutation.Input._meta.fields
-
-
-def test_write_only_field():
- class WriteOnlyFieldModelSerializer(serializers.ModelSerializer):
- password = serializers.CharField(write_only=True)
-
- class Meta:
- model = MyFakeModelWithPassword
- fields = ["cool_name", "password"]
-
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = WriteOnlyFieldModelSerializer
-
- result = MyMutation.mutate_and_get_payload(
- None, mock_info(), **{"cool_name": "New Narf", "password": "admin"}
- )
-
- assert hasattr(result, "cool_name")
- assert not hasattr(
- result, "password"
- ), "'password' is write_only field and shouldn't be visible"
-
-
-def test_write_only_field_using_extra_kwargs():
- class WriteOnlyFieldModelSerializer(serializers.ModelSerializer):
- class Meta:
- model = MyFakeModelWithPassword
- fields = ["cool_name", "password"]
- extra_kwargs = {"password": {"write_only": True}}
-
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = WriteOnlyFieldModelSerializer
-
- result = MyMutation.mutate_and_get_payload(
- None, mock_info(), **{"cool_name": "New Narf", "password": "admin"}
- )
-
- assert hasattr(result, "cool_name")
- assert not hasattr(
- result, "password"
- ), "'password' is write_only field and shouldn't be visible"
-
-
-def test_read_only_fields():
- class ReadOnlyFieldModelSerializer(serializers.ModelSerializer):
- id = serializers.CharField(read_only=True)
- cool_name = serializers.CharField(read_only=True)
-
- class Meta:
- model = MyFakeModelWithPassword
- lookup_field = "id"
- fields = ["id", "cool_name", "password"]
-
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = ReadOnlyFieldModelSerializer
-
- assert "password" in MyMutation.Input._meta.fields
- assert "id" in MyMutation.Input._meta.fields
- assert (
- "cool_name" not in MyMutation.Input._meta.fields
- ), "'cool_name' is read_only field and shouldn't be on arguments"
-
-
-def test_nested_model():
- class MyFakeModelGrapheneType(DjangoObjectType):
- class Meta:
- model = MyFakeModel
- fields = "__all__"
-
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = MySerializer
-
- model_field = MyMutation._meta.fields["model"]
- assert isinstance(model_field, Field)
- assert model_field.type == MyFakeModelGrapheneType
-
- model_input = MyMutation.Input._meta.fields["model"]
- model_input_type = model_input._type.of_type
- assert issubclass(model_input_type, InputObjectType)
- assert "cool_name" in model_input_type._meta.fields
- assert "created" in model_input_type._meta.fields
-
-
-def test_mutate_and_get_payload_success():
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = MySerializer
-
- result = MyMutation.mutate_and_get_payload(
- None, mock_info(), **{"text": "value", "model": {"cool_name": "other_value"}}
- )
- assert result.errors is None
-
-
-def test_model_add_mutate_and_get_payload_success():
- result = MyModelMutation.mutate_and_get_payload(
- None, mock_info(), **{"cool_name": "Narf"}
- )
- assert result.errors is None
- assert result.cool_name == "Narf"
- assert isinstance(result.created, datetime.datetime)
-
-
-def test_model_update_mutate_and_get_payload_success():
- instance = MyFakeModel.objects.create(cool_name="Narf")
- result = MyModelMutation.mutate_and_get_payload(
- None, mock_info(), **{"id": instance.id, "cool_name": "New Narf"}
- )
- assert result.errors is None
- assert result.cool_name == "New Narf"
-
-
-def test_model_partial_update_mutate_and_get_payload_success():
- instance = MyFakeModel.objects.create(cool_name="Narf")
- result = MyModelMutation.mutate_and_get_payload(
- None, mock_info(), **{"id": instance.id}
- )
- assert result.errors is None
- assert result.cool_name == "Narf"
-
-
-def test_model_invalid_update_mutate_and_get_payload_success():
- class InvalidModelMutation(SerializerMutation):
- class Meta:
- serializer_class = MyModelSerializer
- model_operations = ["update"]
-
- with raises(Exception) as exc:
- result = InvalidModelMutation.mutate_and_get_payload(
- None, mock_info(), **{"cool_name": "Narf"}
- )
-
- assert '"id" required' in str(exc.value)
-
-
-def test_perform_mutate_success():
- class MyMethodMutation(SerializerMutation):
- class Meta:
- serializer_class = MyModelSerializerWithMethod
-
- result = MyMethodMutation.mutate_and_get_payload(
- None,
- mock_info(),
- **{"cool_name": "Narf", "last_edited": datetime.date(2020, 1, 4)}
- )
-
- assert result.errors is None
- assert result.cool_name == "Narf"
- assert result.days_since_last_edit == 4
-
-
-def test_mutate_and_get_payload_error():
- class MyMutation(SerializerMutation):
- class Meta:
- serializer_class = MySerializer
-
- # missing required fields
- result = MyMutation.mutate_and_get_payload(None, mock_info(), **{})
- assert len(result.errors) > 0
-
-
-def test_model_mutate_and_get_payload_error():
- # missing required fields
- result = MyModelMutation.mutate_and_get_payload(None, mock_info(), **{})
- assert len(result.errors) > 0
-
-
-def test_mutation_error_camelcased(graphene_settings):
- graphene_settings.CAMELCASE_ERRORS = True
- result = MyModelMutation.mutate_and_get_payload(None, mock_info(), **{})
- assert result.errors[0].field == "coolName"
-
-
-def test_invalid_serializer_operations():
- with raises(Exception) as exc:
-
- class MyModelMutation(SerializerMutation):
- class Meta:
- serializer_class = MyModelSerializer
- model_operations = ["Add"]
-
- assert "model_operations" in str(exc.value)
diff --git a/graphene_django/rest_framework/types.py b/graphene_django/rest_framework/types.py
deleted file mode 100644
index 2472c32..0000000
--- a/graphene_django/rest_framework/types.py
+++ /dev/null
@@ -1,7 +0,0 @@
-import graphene
-from graphene.types.unmountedtype import UnmountedType
-
-
-class DictType(UnmountedType):
- key = graphene.String()
- value = graphene.String()
diff --git a/graphene_django/settings.py b/graphene_django/settings.py
deleted file mode 100644
index 467c6a3..0000000
--- a/graphene_django/settings.py
+++ /dev/null
@@ -1,140 +0,0 @@
-"""
-Settings for Graphene are all namespaced in the GRAPHENE setting.
-For example your project's `settings.py` file might look like this:
-GRAPHENE = {
- 'SCHEMA': 'my_app.schema.schema'
- 'MIDDLEWARE': (
- 'graphene_django.debug.DjangoDebugMiddleware',
- )
-}
-This module provides the `graphene_settings` object, that is used to access
-Graphene settings, checking for user settings first, then falling
-back to the defaults.
-"""
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.test.signals import setting_changed
-
-import importlib # Available in Python 3.1+
-
-
-# Copied shamelessly from Django REST Framework
-
-DEFAULTS = {
- "SCHEMA": None,
- "SCHEMA_OUTPUT": "schema.json",
- "SCHEMA_INDENT": 2,
- "MIDDLEWARE": (),
- # Set to True if the connection fields must have
- # either the first or last argument
- "RELAY_CONNECTION_ENFORCE_FIRST_OR_LAST": False,
- # Max items returned in ConnectionFields / FilterConnectionFields
- "RELAY_CONNECTION_MAX_LIMIT": 100,
- "CAMELCASE_ERRORS": True,
- # Set to True to enable v2 naming convention for choice field Enum's
- "DJANGO_CHOICE_FIELD_ENUM_V2_NAMING": False,
- "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,
- "ATOMIC_MUTATIONS": False,
-}
-
-if settings.DEBUG:
- DEFAULTS["MIDDLEWARE"] += ("graphene_django.debug.DjangoDebugMiddleware",)
-
-# List of settings that may be in string import notation.
-IMPORT_STRINGS = ("MIDDLEWARE", "SCHEMA")
-
-
-def perform_import(val, setting_name):
- """
- If the given setting is a string import notation,
- then perform the necessary import or imports.
- """
- if val is None:
- return None
- elif isinstance(val, str):
- return import_from_string(val, setting_name)
- elif isinstance(val, (list, tuple)):
- return [import_from_string(item, setting_name) for item in val]
- return val
-
-
-def import_from_string(val, setting_name):
- """
- Attempt to import a class from a string representation.
- """
- try:
- # Nod to tastypie's use of importlib.
- parts = val.split(".")
- module_path, class_name = ".".join(parts[:-1]), parts[-1]
- module = importlib.import_module(module_path)
- return getattr(module, class_name)
- except (ImportError, AttributeError) as e:
- msg = "Could not import '%s' for Graphene setting '%s'. %s: %s." % (
- val,
- setting_name,
- e.__class__.__name__,
- e,
- )
- raise ImportError(msg)
-
-
-class GrapheneSettings(object):
- """
- A settings object, that allows API settings to be accessed as properties.
- For example:
- from graphene_django.settings import settings
- print(settings.SCHEMA)
- Any setting with string import paths will be automatically resolved
- and return the class, rather than the string literal.
- """
-
- def __init__(self, user_settings=None, defaults=None, import_strings=None):
- if user_settings:
- self._user_settings = user_settings
- self.defaults = defaults or DEFAULTS
- self.import_strings = import_strings or IMPORT_STRINGS
-
- @property
- def user_settings(self):
- if not hasattr(self, "_user_settings"):
- self._user_settings = getattr(settings, "GRAPHENE", {})
- return self._user_settings
-
- def __getattr__(self, attr):
- if attr not in self.defaults:
- raise AttributeError("Invalid Graphene setting: '%s'" % attr)
-
- try:
- # Check if present in user settings
- val = self.user_settings[attr]
- except KeyError:
- # Fall back to defaults
- val = self.defaults[attr]
-
- # Coerce import strings into classes
- if attr in self.import_strings:
- val = perform_import(val, attr)
-
- # Cache the result
- setattr(self, attr, val)
- return val
-
-
-graphene_settings = GrapheneSettings(None, DEFAULTS, IMPORT_STRINGS)
-
-
-def reload_graphene_settings(*args, **kwargs):
- global graphene_settings
- setting, value = kwargs["setting"], kwargs["value"]
- if setting == "GRAPHENE":
- graphene_settings = GrapheneSettings(value, DEFAULTS, IMPORT_STRINGS)
-
-
-setting_changed.connect(reload_graphene_settings)
diff --git a/graphene_django/static/graphene_django/graphiql.js b/graphene_django/static/graphene_django/graphiql.js
deleted file mode 100644
index ac010e8..0000000
--- a/graphene_django/static/graphene_django/graphiql.js
+++ /dev/null
@@ -1,203 +0,0 @@
-(function (
- document,
-
- GRAPHENE_SETTINGS,
- GraphiQL,
- React,
- ReactDOM,
- SubscriptionsTransportWs,
- fetch,
- history,
- location,
-) {
- // Parse the cookie value for a CSRF token
- var csrftoken;
- var cookies = ("; " + document.cookie).split("; csrftoken=");
- if (cookies.length == 2) {
- csrftoken = cookies.pop().split(";").shift();
- } else {
- csrftoken = document.querySelector("[name=csrfmiddlewaretoken]").value;
- }
-
- // Collect the URL parameters
- var parameters = {};
- location.hash
- .substr(1)
- .split("&")
- .forEach(function (entry) {
- var eq = entry.indexOf("=");
- if (eq >= 0) {
- parameters[decodeURIComponent(entry.slice(0, eq))] = decodeURIComponent(
- entry.slice(eq + 1),
- );
- }
- });
- // Produce a Location fragment string from a parameter object.
- function locationQuery(params) {
- return (
- "#" +
- Object.keys(params)
- .map(function (key) {
- return (
- encodeURIComponent(key) + "=" + encodeURIComponent(params[key])
- );
- })
- .join("&")
- );
- }
- // Derive a fetch URL from the current URL, sans the GraphQL parameters.
- var graphqlParamNames = {
- query: true,
- variables: true,
- operationName: true,
- };
- var otherParams = {};
- for (var k in parameters) {
- if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) {
- otherParams[k] = parameters[k];
- }
- }
-
- var fetchURL = locationQuery(otherParams);
-
- // Defines a GraphQL fetcher using the fetch API.
- 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
- }
- return fetch(fetchURL, {
- method: "post",
- headers: headers,
- body: JSON.stringify(graphQLParams),
- credentials: "include",
- })
- .then(function (response) {
- return response.text();
- })
- .then(function (responseBody) {
- try {
- return JSON.parse(responseBody);
- } catch (error) {
- return responseBody;
- }
- });
- }
-
- // Derive the subscription URL. If the SUBSCRIPTION_URL setting is specified, uses that value. Otherwise
- // assumes the current window location with an appropriate websocket protocol.
- var subscribeURL =
- location.origin.replace(/^http/, "ws") +
- (GRAPHENE_SETTINGS.subscriptionPath || location.pathname);
-
- // Create a subscription client.
- var subscriptionClient = new SubscriptionsTransportWs.SubscriptionClient(
- subscribeURL,
- {
- // Reconnect after any interruptions.
- reconnect: true,
- // Delay socket initialization until the first subscription is started.
- lazy: true,
- },
- );
-
- // Keep a reference to the currently-active subscription, if available.
- var activeSubscription = null;
-
- // Define a GraphQL fetcher that can intelligently route queries based on the operation type.
- function graphQLFetcher(graphQLParams, opts) {
- var operationType = getOperationType(graphQLParams);
-
- // If we're about to execute a new operation, and we have an active subscription,
- // unsubscribe before continuing.
- if (activeSubscription) {
- activeSubscription.unsubscribe();
- activeSubscription = null;
- }
-
- if (operationType === "subscription") {
- return {
- subscribe: function (observer) {
- activeSubscription = subscriptionClient;
- return subscriptionClient.request(graphQLParams, opts).subscribe(observer);
- },
- };
- } else {
- return httpClient(graphQLParams, opts);
- }
- }
-
- // Determine the type of operation being executed for a given set of GraphQL parameters.
- function getOperationType(graphQLParams) {
- // Run a regex against the query to determine the operation type (query, mutation, subscription).
- var operationRegex = new RegExp(
- // Look for lines that start with an operation keyword, ignoring whitespace.
- "^\\s*(query|mutation|subscription)\\s*" +
- // The operation keyword should be followed by whitespace and the operationName in the GraphQL parameters (if available).
- (graphQLParams.operationName ? ("\\s+" + graphQLParams.operationName) : "") +
- // The line should eventually encounter an opening curly brace.
- "[^\\{]*\\{",
- // Enable multiline matching.
- "m",
- );
- var match = operationRegex.exec(graphQLParams.query);
- if (!match) {
- return "query";
- }
-
- return match[1];
- }
-
- // When the query and variables string is edited, update the URL bar so
- // that it can be easily shared.
- function onEditQuery(newQuery) {
- parameters.query = newQuery;
- updateURL();
- }
- function onEditVariables(newVariables) {
- parameters.variables = newVariables;
- updateURL();
- }
- function onEditOperationName(newOperationName) {
- parameters.operationName = newOperationName;
- updateURL();
- }
- function updateURL() {
- history.replaceState(null, null, locationQuery(parameters));
- }
- var options = {
- fetcher: graphQLFetcher,
- onEditQuery: onEditQuery,
- onEditVariables: onEditVariables,
- onEditOperationName: onEditOperationName,
- headerEditorEnabled: GRAPHENE_SETTINGS.graphiqlHeaderEditorEnabled,
- query: parameters.query,
- };
- if (parameters.variables) {
- options.variables = parameters.variables;
- }
- if (parameters.operation_name) {
- options.operationName = parameters.operation_name;
- }
- // Render