Repository: graphql-python/graphene-django

Issue:
SerializerMutation creates excess types for fields with same serializer class
I use ``SerializerMutation`` to create/update objects:

```python
from graphene_django.rest_framework.mutation import SerializerMutation

class ShipmentMutation(SerializerMutation):
    class Meta:
        serializer_class = ShipmentSerializer
```

Here is ``ShipmentSerializer``:

```python
from rest_framework import serializers

from logistics.models import City
from logistics.serializers import CitySerializer

from .models import Shipment


class ShipmentSerializer(serializers.ModelSerializer):
    owner = serializers.PrimaryKeyRelatedField(read_only=True)
    origin = CitySerializer(read_only=True)
    destination = CitySerializer(read_only=True)

    class Meta:
        model = Shipment
        exclude = ('created_at', 'updated_at', )
```

...and ``CitySerializer``:

```python
class CitySerializer(serializers.ModelSerializer):
    class Meta:
        model = City
        fields = ('id', 'name', )
        read_only_fields = ('id', 'name', )
```

So there is ttwo similar fields serialized by ``CitySerializer``: ``origin`` and ``destination`` and while creating appropriate types for it raises an error:

```python
AssertionError: Found different types with the same name in the schema: CitySerializerInput, CitySerializerInput.
```

<details><summary>Full trace</summary>

```python
Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x7fb41bb8b620>
Traceback (most recent call last):
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/utils/autoreload.py", line 225, in wrapper
    fn(*args, **kwargs)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/channels/management/commands/runserver.py", line 55, in inner_run
    self.check(display_num_errors=True)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/core/management/base.py", line 364, in check
    include_deployment_checks=include_deployment_checks,
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/core/management/base.py", line 351, in _run_checks
    return checks.run_checks(**kwargs)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/core/checks/registry.py", line 73, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/core/checks/urls.py", line 40, in check_url_namespaces_unique
    all_namespaces = _load_all_namespaces(resolver)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/core/checks/urls.py", line 57, in _load_all_namespaces
    url_patterns = getattr(resolver, 'url_patterns', [])
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/utils/functional.py", line 36, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/urls/resolvers.py", line 540, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/utils/functional.py", line 36, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/django/urls/resolvers.py", line 533, in urlconf_module
    return import_module(self.urlconf_name)
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/someusernamehere/donnyyy/Workspace/m100/m100-backend/m100/urls.py", line 21, in <module>
    from main.views import graphql_view
  File "/someusernamehere/donnyyy/Workspace/m100/m100-backend/main/views.py", line 100, in <module>
    '.')[:-1])), settings.GRAPHENE['SCHEMA'].split('.')[-1])
  File "/usr/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "/someusernamehere/donnyyy/Workspace/m100/m100-backend/main/schema.py", line 75, in <module>
    + car_types
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/schema.py", line 62, in __init__
    self.build_typemap()
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/schema.py", line 126, in build_typemap
    initial_types, auto_camelcase=self.auto_camelcase, schema=self
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 80, in __init__
    super(TypeMap, self).__init__(types)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/typemap.py", line 28,in __init__
    self.update(reduce(self.reducer, types, OrderedDict()))  # type: ignore
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 88, in reducer
    return self.graphene_reducer(map, type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 117, in graphene_reducer
    return GraphQLTypeMap.reducer(map, internal_type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/typemap.py", line 106, in reducer
    field_map = type.fields
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/pyutils/cached_property.py", line 22, in __get__
    value = obj.__dict__[self.func.__name__] = self.func(obj)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/definition.py", line 221, in fields
    return define_field_map(self, self._fields)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/definition.py", line 235, in define_field_map
    field_map = field_map()
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 286, in construct_fields_for_type
    map = self.reducer(map, arg.type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 88, in reducer
    return self.graphene_reducer(map, type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 93, in graphene_reducer
    return self.reducer(map, type.of_type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 88, in reducer
    return self.graphene_reducer(map, type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 117, in graphene_reducer
    return GraphQLTypeMap.reducer(map, internal_type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/typemap.py", line 106, in reducer
    field_map = type.fields
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/pyutils/cached_property.py", line 22, in __get__
    value = obj.__dict__[self.func.__name__] = self.func(obj)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/definition.py", line 655, in fields
    return self._define_field_map()
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphql/type/definition.py", line 660, in _define_field_map
    fields = self._fields()
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 274, in construct_fields_for_type
    map = self.reducer(map, field.type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 88, in reducer
    return self.graphene_reducer(map, type)
  File "/home/someusernamehere/.local/lib/python3.6/site-packages/graphene/types/typemap.py", line 99, in graphene_reducer
    ).format(_type.graphene_type, type)
AssertionError: Found different types with the same name in the schema: CitySerializerInput, CitySerializerInput.
```

