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.