one to one serializer mutations

This commit is contained in:
Simon Tucker 2018-07-30 01:34:59 -05:00
parent dc561c68c4
commit 22acd07244
7 changed files with 140 additions and 16 deletions

View File

@ -1,12 +1,14 @@
import graphene import graphene
from graphene import Schema, relay, resolve_only_args from graphene import Schema, relay, resolve_only_args
from graphene_django import DjangoConnectionField, DjangoObjectType from graphene_django import DjangoConnectionField, DjangoObjectType
from graphene_django.rest_framework.mutation import SerializerMutation
from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship, from .data import (create_ship, get_empire, get_faction, get_rebels, get_ship,
get_ships) get_ships)
from .models import Character as CharacterModel from .models import Character as CharacterModel
from .models import Faction as FactionModel from .models import Faction as FactionModel
from .models import Ship as ShipModel from .models import Ship as ShipModel
from .serializers import CharacterSerializer
class Ship(DjangoObjectType): class Ship(DjangoObjectType):
@ -54,6 +56,11 @@ class IntroduceShip(relay.ClientIDMutation):
return IntroduceShip(ship=ship, faction=faction) return IntroduceShip(ship=ship, faction=faction)
class CreateCharacter(SerializerMutation):
class Meta:
serializer_class = CharacterSerializer
class Query(graphene.ObjectType): class Query(graphene.ObjectType):
rebels = graphene.Field(Faction) rebels = graphene.Field(Faction)
empire = graphene.Field(Faction) empire = graphene.Field(Faction)
@ -75,7 +82,7 @@ class Query(graphene.ObjectType):
class Mutation(graphene.ObjectType): class Mutation(graphene.ObjectType):
introduce_ship = IntroduceShip.Field() introduce_ship = IntroduceShip.Field()
create_character = CreateCharacter.Field()
# We register the Character Model because if not would be # We register the Character Model because if not would be
# inaccessible for the schema # inaccessible for the schema

View File

@ -0,0 +1,9 @@
from rest_framework import serializers
from .models import Character
class CharacterSerializer(serializers.ModelSerializer):
class Meta:
model = Character
fields = "__all__"

View File

@ -77,3 +77,33 @@ def test_mutations():
result = schema.execute(query) result = schema.execute(query)
assert not result.errors assert not result.errors
assert result.data == expected assert result.data == expected
def test_serializer_mutations():
initialize()
query = '''
mutation createCharacter {
createCharacter(input:{clientMutationId:"def", name: "Luke", ship: "1"}) {
id
name
ship {
id
name
}
}
}
'''
expected = {
'createCharacter': {
'id': 3,
'name': 'Luke',
'ship': {
'id': 'U2hpcDox',
'name': 'X-Wing'
}
}
}
result = schema.execute(query)
assert not result.errors
assert result.data == expected

View File

@ -4,3 +4,9 @@ from django.db import models
class MyFakeModel(models.Model): class MyFakeModel(models.Model):
cool_name = models.CharField(max_length=50) cool_name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
class OneToOneModel(models.Model):
name = models.CharField(max_length=50)
fake = models.ForeignKey(MyFakeModel, on_delete=models.DO_NOTHING)
created = models.DateTimeField(auto_now_add=True)

View File

@ -138,6 +138,9 @@ class SerializerMutation(ClientIDMutation):
kwargs = {} kwargs = {}
for f, field in serializer.fields.items(): for f, field in serializer.fields.items():
kwargs[f] = field.get_attribute(obj) if hasattr(field, 'queryset'):
kwargs[f] = field.queryset.get(pk=str(field.get_attribute(obj)))
else:
kwargs[f] = field.get_attribute(obj)
return cls(errors=None, **kwargs) return cls(errors=None, **kwargs)

View File

