Repository: graphql-python/graphene-django

Issue:
DRF DecimalField is converted to Float instead of Decimal in mutations
When using Graphene-Django 3.0.0b7 with Django REST Framework serializers, DRF's `DecimalField` will be converted to `Float` instead of `Decimal` in mutations

[Minimal demo project](https://github.com/jmp/graphene-django-decimal-example)

Reproduction steps:

1. Clone the demo project:
   ```sh
   git clone git@github.com:jmp/graphene-django-decimal-example.git
   ```
2. Set up the project:
   ```sh
   python3 -m venv .venv
   source .venv/bin/activate
   pip install -r requirements.txt
   ```
3. Generate the GraphQL schema:
   ```sh
   python manage.py graphql_schema --out schema.graphql
   ```
4. Open `schema.graphql` and note how the `DummyMutationInput` is defined as
   ```graphql
   input DummyMutationInput {
     id: Int
     foo: Float!
     clientMutationId: String
   }
   ```
   Also note that the `DummyType` has `foo` correctly as `Decimal!`:
   ```graphql
   type DummyType {
     id: ID!
   
     """"""
     foo: Decimal!
   }
   ```

**Expected behavior:**
In the mutation input, `foo` should have the type `Decimal!`.

**Motivation / use case for changing the behavior:**
I believe this was supposed to be fixed in #91, however the converters for DRF and forms were not updated the same way. So, [`DecimalField` is still converted to `Float`](https://github.com/graphql-python/graphene-django/blob/dd0d6ef28ffce33d30c4c8908f48a9d44a2b75ce/graphene_django/rest_framework/serializer_converter.py#L113) for example when using `SerializerMutation`.


**Environment:**
  - Version: 3.0.0b7
  - Platform: macOS



Discussion:

Resolving patch:
diff --git a/graphene_django/forms/converter.py b/graphene_django/forms/converter.py
--- a/graphene_django/forms/converter.py
+++ b/graphene_django/forms/converter.py
@@ -3,7 +3,19 @@
 from django import forms
 from django.core.exceptions import ImproperlyConfigured
 
-from graphene import ID, Boolean, Float, Int, List, String, UUID, Date, DateTime, Time
+from graphene import (
+    ID,
+    Boolean,
+    Decimal,
+    Float,
+    Int,
+    List,
+    String,
+    UUID,
+    Date,
+    DateTime,
+    Time,
+)
 
 from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField
 
@@ -57,12 +69,18 @@ def convert_form_field_to_nullboolean(field):
     return Boolean(description=get_form_field_description(field))
 
 
-@convert_form_field.register(forms.DecimalField)
 @convert_form_field.register(forms.FloatField)
 def convert_form_field_to_float(field):
     return Float(description=get_form_field_description(field), required=field.required)
 
 
+@convert_form_field.register(forms.DecimalField)
+def convert_form_field_to_decimal(field):
+    return Decimal(
+        description=get_form_field_description(field), required=field.required
+    )
+
+
 @convert_form_field.register(forms.MultipleChoiceField)
 def convert_form_field_to_string_list(field):
     return List(
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
@@ -110,11 +110,15 @@ def convert_serializer_field_to_bool(field):
 
 
 @get_graphene_type_from_serializer_field.register(serializers.FloatField)
-@get_graphene_type_from_serializer_field.register(serializers.DecimalField)
 def convert_serializer_field_to_float(field):
     return graphene.Float
 
 
+@get_graphene_type_from_serializer_field.register(serializers.DecimalField)
+def convert_serializer_field_to_decimal(field):
+    return graphene.Decimal
+
+
 @get_graphene_type_from_serializer_field.register(serializers.DateTimeField)
 def convert_serializer_field_to_datetime_time(field):
     return graphene.types.datetime.DateTime


Test patch:
diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py
--- a/graphene_django/filter/tests/test_fields.py
+++ b/graphene_django/filter/tests/test_fields.py
@@ -5,7 +5,7 @@
 from django.db.models import TextField, Value
 from django.db.models.functions import Concat
 
-from graphene import Argument, Boolean, Field, Float, ObjectType, Schema, String
+from graphene import Argument, Boolean, Decimal, Field, ObjectType, Schema, String
 from graphene.relay import Node
 from graphene_django import DjangoObjectType
 from graphene_django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
@@ -401,7 +401,7 @@ class Meta:
     field = DjangoFilterConnectionField(ArticleNode, filterset_class=ArticleIdFilter)
     max_time = field.args["max_time"]
     assert isinstance(max_time, Argument)
-    assert max_time.type == Float
+    assert max_time.type == Decimal
     assert max_time.description == "The maximum time"
 
 
diff --git a/graphene_django/forms/tests/test_converter.py b/graphene_django/forms/tests/test_converter.py
--- a/graphene_django/forms/tests/test_converter.py
+++ b/graphene_django/forms/tests/test_converter.py
@@ -6,6 +6,7 @@
     String,
     Int,
     Boolean,
+    Decimal,
     Float,
     ID,
     UUID,
@@ -97,8 +98,8 @@ def test_should_float_convert_float():
     assert_conversion(forms.FloatField, Float)
 
 
-def test_should_decimal_convert_float():
-    assert_conversion(forms.DecimalField, Float)
+def test_should_decimal_convert_decimal():
+    assert_conversion(forms.DecimalField, Decimal)
 
 
 def test_should_multiple_choice_convert_list():
diff --git a/graphene_django/rest_framework/tests/test_field_converter.py b/graphene_django/rest_framework/tests/test_field_converter.py
--- a/graphene_django/rest_framework/tests/test_field_converter.py
+++ b/graphene_django/rest_framework/tests/test_field_converter.py
@@ -133,9 +133,9 @@ def test_should_float_convert_float():
     assert_conversion(serializers.FloatField, graphene.Float)
 
 
-def test_should_decimal_convert_float():
+def test_should_decimal_convert_decimal():
     assert_conversion(
-        serializers.DecimalField, graphene.Float, max_digits=4, decimal_places=2
+        serializers.DecimalField, graphene.Decimal, max_digits=4, decimal_places=2
     )