Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability for model types to be placed on input fields [WIP] feedback wanted #160

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 91 additions & 2 deletions graphene_django/tests/test_types.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from mock import patch

from graphene import Interface, ObjectType, Schema
from graphene import Interface, ObjectType, Schema, Mutation, String
from graphene.relay import Node
from mock import patch

from .. import registry
from ..types import DjangoObjectType
from ..types import DjangoObjectType, DjangoModelInput
from .models import Article as ArticleModel
from .models import Reporter as ReporterModel

Expand Down Expand Up @@ -163,3 +164,91 @@ class Meta:

fields = list(Reporter._meta.fields.keys())
assert 'email' not in fields


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',
}
}
53 changes: 53 additions & 0 deletions graphene_django/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,56 @@ def get_node(cls, id, context, info):
return cls._meta.model.objects.get(pk=id)
except cls._meta.model.DoesNotExist:
return None


def convert_fields(model, only_fields, exclude_fields):
model_fields = get_model_fields(model=model)
fields = 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
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 = convert_django_field_with_choices(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(six.with_metaclass(DjangoModelInputMeta)):
"""
Derive a mutation's Input class from this and define a meta class with
`model` and `only_fields` and 'exclude_field' members. This will populate the input class
with the converted django members.
"""
pass