mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-11-04 09:57:53 +03:00 
			
		
		
		
	Merge pull request #450 from graphql-python/form_mutations
Form mutations
This commit is contained in:
		
						commit
						4e7b269b76
					
				
							
								
								
									
										68
									
								
								docs/form-mutations.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								docs/form-mutations.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					Integration with Django forms
 | 
				
			||||||
 | 
					=============================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Graphene-Django comes with mutation classes that will convert the fields on Django forms into inputs on a mutation.
 | 
				
			||||||
 | 
					*Note: the API is experimental and will likely change in the future.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FormMutation
 | 
				
			||||||
 | 
					------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code:: python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class MyForm(forms.Form):
 | 
				
			||||||
 | 
					        name = forms.CharField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class MyMutation(FormMutation):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            form_class = MyForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``MyMutation`` will automatically receive an ``input`` argument. This argument should be a ``dict`` where the key is ``name`` and the value is a string.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ModelFormMutation
 | 
				
			||||||
 | 
					-----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``ModelFormMutation`` will pull the fields from a ``ModelForm``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code:: python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Pet(models.Model):
 | 
				
			||||||
 | 
					        name = models.CharField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class PetForm(forms.ModelForm):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = Pet
 | 
				
			||||||
 | 
					            fields = ('name',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # This will get returned when the mutation completes successfully
 | 
				
			||||||
 | 
					    class PetType(DjangoObjectType):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = Pet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            form_class = PetForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``PetMutation`` will grab the fields from ``PetForm`` and turn them into inputs. If the form is valid then the mutation
 | 
				
			||||||
 | 
					will lookup the ``DjangoObjectType`` for the ``Pet`` model and return that under the key ``pet``. Otherwise it will
 | 
				
			||||||
 | 
					return a list of errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can change the input name (default is ``input``) and the return field name (default is the model name lowercase).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. code:: python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            form_class = PetForm
 | 
				
			||||||
 | 
					            input_field_name = 'data'
 | 
				
			||||||
 | 
					            return_field_name = 'my_pet'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Form validation
 | 
				
			||||||
 | 
					---------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Form mutations will call ``is_valid()`` on your forms.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If the form is valid then ``form_valid(form, info)`` is called on the mutation. Override this method to change how
 | 
				
			||||||
 | 
					the form is saved or to return a different Graphene object type.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If the form is *not* valid then a list of errors will be returned. These errors have two fields: ``field``, a string
 | 
				
			||||||
 | 
					containing the name of the invalid form field, and ``messages``, a list of strings with the validation messages.
 | 
				
			||||||
| 
						 | 
					@ -12,4 +12,5 @@ Contents:
 | 
				
			||||||
   authorization
 | 
					   authorization
 | 
				
			||||||
   debug
 | 
					   debug
 | 
				
			||||||
   rest-framework
 | 
					   rest-framework
 | 
				
			||||||
 | 
					   form-mutations
 | 
				
			||||||
   introspection
 | 
					   introspection
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ def get_filtering_args_from_filterset(filterset_class, type):
 | 
				
			||||||
        a Graphene Field. These arguments will be available to
 | 
					        a Graphene Field. These arguments will be available to
 | 
				
			||||||
        filter against in the GraphQL
 | 
					        filter against in the GraphQL
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    from ..form_converter import convert_form_field
 | 
					    from ..forms.converter import convert_form_field
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    args = {}
 | 
					    args = {}
 | 
				
			||||||
    for name, filter_field in six.iteritems(filterset_class.base_filters):
 | 
					    for name, filter_field in six.iteritems(filterset_class.base_filters):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								graphene_django/forms/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								graphene_django/forms/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField  # noqa
 | 
				
			||||||
| 
						 | 
					@ -1,25 +1,25 @@
 | 
				
			||||||
from django import forms
 | 
					from django import forms
 | 
				
			||||||
from django.forms.fields import BaseTemporalField
 | 
					from django.core.exceptions import ImproperlyConfigured
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from graphene import ID, Boolean, Float, Int, List, String, UUID
 | 
					from graphene import ID, Boolean, Float, Int, List, String, UUID, Date, DateTime, Time
 | 
				
			||||||
from graphene.types.datetime import Date, DateTime, Time
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField
 | 
					from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField
 | 
				
			||||||
from .utils import import_single_dispatch
 | 
					from ..utils import import_single_dispatch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
singledispatch = import_single_dispatch()
 | 
					singledispatch = import_single_dispatch()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@singledispatch
 | 
					@singledispatch
 | 
				
			||||||
def convert_form_field(field):
 | 
					def convert_form_field(field):
 | 
				
			||||||
    raise Exception(
 | 
					    raise ImproperlyConfigured(
 | 
				
			||||||
        "Don't know how to convert the Django form field %s (%s) "
 | 
					        "Don't know how to convert the Django form field %s (%s) "
 | 
				
			||||||
        "to Graphene type" %
 | 
					        "to Graphene type" %
 | 
				
			||||||
        (field, field.__class__)
 | 
					        (field, field.__class__)
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@convert_form_field.register(BaseTemporalField)
 | 
					@convert_form_field.register(forms.fields.BaseTemporalField)
 | 
				
			||||||
@convert_form_field.register(forms.CharField)
 | 
					@convert_form_field.register(forms.CharField)
 | 
				
			||||||
@convert_form_field.register(forms.EmailField)
 | 
					@convert_form_field.register(forms.EmailField)
 | 
				
			||||||
@convert_form_field.register(forms.SlugField)
 | 
					@convert_form_field.register(forms.SlugField)
 | 
				
			||||||
							
								
								
									
										193
									
								
								graphene_django/forms/mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								graphene_django/forms/mutation.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,193 @@
 | 
				
			||||||
 | 
					# from django import forms
 | 
				
			||||||
 | 
					from collections import OrderedDict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import graphene
 | 
				
			||||||
 | 
					from graphene import Field, InputField
 | 
				
			||||||
 | 
					from graphene.relay.mutation import ClientIDMutation
 | 
				
			||||||
 | 
					from graphene.types.mutation import MutationOptions
 | 
				
			||||||
 | 
					# from graphene.types.inputobjecttype import (
 | 
				
			||||||
 | 
					#     InputObjectTypeOptions,
 | 
				
			||||||
 | 
					#     InputObjectType,
 | 
				
			||||||
 | 
					# )
 | 
				
			||||||
 | 
					from graphene.types.utils import yank_fields_from_attrs
 | 
				
			||||||
 | 
					from graphene_django.registry import get_global_registry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .converter import convert_form_field
 | 
				
			||||||
 | 
					from .types import ErrorType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def fields_for_form(form, only_fields, exclude_fields):
 | 
				
			||||||
 | 
					    fields = OrderedDict()
 | 
				
			||||||
 | 
					    for name, field in form.fields.items():
 | 
				
			||||||
 | 
					        is_not_in_only = only_fields and name not in only_fields
 | 
				
			||||||
 | 
					        is_excluded = (
 | 
				
			||||||
 | 
					            name in exclude_fields  # or
 | 
				
			||||||
 | 
					            # name in already_created_fields
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if is_not_in_only or is_excluded:
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fields[name] = convert_form_field(field)
 | 
				
			||||||
 | 
					    return fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class BaseDjangoFormMutation(ClientIDMutation):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def mutate_and_get_payload(cls, root, info, **input):
 | 
				
			||||||
 | 
					        form = cls.get_form(root, info, **input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if form.is_valid():
 | 
				
			||||||
 | 
					            return cls.perform_mutate(form, info)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            errors = [
 | 
				
			||||||
 | 
					                ErrorType(field=key, messages=value)
 | 
				
			||||||
 | 
					                for key, value in form.errors.items()
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return cls(errors=errors)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_form(cls, root, info, **input):
 | 
				
			||||||
 | 
					        form_kwargs = cls.get_form_kwargs(root, info, **input)
 | 
				
			||||||
 | 
					        return cls._meta.form_class(**form_kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_form_kwargs(cls, root, info, **input):
 | 
				
			||||||
 | 
					        kwargs = {'data': input}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pk = input.pop('id', None)
 | 
				
			||||||
 | 
					        if pk:
 | 
				
			||||||
 | 
					            instance = cls._meta.model._default_manager.get(pk=pk)
 | 
				
			||||||
 | 
					            kwargs['instance'] = instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# class DjangoFormInputObjectTypeOptions(InputObjectTypeOptions):
 | 
				
			||||||
 | 
					#     form_class = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# class DjangoFormInputObjectType(InputObjectType):
 | 
				
			||||||
 | 
					#     class Meta:
 | 
				
			||||||
 | 
					#         abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#     @classmethod
 | 
				
			||||||
 | 
					#     def __init_subclass_with_meta__(cls, form_class=None,
 | 
				
			||||||
 | 
					#                                     only_fields=(), exclude_fields=(), _meta=None, **options):
 | 
				
			||||||
 | 
					#         if not _meta:
 | 
				
			||||||
 | 
					#             _meta = DjangoFormInputObjectTypeOptions(cls)
 | 
				
			||||||
 | 
					#         assert isinstance(form_class, forms.Form), (
 | 
				
			||||||
 | 
					#             'form_class must be an instance of django.forms.Form'
 | 
				
			||||||
 | 
					#         )
 | 
				
			||||||
 | 
					#         _meta.form_class = form_class
 | 
				
			||||||
 | 
					#         form = form_class()
 | 
				
			||||||
 | 
					#         fields = fields_for_form(form, only_fields, exclude_fields)
 | 
				
			||||||
 | 
					#         super(DjangoFormInputObjectType, cls).__init_subclass_with_meta__(_meta=_meta, fields=fields, **options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DjangoFormMutationOptions(MutationOptions):
 | 
				
			||||||
 | 
					    form_class = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DjangoFormMutation(BaseDjangoFormMutation):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    errors = graphene.List(ErrorType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def __init_subclass_with_meta__(cls, form_class=None,
 | 
				
			||||||
 | 
					                                    only_fields=(), exclude_fields=(), **options):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not form_class:
 | 
				
			||||||
 | 
					            raise Exception('form_class is required for DjangoFormMutation')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form = form_class()
 | 
				
			||||||
 | 
					        input_fields = fields_for_form(form, only_fields, exclude_fields)
 | 
				
			||||||
 | 
					        output_fields = fields_for_form(form, only_fields, exclude_fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _meta = DjangoFormMutationOptions(cls)
 | 
				
			||||||
 | 
					        _meta.form_class = form_class
 | 
				
			||||||
 | 
					        _meta.fields = yank_fields_from_attrs(
 | 
				
			||||||
 | 
					            output_fields,
 | 
				
			||||||
 | 
					            _as=Field,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        input_fields = yank_fields_from_attrs(
 | 
				
			||||||
 | 
					            input_fields,
 | 
				
			||||||
 | 
					            _as=InputField,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        super(DjangoFormMutation, cls).__init_subclass_with_meta__(_meta=_meta, input_fields=input_fields, **options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def perform_mutate(cls, form, info):
 | 
				
			||||||
 | 
					        form.save()
 | 
				
			||||||
 | 
					        return cls(errors=[])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DjangoModelDjangoFormMutationOptions(DjangoFormMutationOptions):
 | 
				
			||||||
 | 
					    model = None
 | 
				
			||||||
 | 
					    return_field_name = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DjangoModelFormMutation(BaseDjangoFormMutation):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    errors = graphene.List(ErrorType)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def __init_subclass_with_meta__(cls, form_class=None, model=None, return_field_name=None,
 | 
				
			||||||
 | 
					                                    only_fields=(), exclude_fields=(), **options):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not form_class:
 | 
				
			||||||
 | 
					            raise Exception('form_class is required for DjangoModelFormMutation')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not model:
 | 
				
			||||||
 | 
					            model = form_class._meta.model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not model:
 | 
				
			||||||
 | 
					            raise Exception('model is required for DjangoModelFormMutation')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        form = form_class()
 | 
				
			||||||
 | 
					        input_fields = fields_for_form(form, only_fields, exclude_fields)
 | 
				
			||||||
 | 
					        input_fields['id'] = graphene.ID()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        registry = get_global_registry()
 | 
				
			||||||
 | 
					        model_type = registry.get_type_for_model(model)
 | 
				
			||||||
 | 
					        return_field_name = return_field_name
 | 
				
			||||||
 | 
					        if not return_field_name:
 | 
				
			||||||
 | 
					            model_name = model.__name__
 | 
				
			||||||
 | 
					            return_field_name = model_name[:1].lower() + model_name[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        output_fields = OrderedDict()
 | 
				
			||||||
 | 
					        output_fields[return_field_name] = graphene.Field(model_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _meta = DjangoModelDjangoFormMutationOptions(cls)
 | 
				
			||||||
 | 
					        _meta.form_class = form_class
 | 
				
			||||||
 | 
					        _meta.model = model
 | 
				
			||||||
 | 
					        _meta.return_field_name = return_field_name
 | 
				
			||||||
 | 
					        _meta.fields = yank_fields_from_attrs(
 | 
				
			||||||
 | 
					            output_fields,
 | 
				
			||||||
 | 
					            _as=Field,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        input_fields = yank_fields_from_attrs(
 | 
				
			||||||
 | 
					            input_fields,
 | 
				
			||||||
 | 
					            _as=InputField,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        super(DjangoModelFormMutation, cls).__init_subclass_with_meta__(
 | 
				
			||||||
 | 
					            _meta=_meta,
 | 
				
			||||||
 | 
					            input_fields=input_fields,
 | 
				
			||||||
 | 
					            **options
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def perform_mutate(cls, form, info):
 | 
				
			||||||
 | 
					        obj = form.save()
 | 
				
			||||||
 | 
					        kwargs = {cls._meta.return_field_name: obj}
 | 
				
			||||||
 | 
					        return cls(errors=[], **kwargs)
 | 
				
			||||||
							
								
								
									
										0
									
								
								graphene_django/forms/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								graphene_django/forms/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -2,10 +2,9 @@ from django import forms
 | 
				
			||||||
from py.test import raises
 | 
					from py.test import raises
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import graphene
 | 
					import graphene
 | 
				
			||||||
from graphene import ID, List, NonNull
 | 
					from graphene import String, Int, Boolean, Float, ID, UUID, List, NonNull, DateTime, Date, Time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..form_converter import convert_form_field
 | 
					from ..converter import convert_form_field
 | 
				
			||||||
from .models import Reporter
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def assert_conversion(django_field, graphene_field, *args):
 | 
					def assert_conversion(django_field, graphene_field, *args):
 | 
				
			||||||
| 
						 | 
					@ -24,80 +23,80 @@ def test_should_unknown_django_field_raise_exception():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_date_convert_date():
 | 
					def test_should_date_convert_date():
 | 
				
			||||||
    assert_conversion(forms.DateField, graphene.types.datetime.Date)
 | 
					    assert_conversion(forms.DateField, Date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_time_convert_time():
 | 
					def test_should_time_convert_time():
 | 
				
			||||||
    assert_conversion(forms.TimeField, graphene.types.datetime.Time)
 | 
					    assert_conversion(forms.TimeField, Time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_date_time_convert_date_time():
 | 
					def test_should_date_time_convert_date_time():
 | 
				
			||||||
    assert_conversion(forms.DateTimeField, graphene.types.datetime.DateTime)
 | 
					    assert_conversion(forms.DateTimeField, DateTime)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_char_convert_string():
 | 
					def test_should_char_convert_string():
 | 
				
			||||||
    assert_conversion(forms.CharField, graphene.String)
 | 
					    assert_conversion(forms.CharField, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_email_convert_string():
 | 
					def test_should_email_convert_string():
 | 
				
			||||||
    assert_conversion(forms.EmailField, graphene.String)
 | 
					    assert_conversion(forms.EmailField, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_slug_convert_string():
 | 
					def test_should_slug_convert_string():
 | 
				
			||||||
    assert_conversion(forms.SlugField, graphene.String)
 | 
					    assert_conversion(forms.SlugField, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_url_convert_string():
 | 
					def test_should_url_convert_string():
 | 
				
			||||||
    assert_conversion(forms.URLField, graphene.String)
 | 
					    assert_conversion(forms.URLField, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_choice_convert_string():
 | 
					def test_should_choice_convert_string():
 | 
				
			||||||
    assert_conversion(forms.ChoiceField, graphene.String)
 | 
					    assert_conversion(forms.ChoiceField, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_base_field_convert_string():
 | 
					def test_should_base_field_convert_string():
 | 
				
			||||||
    assert_conversion(forms.Field, graphene.String)
 | 
					    assert_conversion(forms.Field, String)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_regex_convert_string():
 | 
					def test_should_regex_convert_string():
 | 
				
			||||||
    assert_conversion(forms.RegexField, graphene.String, '[0-9]+')
 | 
					    assert_conversion(forms.RegexField, String, '[0-9]+')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_uuid_convert_string():
 | 
					def test_should_uuid_convert_string():
 | 
				
			||||||
    if hasattr(forms, 'UUIDField'):
 | 
					    if hasattr(forms, 'UUIDField'):
 | 
				
			||||||
        assert_conversion(forms.UUIDField, graphene.UUID)
 | 
					        assert_conversion(forms.UUIDField, UUID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_integer_convert_int():
 | 
					def test_should_integer_convert_int():
 | 
				
			||||||
    assert_conversion(forms.IntegerField, graphene.Int)
 | 
					    assert_conversion(forms.IntegerField, Int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_boolean_convert_boolean():
 | 
					def test_should_boolean_convert_boolean():
 | 
				
			||||||
    field = assert_conversion(forms.BooleanField, graphene.Boolean)
 | 
					    field = assert_conversion(forms.BooleanField, Boolean)
 | 
				
			||||||
    assert isinstance(field.type, NonNull)
 | 
					    assert isinstance(field.type, NonNull)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_nullboolean_convert_boolean():
 | 
					def test_should_nullboolean_convert_boolean():
 | 
				
			||||||
    field = assert_conversion(forms.NullBooleanField, graphene.Boolean)
 | 
					    field = assert_conversion(forms.NullBooleanField, Boolean)
 | 
				
			||||||
    assert not isinstance(field.type, NonNull)
 | 
					    assert not isinstance(field.type, NonNull)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_float_convert_float():
 | 
					def test_should_float_convert_float():
 | 
				
			||||||
    assert_conversion(forms.FloatField, graphene.Float)
 | 
					    assert_conversion(forms.FloatField, Float)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_decimal_convert_float():
 | 
					def test_should_decimal_convert_float():
 | 
				
			||||||
    assert_conversion(forms.DecimalField, graphene.Float)
 | 
					    assert_conversion(forms.DecimalField, Float)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_multiple_choice_convert_connectionorlist():
 | 
					def test_should_multiple_choice_convert_connectionorlist():
 | 
				
			||||||
    field = forms.ModelMultipleChoiceField(Reporter.objects.all())
 | 
					    field = forms.ModelMultipleChoiceField(queryset=None)
 | 
				
			||||||
    graphene_type = convert_form_field(field)
 | 
					    graphene_type = convert_form_field(field)
 | 
				
			||||||
    assert isinstance(graphene_type, List)
 | 
					    assert isinstance(graphene_type, List)
 | 
				
			||||||
    assert graphene_type.of_type == ID
 | 
					    assert graphene_type.of_type == ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_should_manytoone_convert_connectionorlist():
 | 
					def test_should_manytoone_convert_connectionorlist():
 | 
				
			||||||
    field = forms.ModelChoiceField(Reporter.objects.all())
 | 
					    field = forms.ModelChoiceField(queryset=None)
 | 
				
			||||||
    graphene_type = convert_form_field(field)
 | 
					    graphene_type = convert_form_field(field)
 | 
				
			||||||
    assert isinstance(graphene_type, graphene.ID)
 | 
					    assert isinstance(graphene_type, ID)
 | 
				
			||||||
							
								
								
									
										113
									
								
								graphene_django/forms/tests/test_mutation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								graphene_django/forms/tests/test_mutation.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,113 @@
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					from py.test import raises
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from graphene_django.tests.models import Pet, Film, FilmDetails
 | 
				
			||||||
 | 
					from ..mutation import DjangoFormMutation, DjangoModelFormMutation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MyForm(forms.Form):
 | 
				
			||||||
 | 
					    text = forms.CharField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PetForm(forms.ModelForm):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Pet
 | 
				
			||||||
 | 
					        fields = ('name',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_needs_form_class():
 | 
				
			||||||
 | 
					    with raises(Exception) as exc:
 | 
				
			||||||
 | 
					        class MyMutation(DjangoFormMutation):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert exc.value.args[0] == 'form_class is required for DjangoFormMutation'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_has_output_fields():
 | 
				
			||||||
 | 
					    class MyMutation(DjangoFormMutation):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            form_class = MyForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert 'errors' in MyMutation._meta.fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_has_input_fields():
 | 
				
			||||||
 | 
					    class MyMutation(DjangoFormMutation):
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            form_class = MyForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert 'text' in MyMutation.Input._meta.fields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ModelFormMutationTests(TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_default_meta_fields(self):
 | 
				
			||||||
 | 
					        class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                form_class = PetForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(PetMutation._meta.model, Pet)
 | 
				
			||||||
 | 
					        self.assertEqual(PetMutation._meta.return_field_name, 'pet')
 | 
				
			||||||
 | 
					        self.assertIn('pet', PetMutation._meta.fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_return_field_name_is_camelcased(self):
 | 
				
			||||||
 | 
					        class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                form_class = PetForm
 | 
				
			||||||
 | 
					                model = FilmDetails
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(PetMutation._meta.model, FilmDetails)
 | 
				
			||||||
 | 
					        self.assertEqual(PetMutation._meta.return_field_name, 'filmDetails')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_custom_return_field_name(self):
 | 
				
			||||||
 | 
					        class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                form_class = PetForm
 | 
				
			||||||
 | 
					                model = Film
 | 
				
			||||||
 | 
					                return_field_name = 'animal'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(PetMutation._meta.model, Film)
 | 
				
			||||||
 | 
					        self.assertEqual(PetMutation._meta.return_field_name, 'animal')
 | 
				
			||||||
 | 
					        self.assertIn('animal', PetMutation._meta.fields)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_model_form_mutation_mutate(self):
 | 
				
			||||||
 | 
					        class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                form_class = PetForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pet = Pet.objects.create(name='Axel')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = PetMutation.mutate_and_get_payload(None, None, id=pet.pk, name='Mia')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(Pet.objects.count(), 1)
 | 
				
			||||||
 | 
					        pet.refresh_from_db()
 | 
				
			||||||
 | 
					        self.assertEqual(pet.name, 'Mia')
 | 
				
			||||||
 | 
					        self.assertEqual(result.errors, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_model_form_mutation_updates_existing_(self):
 | 
				
			||||||
 | 
					        class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                form_class = PetForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = PetMutation.mutate_and_get_payload(None, None, name='Mia')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(Pet.objects.count(), 1)
 | 
				
			||||||
 | 
					        pet = Pet.objects.get()
 | 
				
			||||||
 | 
					        self.assertEqual(pet.name, 'Mia')
 | 
				
			||||||
 | 
					        self.assertEqual(result.errors, [])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_model_form_mutation_mutate_invalid_form(self):
 | 
				
			||||||
 | 
					        class PetMutation(DjangoModelFormMutation):
 | 
				
			||||||
 | 
					            class Meta:
 | 
				
			||||||
 | 
					                form_class = PetForm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = PetMutation.mutate_and_get_payload(None, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # A pet was not created
 | 
				
			||||||
 | 
					        self.assertEqual(Pet.objects.count(), 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(len(result.errors), 1)
 | 
				
			||||||
 | 
					        self.assertEqual(result.errors[0].field, 'name')
 | 
				
			||||||
 | 
					        self.assertEqual(result.errors[0].messages, ['This field is required.'])
 | 
				
			||||||
							
								
								
									
										6
									
								
								graphene_django/forms/types.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								graphene_django/forms/types.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					import graphene
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ErrorType(graphene.ObjectType):
 | 
				
			||||||
 | 
					    field = graphene.String()
 | 
				
			||||||
 | 
					    messages = graphene.List(graphene.String)
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user