Repository: graphql-python/graphene-django

Issue:
Rest Framework SerializerMutation Error - NoneType is not callable
Hi,
I'm using the SerializerMutation class to attempt to create mutations based off of my existing Rest Framework serializers. By taking advantage of the ModelSerializer in DRF, I hoped to avoid having to create all the mutations from scratch with validation and whatnot.

However, the use of the SerializerMutation class isn't sufficiently explained in the docs. I did this
```
class ItemMutation(SerializerMutation):
    class Meta:
        serializer_class = ItemSerializer

class Mutation(AbstractType):
    #...other mutations...
    createItem = ItemMutation.Field()
```

from a simple DRF ModelSerializer

```
class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = (
            'id',
            'name',
            'description',
            'website',
        )
```

But Graphiql shows me the following error whenever I try to use the mutation
```
mutation newItem($data: ItemMutationInput!) {
  createItem(input: $data) {
    name,
    description
  }
}

{
  "data": {
    "name": "Blah",
    "description": "Blah blah blah",
    "website": "https://blah.blah.blah"
  }
}
```

```
{
  "errors": [
    {
      "message": "'NoneType' object is not callable",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ]
    }
  ],
  "data": {
    "createItem": null
  }
}
```

Also, Rest Framework's serializers can often have multiple actions (mutations) like create, delete, update, etc and the use of SerializerMutation appears to just produce a single mutation - presumably the "create" action. How do I handle the other actions?


Discussion:
The only way I have found ```SerializerMutation``` to work is for ```serializer_class``` to not be a ```ModelSerializer```.

That looks something like this:

```
class ItemMutation(SerializerMutation):
    class Meta:
        serializer_class = ItemSerializer

class Mutation(AbstractType):
    #...other mutations...
    createOrUpdateItem = ItemMutation.Field()

class ItemModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = (
            'id',
            'name',
            'description',
            'website',
        )

class ItemSerializer(serializers.Serializer):
    item = ItemModelSerializer()

    def create(self, validated_data):
        item_data = validated_data.pop('item')
        return {'item': Item.objects.create(**item_data)}

    # likely want to implement update too
```

On the upside containing all the item fields in an item object does seem a bit cleaner to me and may leave better room for future schema additions. SerializerMutation calls save() so that can cover updates too.

@patrick91 or @spockNinja in case either of you have time to chime in
Thanks. I just tried it out, but quickly ran into another brick wall. The schema definition for `ItemMutationInput` now has only the field `clientMutationId`. I agree it's a little cleaner to have all the `item` fields in an `item` object when retrieving but how would one specify the `item` fields like `name` and `description`, if trying to create an `item`?

The following mutation request for example wouldn't work.
```
mutation newItem($data: ItemMutationInput!) {
  createOrUpdateItem(input: $data) {
    item{
      name,
      description
    }
  }
}

{
  "data": {
    "item": {
      "name": "Blah",
      "description": "Blah blah blah",
      "website": "https://blah.blah.blah"
    }  
  }
}
```
GraphQL expects my `ItemMutationInput` to have only a single field `clientMutationId`. Could you provide a sample mutation request?
That request works for me as long as ```ItemMutationInput``` is ```ItemSerializerInput``` so it matches the name of the Serializer rather than the SerializerMutation. ```clientMutationId``` makes it sound like you have ```ClientIDMutation``` in play somewhere
Ahh looks like that is likely coming from 48bcccd. I'm not using that yet. Seems like things may have changed in newer versions
I'm still fairly new to graphene, I should add. Where does `ItemSerializerInput` come from? It's certainly not in my schema definition. Using it in my mutation request causes Graphiql to throw the following
`Unknown type "ItemSerializerInput". Perhaps you meant "ItemMutationInput"?`

Was that functionality also possibly removed in the newer versions? Or is there a way to add the serializers themselves to the schema definition?
Thanks again for taking the time.
I'm new too and running into the same issues as you. You can ignore ```ItemSerializerInput``` as that seems to have also changed in recent commits.

I played around with the most recent code and it seems like ```SerializerMutation``` may simply be broken. It doesn't look like the handling of input fields is complete:

https://github.com/graphql-python/graphene-django/blob/9cac83b1688f3f608fa22fe2e95e2dd8145ccce1/graphene_django/rest_framework/serializer_converter.py#L43-L50