@ -11,7 +11,7 @@ singledispatch = import_single_dispatch()
@singledispatch @singledispatch
def get_graphene_type_from_serializer_field(field): def get_graphene_type_from_serializer_field(field, **kwargs):
raise ImproperlyConfigured( raise ImproperlyConfigured(
"Don't know how to convert the serializer field %s (%s) " "Don't know how to convert the serializer field %s (%s) "
"to Graphene type" % (field, field.__class__) "to Graphene type" % (field, field.__class__)
@ -25,7 +25,7 @@ def convert_serializer_field(field, is_input=True):
and the field itself is required and the field itself is required
""" """
graphql_type = get_graphene_type_from_serializer_field(field) graphql_type = get_graphene_type_from_serializer_field(field, is_input=is_input)
args = [] args = []
kwargs = {"description": field.help_text, "required": is_input and field.required} kwargs = {"description": field.help_text, "required": is_input and field.required}
@ -36,6 +36,11 @@ def convert_serializer_field(field, is_input=True):
kwargs["of_type"] = graphql_type[1] kwargs["of_type"] = graphql_type[1]
graphql_type = graphql_type[0] graphql_type = graphql_type[0]
if isinstance(field, serializers.PrimaryKeyRelatedField) and not is_input:
global_registry = get_global_registry()
field_model = field.queryset.model
args = [global_registry.get_type_for_model(field_model)]
if isinstance(field, serializers.ModelSerializer): if isinstance(field, serializers.ModelSerializer):
if is_input: if is_input:
graphql_type = convert_serializer_to_input_type(field.__class__) graphql_type = convert_serializer_to_input_type(field.__class__)
@ -53,6 +58,7 @@ def convert_serializer_field(field, is_input=True):
field_model = field.Meta.model field_model = field.Meta.model
args = [global_registry.get_type_for_model(field_model)] args = [global_registry.get_type_for_model(field_model)]
print('graphql_type', graphql_type, args)
return graphql_type(*args, **kwargs) return graphql_type(*args, **kwargs)
@ -72,49 +78,56 @@ def convert_serializer_to_input_type(serializer_class):
@get_graphene_type_from_serializer_field.register(serializers.Field) @get_graphene_type_from_serializer_field.register(serializers.Field)
def convert_serializer_field_to_string(field): def convert_serializer_field_to_string(field, **kwargs):
return graphene.String return graphene.String
@get_graphene_type_from_serializer_field.register(serializers.ModelSerializer) @get_graphene_type_from_serializer_field.register(serializers.ModelSerializer)
def convert_serializer_to_field(field): def convert_serializer_to_field(field, **kwargs):
return graphene.Field
@get_graphene_type_from_serializer_field.register(serializers.PrimaryKeyRelatedField)
def convert_serializer_key_to_field(field, is_input=True):
if is_input:
return graphene.String
return graphene.Field return graphene.Field
@get_graphene_type_from_serializer_field.register(serializers.ListSerializer) @get_graphene_type_from_serializer_field.register(serializers.ListSerializer)
def convert_list_serializer_to_field(field): def convert_list_serializer_to_field(field, **kwargs):
child_type = get_graphene_type_from_serializer_field(field.child) child_type = get_graphene_type_from_serializer_field(field.child)
return (graphene.List, child_type) return (graphene.List, child_type)
@get_graphene_type_from_serializer_field.register(serializers.IntegerField) @get_graphene_type_from_serializer_field.register(serializers.IntegerField)
def convert_serializer_field_to_int(field): def convert_serializer_field_to_int(field, **kwargs):
return graphene.Int return graphene.Int
@get_graphene_type_from_serializer_field.register(serializers.BooleanField) @get_graphene_type_from_serializer_field.register(serializers.BooleanField)
def convert_serializer_field_to_bool(field): def convert_serializer_field_to_bool(field, **kwargs):
return graphene.Boolean return graphene.Boolean
@get_graphene_type_from_serializer_field.register(serializers.FloatField) @get_graphene_type_from_serializer_field.register(serializers.FloatField)
@get_graphene_type_from_serializer_field.register(serializers.DecimalField) @get_graphene_type_from_serializer_field.register(serializers.DecimalField)
def convert_serializer_field_to_float(field): def convert_serializer_field_to_float(field, **kwargs):
return graphene.Float return graphene.Float
@get_graphene_type_from_serializer_field.register(serializers.DateTimeField) @get_graphene_type_from_serializer_field.register(serializers.DateTimeField)
def convert_serializer_field_to_datetime_time(field): def convert_serializer_field_to_datetime_time(field, **kwargs):
return graphene.types.datetime.DateTime return graphene.types.datetime.DateTime
@get_graphene_type_from_serializer_field.register(serializers.DateField) @get_graphene_type_from_serializer_field.register(serializers.DateField)
def convert_serializer_field_to_date_time(field): def convert_serializer_field_to_date_time(field, **kwargs):
return graphene.types.datetime.Date return graphene.types.datetime.Date
@get_graphene_type_from_serializer_field.register(serializers.TimeField) @get_graphene_type_from_serializer_field.register(serializers.TimeField)
def convert_serializer_field_to_time(field): def convert_serializer_field_to_time(field, **kwargs):
return graphene.types.datetime.Time return graphene.types.datetime.Time
@ -126,15 +139,15 @@ def convert_serializer_field_to_list(field, is_input=True):
@get_graphene_type_from_serializer_field.register(serializers.DictField) @get_graphene_type_from_serializer_field.register(serializers.DictField)
def convert_serializer_field_to_dict(field): def convert_serializer_field_to_dict(field, **kwargs):
return DictType return DictType
@get_graphene_type_from_serializer_field.register(serializers.JSONField) @get_graphene_type_from_serializer_field.register(serializers.JSONField)
def convert_serializer_field_to_jsonstring(field): def convert_serializer_field_to_jsonstring(field, **kwargs):
return graphene.types.json.JSONString return graphene.types.json.JSONString
@get_graphene_type_from_serializer_field.register(serializers.MultipleChoiceField) @get_graphene_type_from_serializer_field.register(serializers.MultipleChoiceField)
def convert_serializer_field_to_list_of_string(field): def convert_serializer_field_to_list_of_string(field, **kwargs):
return (graphene.List, graphene.String) return (graphene.List, graphene.String)

View File

@ -8,6 +8,7 @@ from rest_framework import serializers
from ...types import DjangoObjectType from ...types import DjangoObjectType
from ..models import MyFakeModel from ..models import MyFakeModel
from ..models import OneToOneModel
from ..mutation import SerializerMutation from ..mutation import SerializerMutation
@ -32,6 +33,17 @@ class MyModelSerializer(serializers.ModelSerializer):
fields = "__all__" fields = "__all__"
class OneToOneModelSerializer(serializers.ModelSerializer):
class Meta:
model = OneToOneModel
fields = "__all__"
class OneToOneModelMutation(SerializerMutation):
class Meta:
serializer_class = OneToOneModelSerializer
class MyModelMutation(SerializerMutation): class MyModelMutation(SerializerMutation):
class Meta: class Meta:
serializer_class = MyModelSerializer serializer_class = MyModelSerializer
@ -64,6 +76,23 @@ def test_has_fields():
assert "errors" in MyMutation._meta.fields assert "errors" in MyMutation._meta.fields
def test_has_nested_fields():
class MyFakeModelGrapheneType(DjangoObjectType):
class Meta:
model = MyFakeModel
class OneToOneModelMutation(SerializerMutation):
class Meta:
serializer_class = OneToOneModelSerializer
assert "name" in OneToOneModelMutation._meta.fields
assert "fake" in OneToOneModelMutation._meta.fields
model_field = OneToOneModelMutation._meta.fields['fake']
assert isinstance(model_field, Field)
assert model_field.type == MyFakeModelGrapheneType
assert "errors" in OneToOneModelMutation._meta.fields
def test_has_input_fields(): def test_has_input_fields():
class MyMutation(SerializerMutation): class MyMutation(SerializerMutation):
class Meta: class Meta:
@ -127,6 +156,21 @@ def test_model_add_mutate_and_get_payload_success():
assert isinstance(result.created, datetime.datetime) assert isinstance(result.created, datetime.datetime)
@mark.django_db
def test_one_to_one_model_with_add_mutate_and_get_payload_success():
fake = MyModelMutation.mutate_and_get_payload(
None, mock_info(), **{"cool_name": "Narf"}
)
result = OneToOneModelMutation.mutate_and_get_payload(
None, mock_info(), **{"name": "Jinkies", "fake": fake.id}
)
assert result.errors is None
assert result.name == "Jinkies"
assert result.fake.pk == fake.id
assert isinstance(result.created, datetime.datetime)
@mark.django_db @mark.django_db
def test_model_update_mutate_and_get_payload_success(): def test_model_update_mutate_and_get_payload_success():
instance = MyFakeModel.objects.create(cool_name="Narf") instance = MyFakeModel.objects.create(cool_name="Narf")
@ -168,6 +212,18 @@ def test_model_mutate_and_get_payload_error():
assert len(result.errors) > 0 assert len(result.errors) > 0
@mark.django_db
def test_one_to_one_model_with_add_mutate_and_get_payload_error():
MyModelMutation.mutate_and_get_payload(
None, mock_info(), **{"cool_name": "Narf"}
)
result = OneToOneModelMutation.mutate_and_get_payload(
None, mock_info(), **{"name": "Jinkies", "fake": "invalid"}
)
assert len(result.errors) > 0
def test_invalid_serializer_operations(): def test_invalid_serializer_operations():
with raises(Exception) as exc: with raises(Exception) as exc: