Repository: graphql-python/graphene-django

Issue:
Django integer fields gets transformed to Float?
On a project of mine I noticed this unexpected behavior:

Using `graphene_django` with `graphene.relay` converts `IntegrierField`s of my models to `graphene.Float` properties in the API, e.g.:

```python
class Incident(models.Model):
    incident_id = models.IntegerField(unique=True)

class IncidentType(graphene_django.types.DjangoObjectType):
    class Meta:
        model = Incident
        interfaces = (graphene.relay.Node,)
        filter_fields = {
            'incident_id': ['exact'],
    }

class Query:
    incident = graphene.relay.Node.Field(IncidentType)
    incidents = DjangoFilterConnectionField(IncidentType)
```

To be precise, using the `incident` attribute shows the `incidentId` to be of type `Int`. But using the `incidents` attribute suggests that `incidentId` is of type `Float`.

I'd like to point out, that there is no error occurring, but it strikes me as weird.

I observed this with:
* Django 2.2
* django-filter 2.1.0
* graphene 2.1.3
* graphene-django 2.2.0
* graphql-core 2.1
* graphql-relay 0.4.5


Discussion:
@crazyscientist I can't reproduce your issue. If I setup my schema in the following way:

```python
from django.db import models

import graphene

from graphene_django.types import DjangoObjectType
from graphene_django.filter import DjangoFilterConnectionField


class Incident(models.Model):
    incident_id = models.IntegerField(unique=True)


class IncidentType(DjangoObjectType):
    class Meta:
        model = Incident
        interfaces = (graphene.relay.Node,)
        filter_fields = {"incident_id": ["exact"]}


class Query(graphene.ObjectType):
    incident = graphene.relay.Node.Field(IncidentType)
    incidents = DjangoFilterConnectionField(IncidentType)

schema = graphene.Schema(query=Query)
print(str(schema))
```

I get the following schema:

```
schema {
  query: Query
}

type IncidentType implements Node {
  id: ID!
  incidentId: Int!
}

type IncidentTypeConnection {
  pageInfo: PageInfo!
  edges: [IncidentTypeEdge]!
}

type IncidentTypeEdge {
  node: IncidentType
  cursor: String!
}

interface Node {
  id: ID!
}

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type Query {
  incident(id: ID!): IncidentType
  incidents(before: String, after: String, first: Int, last: Int, incidentId: Float): IncidentTypeConnection
}
```

Where `IncidentType` has the field `incidentId` of type `Int` which is what I would expect.

