From ff69b0726758d425cdbdcbaa9f0fb3485d89390d Mon Sep 17 00:00:00 2001 From: colan Date: Wed, 26 Apr 2017 23:11:35 -0400 Subject: [PATCH] add a new subclass and a few tests [WIP] --- graphene_django/input_types.py | 60 +++++++++++++++ graphene_django/tests/test_input_type.py | 96 ++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 graphene_django/input_types.py create mode 100644 graphene_django/tests/test_input_type.py diff --git a/graphene_django/input_types.py b/graphene_django/input_types.py new file mode 100644 index 0000000..7277959 --- /dev/null +++ b/graphene_django/input_types.py @@ -0,0 +1,60 @@ +import collections +from graphene_django import converter, utils + +from graphene.types.utils import merge +from graphene.utils.is_base_type import is_base_type + + +def convert_fields(model, only_fields, exclude_fields): + model_fields = utils.get_model_fields(model=model) + fields = collections.OrderedDict() + + for name, field in model_fields: + is_not_in_only = only_fields and name not in only_fields + is_already_created = name in model_fields + is_excluded = name in exclude_fields or is_already_created + # https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_query_name + is_no_backref = str(name).endswith('+') + if is_not_in_only or is_excluded or is_no_backref: + # We skip this field if we specify only_fields and is not + # in there. Or when we exclude this field in exclude_fields. + # Or when there is no back reference. + continue + converted = converter.convert_django_field(field, None) + if not converted: + continue + + fields[name] = converted + print(fields) + return fields + + +class DjangoModelInputMeta(type): + + @staticmethod + def __new__(cls, name, bases, attrs): + # We'll get called also for non-user classes like DjangoModelInput. Only + # kick in when called for a sub-class. + if not is_base_type(bases, DjangoModelInputMeta): + return type.__new__(cls, name, bases, attrs) + + # Pop Meta info. Must be removed from class, otherwise graphene will + # complain. + meta = attrs.pop('Meta') + if not hasattr(meta, 'exclude_fields'): + setattr(meta, 'exclude_fields', ()) + if not hasattr(meta, 'only_fields'): + setattr(meta, 'only_fields', ()) + fields = convert_fields(model=meta.model, only_fields=meta.only_fields, exclude_fields=meta.exclude_fields) + attrs = merge(attrs, fields) + + return type.__new__(cls, name, bases, attrs) + + +class DjangoModelInput(metaclass=DjangoModelInputMeta): + """ + Derive a mutation's Input class from this and define a meta class with + `model` and `only_fields` members. This will populate the input class + with the converted django members. + """ + pass \ No newline at end of file diff --git a/graphene_django/tests/test_input_type.py b/graphene_django/tests/test_input_type.py new file mode 100644 index 0000000..ab4a10e --- /dev/null +++ b/graphene_django/tests/test_input_type.py @@ -0,0 +1,96 @@ +from mock import patch + +from graphene import ObjectType, Schema, Mutation, String + +from .. import registry +from ..input_types import DjangoModelInput + +from .models import Reporter as ReporterModel + + +def test_mutation_execution_with_exclude_fields(): + registry.reset_global_registry() + + class CreateReporter(Mutation): + + first_name = String() + last_name = String() + email = String() + + class Input(DjangoModelInput): + + class Meta: + model = ReporterModel + exclude_fields = ('id', 'pets', 'a_choice', 'films', 'articles') + + def mutate(self, args, context, info): + first_name = args.get('first_name') + last_name = args.get('last_name') + email = args.get('email') + return CreateReporter(first_name=first_name, last_name=last_name, email=email) + + class MyMutation(ObjectType): + reporter_input = CreateReporter.Field() + + class Query(ObjectType): + a = String() + + schema = Schema(query=Query, mutation=MyMutation) + result = schema.execute(''' mutation mymutation { + reporterInput(firstName:"Peter", lastName: "test", email: "test@test.com") { + firstName + lastName + email + } + } + ''') + assert not result.errors + assert result.data == { + 'reporterInput': { + 'firstName': 'Peter', + 'lastName': 'test', + 'email': "test@test.com" + } + } + + +def test_mutation_execution(): + registry.reset_global_registry() + + class ReporterInput(Mutation): + + first_name = String() + last_name = String() + + class Input(DjangoModelInput): + + class Meta: + model = ReporterModel + only_fields = ('first_name', 'last_name') + + def mutate(self, args, context, info): + first_name = args.get('first_name') + last_name = args.get('last_name') + return ReporterInput(first_name=first_name, last_name=last_name) + + class MyMutation(ObjectType): + reporter_input = ReporterInput.Field() + + class Query(ObjectType): + a = String() + + schema = Schema(query=Query, mutation=MyMutation) + result = schema.execute(''' mutation mymutation { + reporterInput(firstName:"Peter", lastName: "test") { + firstName + lastName + } + } + ''') + assert not result.errors + assert result.data == { + 'reporterInput': { + 'firstName': 'Peter', + 'lastName': 'test', + } + }