mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-10-31 16:07:36 +03:00 
			
		
		
		
	Merge pull request #186 from patrick91/feature/rest-framework
Support for Django Rest Framework serializers
This commit is contained in:
		
						commit
						6ad64ddbef
					
				|  | @ -11,4 +11,5 @@ Contents: | ||||||
|    filtering |    filtering | ||||||
|    authorization |    authorization | ||||||
|    debug |    debug | ||||||
|  |    rest-framework | ||||||
|    introspection |    introspection | ||||||
|  |  | ||||||
							
								
								
									
										21
									
								
								docs/rest-framework.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								docs/rest-framework.rst
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | ||||||
|  | Integration with Django Rest Framework | ||||||
|  | ====================================== | ||||||
|  | 
 | ||||||
|  | You can re-use your Django Rest Framework serializer with | ||||||
|  | graphene django. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Mutation | ||||||
|  | -------- | ||||||
|  | 
 | ||||||
|  | You can create a Mutation based on a serializer by using the | ||||||
|  | `SerializerMutation` base class: | ||||||
|  | 
 | ||||||
|  | .. code:: python | ||||||
|  | 
 | ||||||
|  |     from graphene_django.rest_framework.mutation import SerializerMutation | ||||||
|  | 
 | ||||||
|  |     class MyAwesomeMutation(SerializerMutation): | ||||||
|  |         class Meta: | ||||||
|  |             serializer_class = MySerializer | ||||||
|  | 
 | ||||||
							
								
								
									
										0
									
								
								graphene_django/rest_framework/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene_django/rest_framework/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										129
									
								
								graphene_django/rest_framework/mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								graphene_django/rest_framework/mutation.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,129 @@ | ||||||
|  | from collections import OrderedDict | ||||||
|  | from functools import partial | ||||||
|  | 
 | ||||||
|  | import six | ||||||
|  | import graphene | ||||||
|  | from graphene.types import Argument, Field | ||||||
|  | from graphene.types.mutation import Mutation, MutationMeta | ||||||
|  | from graphene.types.objecttype import ( | ||||||
|  |     ObjectTypeMeta, | ||||||
|  |     merge, | ||||||
|  |     yank_fields_from_attrs | ||||||
|  | ) | ||||||
|  | from graphene.types.options import Options | ||||||
|  | from graphene.types.utils import get_field_as | ||||||
|  | from graphene.utils.is_base_type import is_base_type | ||||||
|  | 
 | ||||||
