mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-04-16 07:02:04 +03:00
Update django form mutation to more granularly handle fields
Set up forms Update docs Nearly working tests
This commit is contained in:
parent
a987035ef3
commit
4246dab3d6
|
@ -63,9 +63,15 @@ DjangoFormMutation
|
|||
class MyForm(forms.Form):
|
||||
name = forms.CharField()
|
||||
|
||||
def clean(self):
|
||||
self.cleaned_data["constructed_output"] = "an item"
|
||||
|
||||
class MyMutation(DjangoFormMutation):
|
||||
class Meta:
|
||||
form_class = MyForm
|
||||
input_fields = "__all__"
|
||||
|
||||
constructed_output = graphene.String()
|
||||
|
||||
``MyMutation`` will automatically receive an ``input`` argument. This argument should be a ``dict`` where the key is ``name`` and the value is a string.
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# from django import forms
|
||||
import warnings
|
||||
from collections import OrderedDict
|
||||
|
||||
import graphene
|
||||
|
@ -71,6 +72,31 @@ class DjangoFormMutationOptions(MutationOptions):
|
|||
|
||||
|
||||
class DjangoFormMutation(BaseDjangoFormMutation):
|
||||
"""Create a mutation based on a Django Form.
|
||||
|
||||
The form's fields will, by default, be set as inputs. Specify ``input_fields`` to limit to a
|
||||
subset.
|
||||
|
||||
You can use ``fields`` and ``exclude`` to limit output fields. Use ``fields = '__all__'`` to
|
||||
select all fields.
|
||||
|
||||
Fields are considered to be required based on the ``required`` attribute of the form.
|
||||
|
||||
Meta fields:
|
||||
form_class (class): the model to base form off of
|
||||
input_fields (List[str], optional): limit the input fields of the form to be used (by default uses all of them)
|
||||
fields (List[str], optional): only output the subset of fields as output (based on ``cleaned_data``), use
|
||||
``__all__`` to get all fields
|
||||
exclude (List[str], optional): remove specified fields from output (uses ``cleaned_data``)
|
||||
|
||||
The default output of the mutation will use ``form.cleaned_data`` as params.
|
||||
|
||||
Override ``perform_mutate(cls, form, info) -> DjangoFormMutation`` to customize this behavior.
|
||||
|
||||
NOTE: ``only_fields`` and ``exclude_fields`` are still supported for backwards compatibility
|
||||
but are deprecated and will be removed in a future version.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
@ -78,15 +104,37 @@ class DjangoFormMutation(BaseDjangoFormMutation):
|
|||
|
||||
@classmethod
|
||||
def __init_subclass_with_meta__(
|
||||
cls, form_class=None, only_fields=(), exclude_fields=(), **options
|
||||
cls, form_class=None, only_fields=(), exclude_fields=(),
|
||||
fields=None, exclude=(), input_fields=None,
|
||||
**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)
|
||||
if (any([fields, exclude, input_fields])
|
||||
and (only_fields or exclude_fields)):
|
||||
raise Exception("Cannot specify legacy `only_fields` or `exclude_fields` params with"
|
||||
" `only`, `exclude`, or `input_fields` params")
|
||||
if only_fields or exclude_fields:
|
||||
warnings.warn(
|
||||
"only_fields/exclude_fields have been deprecated, use "
|
||||
"input_fields or only/exclude (for output fields)"
|
||||
"instead",
|
||||
DeprecationWarning
|
||||
)
|
||||
if not fields or exclude:
|
||||
warnings.warn(
|
||||
"a future version of graphene-django will require fields or exclude."
|
||||
" Set fields='__all__' to allow all fields through.",
|
||||
DeprecationWarning
|
||||
)
|
||||
if not input_fields and input_fields is not None:
|
||||
input_fields = {}
|
||||
else:
|
||||
input_fields = fields_for_form(form, only_fields or input_fields, exclude_fields)
|
||||
output_fields = fields_for_form(form, only_fields or fields, exclude_fields or exclude)
|
||||
|
||||
_meta = DjangoFormMutationOptions(cls)
|
||||
_meta.form_class = form_class
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import pytest
|
||||
from django import forms
|
||||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
from py.test import raises
|
||||
|
||||
from graphene import Field, ObjectType, Schema, String
|
||||
from graphene import Field, Int, ObjectType, Schema, String
|
||||
from graphene_django import DjangoObjectType
|
||||
from graphene_django.tests.models import Pet
|
||||
|
||||
|
@ -22,6 +23,7 @@ def pet_type():
|
|||
|
||||
class MyForm(forms.Form):
|
||||
text = forms.CharField()
|
||||
another = forms.CharField(required=False)
|
||||
|
||||
def clean_text(self):
|
||||
text = self.cleaned_data["text"]
|
||||
|
@ -29,6 +31,9 @@ class MyForm(forms.Form):
|
|||
raise ValidationError("Invalid input")
|
||||
return text
|
||||
|
||||
def clean_another(self):
|
||||
self.cleaned_data["another"] = self.cleaned_data["another"] or "defaultvalue"
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
|
@ -68,6 +73,83 @@ def test_has_input_fields():
|
|||
form_class = MyForm
|
||||
|
||||
assert "text" in MyMutation.Input._meta.fields
|
||||
assert "another" in MyMutation.Input._meta.fields
|
||||
|
||||
|
||||
def test_no_input_fields():
|
||||
class MyMutation(DjangoFormMutation):
|
||||
class Meta:
|
||||
form_class = MyForm
|
||||
input_fields = []
|
||||
assert set(MyMutation.Input._meta.fields.keys()) == set(["client_mutation_id"])
|
||||
|
||||
|
||||
def test_filtering_input_fields():
|
||||
class MyMutation(DjangoFormMutation):
|
||||
class Meta:
|
||||
form_class = MyForm
|
||||
input_fields = ["text"]
|
||||
|
||||
assert "text" in MyMutation.Input._meta.fields
|
||||
assert "another" not in MyMutation.Input._meta.fields
|
||||
|
||||
|
||||
def test_select_output_fields():
|
||||
class MyMutation(DjangoFormMutation):
|
||||
class Meta:
|
||||
form_class = MyForm
|
||||
fields = ["text"]
|
||||
assert "text" in MyMutation._meta.fields
|
||||
assert "another" not in MyMutation._meta.fields
|
||||
|
||||
|
||||
def test_filtering_output_fields_exclude():
|
||||
class FormWithWeirdOutput(MyForm):
|
||||
"""Weird form that has extra cleaned_data we want to expose"""
|
||||
text = forms.CharField()
|
||||
another = forms.CharField(required=False)
|
||||
def clean(self):
|
||||
super(FormWithWeirdOutput, self).clean()
|
||||
self.cleaned_data["some_integer"] = 5
|
||||
return self.cleaned_data
|
||||
|
||||
class MyMutation(DjangoFormMutation):
|
||||
class Meta:
|
||||
form_class = FormWithWeirdOutput
|
||||
exclude = ["text"]
|
||||
|
||||
some_integer = Int()
|
||||
|
||||
assert "text" in MyMutation.Input._meta.fields
|
||||
assert "another" in MyMutation.Input._meta.fields
|
||||
|
||||
assert "text" not in MyMutation._meta.fields
|
||||
assert "another" in MyMutation._meta.fields
|
||||
assert "some_integer" in MyMutation._meta.fields
|
||||
|
||||
class Mutation(ObjectType):
|
||||
my_mutation = MyMutation.Field()
|
||||
|
||||
schema = Schema(query=MockQuery, mutation=Mutation)
|
||||
|
||||
result = schema.execute(
|
||||
""" mutation MyMutation {
|
||||
myMutation(input: { text: "VALID_INPUT" }) {
|
||||
errors {
|
||||
field
|
||||
messages
|
||||
}
|
||||
another
|
||||
someInteger
|
||||
}
|
||||
}
|
||||
"""
|
||||
)
|
||||
|
||||
assert result.errors is None
|
||||
assert result.data["myMutation"]["errors"] == []
|
||||
assert result.data["myMutation"]["someInteger"] == 5
|
||||
assert result.data["myMutation"]["another"] == "defaultvalue"
|
||||
|
||||
|
||||
def test_mutation_error_camelcased(pet_type, graphene_settings):
|
||||
|
|
Loading…
Reference in New Issue
Block a user