mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-04-13 05:34:20 +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 ..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.fields = django_fields
|
||||
_meta.connection = connection
|
||||
_meta.convert_choices_to_enum = convert_choices_to_enum
|
||||
|
||||
super().__init_subclass_with_meta__(
|
||||
_meta=_meta, interfaces=interfaces, **options
|
||||
|
|
Loading…
Reference in New Issue
Block a user