The only difference I can see with your code is that you should make sure that `Query` inherits from `graphene.ObjectType` (it doesn't in your example).

Can you make a small reproducible example of the issue you are seeing?
@jkimbo thanks for the reply, but you actually reproduced it:

```
# as expected an Int
type IncidentType implements Node {
  id: ID!
  incidentId: Int!
}

# here incidentId has become a Float?!
type Query {
  incident(id: ID!): IncidentType
  incidents(
    before: String, 
    after: String, 
    first: Int, last: Int, 
    incidentId: Float 
  ): IncidentTypeConnection
}
```

Don't get me wrong, the API is working as expected. I was just suprised that the type is `Float`
Huh I didn't notice that! Very strange, I'll look into why that's happening.
Ok so I've figured out what is going on but I'm not sure how to fix it. Basically Django-filter converts an IntegerField into a [NumberFilter](https://django-filter.readthedocs.io/en/master/ref/filters.html#numberfilter) and a NumberFilter defines it's Django form field as a [`DecimalField`](https://github.com/carltongibson/django-filter/blob/master/django_filters/filters.py#L361-L362). Graphene-Django uses that form field to determine the GraphQL type to use for the filter: https://github.com/graphql-python/graphene-django/blob/612ba5a4eaea0336a5dffcba3dbe7909b9d94646/graphene_django/forms/converter.py#L54-L57

I'm trying to figure out a way for the argument conversion to look at the actual underlying model field rather than the filter field but it's proving to be tricky. The relevant code is here: https://github.com/graphql-python/graphene-django/blob/612ba5a4eaea0336a5dffcba3dbe7909b9d94646/graphene_django/filter/utils.py#L6
Thanks for the investigation @jkimbo .

To me this sounds more like an issue for Django-filter :thinking: 
Possibly though from their perspective the NumberField makes sense, it's just that Graphene needs more accurate type information. I think I have a fix that could work, just doing some testing on it now.

Resolving patch:
diff --git a/graphene_django/filter/utils.py b/graphene_django/filter/utils.py
--- a/graphene_django/filter/utils.py
+++ b/graphene_django/filter/utils.py
@@ -11,8 +11,25 @@ def get_filtering_args_from_filterset(filterset_class, type):
     from ..forms.converter import convert_form_field
 
     args = {}
+    model = filterset_class._meta.model
     for name, filter_field in six.iteritems(filterset_class.base_filters):
-        field_type = convert_form_field(filter_field.field).Argument()
+        if name in filterset_class.declared_filters:
+            form_field = filter_field.field
+        else:
+            field_name = name.split("__", 1)[0]
+            model_field = model._meta.get_field(field_name)
+
+            if hasattr(model_field, "formfield"):
+                form_field = model_field.formfield(
+                    required=filter_field.extra.get("required", False)
+                )
+
+            # Fallback to field defined on filter if we can't get it from the
+            # model field
+            if not form_field:
+                form_field = filter_field.field
+
+        field_type = convert_form_field(form_field).Argument()
         field_type.description = filter_field.label
         args[name] = field_type
 


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
@@ -1,18 +1,17 @@
 from datetime import datetime
+from textwrap import dedent
 
 import pytest
+from django.db.models import TextField, Value
+from django.db.models.functions import Concat
 
-from graphene import Field, ObjectType, Schema, Argument, Float, Boolean, String
+from graphene import Argument, Boolean, Field, Float, ObjectType, Schema, String
 from graphene.relay import Node
 from graphene_django import DjangoObjectType
 from graphene_django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
 from graphene_django.tests.models import Article, Pet, Reporter
 from graphene_django.utils import DJANGO_FILTER_INSTALLED
 
-# for annotation test
-from django.db.models import TextField, Value
-from django.db.models.functions import Concat
-
 pytestmark = []
 
 if DJANGO_FILTER_INSTALLED:
@@ -183,7 +182,7 @@ class context(object):
     }
     """
     schema = Schema(query=Query)
-    result = schema.execute(query, context_value=context())
+    result = schema.execute(query, context=context())
     assert not result.errors
 
     assert len(result.data["contextArticles"]["edges"]) == 1
@@ -462,15 +461,15 @@ class Meta:
     class Query(ObjectType):
         all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
 
-    r1 = Reporter.objects.create(
+    Reporter.objects.create(
         first_name="A test user", last_name="Last Name", email="test1@test.com"
     )
-    r2 = Reporter.objects.create(
+    Reporter.objects.create(
         first_name="Other test user",
         last_name="Other Last Name",
         email="test2@test.com",
     )
-    r3 = Reporter.objects.create(
+    Reporter.objects.create(
         first_name="Random", last_name="RandomLast", email="random@test.com"
     )
 
@@ -638,7 +637,7 @@ def resolve_all_reporters(self, info, **args):
     Reporter.objects.create(
         first_name="Bob", last_name="Doe", email="bobdoe@example.com", a_choice=2
     )
-    r = Reporter.objects.create(
+    Reporter.objects.create(
         first_name="John", last_name="Doe", email="johndoe@example.com", a_choice=1
     )
 
@@ -684,7 +683,7 @@ def resolve_all_reporters(self, info, reverse_order=False, **args):
             return reporters
 
     Reporter.objects.create(first_name="b")
-    r = Reporter.objects.create(first_name="a")
+    Reporter.objects.create(first_name="a")
 
     schema = Schema(query=Query)
     query = """
@@ -767,3 +766,55 @@ def resolve_all_reporters(self, info, **args):
 
     assert not result.errors
     assert result.data == expected
+
+
+def test_integer_field_filter_type():
+    class PetType(DjangoObjectType):
+        class Meta:
+            model = Pet
+            interfaces = (Node,)
+            filter_fields = {"age": ["exact"]}
+            only_fields = ["age"]
+
+    class Query(ObjectType):
+        pets = DjangoFilterConnectionField(PetType)
+
+    schema = Schema(query=Query)
+
+    assert str(schema) == dedent(
+        """\
+        schema {
+          query: Query
+        }
+
+        interface Node {
+          id: ID!
+        }
+
+        type PageInfo {
+          hasNextPage: Boolean!
+          hasPreviousPage: Boolean!
+          startCursor: String
+          endCursor: String
+        }
+
+        type PetType implements Node {
+          age: Int!
+          id: ID!
+        }
+
+        type PetTypeConnection {
+          pageInfo: PageInfo!
+          edges: [PetTypeEdge]!
+        }
+
+        type PetTypeEdge {
+          node: PetType
+          cursor: String!
+        }
+
+        type Query {
+          pets(before: String, after: String, first: Int, last: Int, age: Int): PetTypeConnection
+        }
+    """
+    )