diff --git a/graphene_django/fields.py b/graphene_django/fields.py index 853fb98..abf6f4c 100644 --- a/graphene_django/fields.py +++ b/graphene_django/fields.py @@ -205,7 +205,9 @@ class DjangoConnectionField(ConnectionField): class DjangoField(Field): """Class to manage permission for fields""" - def __init__(self, type, permissions=(), permissions_resolver=auth_resolver, *args, **kwargs): + def __init__( + self, type, permissions=(), permissions_resolver=auth_resolver, *args, **kwargs + ): """Get permissions to access a field""" super(DjangoField, self).__init__(type, *args, **kwargs) self.permissions = permissions @@ -215,15 +217,23 @@ class DjangoField(Field): """Intercept resolver to analyse permissions""" parent_resolver = super(DjangoField, self).get_resolver(parent_resolver) if self.permissions: - return partial(get_unbound_function(self.permissions_resolver), parent_resolver, self.permissions, None, - None, True) + return partial( + get_unbound_function(self.permissions_resolver), + parent_resolver, + self.permissions, + None, + None, + True, + ) return parent_resolver class DataLoaderField(DjangoField): """Class to manage access to data-loader when resolve the field""" - def __init__(self, type, data_loader, source_loader, load_many=False, *args, **kwargs): + def __init__( + self, type, data_loader, source_loader, load_many=False, *args, **kwargs + ): """ Initialization of data-loader to resolve field :param data_loader: data-loader to resolve field @@ -243,7 +253,9 @@ class DataLoaderField(DjangoField): def resolver_data_loader(self, root, info, *args, **kwargs): """Resolve field through dataloader""" if root: - source_loader = reduce(lambda x, y: getattr(x, y), self.source_loader.split('.'), root) + source_loader = reduce( + lambda x, y: getattr(x, y), self.source_loader.split("."), root + ) else: source_loader = kwargs.get(self.source_loader) diff --git a/graphene_django/forms/converter.py b/graphene_django/forms/converter.py index f83c45f..9fc5b8d 100644 --- a/graphene_django/forms/converter.py +++ b/graphene_django/forms/converter.py @@ -1,7 +1,19 @@ from django import forms from django.core.exceptions import ImproperlyConfigured -from graphene import ID, Boolean, Float, Int, List, String, UUID, Date, DateTime, Time, Enum +from graphene import ( + ID, + Boolean, + Float, + Int, + List, + String, + UUID, + Date, + DateTime, + Time, + Enum, +) from graphene.utils.str_converters import to_camel_case from graphene_django.converter import get_choices @@ -87,7 +99,7 @@ def convert_form_field_to_id(field): def get_form_name(form): """Get form name""" - class_name = str(form.__class__).split('.')[-1] + class_name = str(form.__class__).split(".")[-1] return class_name[:-2] @@ -99,15 +111,20 @@ def convert_form_field_with_choices(field, name=None, form=None): :param form: field's form :return: graphene Field """ - choices = getattr(field, 'choices', None) + choices = getattr(field, "choices", None) # If is a choice field, but not depends on models - if not isinstance(field, (forms.ModelMultipleChoiceField, forms.ModelChoiceField)) and choices: + if ( + not isinstance(field, (forms.ModelMultipleChoiceField, forms.ModelChoiceField)) + and choices + ): if form: - name = to_camel_case("{}_{}".format(get_form_name(form), field.label or name)) + name = to_camel_case( + "{}_{}".format(get_form_name(form), field.label or name) + ) else: name = field.label or name - name = to_camel_case(name.replace(' ', '_')) + name = to_camel_case(name.replace(" ", "_")) 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} @@ -122,5 +139,7 @@ def convert_form_field_with_choices(field, name=None, form=None): return named_choices_descriptions[self.name] enum = Enum(name, list(named_choices), type=EnumWithDescriptionsType) - return enum(description=field.help_text, required=field.required) # pylint: disable=E1102 + return enum( + description=field.help_text, required=field.required + ) # pylint: disable=E1102 return convert_form_field(field) diff --git a/graphene_django/forms/mutation.py b/graphene_django/forms/mutation.py index c78e110..440b1af 100644 --- a/graphene_django/forms/mutation.py +++ b/graphene_django/forms/mutation.py @@ -22,8 +22,8 @@ def fields_for_form(form, only_fields, exclude_fields): 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 exclude_fields # or # name in already_created_fields ) @@ -46,7 +46,10 @@ class BaseDjangoFormMutation(ClientIDMutation): return cls.perform_mutate(form, info) else: errors = [ - ErrorType(field=to_camel_case(key) if key != '__all__' else key, messages=value) + ErrorType( + field=to_camel_case(key) if key != "__all__" else key, + messages=value, + ) for key, value in form.errors.items() ] @@ -81,7 +84,12 @@ class DjangoFormMutation(BaseDjangoFormMutation): @classmethod def __init_subclass_with_meta__( - cls, form_class=None, mirror_input=False, only_fields=(), exclude_fields=(), **options + cls, + form_class=None, + mirror_input=False, + only_fields=(), + exclude_fields=(), + **options ): if not form_class: @@ -122,13 +130,13 @@ class DjangoModelFormMutation(BaseDjangoFormMutation): @classmethod def __init_subclass_with_meta__( - cls, - form_class=None, - model=None, - return_field_name=None, - only_fields=(), - exclude_fields=(), - **options + cls, + form_class=None, + model=None, + return_field_name=None, + only_fields=(), + exclude_fields=(), + **options ): if not form_class: diff --git a/graphene_django/forms/tests/test_converter.py b/graphene_django/forms/tests/test_converter.py index 8251491..b6ee2b7 100644 --- a/graphene_django/forms/tests/test_converter.py +++ b/graphene_django/forms/tests/test_converter.py @@ -115,6 +115,8 @@ def test_should_manytoone_convert_connectionorlist(): def test_should_typed_choice_convert_enum(): - field = forms.TypedChoiceField(choices=(('A', 'Choice A'), ('B', 'Choice B')), label='field') - graphene_type = convert_form_field_with_choices(field, name='field_name') + field = forms.TypedChoiceField( + choices=(("A", "Choice A"), ("B", "Choice B")), label="field" + ) + graphene_type = convert_form_field_with_choices(field, name="field_name") assert isinstance(graphene_type, Enum) diff --git a/graphene_django/forms/tests/test_mutation.py b/graphene_django/forms/tests/test_mutation.py index aaf215b..7384d86 100644 --- a/graphene_django/forms/tests/test_mutation.py +++ b/graphene_django/forms/tests/test_mutation.py @@ -52,6 +52,7 @@ class FilmType(DjangoObjectType): def test_needs_form_class(): with raises(Exception) as exc: + class MyMutation(DjangoFormMutation): pass @@ -180,7 +181,7 @@ class FormMutationTests(TestCase): class MyMutation(DjangoFormMutation): class Meta: form_class = MyForm - exclude_fields = ['text'] + exclude_fields = ["text"] self.assertNotIn("text", MyMutation.Input._meta.fields) self.assertIn("client_mutation_id", MyMutation.Input._meta.fields) @@ -331,7 +332,7 @@ class ModelFormMutationTests(TestCase): self.assertEqual(result.data["petMutation"]["pet"], None) self.assertEqual( result.data["petMutation"]["errors"], - [{"field": "age", "messages": ["Too old"], }], + [{"field": "age", "messages": ["Too old"],}], ) self.assertEqual(Pet.objects.count(), 0) @@ -341,7 +342,7 @@ class ModelFormMutationTests(TestCase): class Meta: form_class = PetForm - result = PetMutation.mutate_and_get_payload(None, None, test_camel='text') + result = PetMutation.mutate_and_get_payload(None, None, test_camel="text") # A pet was not created self.assertEqual(Pet.objects.count(), 0) @@ -349,8 +350,8 @@ class ModelFormMutationTests(TestCase): fields_w_error = {e.field: e.messages for e in result.errors} self.assertEqual(len(result.errors), 3) self.assertIn("test_camel", fields_w_error) - self.assertEqual(fields_w_error['test_camel'], ["Enter a whole number."]) + self.assertEqual(fields_w_error["test_camel"], ["Enter a whole number."]) self.assertIn("name", fields_w_error) - self.assertEqual(fields_w_error['name'], ["This field is required."]) + self.assertEqual(fields_w_error["name"], ["This field is required."]) self.assertIn("age", fields_w_error) - self.assertEqual(fields_w_error['age'], ["This field is required."]) + self.assertEqual(fields_w_error["age"], ["This field is required."]) diff --git a/graphene_django/tests/test_fields.py b/graphene_django/tests/test_fields.py index 6b0f405..4abaf15 100644 --- a/graphene_django/tests/test_fields.py +++ b/graphene_django/tests/test_fields.py @@ -231,15 +231,14 @@ data_loader = DataLoader(batch_load_fn=batch_load_fn) class PermissionFieldTests(TestCase): - def test_permission_field(self): MyType = object() - field = DjangoField(MyType, permissions=['perm1', 'perm2'], source='resolver') + field = DjangoField(MyType, permissions=["perm1", "perm2"], source="resolver") resolver = field.get_resolver(None) class Viewer(object): def has_perm(self, perm): - return perm == 'perm2' + return perm == "perm2" info = mock.Mock(context=mock.Mock(user=Viewer())) @@ -247,7 +246,7 @@ class PermissionFieldTests(TestCase): def test_permission_field_without_permission(self): MyType = object() - field = DjangoField(MyType, permissions=['perm1', 'perm2'], source='resolver') + field = DjangoField(MyType, permissions=["perm1", "perm2"], source="resolver") resolver = field.get_resolver(field.resolver) class Viewer(object): @@ -261,10 +260,11 @@ class PermissionFieldTests(TestCase): class DataLoaderFieldTests(TestCase): - def test_dataloaderfield(self): MyType = object() - data_loader_field = DataLoaderField(data_loader=data_loader, source_loader='key', type=MyType) + data_loader_field = DataLoaderField( + data_loader=data_loader, source_loader="key", type=MyType + ) resolver = data_loader_field.get_resolver(None) instance = MyInstance() @@ -273,7 +273,9 @@ class DataLoaderFieldTests(TestCase): def test_dataloaderfield_many(self): MyType = object() - data_loader_field = DataLoaderField(data_loader=data_loader, source_loader='keys', type=MyType, load_many=True) + data_loader_field = DataLoaderField( + data_loader=data_loader, source_loader="keys", type=MyType, load_many=True + ) resolver = data_loader_field.get_resolver(None) instance = MyInstance() @@ -282,7 +284,9 @@ class DataLoaderFieldTests(TestCase): def test_dataloaderfield_inner_prop(self): MyType = object() - data_loader_field = DataLoaderField(data_loader=data_loader, source_loader='InnerClass.key', type=MyType) + data_loader_field = DataLoaderField( + data_loader=data_loader, source_loader="InnerClass.key", type=MyType + ) resolver = data_loader_field.get_resolver(None) instance = MyInstance() @@ -291,8 +295,12 @@ class DataLoaderFieldTests(TestCase): def test_dataloaderfield_many_inner_prop(self): MyType = object() - data_loader_field = DataLoaderField(data_loader=data_loader, source_loader='InnerClass.keys', type=MyType, - load_many=True) + data_loader_field = DataLoaderField( + data_loader=data_loader, + source_loader="InnerClass.keys", + type=MyType, + load_many=True, + ) resolver = data_loader_field.get_resolver(None) instance = MyInstance() @@ -301,15 +309,19 @@ class DataLoaderFieldTests(TestCase): def test_dataloaderfield_permissions(self): MyType = object() - data_loader_field = DataLoaderField(data_loader=data_loader, source_loader='key', type=MyType, - permissions=['perm1', 'perm2']) + data_loader_field = DataLoaderField( + data_loader=data_loader, + source_loader="key", + type=MyType, + permissions=["perm1", "perm2"], + ) resolver = data_loader_field.get_resolver(None) instance = MyInstance() class Viewer(object): def has_perm(self, perm): - return perm == 'perm2' + return perm == "perm2" info = mock.Mock(context=mock.Mock(user=Viewer())) @@ -317,8 +329,12 @@ class DataLoaderFieldTests(TestCase): def test_dataloaderfield_without_permissions(self): MyType = object() - data_loader_field = DataLoaderField(data_loader=data_loader, source_loader='key', type=MyType, - permissions=['perm1', 'perm2']) + data_loader_field = DataLoaderField( + data_loader=data_loader, + source_loader="key", + type=MyType, + permissions=["perm1", "perm2"], + ) resolver = data_loader_field.get_resolver(None) instance = MyInstance() diff --git a/graphene_django/tests/test_types.py b/graphene_django/tests/test_types.py index 830b1fa..74e98ec 100644 --- a/graphene_django/tests/test_types.py +++ b/graphene_django/tests/test_types.py @@ -213,6 +213,7 @@ def with_local_registry(func): @with_local_registry def test_django_objecttype_only_fields(): with pytest.warns(PendingDeprecationWarning): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel @@ -236,6 +237,7 @@ def test_django_objecttype_fields(): @with_local_registry def test_django_objecttype_only_fields_and_fields(): with pytest.raises(Exception): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel @@ -257,6 +259,7 @@ def test_django_objecttype_all_fields(): @with_local_registry def test_django_objecttype_exclude_fields(): with pytest.warns(PendingDeprecationWarning): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel @@ -280,6 +283,7 @@ def test_django_objecttype_exclude(): @with_local_registry def test_django_objecttype_exclude_fields_and_exclude(): with pytest.raises(Exception): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel @@ -290,6 +294,7 @@ def test_django_objecttype_exclude_fields_and_exclude(): @with_local_registry def test_django_objecttype_exclude_and_only(): with pytest.raises(AssertionError): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel @@ -300,12 +305,14 @@ def test_django_objecttype_exclude_and_only(): @with_local_registry def test_django_objecttype_fields_exclude_type_checking(): with pytest.raises(TypeError): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel fields = "foo" with pytest.raises(TypeError): + class Reporter2(DjangoObjectType): class Meta: model = ReporterModel @@ -315,15 +322,17 @@ def test_django_objecttype_fields_exclude_type_checking(): @with_local_registry def test_django_objecttype_fields_exist_on_model(): with pytest.warns(UserWarning, match=r"Field name .* doesn't exist"): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel fields = ["first_name", "foo", "email"] with pytest.warns( - UserWarning, - match=r"Field name .* matches an attribute on Django model .* but it's not a model field", + UserWarning, + match=r"Field name .* matches an attribute on Django model .* but it's not a model field", ) as record: + class Reporter2(DjangoObjectType): class Meta: model = ReporterModel @@ -331,6 +340,7 @@ def test_django_objecttype_fields_exist_on_model(): # Don't warn if selecting a custom field with pytest.warns(None) as record: + class Reporter3(DjangoObjectType): custom_field = String() @@ -344,9 +354,10 @@ def test_django_objecttype_fields_exist_on_model(): @with_local_registry def test_django_objecttype_exclude_fields_exist_on_model(): with pytest.warns( - UserWarning, - match=r"Django model .* does not have a field or attribute named .*", + UserWarning, + match=r"Django model .* does not have a field or attribute named .*", ): + class Reporter(DjangoObjectType): class Meta: model = ReporterModel @@ -354,9 +365,10 @@ def test_django_objecttype_exclude_fields_exist_on_model(): # Don't warn if selecting a custom field with pytest.warns( - UserWarning, - match=r"Excluding the custom field .* on DjangoObjectType .* has no effect.", + UserWarning, + match=r"Excluding the custom field .* on DjangoObjectType .* has no effect.", ): + class Reporter3(DjangoObjectType): custom_field = String() @@ -366,6 +378,7 @@ def test_django_objecttype_exclude_fields_exist_on_model(): # Don't warn on exclude fields with pytest.warns(None) as record: + class Reporter4(DjangoObjectType): class Meta: model = ReporterModel @@ -562,7 +575,7 @@ class TestDjangoObjectType: def extra_field_resolver(root, info, **kwargs): - return 'extra field' + return "extra field" class PermissionArticle(DjangoObjectType): @@ -570,27 +583,28 @@ class PermissionArticle(DjangoObjectType): class Meta(object): """Meta Class""" + field_to_permission = { - 'headline': ('content_type.permission1',), - 'pub_date': ('content_type.permission2',) + "headline": ("content_type.permission1",), + "pub_date": ("content_type.permission2",), } permission_to_field = { - 'content_type.permission3': ('headline', 'reporter', 'extra_field',) + "content_type.permission3": ("headline", "reporter", "extra_field",) } model = ArticleModel extra_field = Field(String, resolver=extra_field_resolver) def resolve_headline(self, info, **kwargs): - return 'headline' + return "headline" def test_django_permissions(): expected = { - 'headline': ('content_type.permission1', 'content_type.permission3'), - 'pub_date': ('content_type.permission2',), - 'reporter': ('content_type.permission3',), - 'extra_field': ('content_type.permission3',), + "headline": ("content_type.permission1", "content_type.permission3"), + "pub_date": ("content_type.permission2",), + "reporter": ("content_type.permission3",), + "extra_field": ("content_type.permission3",), } assert PermissionArticle.field_permissions == expected @@ -600,7 +614,7 @@ def test_permission_resolver(): class Viewer(object): def has_perm(self, perm): - return perm == 'content_type.permission3' + return perm == "content_type.permission3" class Info(object): class Context(object): @@ -609,7 +623,7 @@ def test_permission_resolver(): context = Context() resolved = PermissionArticle.resolve_headline(MyType, Info()) - assert resolved == 'headline' + assert resolved == "headline" def test_resolver_without_permission(): @@ -634,7 +648,7 @@ def test_permission_resolver_to_field(): class Viewer(object): def has_perm(self, perm): - return perm == 'content_type.permission3' + return perm == "content_type.permission3" class Info(object): class Context(object): @@ -643,7 +657,7 @@ def test_permission_resolver_to_field(): context = Context() resolved = PermissionArticle.resolve_extra_field(MyType, Info()) - assert resolved == 'extra field' + assert resolved == "extra field" def test_resolver_to_field_without_permission(): @@ -651,7 +665,7 @@ def test_resolver_to_field_without_permission(): class Viewer(object): def has_perm(self, perm): - return perm != 'content_type.permission3' + return perm != "content_type.permission3" class Info(object): class Context(object): diff --git a/graphene_django/types.py b/graphene_django/types.py index 11d6a92..a1cc346 100644 --- a/graphene_django/types.py +++ b/graphene_django/types.py @@ -3,7 +3,6 @@ from collections import OrderedDict from functools import partial import six -from django.db.models import Model from django.utils.functional import SimpleLazyObject import graphene @@ -76,7 +75,8 @@ def validate_fields(type_, model, fields, only_fields, exclude_fields): ( 'Field name "{field_name}" matches an attribute on Django model "{app_label}.{object_name}" ' "but it's not a model field so Graphene cannot determine what type it should be. " - 'Either define the type of the field on DjangoObjectType "{type_}" or remove it from the "fields" list.' + 'Either define the type of the field on DjangoObjectType "{type_}" or remove it from the "fields" ' + "list. " ).format( field_name=name, app_label=model._meta.app_label, @@ -89,7 +89,8 @@ def validate_fields(type_, model, fields, only_fields, exclude_fields): warnings.warn( ( 'Field name "{field_name}" doesn\'t exist on Django model "{app_label}.{object_name}". ' - 'Consider removing the field from the "fields" list of DjangoObjectType "{type_}" because it has no effect.' + 'Consider removing the field from the "fields" list of DjangoObjectType "{type_}" because it has ' + "no effect. " ).format( field_name=name, app_label=model._meta.app_label, @@ -117,8 +118,10 @@ def validate_fields(type_, model, fields, only_fields, exclude_fields): if not hasattr(model, name): warnings.warn( ( - 'Django model "{app_label}.{object_name}" does not have a field or attribute named "{field_name}". ' - 'Consider removing the field from the "exclude" list of DjangoObjectType "{type_}" because it has no effect' + 'Django model "{app_label}.{object_name}" does not have a field or attribute named "{' + 'field_name}". ' + 'Consider removing the field from the "exclude" list of DjangoObjectType "{type_}" because it ' + "has no effect " ).format( field_name=name, app_label=model._meta.app_label, @@ -268,7 +271,9 @@ class DjangoObjectType(ObjectType): _meta.fields = django_fields _meta.connection = connection - field_permissions = cls.__get_field_permissions__(field_to_permission, permission_to_field) + field_permissions = cls.__get_field_permissions__( + field_to_permission, permission_to_field + ) if field_permissions: cls.__set_as_nullable__(field_permissions, model, registry) @@ -323,13 +328,17 @@ class DjangoObjectType(ObjectType): def __set_permissions_resolvers__(cls, permissions): """Set permission resolvers""" for field_name, field_permissions in permissions.items(): - attr = 'resolve_{}'.format(field_name) - resolver = getattr(cls._meta.fields[field_name], 'resolver', None) or getattr(cls, attr, None) + attr = "resolve_{}".format(field_name) + resolver = getattr( + cls._meta.fields[field_name], "resolver", None + ) or getattr(cls, attr, None) - if not hasattr(field_permissions, '__iter__'): + if not hasattr(field_permissions, "__iter__"): field_permissions = tuple(field_permissions) - setattr(cls, attr, get_auth_resolver(field_name, field_permissions, resolver)) + setattr( + cls, attr, get_auth_resolver(field_name, field_permissions, resolver) + ) @classmethod def __set_as_nullable__(cls, field_permissions, model, registry): @@ -339,7 +348,7 @@ class DjangoObjectType(ObjectType): _as=Field, ) for name, field in django_fields.items(): - if hasattr(field, '_type') and isinstance(field._type, NonNull): + if hasattr(field, "_type") and isinstance(field._type, NonNull): field._type = field._type._of_type setattr(cls, name, field) diff --git a/graphene_django/utils/utils.py b/graphene_django/utils/utils.py index d148932..4bbfdd6 100644 --- a/graphene_django/utils/utils.py +++ b/graphene_django/utils/utils.py @@ -132,7 +132,16 @@ def resolve_bound_resolver(resolver, root, info, **args): return resolver(root, info, **args) -def auth_resolver(parent_resolver, permissions, attname, default_value, raise_exception, root, info, **args): +def auth_resolver( + parent_resolver, + permissions, + attname, + default_value, + raise_exception, + root, + info, + **args +): """ Middleware resolver to check viewer's permissions :param parent_resolver: Field resolver @@ -146,7 +155,7 @@ def auth_resolver(parent_resolver, permissions, attname, default_value, raise_ex :return: Resolved field. None if the viewer does not have permission to access the field. """ # Get viewer from context - if not hasattr(info.context, 'user'): + if not hasattr(info.context, "user"): raise PermissionDenied() user = info.context.user