Skip to content

Commit 463ce68

Browse files
author
Grant McConnaughey
committed
Change mutations to new 2.0 format
1 parent 666ddb2 commit 463ce68

File tree

5 files changed

+124
-231
lines changed

5 files changed

+124
-231
lines changed

graphene_django/forms/converter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import graphene
55

66
from .forms import GlobalIDFormField, GlobalIDMultipleChoiceField
7-
from .utils import import_single_dispatch
7+
from ..utils import import_single_dispatch
88

99
try:
1010
UUIDField = forms.UUIDField
@@ -40,7 +40,7 @@ def convert_form_field(field):
4040
)
4141

4242

43-
@convert_form_field.register(forms.BaseTemporalField)
43+
@convert_form_field.register(forms.fields.BaseTemporalField)
4444
@convert_form_field.register(forms.CharField)
4545
@convert_form_field.register(forms.EmailField)
4646
@convert_form_field.register(forms.SlugField)

graphene_django/forms/mutation.py

Lines changed: 98 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,141 @@
1-
from functools import partial
1+
from collections import OrderedDict
22

3-
import six
43
import graphene
5-
from graphene import Field, Argument
6-
from graphene.types.mutation import MutationMeta
7-
from graphene.types.objecttype import ObjectTypeMeta
8-
from graphene.types.options import Options
9-
from graphene.types.utils import get_field_as, merge
10-
from graphene.utils.is_base_type import is_base_type
4+
from graphene import Field, InputField
5+
from graphene.relay.mutation import ClientIDMutation
6+
from graphene.types.mutation import MutationOptions
7+
from graphene.types.utils import yank_fields_from_attrs
118
from graphene_django.registry import get_global_registry
129

13-
from .converter import convert_form_to_input_type
10+
from .converter import convert_form_field
1411
from .types import ErrorType
1512

1613