If you don't need to use any of the latest unreleased features you could try ```git+https://github.com/graphql-python/graphene-django@cec1a8448048ce55a2844df63b295b8d9d5000d7``` -- that's after ```SerializerMutation``` was added but before the changes for graphene 2.0
Thanks very much for all your help. You saved me a lot of time and effort. I would have kept running in circles. GraphQL was just an experiment for me to see if I could upgrade my existing DRF projects. But it doesn't appear to be as production ready as I'd hoped, so I'll suspend my curiosity. I'll stick with just DRF for now.
@khalibloo Using DRF serializers as mutations in Graphene used to work for Graphene v1.0. As @syrusakbary is not the main contributor to the conversion of DRF serializers, I would say the main contributor (@patrick91) will make the conversion fully compatible with v2.0 which was only released lately.
Oh thanks for clearing that up. I already tried using v1.0. I get an error saying `No module named 'graphene_django.rest_framework'`. I checked the site-packages folder and yeah there's no rest_framework folder. Since SerializerMutation resides in the rest_framework folder, it appears it's not been implemented in v1.0. Even v1.3 doesn't have the rest_framework folder.

I remember while following the docs on `SerializerMutation`, I had to manually download this repo as well as its graphql dependencies to get `SerializerMutation`.
There must've been something wrong with publishing. I didn't experience it because I had a similar scenario as yours where I had a local copy of `graphene-django` in my app rather than the version on PyPy.

Resolving patch:
diff --git a/graphene_django/rest_framework/mutation.py b/graphene_django/rest_framework/mutation.py
--- a/graphene_django/rest_framework/mutation.py
+++ b/graphene_django/rest_framework/mutation.py
@@ -55,6 +55,7 @@ def __init_subclass_with_meta__(cls, serializer_class=None,
         output_fields = fields_for_serializer(serializer, only_fields, exclude_fields, is_input=False)
 
         _meta = SerializerMutationOptions(cls)
+        _meta.serializer_class = serializer_class
         _meta.fields = yank_fields_from_attrs(
             output_fields,
             _as=Field,
@@ -83,4 +84,4 @@ def mutate_and_get_payload(cls, root, info, **input):
     @classmethod
     def perform_mutate(cls, serializer, info):
         obj = serializer.save()
-        return cls(**obj)
+        return cls(errors=None, **obj)
diff --git a/graphene_django/rest_framework/serializer_converter.py b/graphene_django/rest_framework/serializer_converter.py
--- a/graphene_django/rest_framework/serializer_converter.py
+++ b/graphene_django/rest_framework/serializer_converter.py
@@ -42,8 +42,7 @@ def convert_serializer_field(field, is_input=True):
 
     if isinstance(field, serializers.ModelSerializer):
         if is_input:
-            return Dynamic(lambda: None)
-            # graphql_type = convert_serializer_to_input_type(field.__class__)
+            graphql_type = convert_serializer_to_input_type(field.__class__)
         else:
             global_registry = get_global_registry()
             field_model = field.Meta.model
@@ -52,6 +51,21 @@ def convert_serializer_field(field, is_input=True):
     return graphql_type(*args, **kwargs)
 
 
+def convert_serializer_to_input_type(serializer_class):
+    serializer = serializer_class()
+
+    items = {
+        name: convert_serializer_field(field)
+        for name, field in serializer.fields.items()
+    }
+
+    return type(
+        '{}Input'.format(serializer.__class__.__name__),
+        (graphene.InputObjectType,),
+        items
+    )
+
+
 @get_graphene_type_from_serializer_field.register(serializers.Field)
 def convert_serializer_field_to_string(field):
     return graphene.String


Test patch:
diff --git a/graphene_django/rest_framework/tests/test_mutation.py b/graphene_django/rest_framework/tests/test_mutation.py
--- a/graphene_django/rest_framework/tests/test_mutation.py
+++ b/graphene_django/rest_framework/tests/test_mutation.py
@@ -22,6 +22,9 @@ class MySerializer(serializers.Serializer):
     text = serializers.CharField()
     model = MyModelSerializer()
 
+    def create(self, validated_data):
+        return validated_data
+
 
 def test_needs_serializer_class():
     with raises(Exception) as exc:
@@ -65,7 +68,32 @@ class Meta:
     assert model_field.type == MyFakeModelGrapheneType
 
     model_input = MyMutation.Input._meta.fields['model']
-    model_input_type = model_input.get_type()
-    assert not model_input_type
-    # assert issubclass(model_input_type, InputObjectType)
-    # assert 'cool_name' in model_input_type._meta.fields
+    model_input_type = model_input._type.of_type
+    assert issubclass(model_input_type, InputObjectType)
+    assert 'cool_name' in model_input_type._meta.fields
+
+
+def test_mutate_and_get_payload_success():
+
+    class MyMutation(SerializerMutation):
+        class Meta:
+            serializer_class = MySerializer
+
+    result = MyMutation.mutate_and_get_payload(None, None, **{
+        'text': 'value',
+        'model': {
+            'cool_name': 'other_value'
+        }
+    })
+    assert result.errors is None
+
+
+def test_mutate_and_get_payload_error():
+
+    class MyMutation(SerializerMutation):
+        class Meta:
+            serializer_class = MySerializer
+
+    # missing required fields
+    result = MyMutation.mutate_and_get_payload(None, None, **{})
+    assert len(result.errors) > 0
\ No newline at end of file