Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Improve form and serializer mutations #546

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/cookbook-plain/cookbook/ingredients/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django import forms
from cookbook.ingredients.models import Category, Ingredient


class CategoryForm(forms.ModelForm):
class Meta:
model = Category
exclude = []


class IngredientForm(forms.ModelForm):
class Meta:
model = Ingredient
exclude = []

17 changes: 17 additions & 0 deletions examples/cookbook-plain/cookbook/ingredients/schema.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import graphene
from graphene_django.types import DjangoObjectType
from graphene_django.forms.mutation import DjangoFormMutation

from .models import Category, Ingredient
from .forms import CategoryForm, IngredientForm


class CategoryType(DjangoObjectType):
Expand Down Expand Up @@ -49,3 +51,18 @@ def resolve_ingredient(self, context, id=None, name=None):
return Ingredient.objects.get(name=name)

return None


class CategoryMutation(DjangoFormMutation):
class Meta:
form_class = CategoryForm


class IngredientMutation(DjangoFormMutation):
class Meta:
form_class = IngredientForm


class Mutation(object):
category = CategoryMutation.Field()
ingredient = IngredientMutation.Field()
7 changes: 6 additions & 1 deletion examples/cookbook-plain/cookbook/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ class Query(cookbook.ingredients.schema.Query,
debug = graphene.Field(DjangoDebug, name='__debug')


schema = graphene.Schema(query=Query)
class Mutation(cookbook.ingredients.schema.Mutation,
graphene.ObjectType):
pass


schema = graphene.Schema(query=Query, mutation=Mutation)
109 changes: 50 additions & 59 deletions graphene_django/forms/mutation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from graphene.types.utils import yank_fields_from_attrs
from graphene_django.registry import get_global_registry

from ..utils import create_errors_type
from .converter import convert_form_field
from .types import ErrorType

Expand Down Expand Up @@ -45,10 +46,8 @@ def mutate_and_get_payload(cls, 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()
]
# TODO: double check non field errors name
errors = cls.Errors(**form.errors)

return cls(errors=errors)

Expand Down Expand Up @@ -93,100 +92,92 @@ def get_form_kwargs(cls, root, info, **input):

class DjangoFormMutationOptions(MutationOptions):
form_class = None

model = None
return_field_name = 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
cls,
form_class=None,
return_field_name=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)
input_fields = yank_fields_from_attrs(input_fields, _as=InputField)

base_name = cls.__name__

_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
cls.Errors = create_errors_type(
"{}Errors".format(base_name),
input_fields
)

@classmethod
def perform_mutate(cls, form, info):
form.save()
return cls(errors=[])
output_fields = OrderedDict()

if hasattr(form, '_meta') and hasattr(form._meta, 'model'):
model = form._meta.model
_meta.model = model

class DjangoModelDjangoFormMutationOptions(DjangoFormMutationOptions):
model = None
return_field_name = None
registry = get_global_registry()
model_type = registry.get_type_for_model(model)
return_field_name = return_field_name

if "id" not in exclude_fields:
input_fields["id"] = graphene.ID()

class DjangoModelFormMutation(BaseDjangoFormMutation):
class Meta:
abstract = True
if not return_field_name:
model_name = model.__name__

errors = graphene.List(ErrorType)
return_field_name = model_name[:1].lower() + model_name[1:]

@classmethod
def __init_subclass_with_meta__(
cls,
form_class=None,
model=None,
return_field_name=None,
only_fields=(),
exclude_fields=(),
**options
):
# TODO: model_type might be none

if not form_class:
raise Exception("form_class is required for DjangoModelFormMutation")
output_fields[return_field_name] = graphene.Field(model_type)
else:
form_name = form.__class__.__name__

if not model:
model = form_class._meta.model
if not return_field_name:
return_field_name = form_name[:1].lower() + form_name[1:]

if not model:
raise Exception("model is required for DjangoModelFormMutation")
# TODO: registry

form = form_class()
input_fields = fields_for_form(form, only_fields, exclude_fields)
if "id" not in exclude_fields:
input_fields["id"] = graphene.ID()
form_fields = fields_for_form(
form, only_fields, exclude_fields
)

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:]
form_type = type(
form_name,
(graphene.ObjectType, ),
yank_fields_from_attrs(form_fields, _as=graphene.Field),
)

output_fields = OrderedDict()
output_fields[return_field_name] = graphene.Field(model_type)
output_fields[return_field_name] = graphene.Field(form_type)

output_fields['errors'] = graphene.Field(cls.Errors, required=True)

_meta = DjangoModelDjangoFormMutationOptions(cls)
_meta.form_class = form_class
_meta.model = model
_meta.return_field_name = return_field_name
_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(DjangoModelFormMutation, cls).__init_subclass_with_meta__(
super(DjangoFormMutation, 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)
return cls(errors={}, **kwargs)
Loading