17-
class FormMutationMeta(MutationMeta):
18-
def __new__(cls, name, bases, attrs):
19-
if not is_base_type(bases, FormMutationMeta):
20-
return type.__new__(cls, name, bases, attrs)
21-
22-
options = Options(
23-
attrs.pop('Meta', None),
24-
name=name,
25-
description=attrs.pop('__doc__', None),
26-
form_class=None,
27-
input_field_name='input',
28-
local_fields=None,
29-
only_fields=(),
30-
exclude_fields=(),
31-
interfaces=(),
32-
registry=None
14+
def fields_for_form(form, only_fields, exclude_fields):
15+
fields = OrderedDict()
16+
for name, field in form.fields.items():
17+
is_not_in_only = only_fields and name not in only_fields
18+
is_excluded = (
19+
name in exclude_fields # or
20+
# name in already_created_fields
3321
)
3422

35-
if not options.form_class:
36-
raise Exception('Missing form_class')
23+
if is_not_in_only or is_excluded:
24+
continue
3725

38-
cls = ObjectTypeMeta.__new__(
39-
cls, name, bases, dict(attrs, _meta=options)
40-
)
41-
42-
options.fields = merge(
43-
options.interface_fields, options.base_fields, options.local_fields,
44-
{'errors': get_field_as(cls.errors, Field)}
45-
)
46-
47-
cls.Input = convert_form_to_input_type(options.form_class)
26+
fields[name] = convert_form_field(field)
27+
return fields
4828

49-
field_kwargs = {options.input_field_name: Argument(cls.Input, required=True)}
50-
cls.Field = partial(
51-
Field,
52-
cls,
53-
resolver=cls.mutate,
54-
**field_kwargs
55-
)
56-
57-
return cls
5829

59-
60-
class BaseFormMutation(graphene.Mutation):
30+
class BaseFormMutation(ClientIDMutation):
31+
class Meta:
32+
abstract = True
6133

6234
@classmethod
63-
def mutate(cls, root, args, context, info):
64-
form = cls.get_form(root, args, context, info)
35+
def mutate_and_get_payload(cls, root, info, **input):
36+
form = cls._meta.form_class(data=input)
6537

6638
if form.is_valid():
67-
return cls.form_valid(form, info)
39+
return cls.perform_mutate(form, info)
6840
else:
69-
return cls.form_invalid(form, info)
41+
errors = [
42+
ErrorType(field=key, messages=value)
43+
for key, value in form.errors.items()
44+
]
7045

71-
@classmethod
72-
def form_valid(cls, form, info):
73-
form.save()
74-
return cls(errors=[])
46+
return cls(errors=errors)
7547

76-
@classmethod
77-
def form_invalid(cls, form, info):
78-
errors = [
79-
ErrorType(field=key, messages=value)
80-
for key, value in form.errors.items()
81-
]
82-
return cls(errors=errors)
8348

84-
@classmethod
85-
def get_form(cls, root, args, context, info):
86-
form_data = args.get(cls._meta.input_field_name)
87-
kwargs = cls.get_form_kwargs(root, args, context, info)
88-
return cls._meta.form_class(data=form_data, **kwargs)
49+
class FormMutationOptions(MutationOptions):
50+
form_class = None
8951

90-
@classmethod
91-
def get_form_kwargs(cls, root, args, context, info):
92-
return {}
9352

94-
95-
class FormMutation(six.with_metaclass(FormMutationMeta, BaseFormMutation)):
53+
class FormMutation(BaseFormMutation):
54+
class Meta:
55+
abstract = True
9656

9757
errors = graphene.List(ErrorType)
9858

59+
@classmethod
60+
def __init_subclass_with_meta__(cls, form_class=None,
61+
only_fields=(), exclude_fields=(), **options):
9962

100-
class ModelFormMutationMeta(MutationMeta):
101-
def __new__(cls, name, bases, attrs):
102-
if not is_base_type(bases, ModelFormMutationMeta):
103-
return type.__new__(cls, name, bases, attrs)
104-
105-
options = Options(
106-
attrs.pop('Meta', None),
107-
name=name,
108-
description=attrs.pop('__doc__', None),
109-
form_class=None,
110-
input_field_name='input',
111-
return_field_name=None,
112-
model=None,
113-
local_fields=None,
114-
only_fields=(),
115-
exclude_fields=(),
116-
interfaces=(),
117-
registry=None
118-
)
63+
if not form_class:
64+
raise Exception('form_class is required for FormMutation')
11965

120-
if not options.form_class:
121-
raise Exception('Missing form_class')
66+
form = form_class()
67+
input_fields = fields_for_form(form, only_fields, exclude_fields)
68+
output_fields = fields_for_form(form, only_fields, exclude_fields)
12269

123-
cls = ObjectTypeMeta.__new__(
124-
cls, name, bases, dict(attrs, _meta=options)
70+
_meta = FormMutationOptions(cls)
71+
_meta.form_class = form_class
72+
_meta.fields = yank_fields_from_attrs(
73+
output_fields,
74+
_as=Field,
12575
)
12676

127-
options.fields = merge(
128-
options.interface_fields, options.base_fields, options.local_fields,
129-
{'errors': get_field_as(cls.errors, Field)}
77+
input_fields = yank_fields_from_attrs(
78+
input_fields,
79+
_as=InputField,
13080
)
81+
super(FormMutation, cls).__init_subclass_with_meta__(_meta=_meta, input_fields=input_fields, **options)
13182

132-
cls.Input = convert_form_to_input_type(options.form_class)
83+
@classmethod
84+
def perform_mutate(cls, form, info):
85+
form.save()
86+
return cls(errors=None)
13387

134-
field_kwargs = {options.input_field_name: Argument(cls.Input, required=True)}
135-
cls.Field = partial(
136-
Field,
137-
cls,
138-
resolver=cls.mutate,
139-
**field_kwargs
140-
)
14188

142-
cls.model = options.model or options.form_class.Meta.model
143-
cls.return_field_name = cls._meta.return_field_name or cls.model._meta.model_name
89+
class ModelFormMutationOptions(FormMutationOptions):
90+
model = None
91+
return_field_name = None
14492

145-
registry = get_global_registry()
146-
model_type = registry.get_type_for_model(cls.model)
14793

148-
options.fields[cls.return_field_name] = graphene.Field(model_type)
94+
class ModelFormMutation(BaseFormMutation):
95+
class Meta:
96+
abstract = True
14997

150-
return cls
98+
errors = graphene.List(ErrorType)
15199

100+
@classmethod
101+
def __init_subclass_with_meta__(cls, form_class=None, model=None, return_field_name=None,
102+
only_fields=(), exclude_fields=(), **options):
152103

153-
class ModelFormMutation(six.with_metaclass(ModelFormMutationMeta, BaseFormMutation)):
104+
if not form_class:
105+
raise Exception('form_class is required for ModelFormMutation')
154106

155-
errors = graphene.List(ErrorType)
107+
if not model:
108+
model = form_class._meta.model
109+
110+
if not model:
111+
raise Exception('model is required for ModelFormMutation')
112+
113+
form = form_class()
114+
input_fields = fields_for_form(form, only_fields, exclude_fields)
115+
116+
registry = get_global_registry()
117+
model_type = registry.get_type_for_model(model)
118+
return_field_name = return_field_name or model._meta.model_name
119+
output_fields = OrderedDict()
120+
output_fields[return_field_name] = graphene.Field(model_type)
121+
122+
_meta = ModelFormMutationOptions(cls)
123+
_meta.form_class = form_class
124+
_meta.model = model
125+
_meta.return_field_name = return_field_name
126+
_meta.fields = yank_fields_from_attrs(
127+
output_fields,
128+
_as=Field,
129+
)
130+
131+
input_fields = yank_fields_from_attrs(
132+
input_fields,
133+
_as=InputField,
134+
)
135+
super(ModelFormMutation, cls).__init_subclass_with_meta__(_meta=_meta, input_fields=input_fields, **options)
156136

157137
@classmethod
158-
def form_valid(cls, form, info):
138+
def perform_mutate(cls, form, info):
159139
obj = form.save()
160-
kwargs = {cls.return_field_name: obj}
161-
return cls(errors=[], **kwargs)
140+
kwargs = {cls._meta.return_field_name: obj}
141+
return cls(errors=None, **kwargs)

graphene_django/forms/tests/test_converter.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from graphene import ID, List, NonNull
66

77
from ..converter import convert_form_field
8-
from .models import Reporter
98

109

1110
def assert_conversion(django_field, graphene_field, *args):
@@ -24,15 +23,15 @@ def test_should_unknown_django_field_raise_exception():
2423

2524

2625
def test_should_date_convert_string():
27-
assert_conversion(forms.DateField, graphene.String)
26+
assert_conversion(forms.DateField, graphene.types.datetime.DateTime)
2827

2928

3029
def test_should_time_convert_string():
31-
assert_conversion(forms.TimeField, graphene.String)
30+
assert_conversion(forms.TimeField, graphene.types.datetime.Time)
3231

3332

3433
def test_should_date_time_convert_string():
35-
assert_conversion(forms.DateTimeField, graphene.String)
34+
assert_conversion(forms.DateTimeField, graphene.types.datetime.DateTime)
3635

3736

3837
def test_should_char_convert_string():
@@ -91,13 +90,13 @@ def test_should_decimal_convert_float():
9190

9291

9392
def test_should_multiple_choice_convert_connectionorlist():
94-
field = forms.ModelMultipleChoiceField(Reporter.objects.all())
93+
field = forms.ModelMultipleChoiceField(queryset=None)
9594
graphene_type = convert_form_field(field)
9695
assert isinstance(graphene_type, List)
9796
assert graphene_type.of_type == ID
9897

9998

10099
def test_should_manytoone_convert_connectionorlist():
101-
field = forms.ModelChoiceField(Reporter.objects.all())
100+
field = forms.ModelChoiceField(queryset=None)
102101
graphene_type = convert_form_field(field)
103102
assert isinstance(graphene_type, graphene.ID)

0 commit comments

Comments
 (0)