From dcd8edb59af9eba9ee9c4fda6b59ff8268a17a9e Mon Sep 17 00:00:00 2001
From: Syrus Akbary <me@syrusakbary.com>
Date: Sat, 3 Oct 2015 01:47:55 -0700
Subject: [PATCH] Improved Django Connection resolver. Added exclude_fields
 option to Django Types

---
 graphene/contrib/django/__init__.py |  4 +++
 graphene/contrib/django/fields.py   |  4 +--
 graphene/contrib/django/options.py  |  3 ++-
 graphene/contrib/django/types.py    |  6 ++++-
 graphene/contrib/django/views.py    | 40 +++++++++++++++++++++++++++++
 graphene/utils.py                   |  1 -
 6 files changed, 53 insertions(+), 5 deletions(-)
 create mode 100644 graphene/contrib/django/views.py

diff --git a/graphene/contrib/django/__init__.py b/graphene/contrib/django/__init__.py
index 96edc3d3..9e4c8aea 100644
--- a/graphene/contrib/django/__init__.py
+++ b/graphene/contrib/django/__init__.py
@@ -2,3 +2,7 @@ from graphene.contrib.django.types import (
     DjangoObjectType,
     DjangoNode
 )
+from graphene.contrib.django.fields import (
+    DjangoConnectionField,
+    DjangoModelField
+)
diff --git a/graphene/contrib/django/fields.py b/graphene/contrib/django/fields.py
index 4cccc9f6..292e6333 100644
--- a/graphene/contrib/django/fields.py
+++ b/graphene/contrib/django/fields.py
@@ -32,13 +32,13 @@ def lazy_map(value, func):
 
 class DjangoConnectionField(relay.ConnectionField):
     def wrap_resolved(self, value, instance, args, info):
-        return lazy_map(value, instance.__class__)
+        return lazy_map(value, self.field_type)
 
 
 class LazyListField(ListField):
     def resolve(self, value, instance, args, info):
         resolved = super(LazyListField, self).resolve(value, instance, args, info)
-        return lazy_map(resolved, instance.__class__)
+        return lazy_map(resolved, self.field_type)
 
 
 class ConnectionOrListField(LazyField):
diff --git a/graphene/contrib/django/options.py b/graphene/contrib/django/options.py
index 69415cc0..3ee72a67 100644
--- a/graphene/contrib/django/options.py
+++ b/graphene/contrib/django/options.py
@@ -5,7 +5,7 @@ from graphene.core.options import Options
 from graphene.core.types import BaseObjectType
 from graphene.relay.utils import is_node
 
-VALID_ATTRS = ('model', 'only_fields')
+VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
 
 
 def is_base(cls):
@@ -20,6 +20,7 @@ class DjangoOptions(Options):
         super(DjangoOptions, self).__init__(*args, **kwargs)
         self.valid_attrs += VALID_ATTRS
         self.only_fields = None
+        self.exclude_fields = []
 
     def contribute_to_class(self, cls, name):
         super(DjangoOptions, self).contribute_to_class(cls, name)
diff --git a/graphene/contrib/django/types.py b/graphene/contrib/django/types.py
index 6e23bd6d..d88a7f14 100644
--- a/graphene/contrib/django/types.py
+++ b/graphene/contrib/django/types.py
@@ -27,7 +27,11 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
         only_fields = cls._meta.only_fields
         reverse_fields = tuple(get_reverse_fields(cls._meta.model))
         for field in cls._meta.model._meta.fields + reverse_fields:
-            if only_fields and field.name not in only_fields:
+            is_not_in_only = only_fields and field.name not in only_fields
+            is_excluded = field.name in cls._meta.exclude_fields
+            if is_not_in_only or is_excluded:
+                # We skip this field if we specify only_fields and is not
+                # in there. Or when we excldue this field in exclude_fields
                 continue
             converted_field = convert_django_field(field, cls)
             cls.add_to_class(field.name, converted_field)
diff --git a/graphene/contrib/django/views.py b/graphene/contrib/django/views.py
new file mode 100644
index 00000000..01f50387
--- /dev/null
+++ b/graphene/contrib/django/views.py
@@ -0,0 +1,40 @@
+import json
+
+from django.http import JsonResponse
+from django.views.generic import View
+
+from graphql.core.error import GraphQLError, format_error
+
+
+def form_error(error):
+    if isinstance(error, GraphQLError):
+        return format_error(error)
+    return error
+
+
+class GraphQLView(View):
+    schema = None
+    @staticmethod
+    def format_result(result):
+        data = {'data': result.data}
+        if result.errors:
+            data['errors'] = map(form_error, result.errors)
+
+        return data
+
+    def execute_query(self, request, query):
+        result = self.schema.execute(query, root=object())
+        data = self.format_result(result)
+        return JsonResponse(data)
+
+    def get(self, request, *args, **kwargs):
+        query = request.GET.get('query') or request.GET.get('q') or ''
+        return self.execute_query(request, query)
+
+    def post(self, request, *args, **kwargs):
+        if request.body:
+            received_json_data = json.loads(request.body)
+            query = received_json_data.get('query') or ''
+        else:
+            query = request.POST.get('query') or request.POST.get('q')
+        return self.execute_query(request, query)
diff --git a/graphene/utils.py b/graphene/utils.py
index b7a7864e..f551f57f 100644
--- a/graphene/utils.py
+++ b/graphene/utils.py
@@ -2,7 +2,6 @@ from functools import wraps
 
 
 class cached_property(object):
-
     """
     A property that is only computed once per instance and then replaces itself
     with an ordinary attribute. Deleting the attribute resets the property.