mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-10-31 16:07:36 +03:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "main" and "v3.2.2" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										4
									
								
								.github/workflows/deploy.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/deploy.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -15,9 +15,9 @@ jobs: | |||
|     needs: [lint, tests] | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Set up Python 3.11 | ||||
|       uses: actions/setup-python@v5 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: '3.11' | ||||
|     - name: Build wheel and source tarball | ||||
|  |  | |||
							
								
								
									
										4
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/lint.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -11,9 +11,9 @@ jobs: | |||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Set up Python 3.11 | ||||
|       uses: actions/setup-python@v5 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: '3.11' | ||||
|     - name: Install dependencies | ||||
|  |  | |||
							
								
								
									
										14
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								.github/workflows/tests.yml
									
									
									
									
										vendored
									
									
								
							|  | @ -12,7 +12,7 @@ jobs: | |||
|     strategy: | ||||
|       max-parallel: 4 | ||||
|       matrix: | ||||
|         django: ["3.2", "4.2", "5.0", "5.1", "5.2"] | ||||
|         django: ["3.2", "4.2", "5.0"] | ||||
|         python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] | ||||
|         exclude: | ||||
|           - django: "3.2" | ||||
|  | @ -23,18 +23,10 @@ jobs: | |||
|             python-version: "3.8" | ||||
|           - django: "5.0" | ||||
|             python-version: "3.9" | ||||
|           - django: "5.1" | ||||
|             python-version: "3.8" | ||||
|           - django: "5.1" | ||||
|             python-version: "3.9" | ||||
|           - django: "5.2" | ||||
|             python-version: "3.8" | ||||
|           - django: "5.2" | ||||
|             python-version: "3.9" | ||||
|     steps: | ||||
|     - uses: actions/checkout@v4 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Set up Python ${{ matrix.python-version }} | ||||
|       uses: actions/setup-python@v5 | ||||
|       uses: actions/setup-python@v4 | ||||
|       with: | ||||
|         python-version: ${{ matrix.python-version }} | ||||
|     - name: Install dependencies | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ target-version = "py38" | |||
| [per-file-ignores] | ||||
| # Ignore unused imports (F401) in these files | ||||
| "__init__.py" = ["F401"] | ||||
| "graphene_django/compat.py" = ["F401"] | ||||
| 
 | ||||