</details>







Discussion:
I am also getting the same issue with DjangoModelFormMutation
I'm seeing this as well
I am also getting the same issue

Resolving patch:
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
@@ -57,18 +57,25 @@ def convert_serializer_field(field, is_input=True):
 
 
 def convert_serializer_to_input_type(serializer_class):
+    cached_type = convert_serializer_to_input_type.cache.get(serializer_class.__name__, None)
+    if cached_type:
+        return cached_type
     serializer = serializer_class()
 
     items = {
         name: convert_serializer_field(field)
         for name, field in serializer.fields.items()
     }
-
-    return type(
+    ret_type = type(
         "{}Input".format(serializer.__class__.__name__),
         (graphene.InputObjectType,),
         items,
     )
+    convert_serializer_to_input_type.cache[serializer_class.__name__] = ret_type
+    return ret_type
+
+
+convert_serializer_to_input_type.cache = {}
 
 
 @get_graphene_type_from_serializer_field.register(serializers.Field)


Test patch:
diff --git a/graphene_django/rest_framework/tests/test_multiple_model_serializers.py b/graphene_django/rest_framework/tests/test_multiple_model_serializers.py
new file mode 100644
--- /dev/null
+++ b/graphene_django/rest_framework/tests/test_multiple_model_serializers.py
@@ -0,0 +1,63 @@
+import graphene
+import pytest
+from django.db import models
+from graphene import Schema
+from rest_framework import serializers
+
+from graphene_django import DjangoObjectType
+from graphene_django.rest_framework.mutation import SerializerMutation
+
+pytestmark = pytest.mark.django_db
+
+
+class MyFakeChildModel(models.Model):
+    name = models.CharField(max_length=50)
+    created = models.DateTimeField(auto_now_add=True)
+
+
+class MyFakeParentModel(models.Model):
+    name = models.CharField(max_length=50)
+    created = models.DateTimeField(auto_now_add=True)
+    child1 = models.OneToOneField(MyFakeChildModel, related_name='parent1', on_delete=models.CASCADE)
+    child2 = models.OneToOneField(MyFakeChildModel, related_name='parent2', on_delete=models.CASCADE)
+
+
+class ParentType(DjangoObjectType):
+    class Meta:
+        model = MyFakeParentModel
+        interfaces = (graphene.relay.Node,)
+
+
+class ChildType(DjangoObjectType):
+    class Meta:
+        model = MyFakeChildModel
+        interfaces = (graphene.relay.Node,)
+
+
+class MyModelChildSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = MyFakeChildModel
+        fields = "__all__"
+
+
+class MyModelParentSerializer(serializers.ModelSerializer):
+    child1 = MyModelChildSerializer()
+    child2 = MyModelChildSerializer()
+
+    class Meta:
+        model = MyFakeParentModel
+        fields = "__all__"
+
+
+class MyParentModelMutation(SerializerMutation):
+    class Meta:
+        serializer_class = MyModelParentSerializer
+
+
+class Mutation(graphene.ObjectType):
+    createParentWithChild = MyParentModelMutation.Field()
+
+
+def test_create_schema():
+    schema = Schema(mutation=Mutation, types=[ParentType, ChildType])
+    assert schema