|  | from .serializer_converter import ( | ||||||
|  |     convert_serializer_to_input_type, | ||||||
|  |     convert_serializer_field | ||||||
|  | ) | ||||||
|  | from .types import ErrorType | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SerializerMutationOptions(Options): | ||||||
|  |     def __init__(self, *args, **kwargs): | ||||||
|  |         super().__init__(*args, serializer_class=None, **kwargs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SerializerMutationMeta(MutationMeta): | ||||||
|  |     def __new__(cls, name, bases, attrs): | ||||||
|  |         if not is_base_type(bases, SerializerMutationMeta): | ||||||
|  |             return type.__new__(cls, name, bases, attrs) | ||||||
|  | 
 | ||||||
|  |         options = Options( | ||||||
|  |             attrs.pop('Meta', None), | ||||||
|  |             name=name, | ||||||
|  |             description=attrs.pop('__doc__', None), | ||||||
|  |             serializer_class=None, | ||||||
|  |             local_fields=None, | ||||||
|  |             only_fields=(), | ||||||
|  |             exclude_fields=(), | ||||||
|  |             interfaces=(), | ||||||
|  |             registry=None | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         if not options.serializer_class: | ||||||
|  |             raise Exception('Missing serializer_class') | ||||||
|  | 
 | ||||||
|  |         cls = ObjectTypeMeta.__new__( | ||||||
|  |             cls, name, bases, dict(attrs, _meta=options) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         serializer_fields = cls.fields_for_serializer(options) | ||||||
|  |         options.serializer_fields = yank_fields_from_attrs( | ||||||
|  |             serializer_fields, | ||||||
|  |             _as=Field, | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         options.fields = merge( | ||||||
|  |             options.interface_fields, options.serializer_fields, | ||||||
|  |             options.base_fields, options.local_fields, | ||||||
|  |             {'errors': get_field_as(cls.errors, Field)} | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         cls.Input = convert_serializer_to_input_type(options.serializer_class) | ||||||
|  | 
 | ||||||
|  |         cls.Field = partial( | ||||||
|  |             Field, | ||||||
|  |             cls, | ||||||
|  |             resolver=cls.mutate, | ||||||
|  |             input=Argument(cls.Input, required=True) | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
|  |         return cls | ||||||
|  | 
 | ||||||
|  |     @staticmethod | ||||||
|  |     def fields_for_serializer(options): | ||||||
|  |         serializer = options.serializer_class() | ||||||
|  | 
 | ||||||
|  |         only_fields = options.only_fields | ||||||
|  | 
 | ||||||
|  |         already_created_fields = { | ||||||
|  |             name | ||||||
|  |             for name, _ in options.local_fields.items() | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         fields = OrderedDict() | ||||||
|  |         for name, field in serializer.fields.items(): | ||||||
|  |             is_not_in_only = only_fields and name not in only_fields | ||||||
|  |             is_excluded = ( | ||||||
|  |                 name in options.exclude_fields or | ||||||
|  |                 name in already_created_fields | ||||||
|  |             ) | ||||||
|  | 
 | ||||||
|  |             if is_not_in_only or is_excluded: | ||||||
|  |                 continue | ||||||
|  | 
 | ||||||
|  |             fields[name] = convert_serializer_field(field, is_input=False) | ||||||
|  |         return fields | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class SerializerMutation(six.with_metaclass(SerializerMutationMeta, Mutation)): | ||||||
|  |     errors = graphene.List( | ||||||
|  |         ErrorType, | ||||||
|  |         description='May contain more than one error for ' | ||||||
|  |         'same field.' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def mutate(cls, instance, args, request, info): | ||||||
|  |         input = args.get('input') | ||||||
|  | 
 | ||||||
|  |         serializer = cls._meta.serializer_class(data=dict(input)) | ||||||
|  | 
 | ||||||
|  |         if serializer.is_valid(): | ||||||
|  |             return cls.perform_mutate(serializer, info) | ||||||
|  |         else: | ||||||
|  |             errors = [ | ||||||
|  |                 ErrorType(field=key, messages=value) | ||||||
|  |                 for key, value in serializer.errors.items() | ||||||
|  |             ] | ||||||
|  | 
 | ||||||
|  |             return cls(errors=errors) | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def perform_mutate(cls, serializer, info): | ||||||
|  |         obj = serializer.save() | ||||||
|  | 
 | ||||||
|  |         return cls(errors=[], **obj) | ||||||
							
								
								
									
										124
									
								
								graphene_django/rest_framework/serializer_converter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								graphene_django/rest_framework/serializer_converter.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,124 @@ | ||||||
|  | from django.core.exceptions import ImproperlyConfigured | ||||||
|  | from rest_framework import serializers | ||||||
|  | 
 | ||||||
|  | import graphene | ||||||
|  | 
 | ||||||
|  | from ..registry import get_global_registry | ||||||
|  | from ..utils import import_single_dispatch | ||||||
|  | from .types import DictType | ||||||
|  | 
 | ||||||
|  | singledispatch = import_single_dispatch() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def convert_serializer_to_input_type(serializer_class): | ||||||
|  |     serializer = serializer_class() | ||||||
|  | 
 | ||||||
|  |     items = { | ||||||
|  |         name: convert_serializer_field(field) | ||||||
|  |         for name, field in serializer.fields.items() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return type( | ||||||
|  |         '{}Input'.format(serializer.__class__.__name__), | ||||||
|  |         (graphene.InputObjectType, ), | ||||||
|  |         items | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @singledispatch | ||||||
|  | def get_graphene_type_from_serializer_field(field): | ||||||
|  |     raise ImproperlyConfigured( | ||||||
|  |         "Don't know how to convert the serializer field %s (%s) " | ||||||
|  |         "to Graphene type" % (field, field.__class__) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def convert_serializer_field(field, is_input=True): | ||||||
|  |     """ | ||||||
|  |     Converts a django rest frameworks field to a graphql field | ||||||
|  |     and marks the field as required if we are creating an input type | ||||||
|  |     and the field itself is required | ||||||
|  |     """ | ||||||
|  | 
 | ||||||
|  |     graphql_type = get_graphene_type_from_serializer_field(field) | ||||||
|  | 
 | ||||||
|  |     args = [] | ||||||
|  |     kwargs = { | ||||||
|  |         'description': field.help_text, | ||||||
|  |         'required': is_input and field.required, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     # if it is a tuple or a list it means that we are returning | ||||||
|  |     # the graphql type and the child type | ||||||
|  |     if isinstance(graphql_type, (list, tuple)): | ||||||
|  |         kwargs['of_type'] = graphql_type[1] | ||||||
|  |         graphql_type = graphql_type[0] | ||||||
|  | 
 | ||||||
|  |     if isinstance(field, serializers.ModelSerializer): | ||||||
|  |         if is_input: | ||||||
|  |             graphql_type = convert_serializer_to_input_type(field.__class__) | ||||||
|  |         else: | ||||||
|  |             global_registry = get_global_registry() | ||||||
|  |             field_model = field.Meta.model | ||||||
|  |             args = [global_registry.get_type_for_model(field_model)] | ||||||
|  | 
 | ||||||
|  |     return graphql_type(*args, **kwargs) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.Field) | ||||||
|  | def convert_serializer_field_to_string(field): | ||||||
|  |     return graphene.String | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.ModelSerializer) | ||||||
|  | def convert_serializer_to_field(field): | ||||||
|  |     return graphene.Field | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.IntegerField) | ||||||
|  | def convert_serializer_field_to_int(field): | ||||||
|  |     return graphene.Int | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.BooleanField) | ||||||
|  | def convert_serializer_field_to_bool(field): | ||||||
|  |     return graphene.Boolean | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @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.DateTimeField) | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.DateField) | ||||||
|  | def convert_serializer_field_to_date_time(field): | ||||||
|  |     return graphene.types.datetime.DateTime | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.TimeField) | ||||||
|  | def convert_serializer_field_to_time(field): | ||||||
|  |     return graphene.types.datetime.Time | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.ListField) | ||||||
|  | def convert_serializer_field_to_list(field, is_input=True): | ||||||
|  |     child_type = get_graphene_type_from_serializer_field(field.child) | ||||||
|  | 
 | ||||||
|  |     return (graphene.List, child_type) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.DictField) | ||||||
|  | def convert_serializer_field_to_dict(field): | ||||||
|  |     return DictType | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.JSONField) | ||||||
|  | def convert_serializer_field_to_jsonstring(field): | ||||||
|  |     return graphene.types.json.JSONString | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @get_graphene_type_from_serializer_field.register(serializers.MultipleChoiceField) | ||||||
|  | def convert_serializer_field_to_list_of_string(field): | ||||||
|  |     return (graphene.List, graphene.String) | ||||||
							
								
								
									
										0
									
								
								graphene_django/rest_framework/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene_django/rest_framework/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										162
									
								
								graphene_django/rest_framework/tests/test_field_converter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								graphene_django/rest_framework/tests/test_field_converter.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | ||||||
|  | import copy | ||||||
|  | from rest_framework import serializers | ||||||
|  | from py.test import raises | ||||||
|  | 
 | ||||||
|  | import graphene | ||||||
|  | 
 | ||||||
|  | from ..serializer_converter import convert_serializer_field | ||||||
|  | from ..types import DictType | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _get_type(rest_framework_field, is_input=True, **kwargs): | ||||||
|  |     # prevents the following error: | ||||||
|  |     # AssertionError: The `source` argument is not meaningful when applied to a `child=` field. | ||||||
|  |     # Remove `source=` from the field declaration. | ||||||
|  |     # since we are reusing the same child in when testing the required attribute | ||||||
|  | 
 | ||||||
|  |     if 'child' in kwargs: | ||||||
|  |         kwargs['child'] = copy.deepcopy(kwargs['child']) | ||||||
|  | 
 | ||||||
|  |     field = rest_framework_field(**kwargs) | ||||||
|  | 
 | ||||||
|  |     return convert_serializer_field(field, is_input=is_input) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def assert_conversion(rest_framework_field, graphene_field, **kwargs): | ||||||
|  |     graphene_type = _get_type(rest_framework_field, help_text='Custom Help Text', **kwargs) | ||||||
|  |     assert isinstance(graphene_type, graphene_field) | ||||||
|  | 
 | ||||||
|  |     graphene_type_required = _get_type( | ||||||
|  |         rest_framework_field, help_text='Custom Help Text', required=True, **kwargs | ||||||
|  |     ) | ||||||
|  |     assert isinstance(graphene_type_required, graphene_field) | ||||||
|  | 
 | ||||||
|  |     return graphene_type | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_unknown_rest_framework_field_raise_exception(): | ||||||
|  |     with raises(Exception) as excinfo: | ||||||
|  |         convert_serializer_field(None) | ||||||
|  |     assert 'Don\'t know how to convert the serializer field' in str(excinfo.value) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_char_convert_string(): | ||||||
|  |     assert_conversion(serializers.CharField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_email_convert_string(): | ||||||
|  |     assert_conversion(serializers.EmailField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_slug_convert_string(): | ||||||
|  |     assert_conversion(serializers.SlugField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_url_convert_string(): | ||||||
|  |     assert_conversion(serializers.URLField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_choice_convert_string(): | ||||||
|  |     assert_conversion(serializers.ChoiceField, graphene.String, choices=[]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_base_field_convert_string(): | ||||||
|  |     assert_conversion(serializers.Field, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_regex_convert_string(): | ||||||
|  |     assert_conversion(serializers.RegexField, graphene.String, regex='[0-9]+') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_uuid_convert_string(): | ||||||
|  |     if hasattr(serializers, 'UUIDField'): | ||||||
|  |         assert_conversion(serializers.UUIDField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_model_convert_field(): | ||||||
|  | 
 | ||||||
|  |     class MyModelSerializer(serializers.ModelSerializer): | ||||||
|  |         class Meta: | ||||||
|  |             model = None | ||||||
|  |             fields = '__all__' | ||||||
|  | 
 | ||||||
|  |     assert_conversion(MyModelSerializer, graphene.Field, is_input=False) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_date_time_convert_datetime(): | ||||||
|  |     assert_conversion(serializers.DateTimeField, graphene.types.datetime.DateTime) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_date_convert_datetime(): | ||||||
|  |     assert_conversion(serializers.DateField, graphene.types.datetime.DateTime) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_time_convert_time(): | ||||||
|  |     assert_conversion(serializers.TimeField, graphene.types.datetime.Time) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_integer_convert_int(): | ||||||
|  |     assert_conversion(serializers.IntegerField, graphene.Int) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_boolean_convert_boolean(): | ||||||
|  |     assert_conversion(serializers.BooleanField, graphene.Boolean) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_float_convert_float(): | ||||||
|  |     assert_conversion(serializers.FloatField, graphene.Float) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_decimal_convert_float(): | ||||||
|  |     assert_conversion(serializers.DecimalField, graphene.Float, max_digits=4, decimal_places=2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_list_convert_to_list(): | ||||||
|  |     class StringListField(serializers.ListField): | ||||||
|  |         child = serializers.CharField() | ||||||
|  | 
 | ||||||
|  |     field_a = assert_conversion( | ||||||
|  |         serializers.ListField, | ||||||
|  |         graphene.List, | ||||||
|  |         child=serializers.IntegerField(min_value=0, max_value=100) | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     assert field_a.of_type == graphene.Int | ||||||
|  | 
 | ||||||
|  |     field_b = assert_conversion(StringListField, graphene.List) | ||||||
|  | 
 | ||||||
|  |     assert field_b.of_type == graphene.String | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_dict_convert_dict(): | ||||||
|  |     assert_conversion(serializers.DictField, DictType) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_duration_convert_string(): | ||||||
|  |     assert_conversion(serializers.DurationField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_file_convert_string(): | ||||||
|  |     assert_conversion(serializers.FileField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_filepath_convert_string(): | ||||||
|  |     assert_conversion(serializers.FilePathField, graphene.String, path='/') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_ip_convert_string(): | ||||||
|  |     assert_conversion(serializers.IPAddressField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_image_convert_string(): | ||||||
|  |     assert_conversion(serializers.ImageField, graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_json_convert_jsonstring(): | ||||||
|  |     assert_conversion(serializers.JSONField, graphene.types.json.JSONString) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_should_multiplechoicefield_convert_to_list_of_string(): | ||||||
|  |     field = assert_conversion(serializers.MultipleChoiceField, graphene.List, choices=[1,2,3]) | ||||||
|  | 
 | ||||||
|  |     assert field.of_type == graphene.String | ||||||
							
								
								
									
										70
									
								
								graphene_django/rest_framework/tests/test_mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								graphene_django/rest_framework/tests/test_mutation.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,70 @@ | ||||||
|  | from django.db import models | ||||||
|  | from graphene import Field | ||||||
|  | from graphene.types.inputobjecttype import InputObjectType | ||||||
|  | from py.test import raises | ||||||
|  | from rest_framework import serializers | ||||||
|  | 
 | ||||||
|  | from ...types import DjangoObjectType | ||||||
|  | from ..mutation import SerializerMutation | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MyFakeModel(models.Model): | ||||||
|  |     cool_name = models.CharField(max_length=50) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MyModelSerializer(serializers.ModelSerializer): | ||||||
|  |     class Meta: | ||||||
|  |         model = MyFakeModel | ||||||
|  |         fields = '__all__' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MySerializer(serializers.Serializer): | ||||||
|  |     text = serializers.CharField() | ||||||
|  |     model = MyModelSerializer() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_needs_serializer_class(): | ||||||
|  |     with raises(Exception) as exc: | ||||||
|  |         class MyMutation(SerializerMutation): | ||||||
|  |             pass | ||||||
|  | 
 | ||||||
|  |     assert exc.value.args[0] == 'Missing serializer_class' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_has_fields(): | ||||||
|  |     class MyMutation(SerializerMutation): | ||||||
|  |         class Meta: | ||||||
|  |             serializer_class = MySerializer | ||||||
|  | 
 | ||||||
|  |     assert 'text' in MyMutation._meta.fields | ||||||
|  |     assert 'model' in MyMutation._meta.fields | ||||||
|  |     assert 'errors' in MyMutation._meta.fields | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_has_input_fields(): | ||||||
|  |     class MyMutation(SerializerMutation): | ||||||
|  |         class Meta: | ||||||
|  |             serializer_class = MySerializer | ||||||
|  | 
 | ||||||
|  |     assert 'text' in MyMutation.Input._meta.fields | ||||||
|  |     assert 'model' in MyMutation.Input._meta.fields | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_nested_model(): | ||||||
|  | 
 | ||||||
|  |     class MyFakeModelGrapheneType(DjangoObjectType): | ||||||
|  |         class Meta: | ||||||
|  |             model = MyFakeModel | ||||||
|  | 
 | ||||||
|  |     class MyMutation(SerializerMutation): | ||||||
|  |         class Meta: | ||||||
|  |             serializer_class = MySerializer | ||||||
|  | 
 | ||||||
|  |     model_field = MyMutation._meta.fields['model'] | ||||||
|  |     assert isinstance(model_field, Field) | ||||||
|  |     assert model_field.type == MyFakeModelGrapheneType | ||||||
|  | 
 | ||||||
|  |     model_input = MyMutation.Input._meta.fields['model'] | ||||||
|  |     model_input_type = model_input._type.of_type | ||||||
|  |     assert issubclass(model_input_type, InputObjectType) | ||||||
|  |     assert 'cool_name' in model_input_type._meta.fields | ||||||
							
								
								
									
										12
									
								
								graphene_django/rest_framework/types.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								graphene_django/rest_framework/types.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | import graphene | ||||||
|  | from graphene.types.unmountedtype import UnmountedType | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ErrorType(graphene.ObjectType): | ||||||
|  |     field = graphene.String() | ||||||
|  |     messages = graphene.List(graphene.String) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DictType(UnmountedType): | ||||||
|  |     key = graphene.String() | ||||||
|  |     value = graphene.String() | ||||||
|  | @ -11,8 +11,11 @@ class LazyList(object): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | try: | ||||||
|     import django_filters  # noqa |     import django_filters  # noqa | ||||||
|     DJANGO_FILTER_INSTALLED = True |     DJANGO_FILTER_INSTALLED = True | ||||||
|  | except ImportError: | ||||||
|  |     DJANGO_FILTER_INSTALLED = False | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_reverse_fields(model, local_field_names): | def get_reverse_fields(model, local_field_names): | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								setup.py
									
									
									
									
									
								
							|  | @ -1,5 +1,10 @@ | ||||||
| from setuptools import find_packages, setup | from setuptools import find_packages, setup | ||||||
| 
 | 
 | ||||||
|  | rest_framework_require = [ | ||||||
|  |     'djangorestframework==3.6.3', | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| tests_require = [ | tests_require = [ | ||||||
|     'pytest>=2.7.2', |     'pytest>=2.7.2', | ||||||
|     'pytest-cov', |     'pytest-cov', | ||||||
|  | @ -8,7 +13,7 @@ tests_require = [ | ||||||
|     'pytz', |     'pytz', | ||||||
|     'django-filter', |     'django-filter', | ||||||
|     'pytest-django==2.9.1', |     'pytest-django==2.9.1', | ||||||
| ] | ] + rest_framework_require | ||||||
| 
 | 
 | ||||||
| setup( | setup( | ||||||
|     name='graphene-django', |     name='graphene-django', | ||||||
|  | @ -53,8 +58,10 @@ setup( | ||||||
|         'pytest-runner', |         'pytest-runner', | ||||||
|     ], |     ], | ||||||
|     tests_require=tests_require, |     tests_require=tests_require, | ||||||
|  |     rest_framework_require=rest_framework_require, | ||||||
|     extras_require={ |     extras_require={ | ||||||
|         'test': tests_require, |         'test': tests_require, | ||||||
|  |         'rest_framework': rest_framework_require, | ||||||
|     }, |     }, | ||||||
|     include_package_data=True, |     include_package_data=True, | ||||||
|     zip_safe=False, |     zip_safe=False, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user