| [isort] | ||||
| known-first-party = ["graphene", "graphene-django"] | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| graphene>=2.1,<3 | ||||
| graphene-django>=2.1,<3 | ||||
| graphql-core>=2.1,<3 | ||||
| django==4.2.18 | ||||
| django==3.2.25 | ||||
| django-filter>=2 | ||||
|  |  | |||
|  | @ -28,5 +28,3 @@ TEMPLATES = [ | |||
| GRAPHENE = {"SCHEMA": "graphene_django.tests.schema_view.schema"} | ||||
| 
 | ||||
| ROOT_URLCONF = "graphene_django.tests.urls" | ||||
| 
 | ||||
| USE_TZ = True | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import graphene | ||||
| from graphene import Schema, relay | ||||
| from graphene import Schema, relay, resolve_only_args | ||||
| from graphene_django import DjangoConnectionField, DjangoObjectType | ||||
| 
 | ||||
| from .data import create_ship, get_empire, get_faction, get_rebels, get_ship, get_ships | ||||
|  | @ -62,13 +62,16 @@ class Query(graphene.ObjectType): | |||
|     node = relay.Node.Field() | ||||
|     ships = DjangoConnectionField(Ship, description="All the ships.") | ||||
| 
 | ||||
|     def resolve_ships(self, info): | ||||
|     @resolve_only_args | ||||
|     def resolve_ships(self): | ||||
|         return get_ships() | ||||
| 
 | ||||
|     def resolve_rebels(self, info): | ||||
|     @resolve_only_args | ||||
|     def resolve_rebels(self): | ||||
|         return get_rebels() | ||||
| 
 | ||||
|     def resolve_empire(self, info): | ||||
|     @resolve_only_args | ||||
|     def resolve_empire(self): | ||||
|         return get_empire() | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ from .fields import DjangoConnectionField, DjangoListField | |||
| from .types import DjangoObjectType | ||||
| from .utils import bypass_get_queryset | ||||
| 
 | ||||
| __version__ = "3.2.3" | ||||
| __version__ = "3.2.2" | ||||
| 
 | ||||
| __all__ = [ | ||||
|     "__version__", | ||||
|  |  | |||
|  | @ -1,11 +1,10 @@ | |||
| import sys | ||||
| from collections.abc import Callable | ||||
| from pathlib import PurePath | ||||
| 
 | ||||
| # For backwards compatibility, we import JSONField to have it available for import via | ||||
| # this compat module (https://github.com/graphql-python/graphene-django/issues/1428). | ||||
| # Django's JSONField is available in Django 3.2+ (the minimum version we support) | ||||
| from django.db.models import Choices, JSONField | ||||
| from django.db.models import JSONField | ||||
| 
 | ||||
| 
 | ||||
| class MissingType: | ||||
|  | @ -43,23 +42,3 @@ except ImportError: | |||
| 
 | ||||
|     else: | ||||
|         ArrayField = MissingType | ||||
| 
 | ||||
| 
 | ||||
| try: | ||||
|     from django.utils.choices import normalize_choices | ||||
| except ImportError: | ||||
| 
 | ||||
|     def normalize_choices(choices): | ||||
|         if isinstance(choices, type) and issubclass(choices, Choices): | ||||
|             choices = choices.choices | ||||
| 
 | ||||
|         if isinstance(choices, Callable): | ||||
|             choices = choices() | ||||
| 
 | ||||
|         # In restframework==3.15.0, choices are not passed | ||||
|         # as OrderedDict anymore, so it's safer to check | ||||
|         # for a dict | ||||
|         if isinstance(choices, dict): | ||||
|             choices = choices.items() | ||||
| 
 | ||||
|         return choices | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| import inspect | ||||
| from collections.abc import Callable | ||||
| from functools import partial, singledispatch, wraps | ||||
| 
 | ||||
| from django.db import models | ||||
|  | @ -36,7 +37,7 @@ except ImportError: | |||
|     from graphql import assert_valid_name as assert_name | ||||
| from graphql.pyutils import register_description | ||||
| 
 | ||||
| from .compat import ArrayField, HStoreField, RangeField, normalize_choices | ||||
| from .compat import ArrayField, HStoreField, RangeField | ||||
| from .fields import DjangoConnectionField, DjangoListField | ||||
| from .settings import graphene_settings | ||||
| from .utils.str_converters import to_const | ||||
|  | @ -60,24 +61,6 @@ class BlankValueField(Field): | |||
|         return blank_field_wrapper(resolver) | ||||
| 
 | ||||
| 
 | ||||
| class EnumValueField(BlankValueField): | ||||
|     def wrap_resolve(self, parent_resolver): | ||||
|         resolver = super().wrap_resolve(parent_resolver) | ||||
| 
 | ||||
|         # create custom resolver | ||||
|         def enum_field_wrapper(func): | ||||
|             @wraps(func) | ||||
|             def wrapped_resolver(*args, **kwargs): | ||||
|                 return_value = func(*args, **kwargs) | ||||
|                 if isinstance(return_value, models.Choices): | ||||
|                     return_value = return_value.value | ||||
|                 return return_value | ||||
| 
 | ||||
|             return wrapped_resolver | ||||
| 
 | ||||
|         return enum_field_wrapper(resolver) | ||||
| 
 | ||||
| 
 | ||||
| def convert_choice_name(name): | ||||
|     name = to_const(force_str(name)) | ||||
|     try: | ||||
|  | @ -89,7 +72,15 @@ def convert_choice_name(name): | |||
| 
 | ||||
| def get_choices(choices): | ||||
|     converted_names = [] | ||||
|     choices = normalize_choices(choices) | ||||
|     if isinstance(choices, Callable): | ||||
|         choices = choices() | ||||
| 
 | ||||
|     # In restframework==3.15.0, choices are not passed | ||||
|     # as OrderedDict anymore, so it's safer to check | ||||
|     # for a dict | ||||
|     if isinstance(choices, dict): | ||||
|         choices = choices.items() | ||||
| 
 | ||||
|     for value, help_text in choices: | ||||
|         if isinstance(help_text, (tuple, list)): | ||||
|             yield from get_choices(help_text) | ||||
|  | @ -166,7 +157,7 @@ def convert_django_field_with_choices( | |||
| 
 | ||||
|         converted = EnumCls( | ||||
|             description=get_django_field_description(field), required=required | ||||
|         ).mount_as(EnumValueField) | ||||
|         ).mount_as(BlankValueField) | ||||
|     else: | ||||
|         converted = convert_django_field(field, registry) | ||||
|     if registry is not None: | ||||
|  | @ -199,13 +190,19 @@ def convert_field_to_string(field, registry=None): | |||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| @convert_django_field.register(models.AutoField) | ||||
| @convert_django_field.register(models.BigAutoField) | ||||
| @convert_django_field.register(models.SmallAutoField) | ||||
| @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( | ||||
|  |  | |||
|  | @ -247,7 +247,7 @@ class DjangoConnectionField(ConnectionField): | |||
|     def wrap_resolve(self, parent_resolver): | ||||
|         return partial( | ||||
|             self.connection_resolver, | ||||
|             self.resolver or parent_resolver, | ||||
|             parent_resolver, | ||||
|             self.connection_type, | ||||
|             self.get_manager(), | ||||
|             self.get_queryset_resolver(), | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| from django import VERSION as DJANGO_VERSION, forms | ||||
| from django import forms | ||||
| from pytest import raises | ||||
| 
 | ||||
| from graphene import ( | ||||
|  | @ -19,16 +19,12 @@ from graphene import ( | |||
| from ..converter import convert_form_field | ||||
| 
 | ||||
| 
 | ||||
| def assert_conversion(django_field, graphene_field, *args, **kwargs): | ||||
|     # Arrange | ||||
|     help_text = kwargs.setdefault("help_text", "Custom Help Text") | ||||
|     field = django_field(*args, **kwargs) | ||||
|     # Act | ||||
| def assert_conversion(django_field, graphene_field, *args): | ||||
|     field = django_field(*args, help_text="Custom Help Text") | ||||
|     graphene_type = convert_form_field(field) | ||||
|     # Assert | ||||
|     assert isinstance(graphene_type, graphene_field) | ||||
|     field = graphene_type.Field() | ||||
|     assert field.description == help_text | ||||
|     assert field.description == "Custom Help Text" | ||||
|     return field | ||||
| 
 | ||||
| 
 | ||||
|  | @ -63,12 +59,7 @@ def test_should_slug_convert_string(): | |||
| 
 | ||||
| 
 | ||||
| def test_should_url_convert_string(): | ||||
|     kwargs = {} | ||||
|     if DJANGO_VERSION >= (5, 0): | ||||
|         # silence RemovedInDjango60Warning | ||||
|         kwargs["assume_scheme"] = "https" | ||||
| 
 | ||||
|     assert_conversion(forms.URLField, String, **kwargs) | ||||
|     assert_conversion(forms.URLField, String) | ||||
| 
 | ||||
| 
 | ||||
| def test_should_choice_convert_string(): | ||||
|  | @ -84,7 +75,8 @@ def test_should_regex_convert_string(): | |||
| 
 | ||||
| 
 | ||||
| def test_should_uuid_convert_string(): | ||||
|     assert_conversion(forms.UUIDField, UUID) | ||||
|     if hasattr(forms, "UUIDField"): | ||||
|         assert_conversion(forms.UUIDField, UUID) | ||||
| 
 | ||||
| 
 | ||||
| def test_should_integer_convert_int(): | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ from graphene import ID | |||
| from graphene.types.inputobjecttype import InputObjectType | ||||
| from graphene.utils.str_converters import to_camel_case | ||||
| 
 | ||||
| from ..converter import EnumValueField | ||||
| from ..converter import BlankValueField | ||||
| from ..types import ErrorType  # noqa Import ErrorType for backwards compatibility | ||||
| from .mutation import fields_for_form | ||||
| 
 | ||||
|  | @ -57,10 +57,11 @@ class DjangoFormInputObjectType(InputObjectType): | |||
|             if ( | ||||
|                 object_type | ||||
|                 and name in object_type._meta.fields | ||||
|                 and isinstance(object_type._meta.fields[name], EnumValueField) | ||||
|                 and isinstance(object_type._meta.fields[name], BlankValueField) | ||||
|             ): | ||||
|                 # Field type EnumValueField here means that field | ||||
|                 # Field type BlankValueField here means that field | ||||
|                 # with choices have been converted to enum | ||||
|                 # (BlankValueField is using only for that task ?) | ||||
|                 setattr(cls, name, cls.get_enum_cnv_cls_instance(name, object_type)) | ||||
|             elif ( | ||||
|                 object_type | ||||
|  |  | |||
|  | @ -96,7 +96,8 @@ def test_should_regex_convert_string(): | |||
| 
 | ||||
| 
 | ||||
| def test_should_uuid_convert_string(): | ||||
|     assert_conversion(serializers.UUIDField, graphene.String) | ||||
|     if hasattr(serializers, "UUIDField"): | ||||
|         assert_conversion(serializers.UUIDField, graphene.String) | ||||
| 
 | ||||
| 
 | ||||
| def test_should_model_convert_field(): | ||||
|  |  | |||
|  | @ -1,38 +1,9 @@ | |||
| import django | ||||
| from django.db import models | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
| 
 | ||||
| CHOICES = ((1, "this"), (2, _("that"))) | ||||
| 
 | ||||
| 
 | ||||
| def get_choices_as_class(choices_class): | ||||
|     if django.VERSION >= (5, 0): | ||||
|         return choices_class | ||||
|     else: | ||||
|         return choices_class.choices | ||||
| 
 | ||||
| 
 | ||||
| def get_choices_as_callable(choices_class): | ||||
|     if django.VERSION >= (5, 0): | ||||
| 
 | ||||
|         def choices(): | ||||
|             return choices_class.choices | ||||
| 
 | ||||
|         return choices | ||||
|     else: | ||||
|         return choices_class.choices | ||||
| 
 | ||||
| 
 | ||||
| class TypedIntChoice(models.IntegerChoices): | ||||
|     CHOICE_THIS = 1 | ||||
|     CHOICE_THAT = 2 | ||||
| 
 | ||||
| 
 | ||||
| class TypedStrChoice(models.TextChoices): | ||||
|     CHOICE_THIS = "this" | ||||
|     CHOICE_THAT = "that" | ||||
| 
 | ||||
| 
 | ||||
| class Person(models.Model): | ||||
|     name = models.CharField(max_length=30) | ||||
|     parent = models.ForeignKey( | ||||
|  | @ -80,21 +51,6 @@ class Reporter(models.Model): | |||
|     email = models.EmailField() | ||||
|     pets = models.ManyToManyField("self") | ||||
|     a_choice = models.IntegerField(choices=CHOICES, null=True, blank=True) | ||||
|     typed_choice = models.IntegerField( | ||||
|         choices=TypedIntChoice.choices, | ||||
|         null=True, | ||||
|         blank=True, | ||||
|     ) | ||||
|     class_choice = models.IntegerField( | ||||
|         choices=get_choices_as_class(TypedIntChoice), | ||||
|         null=True, | ||||
|         blank=True, | ||||
|     ) | ||||
|     callable_choice = models.IntegerField( | ||||
|         choices=get_choices_as_callable(TypedStrChoice), | ||||
|         null=True, | ||||
|         blank=True, | ||||
|     ) | ||||
|     objects = models.Manager() | ||||
|     doe_objects = DoeReporterManager() | ||||
|     fans = models.ManyToManyField(Person) | ||||
|  |  | |||
|  | @ -25,7 +25,7 @@ from ..converter import ( | |||
| ) | ||||
| from ..registry import Registry | ||||
| from ..types import DjangoObjectType | ||||
| from .models import Article, Film, FilmDetails, Reporter, TypedIntChoice, TypedStrChoice | ||||
| from .models import Article, Film, FilmDetails, Reporter | ||||
| 
 | ||||
| # from graphene.core.types.custom_scalars import DateTime, Time, JSONString | ||||
| 
 | ||||
|  | @ -53,8 +53,9 @@ def assert_conversion(django_field, graphene_field, *args, **kwargs): | |||
| 
 | ||||
| 
 | ||||
| def test_should_unknown_django_field_raise_exception(): | ||||
|     with raises(Exception, match="Don't know how to convert the Django field"): | ||||
|     with raises(Exception) as excinfo: | ||||
|         convert_django_field(None) | ||||
|     assert "Don't know how to convert the Django field" in str(excinfo.value) | ||||
| 
 | ||||
| 
 | ||||
| def test_should_date_time_convert_string(): | ||||
|  | @ -114,7 +115,8 @@ def test_should_big_auto_convert_id(): | |||
| 
 | ||||
| 
 | ||||
| def test_should_small_auto_convert_id(): | ||||
|     assert_conversion(models.SmallAutoField, graphene.ID, primary_key=True) | ||||
|     if hasattr(models, "SmallAutoField"): | ||||
|         assert_conversion(models.SmallAutoField, graphene.ID, primary_key=True) | ||||
| 
 | ||||
| 
 | ||||
| def test_should_uuid_convert_id(): | ||||
|  | @ -164,14 +166,14 @@ def test_field_with_choices_convert_enum(): | |||
|         help_text="Language", choices=(("es", "Spanish"), ("en", "English")) | ||||
|     ) | ||||
| 
 | ||||
|     class ChoicesModel(models.Model): | ||||
|     class TranslatedModel(models.Model): | ||||
|         language = field | ||||
| 
 | ||||
|         class Meta: | ||||
|             app_label = "test" | ||||
| 
 | ||||
|     graphene_type = convert_django_field_with_choices(field).type.of_type | ||||
|     assert graphene_type._meta.name == "TestChoicesModelLanguageChoices" | ||||
|     assert graphene_type._meta.name == "TestTranslatedModelLanguageChoices" | ||||
|     assert graphene_type._meta.enum.__members__["ES"].value == "es" | ||||
|     assert graphene_type._meta.enum.__members__["ES"].description == "Spanish" | ||||
|     assert graphene_type._meta.enum.__members__["EN"].value == "en" | ||||
|  | @ -184,14 +186,14 @@ def test_field_with_callable_choices_convert_enum(): | |||
| 
 | ||||
|     field = models.CharField(help_text="Language", choices=get_choices) | ||||
| 
 | ||||
|     class CallableChoicesModel(models.Model): | ||||
|     class TranslatedModel(models.Model): | ||||
|         language = field | ||||
| 
 | ||||
|         class Meta: | ||||
|             app_label = "test" | ||||
| 
 | ||||
|     graphene_type = convert_django_field_with_choices(field).type.of_type | ||||
|     assert graphene_type._meta.name == "TestCallableChoicesModelLanguageChoices" | ||||
|     assert graphene_type._meta.name == "TestTranslatedModelLanguageChoices" | ||||
|     assert graphene_type._meta.enum.__members__["ES"].value == "es" | ||||
|     assert graphene_type._meta.enum.__members__["ES"].description == "Spanish" | ||||
|     assert graphene_type._meta.enum.__members__["EN"].value == "en" | ||||
|  | @ -441,102 +443,35 @@ def test_choice_enum_blank_value(): | |||
|     class ReporterType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = Reporter | ||||
|             fields = ("callable_choice",) | ||||
|             fields = ( | ||||
|                 "first_name", | ||||
|                 "a_choice", | ||||
|             ) | ||||
| 
 | ||||
|     class Query(graphene.ObjectType): | ||||
|         reporter = graphene.Field(ReporterType) | ||||
| 
 | ||||
|         def resolve_reporter(root, info): | ||||
|             # return a model instance with blank choice field value | ||||
|             return Reporter(callable_choice="") | ||||
|             return Reporter.objects.first() | ||||
| 
 | ||||
|     schema = graphene.Schema(query=Query) | ||||
| 
 | ||||
|     # Create model with empty choice option | ||||
|     Reporter.objects.create( | ||||
|         first_name="Bridget", last_name="Jones", email="bridget@example.com" | ||||
|     ) | ||||
| 
 | ||||
|     result = schema.execute( | ||||
|         """ | ||||
|         query { | ||||
|             reporter { | ||||
|                 callableChoice | ||||
|                 firstName | ||||
|                 aChoice | ||||
|             } | ||||
|         } | ||||
|     """ | ||||
|     ) | ||||
|     assert not result.errors | ||||
|     assert result.data == { | ||||
|         "reporter": {"callableChoice": None}, | ||||
|         "reporter": {"firstName": "Bridget", "aChoice": None}, | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| def test_typed_choice_value(): | ||||
|     """Test that typed choices fields are resolved correctly to the enum values""" | ||||
| 
 | ||||
|     class ReporterType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = Reporter | ||||
|             fields = ("typed_choice", "class_choice", "callable_choice") | ||||
| 
 | ||||
|     class Query(graphene.ObjectType): | ||||
|         reporter = graphene.Field(ReporterType) | ||||
| 
 | ||||
|         def resolve_reporter(root, info): | ||||
|             # assign choice values to the fields instead of their str or int values | ||||
|             return Reporter( | ||||
|                 typed_choice=TypedIntChoice.CHOICE_THIS, | ||||
|                 class_choice=TypedIntChoice.CHOICE_THAT, | ||||
|                 callable_choice=TypedStrChoice.CHOICE_THIS, | ||||
|             ) | ||||
| 
 | ||||
|     class CreateReporter(graphene.Mutation): | ||||
|         reporter = graphene.Field(ReporterType) | ||||
| 
 | ||||
|         def mutate(root, info, **kwargs): | ||||
|             return CreateReporter( | ||||
|                 reporter=Reporter( | ||||
|                     typed_choice=TypedIntChoice.CHOICE_THIS, | ||||
|                     class_choice=TypedIntChoice.CHOICE_THAT, | ||||
|                     callable_choice=TypedStrChoice.CHOICE_THIS, | ||||
|                 ), | ||||
|             ) | ||||
| 
 | ||||
|     class Mutation(graphene.ObjectType): | ||||
|         create_reporter = CreateReporter.Field() | ||||
| 
 | ||||
|     schema = graphene.Schema(query=Query, mutation=Mutation) | ||||
| 
 | ||||
|     reporter_fragment = """ | ||||
|         fragment reporter on ReporterType { | ||||
|             typedChoice | ||||
|             classChoice | ||||
|             callableChoice | ||||
|         } | ||||
|     """ | ||||
| 
 | ||||
|     expected_reporter = { | ||||
|         "typedChoice": "A_1", | ||||
|         "classChoice": "A_2", | ||||
|         "callableChoice": "THIS", | ||||
|     } | ||||
| 
 | ||||
|     result = schema.execute( | ||||
|         reporter_fragment | ||||
|         + """ | ||||
|         query { | ||||
|             reporter { ...reporter } | ||||
|         } | ||||
|         """ | ||||
|     ) | ||||
|     assert not result.errors | ||||
|     assert result.data["reporter"] == expected_reporter | ||||
| 
 | ||||
|     result = schema.execute( | ||||
|         reporter_fragment | ||||
|         + """ | ||||
|         mutation { | ||||
|             createReporter { | ||||
|                 reporter { ...reporter } | ||||
|             } | ||||
|         } | ||||
|         """ | ||||
|     ) | ||||
|     assert not result.errors | ||||
|     assert result.data["createReporter"]["reporter"] == expected_reporter | ||||
|  |  | |||
|  | @ -26,7 +26,6 @@ class TestShouldCallGetQuerySetOnForeignKey: | |||
|         class ReporterType(DjangoObjectType): | ||||
|             class Meta: | ||||
|                 model = Reporter | ||||
|                 fields = "__all__" | ||||
| 
 | ||||
|             @classmethod | ||||
|             def get_queryset(cls, queryset, info): | ||||
|  | @ -37,7 +36,6 @@ class TestShouldCallGetQuerySetOnForeignKey: | |||
|         class ArticleType(DjangoObjectType): | ||||
|             class Meta: | ||||
|                 model = Article | ||||
|                 fields = "__all__" | ||||
| 
 | ||||
|             @classmethod | ||||
|             def get_queryset(cls, queryset, info): | ||||
|  | @ -202,7 +200,6 @@ class TestShouldCallGetQuerySetOnForeignKeyNode: | |||
|         class ReporterType(DjangoObjectType): | ||||
|             class Meta: | ||||
|                 model = Reporter | ||||
|                 fields = "__all__" | ||||
|                 interfaces = (Node,) | ||||
| 
 | ||||
|             @classmethod | ||||
|  | @ -214,7 +211,6 @@ class TestShouldCallGetQuerySetOnForeignKeyNode: | |||
|         class ArticleType(DjangoObjectType): | ||||
|             class Meta: | ||||
|                 model = Article | ||||
|                 fields = "__all__" | ||||
|                 interfaces = (Node,) | ||||
| 
 | ||||
|             @classmethod | ||||
|  | @ -374,7 +370,6 @@ class TestShouldCallGetQuerySetOnOneToOne: | |||
|         class FilmDetailsType(DjangoObjectType): | ||||
|             class Meta: | ||||
|                 model = FilmDetails | ||||
|                 fields = "__all__" | ||||
| 
 | ||||
|             @classmethod | ||||
|             def get_queryset(cls, queryset, info): | ||||
|  | @ -385,7 +380,6 @@ class TestShouldCallGetQuerySetOnOneToOne: | |||
|         class FilmType(DjangoObjectType): | ||||
|             class Meta: | ||||
|                 model = Film | ||||
|                 fields = "__all__" | ||||
| 
 | ||||
|             @classmethod | ||||
|             def get_queryset(cls, queryset, info): | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| import base64 | ||||
| import datetime | ||||
| from unittest.mock import ANY, Mock | ||||
| 
 | ||||
| import pytest | ||||
| from django.db import models | ||||
|  | @ -2001,62 +2000,14 @@ def test_connection_should_succeed_if_last_higher_than_number_of_objects(): | |||
|     assert result.data == expected | ||||
| 
 | ||||
| 
 | ||||
| def test_connection_should_call_resolver_function(): | ||||
|     resolver_mock = Mock( | ||||
|         name="resolver", | ||||
|         return_value=[ | ||||
|             Reporter(first_name="Some", last_name="One"), | ||||
|             Reporter(first_name="John", last_name="Doe"), | ||||
|         ], | ||||
|     ) | ||||
| 
 | ||||
|     class ReporterType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = Reporter | ||||
|             fields = "__all__" | ||||
|             interfaces = [Node] | ||||
| 
 | ||||
|     class Query(graphene.ObjectType): | ||||
|         reporters = DjangoConnectionField(ReporterType, resolver=resolver_mock) | ||||
| 
 | ||||
|     schema = graphene.Schema(query=Query) | ||||
|     result = schema.execute( | ||||
|         """ | ||||
|         query { | ||||
|             reporters { | ||||
|                 edges { | ||||
|                     node { | ||||
|                         firstName | ||||
|                         lastName | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         """ | ||||
|     ) | ||||
| 
 | ||||
|     resolver_mock.assert_called_once_with(None, ANY) | ||||
|     assert not result.errors | ||||
|     assert result.data == { | ||||
|         "reporters": { | ||||
|             "edges": [ | ||||
|                 {"node": {"firstName": "Some", "lastName": "One"}}, | ||||
|                 {"node": {"firstName": "John", "lastName": "Doe"}}, | ||||
|             ], | ||||
|         }, | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| def test_should_query_nullable_foreign_key(): | ||||
|     class PetType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = Pet | ||||
|             fields = "__all__" | ||||
| 
 | ||||
|     class PersonType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = Person | ||||
|             fields = "__all__" | ||||
| 
 | ||||
|     class Query(graphene.ObjectType): | ||||
|         pet = graphene.Field(PetType, name=graphene.String(required=True)) | ||||
|  | @ -2071,8 +2022,10 @@ def test_should_query_nullable_foreign_key(): | |||
|     schema = graphene.Schema(query=Query) | ||||
| 
 | ||||
|     person = Person.objects.create(name="Jane") | ||||
|     Pet.objects.create(name="Stray dog", age=1) | ||||
|     Pet.objects.create(name="Jane's dog", owner=person, age=1) | ||||
|     [ | ||||
|         Pet.objects.create(name="Stray dog", age=1), | ||||
|         Pet.objects.create(name="Jane's dog", owner=person, age=1), | ||||
|     ] | ||||
| 
 | ||||
|     query_pet = """ | ||||
|         query getPet($name: String!) { | ||||
|  | @ -2115,7 +2068,6 @@ def test_should_query_nullable_one_to_one_relation_with_custom_resolver(): | |||
|     class FilmType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = Film | ||||
|             fields = "__all__" | ||||
| 
 | ||||
|         @classmethod | ||||
|         def get_queryset(cls, queryset, info): | ||||
|  | @ -2124,7 +2076,6 @@ def test_should_query_nullable_one_to_one_relation_with_custom_resolver(): | |||
|     class FilmDetailsType(DjangoObjectType): | ||||
|         class Meta: | ||||
|             model = FilmDetails | ||||
|             fields = "__all__" | ||||
| 
 | ||||
|         @classmethod | ||||
|         def get_queryset(cls, queryset, info): | ||||
|  |  | |||
|  | @ -40,9 +40,6 @@ def test_should_map_fields_correctly(): | |||
|         "email", | ||||
|         "pets", | ||||
|         "a_choice", | ||||
|         "typed_choice", | ||||
|         "class_choice", | ||||
|         "callable_choice", | ||||
|         "fans", | ||||
|         "reporter_type", | ||||
|     ] | ||||
|  |  | |||
|  | @ -77,9 +77,6 @@ def test_django_objecttype_map_correct_fields(): | |||
|         "email", | ||||
|         "pets", | ||||
|         "a_choice", | ||||
|         "typed_choice", | ||||
|         "class_choice", | ||||
|         "callable_choice", | ||||
|         "fans", | ||||
|         "reporter_type", | ||||
|     ] | ||||
|  | @ -189,9 +186,6 @@ def test_schema_representation(): | |||
|           email: String! | ||||
|           pets: [Reporter!]! | ||||
|           aChoice: TestsReporterAChoiceChoices | ||||
|           typedChoice: TestsReporterTypedChoiceChoices | ||||
|           classChoice: TestsReporterClassChoiceChoices | ||||
|           callableChoice: TestsReporterCallableChoiceChoices | ||||
|           reporterType: TestsReporterReporterTypeChoices | ||||
|           articles(offset: Int, before: String, after: String, first: Int, last: Int): ArticleConnection! | ||||
|         } | ||||
|  | @ -205,33 +199,6 @@ def test_schema_representation(): | |||
|           A_2 | ||||
|         } | ||||
| 
 | ||||
|         \"""An enumeration.\""" | ||||
|         enum TestsReporterTypedChoiceChoices { | ||||
|           \"""Choice This\""" | ||||
|           A_1 | ||||
| 
 | ||||
|           \"""Choice That\""" | ||||
|           A_2 | ||||
|         } | ||||
| 
 | ||||
|         \"""An enumeration.\""" | ||||
|         enum TestsReporterClassChoiceChoices { | ||||
|           \"""Choice This\""" | ||||
|           A_1 | ||||
| 
 | ||||
|           \"""Choice That\""" | ||||
|           A_2 | ||||
|         } | ||||
| 
 | ||||
|         \"""An enumeration.\""" | ||||
|         enum TestsReporterCallableChoiceChoices { | ||||
|           \"""Choice This\""" | ||||
|           THIS | ||||
| 
 | ||||
|           \"""Choice That\""" | ||||
|           THAT | ||||
|         } | ||||
| 
 | ||||
|         \"""An enumeration.\""" | ||||
|         enum TestsReporterReporterTypeChoices { | ||||
|           \"""Regular\""" | ||||
|  |  | |||
|  | @ -111,7 +111,24 @@ def is_valid_django_model(model): | |||
| 
 | ||||
| 
 | ||||
| def import_single_dispatch(): | ||||
|     from functools import singledispatch | ||||
|     try: | ||||
|         from functools import singledispatch | ||||
|     except ImportError: | ||||
|         singledispatch = None | ||||
| 
 | ||||
|     if not singledispatch: | ||||
|         try: | ||||
|             from singledispatch import singledispatch | ||||
|         except ImportError: | ||||
|             pass | ||||
| 
 | ||||
|     if not singledispatch: | ||||
|         raise Exception( | ||||
|             "It seems your python version does not include " | ||||
|             "functools.singledispatch. Please install the 'singledispatch' " | ||||
|             "package. More information here: " | ||||
|             "https://pypi.python.org/pypi/singledispatch" | ||||
|         ) | ||||
| 
 | ||||
|     return singledispatch | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,7 +10,3 @@ omit = */tests/* | |||
| [tool:pytest] | ||||
| DJANGO_SETTINGS_MODULE = examples.django_test_settings | ||||
| addopts = --random-order | ||||
| filterwarnings = | ||||
|     error | ||||
|     # we can't do anything about the DeprecationWarning about typing.ByteString in graphql | ||||
|     default:'typing\.ByteString' is deprecated:DeprecationWarning:graphql\.pyutils\.is_iterable | ||||
|  |  | |||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							|  | @ -55,8 +55,6 @@ setup( | |||
|         "Framework :: Django :: 3.2", | ||||
|         "Framework :: Django :: 4.1", | ||||
|         "Framework :: Django :: 4.2", | ||||
|         "Framework :: Django :: 5.1", | ||||
|         "Framework :: Django :: 5.2", | ||||
|     ], | ||||
|     keywords="api graphql protocol rest relay graphene", | ||||
|     packages=find_packages(exclude=["tests", "examples", "examples.*"]), | ||||
|  |  | |||
							
								
								
									
										7
									
								
								tox.ini
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								tox.ini
									
									
									
									
									
								
							|  | @ -2,7 +2,8 @@ | |||
| envlist = | ||||
|     py{38,39,310}-django32 | ||||
|     py{38,39}-django42 | ||||
|     py{310,311,312}-django{42,50,51,main} | ||||
|     py{310,311}-django{42,50,main} | ||||
|     py312-django{42,50,main} | ||||
|     pre-commit | ||||
| 
 | ||||
| [gh-actions] | ||||
|  | @ -18,8 +19,6 @@ DJANGO = | |||
|     3.2: django32 | ||||
|     4.2: django42 | ||||
|     5.0: django50 | ||||
|     5.1: django51 | ||||
|     5.2: django52 | ||||
|     main: djangomain | ||||
| 
 | ||||
| [testenv] | ||||
|  | @ -34,8 +33,6 @@ deps = | |||
|     django32: Django>=3.2,<4.0 | ||||
|     django42: Django>=4.2,<4.3 | ||||
|     django50: Django>=5.0,<5.1 | ||||
|     django51: Django>=5.1,<5.2 | ||||
|     django52: Django>=5.2,<6.0 | ||||
|     djangomain: https://github.com/django/django/archive/main.zip | ||||
| commands = {posargs:pytest --cov=graphene_django graphene_django examples} | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user