mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-05 12:53:12 +03:00
Add DjangoFormInputObjectType to forms/types
InputObjectType derived class which gets fields from django form. Type of fields with choices (converted to enum) is set to custom scalar type (using Meta.object_type) to dynamically convert enum values back.
This commit is contained in:
parent
6f13d28b6e
commit
d5f065e429
|
@ -1 +1,120 @@
|
||||||
|
import graphene
|
||||||
|
|
||||||
|
from graphene import ID
|
||||||
|
from graphene.types.inputobjecttype import InputObjectType
|
||||||
|
from graphene.utils.str_converters import to_camel_case
|
||||||
|
|
||||||
|
from .mutation import fields_for_form
|
||||||
from ..types import ErrorType # noqa Import ErrorType for backwards compatability
|
from ..types import ErrorType # noqa Import ErrorType for backwards compatability
|
||||||
|
from ..converter import BlankValueField
|
||||||
|
|
||||||
|
|
||||||
|
class DjangoFormInputObjectType(InputObjectType):
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass_with_meta__(
|
||||||
|
cls,
|
||||||
|
container=None,
|
||||||
|
_meta=None,
|
||||||
|
only_fields=(),
|
||||||
|
exclude_fields=(),
|
||||||
|
form_class=None,
|
||||||
|
object_type=None,
|
||||||
|
add_id_field_name=None,
|
||||||
|
add_id_field_type=None,
|
||||||
|
**options,
|
||||||
|
):
|
||||||
|
"""Retrieve fields from django form (Meta.form_class). Received
|
||||||
|
fields are set to cls (they will be converted to input fields
|
||||||
|
by InputObjectType). Type of fields with choices (converted
|
||||||
|
to enum) is set to custom scalar type (using Meta.object_type)
|
||||||
|
to dynamically convert enum values back.
|
||||||
|
|
||||||
|
class MyDjangoFormInput(DjangoFormInputObjectType):
|
||||||
|
# any other fields can be placed here and other inputobjectforms as well
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
form_class = MyDjangoModelForm
|
||||||
|
object_type = MyModelType
|
||||||
|
|
||||||
|
class SomeMutation(graphene.Mutation):
|
||||||
|
class Arguments:
|
||||||
|
data = MyDjangoFormInput(required=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mutate(_root, _info, data):
|
||||||
|
form_inst = MyDjangoModelForm(data=data)
|
||||||
|
if form_inst.is_valid():
|
||||||
|
django_model_instance = form_inst.save(commit=False)
|
||||||
|
# ... etc ...
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not form_class:
|
||||||
|
raise Exception("form_class is required for DjangoFormInputObjectType")
|
||||||
|
|
||||||
|
form = form_class()
|
||||||
|
form_fields = fields_for_form(form, only_fields, exclude_fields)
|
||||||
|
|
||||||
|
for name, field in form_fields.items():
|
||||||
|
if (
|
||||||
|
object_type
|
||||||
|
and name in object_type._meta.fields
|
||||||
|
and isinstance(object_type._meta.fields[name], BlankValueField)
|
||||||
|
):
|
||||||
|
# Field type BlankValueField here means that field
|
||||||
|
# with choises have been converted to enum
|
||||||
|
# (BlankValueField is using only for that task ?)
|
||||||
|
setattr(cls, name, cls.get_enum_cnv_cls_instance(name, object_type))
|
||||||
|
elif (
|
||||||
|
object_type
|
||||||
|
and name in object_type._meta.fields
|
||||||
|
and object_type._meta.convert_choices_to_enum is False
|
||||||
|
and form.fields[name].__class__.__name__ == "TypedChoiceField"
|
||||||
|
):
|
||||||
|
# FIXME
|
||||||
|
# in case if convert_choices_to_enum is False
|
||||||
|
# form field class is converted to String but original
|
||||||
|
# model field type is needed here... (.converter.py bug?)
|
||||||
|
# This is temp workaround to get field type from ObjectType field
|
||||||
|
# TEST: test_enum_not_converted_and_field_type_as_in_model
|
||||||
|
setattr(cls, name, object_type._meta.fields[name].type())
|
||||||
|
else:
|
||||||
|
# set input field according to django form field
|
||||||
|
setattr(cls, name, field)
|
||||||
|
|
||||||
|
# explicitly adding id field (absent in django form fields)
|
||||||
|
# with name and type from Meta or 'id' with graphene.ID by default
|
||||||
|
if add_id_field_name:
|
||||||
|
setattr(cls, add_id_field_name, add_id_field_type or ID(required=False))
|
||||||
|
elif "id" not in exclude_fields:
|
||||||
|
cls.id = ID(required=False)
|
||||||
|
|
||||||
|
super(DjangoFormInputObjectType, cls).__init_subclass_with_meta__(
|
||||||
|
container=container, _meta=_meta, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_enum_cnv_cls_instance(field_name, object_type):
|
||||||
|
"""Saves args in context to convert enum values in
|
||||||
|
Dynamically created Scalar derived class
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_value(value):
|
||||||
|
# field_name & object_type have been saved in context (closure)
|
||||||
|
field = object_type._meta.fields[field_name]
|
||||||
|
if isinstance(field.type, graphene.NonNull):
|
||||||
|
val_before_convert = field.type._of_type[value].value
|
||||||
|
else:
|
||||||
|
val_before_convert = field.type[value].value
|
||||||
|
return graphene.String.parse_value(val_before_convert)
|
||||||
|
|
||||||
|
cls_doc = "String scalar to convert choice value back from enum to original"
|
||||||
|
scalar_type = type(
|
||||||
|
(
|
||||||
|
f"{field_name[0].upper()}{to_camel_case(field_name[1:])}"
|
||||||
|
"EnumBackConvString"
|
||||||
|
),
|
||||||
|
(graphene.String,),
|
||||||
|
{"parse_value": parse_value, "__doc__": cls_doc},
|
||||||
|
)
|
||||||
|
return scalar_type()
|
||||||
|
|
|
@ -252,6 +252,7 @@ class DjangoObjectType(ObjectType):
|
||||||
_meta.filterset_class = filterset_class
|
_meta.filterset_class = filterset_class
|
||||||
_meta.fields = django_fields
|
_meta.fields = django_fields
|
||||||
_meta.connection = connection
|
_meta.connection = connection
|
||||||
|
_meta.convert_choices_to_enum = convert_choices_to_enum
|
||||||
|
|
||||||
super().__init_subclass_with_meta__(
|
super().__init_subclass_with_meta__(
|
||||||
_meta=_meta, interfaces=interfaces, **options
|
_meta=_meta, interfaces=interfaces, **options
|
||||||
|
|
Loading…
Reference in New Issue
Block a user