Repository: graphql-python/graphene-django

Issue:
Incorrect Serializer Usage
https://github.com/graphql-python/graphene-django/blob/1bea96b31e9e8e77593946b78ab39b93d6f2cfec/graphene_django/rest_framework/mutation.py#L86

The object returned by `serializer.save()` is a model instance which is not a dictionary or a mappable object which could be used here.


Discussion:

Resolving patch:
diff --git a/graphene_django/rest_framework/models.py b/graphene_django/rest_framework/models.py
new file mode 100644
--- /dev/null
+++ b/graphene_django/rest_framework/models.py
@@ -0,0 +1,6 @@
+from django.db import models
+
+
+class MyFakeModel(models.Model):
+    cool_name = models.CharField(max_length=50)
+    created = models.DateTimeField(auto_now_add=True)
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
@@ -84,4 +84,9 @@ def mutate_and_get_payload(cls, root, info, **input):
     @classmethod
     def perform_mutate(cls, serializer, info):
         obj = serializer.save()
-        return cls(errors=None, **obj)
+
+        kwargs = {}
+        for f, field in serializer.fields.items():
+            kwargs[f] = field.get_attribute(obj)
+
+        return cls(errors=None, **kwargs)


Test patch:
diff --git a/django_test_settings.py b/django_test_settings.py
--- a/django_test_settings.py
+++ b/django_test_settings.py
@@ -8,6 +8,7 @@
 
 INSTALLED_APPS = [
     'graphene_django',
+    'graphene_django.rest_framework',
     'graphene_django.tests',
     'starwars',
 ]
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
@@ -1,17 +1,16 @@
-from django.db import models
+import datetime
+
 from graphene import Field
 from graphene.types.inputobjecttype import InputObjectType
 from py.test import raises
+from py.test import mark
 from rest_framework import serializers
 
 from ...types import DjangoObjectType
+from ..models import MyFakeModel
 from ..mutation import SerializerMutation
 
 
-class MyFakeModel(models.Model):
-    cool_name = models.CharField(max_length=50)
-
-
 class MyModelSerializer(serializers.ModelSerializer):
     class Meta:
         model = MyFakeModel
@@ -71,6 +70,7 @@ class Meta:
     model_input_type = model_input._type.of_type
     assert issubclass(model_input_type, InputObjectType)
     assert 'cool_name' in model_input_type._meta.fields
+    assert 'created' in model_input_type._meta.fields
 
 
 def test_mutate_and_get_payload_success():
@@ -88,6 +88,19 @@ class Meta:
     assert result.errors is None
 
 
+@mark.django_db
+def test_model_mutate_and_get_payload_success():
+    class MyMutation(SerializerMutation):
+        class Meta:
+            serializer_class = MyModelSerializer
+
+    result = MyMutation.mutate_and_get_payload(None, None, **{
+        'cool_name': 'Narf',
+    })
+    assert result.errors is None
+    assert result.cool_name == 'Narf'
+    assert isinstance(result.created, datetime.datetime)
+
 def test_mutate_and_get_payload_error():
 
     class MyMutation(SerializerMutation):
@@ -96,4 +109,14 @@ class Meta:
 
     # missing required fields
     result = MyMutation.mutate_and_get_payload(None, None, **{})
-    assert len(result.errors) > 0
\ No newline at end of file
+    assert len(result.errors) > 0
+
+def test_model_mutate_and_get_payload_error():
+
+    class MyMutation(SerializerMutation):
+        class Meta:
+            serializer_class = MyModelSerializer
+
+    # missing required fields
+    result = MyMutation.mutate_and_get_payload(None, None, **{})
+    assert len(result.errors) > 0