mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-25 02:54:06 +03:00
Add DjangoFormInputObjectType to forms/types (#1325)
* 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. * Correct Reporter model a_choice field type according to CHOICES tuple * Add tests for DjangoFormInputObjectType * Add pyenv files to .gitignore * Fix pyupgrade * Fix tests * Add docs * Fix docs example --------- Co-authored-by: Firas Kafri <3097061+firaskafri@users.noreply.github.com>
This commit is contained in:
parent
a6596273cf
commit
be17278b49
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -80,3 +80,8 @@ Session.vim
|
||||||
tags
|
tags
|
||||||
.tox/
|
.tox/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
.python-version
|
||||||
|
|
|
@ -125,6 +125,55 @@ to change how the form is saved or to return a different Graphene object type.
|
||||||
If the form is *not* valid then a list of errors will be returned. These errors have two fields: ``field``, a string
|
If the form is *not* valid then a list of errors will be returned. These errors have two fields: ``field``, a string
|
||||||
containing the name of the invalid form field, and ``messages``, a list of strings with the validation messages.
|
containing the name of the invalid form field, and ``messages``, a list of strings with the validation messages.
|
||||||
|
|
||||||
|
DjangoFormInputObjectType
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
``DjangoFormInputObjectType`` is used in mutations to create input fields by **using django form** to retrieve input data structure from it. This can be helpful in situations where you need to pass data to several django forms in one mutation.
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django.forms.types import DjangoFormInputObjectType
|
||||||
|
|
||||||
|
|
||||||
|
class PetFormInput(DjangoFormInputObjectType):
|
||||||
|
# any other fields can be placed here as well as
|
||||||
|
# other djangoforminputobjects and intputobjects
|
||||||
|
class Meta:
|
||||||
|
form_class = PetForm
|
||||||
|
object_type = PetType
|
||||||
|
|
||||||
|
class QuestionFormInput(DjangoFormInputObjectType)
|
||||||
|
class Meta:
|
||||||
|
form_class = QuestionForm
|
||||||
|
object_type = QuestionType
|
||||||
|
|
||||||
|
class SeveralFormsInputData(graphene.InputObjectType):
|
||||||
|
pet = PetFormInput(required=True)
|
||||||
|
question = QuestionFormInput(required=True)
|
||||||
|
|
||||||
|
class SomeSophisticatedMutation(graphene.Mutation):
|
||||||
|
class Arguments:
|
||||||
|
data = SeveralFormsInputData(required=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def mutate(_root, _info, data):
|
||||||
|
pet_form_inst = PetForm(data=data.pet)
|
||||||
|
question_form_inst = QuestionForm(data=data.question)
|
||||||
|
|
||||||
|
if pet_form_inst.is_valid():
|
||||||
|
pet_model_instance = pet_form_inst.save(commit=False)
|
||||||
|
|
||||||
|
if question_form_inst.is_valid():
|
||||||
|
question_model_instance = question_form_inst.save(commit=False)
|
||||||
|
|
||||||
|
# ...
|
||||||
|
|
||||||
|
Additional to **InputObjectType** ``Meta`` class attributes:
|
||||||
|
|
||||||
|
* ``form_class`` is required and should be equal to django form class.
|
||||||
|
* ``object_type`` is not required and used to enable convertion of enum values back to original if model object type ``convert_choices_to_enum`` ``Meta`` class attribute is not set to ``False``. Any data field, which have choices in django, with value ``A_1`` (for example) from client will be automatically converted to ``1`` in mutation data.
|
||||||
|
* ``add_id_field_name`` is used to specify `id` field name (not required, by default equal to ``id``)
|
||||||
|
* ``add_id_field_type`` is used to specify `id` field type (not required, default is ``graphene.ID``)
|
||||||
|
|
||||||
Django REST Framework
|
Django REST Framework
|
||||||
---------------------
|
---------------------
|
||||||
|
|
333
graphene_django/forms/tests/test_djangoinputobject.py
Normal file
333
graphene_django/forms/tests/test_djangoinputobject.py
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
import graphene
|
||||||
|
|
||||||
|
from django import forms
|
||||||
|
from pytest import raises
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from ..types import DjangoFormInputObjectType
|
||||||
|
from ...tests.models import Reporter, Film, CHOICES
|
||||||
|
|
||||||
|
# Reporter a_choice CHOICES = ((1, "this"), (2, _("that")))
|
||||||
|
THIS = CHOICES[0][0]
|
||||||
|
THIS_ON_CLIENT_CONVERTED = "A_1"
|
||||||
|
|
||||||
|
# Film genre choices=[("do", "Documentary"), ("ac", "Action"), ("ot", "Other")],
|
||||||
|
DOCUMENTARY = "do"
|
||||||
|
DOCUMENTARY_ON_CLIENT_CONVERTED = "DO"
|
||||||
|
|
||||||
|
|
||||||
|
class FilmForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Film
|
||||||
|
exclude = ()
|
||||||
|
|
||||||
|
|
||||||
|
class ReporterType(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = Reporter
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class ReporterForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Reporter
|
||||||
|
exclude = ("pets", "email")
|
||||||
|
|
||||||
|
|
||||||
|
class MyForm(forms.Form):
|
||||||
|
text_field = forms.CharField()
|
||||||
|
int_field = forms.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
|
def test_needs_form_class():
|
||||||
|
with raises(Exception) as exc:
|
||||||
|
|
||||||
|
class MyInputType(DjangoFormInputObjectType):
|
||||||
|
pass
|
||||||
|
|
||||||
|
assert exc.value.args[0] == "form_class is required for DjangoFormInputObjectType"
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_from_modelform_has_input_fields():
|
||||||
|
class ReporterInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = ReporterForm
|
||||||
|
only_fields = ("first_name", "last_name", "a_choice")
|
||||||
|
|
||||||
|
fields = ["first_name", "last_name", "a_choice", "id"]
|
||||||
|
assert all(f in ReporterInputType._meta.fields for f in fields)
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_from_form_has_input_fields():
|
||||||
|
class MyFormInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = MyForm
|
||||||
|
|
||||||
|
fields = ["text_field", "int_field", "id"]
|
||||||
|
assert all(f in MyFormInputType._meta.fields for f in fields)
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_custom_id_field():
|
||||||
|
class MyFormInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = MyForm
|
||||||
|
add_id_field_name = "pk"
|
||||||
|
|
||||||
|
fields = ["text_field", "int_field", "pk"]
|
||||||
|
assert all(f in MyFormInputType._meta.fields for f in fields)
|
||||||
|
assert MyFormInputType._meta.fields["pk"].type is graphene.ID
|
||||||
|
|
||||||
|
|
||||||
|
def test_type_custom_id_field_type():
|
||||||
|
class MyFormInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = MyForm
|
||||||
|
add_id_field_name = "pk"
|
||||||
|
add_id_field_type = graphene.String(required=False)
|
||||||
|
|
||||||
|
fields = ["text_field", "int_field", "pk"]
|
||||||
|
assert all(f in MyFormInputType._meta.fields for f in fields)
|
||||||
|
assert MyFormInputType._meta.fields["pk"].type is graphene.String
|
||||||
|
|
||||||
|
|
||||||
|
class MockQuery(graphene.ObjectType):
|
||||||
|
a = graphene.String()
|
||||||
|
|
||||||
|
|
||||||
|
def test_mutation_with_form_djangoforminputtype():
|
||||||
|
class MyFormInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = MyForm
|
||||||
|
|
||||||
|
class MyFormMutation(graphene.Mutation):
|
||||||
|
class Arguments:
|
||||||
|
form_data = MyFormInputType(required=True)
|
||||||
|
|
||||||
|
result = graphene.Boolean()
|
||||||
|
|
||||||
|
def mutate(_root, _info, form_data):
|
||||||
|
form = MyForm(data=form_data)
|
||||||
|
if form.is_valid():
|
||||||
|
result = form.cleaned_data == {
|
||||||
|
"text_field": "text",
|
||||||
|
"int_field": 777,
|
||||||
|
}
|
||||||
|
return MyFormMutation(result=result)
|
||||||
|
return MyFormMutation(result=False)
|
||||||
|
|
||||||
|
class Mutation(graphene.ObjectType):
|
||||||
|
myForm_mutation = MyFormMutation.Field()
|
||||||
|
|
||||||
|
schema = graphene.Schema(query=MockQuery, mutation=Mutation)
|
||||||
|
|
||||||
|
result = schema.execute(
|
||||||
|
""" mutation MyFormMutation($formData: MyFormInputType!) {
|
||||||
|
myFormMutation(formData: $formData) {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
variable_values={"formData": {"textField": "text", "intField": 777}},
|
||||||
|
)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data == {"myFormMutation": {"result": True}}
|
||||||
|
|
||||||
|
|
||||||
|
def test_mutation_with_modelform_djangoforminputtype():
|
||||||
|
class ReporterInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = ReporterForm
|
||||||
|
object_type = ReporterType
|
||||||
|
only_fields = ("first_name", "last_name", "a_choice")
|
||||||
|
|
||||||
|
class ReporterMutation(graphene.Mutation):
|
||||||
|
class Arguments:
|
||||||
|
reporter_data = ReporterInputType(required=True)
|
||||||
|
|
||||||
|
result = graphene.Field(ReporterType)
|
||||||
|
|
||||||
|
def mutate(_root, _info, reporter_data):
|
||||||
|
reporter = Reporter.objects.get(pk=reporter_data.id)
|
||||||
|
form = ReporterForm(data=reporter_data, instance=reporter)
|
||||||
|
if form.is_valid():
|
||||||
|
reporter = form.save()
|
||||||
|
return ReporterMutation(result=reporter)
|
||||||
|
|
||||||
|
return ReporterMutation(result=None)
|
||||||
|
|
||||||
|
class Mutation(graphene.ObjectType):
|
||||||
|
report_mutation = ReporterMutation.Field()
|
||||||
|
|
||||||
|
schema = graphene.Schema(query=MockQuery, mutation=Mutation)
|
||||||
|
|
||||||
|
reporter = Reporter.objects.create(
|
||||||
|
first_name="Bob", last_name="Roberts", a_choice=THIS
|
||||||
|
)
|
||||||
|
|
||||||
|
result = schema.execute(
|
||||||
|
""" mutation ReportMutation($reporterData: ReporterInputType!) {
|
||||||
|
reportMutation(reporterData: $reporterData) {
|
||||||
|
result {
|
||||||
|
id,
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
aChoice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
variable_values={
|
||||||
|
"reporterData": {
|
||||||
|
"id": reporter.pk,
|
||||||
|
"firstName": "Dave",
|
||||||
|
"lastName": "Smith",
|
||||||
|
"aChoice": THIS_ON_CLIENT_CONVERTED,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data["reportMutation"]["result"] == {
|
||||||
|
"id": "1",
|
||||||
|
"firstName": "Dave",
|
||||||
|
"lastName": "Smith",
|
||||||
|
"aChoice": THIS_ON_CLIENT_CONVERTED,
|
||||||
|
}
|
||||||
|
assert Reporter.objects.count() == 1
|
||||||
|
reporter.refresh_from_db()
|
||||||
|
assert reporter.first_name == "Dave"
|
||||||
|
|
||||||
|
|
||||||
|
def reporter_enum_convert_mutation_result(
|
||||||
|
ReporterInputType, choice_val_on_client=THIS_ON_CLIENT_CONVERTED
|
||||||
|
):
|
||||||
|
class ReporterMutation(graphene.Mutation):
|
||||||
|
class Arguments:
|
||||||
|
reporter = ReporterInputType(required=True)
|
||||||
|
|
||||||
|
result_str = graphene.String()
|
||||||
|
result_int = graphene.Int()
|
||||||
|
|
||||||
|
def mutate(_root, _info, reporter):
|
||||||
|
if isinstance(reporter.a_choice, int) or reporter.a_choice.isdigit():
|
||||||
|
return ReporterMutation(result_int=reporter.a_choice, result_str=None)
|
||||||
|
return ReporterMutation(result_int=None, result_str=reporter.a_choice)
|
||||||
|
|
||||||
|
class Mutation(graphene.ObjectType):
|
||||||
|
report_mutation = ReporterMutation.Field()
|
||||||
|
|
||||||
|
schema = graphene.Schema(query=MockQuery, mutation=Mutation)
|
||||||
|
|
||||||
|
return schema.execute(
|
||||||
|
""" mutation ReportMutation($reporter: ReporterInputType!) {
|
||||||
|
reportMutation(reporter: $reporter) {
|
||||||
|
resultStr,
|
||||||
|
resultInt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
variable_values={"reporter": {"aChoice": choice_val_on_client}},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_enum_not_converted():
|
||||||
|
class ReporterInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = ReporterForm
|
||||||
|
only_fields = ("a_choice",)
|
||||||
|
|
||||||
|
result = reporter_enum_convert_mutation_result(ReporterInputType)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data["reportMutation"]["resultStr"] == THIS_ON_CLIENT_CONVERTED
|
||||||
|
assert result.data["reportMutation"]["resultInt"] is None
|
||||||
|
assert ReporterInputType._meta.fields["a_choice"].type is graphene.String
|
||||||
|
|
||||||
|
|
||||||
|
def test_enum_is_converted_to_original():
|
||||||
|
class ReporterInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = ReporterForm
|
||||||
|
object_type = ReporterType
|
||||||
|
only_fields = ("a_choice",)
|
||||||
|
|
||||||
|
result = reporter_enum_convert_mutation_result(ReporterInputType)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data["reportMutation"]["resultInt"] == THIS
|
||||||
|
assert result.data["reportMutation"]["resultStr"] is None
|
||||||
|
assert (
|
||||||
|
ReporterInputType._meta.fields["a_choice"].type.__name__
|
||||||
|
== "AChoiceEnumBackConvString"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_convert_choices_to_enum_is_false_and_field_type_as_in_model():
|
||||||
|
class ReporterTypeNotConvertChoices(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = Reporter
|
||||||
|
convert_choices_to_enum = False
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
class ReporterInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = ReporterForm
|
||||||
|
object_type = ReporterTypeNotConvertChoices
|
||||||
|
only_fields = ("a_choice",)
|
||||||
|
|
||||||
|
result = reporter_enum_convert_mutation_result(ReporterInputType, THIS)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data["reportMutation"]["resultInt"] == THIS
|
||||||
|
assert result.data["reportMutation"]["resultStr"] is None
|
||||||
|
assert ReporterInputType._meta.fields["a_choice"].type is graphene.Int
|
||||||
|
|
||||||
|
|
||||||
|
def enum_convert_mutation_result_film(FilmInputType):
|
||||||
|
class FilmMutation(graphene.Mutation):
|
||||||
|
class Arguments:
|
||||||
|
film = FilmInputType(required=True)
|
||||||
|
|
||||||
|
result = graphene.String()
|
||||||
|
|
||||||
|
def mutate(_root, _info, film):
|
||||||
|
return FilmMutation(result=film.genre)
|
||||||
|
|
||||||
|
class Mutation(graphene.ObjectType):
|
||||||
|
film_mutation = FilmMutation.Field()
|
||||||
|
|
||||||
|
schema = graphene.Schema(query=MockQuery, mutation=Mutation)
|
||||||
|
|
||||||
|
return schema.execute(
|
||||||
|
""" mutation FilmMutation($film: FilmInputType!) {
|
||||||
|
filmMutation(film: $film) {
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
variable_values={"film": {"genre": DOCUMENTARY_ON_CLIENT_CONVERTED}},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_enum_not_converted_required_non_number():
|
||||||
|
class FilmInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = FilmForm
|
||||||
|
only_fields = ("genre",)
|
||||||
|
|
||||||
|
result = enum_convert_mutation_result_film(FilmInputType)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data["filmMutation"]["result"] == DOCUMENTARY_ON_CLIENT_CONVERTED
|
||||||
|
|
||||||
|
|
||||||
|
def test_enum_is_converted_to_original_required_non_number():
|
||||||
|
class FilmType(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = Film
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
class FilmInputType(DjangoFormInputObjectType):
|
||||||
|
class Meta:
|
||||||
|
form_class = FilmForm
|
||||||
|
object_type = FilmType
|
||||||
|
only_fields = ("genre",)
|
||||||
|
|
||||||
|
result = enum_convert_mutation_result_film(FilmInputType)
|
||||||
|
assert result.errors is None
|
||||||
|
assert result.data["filmMutation"]["result"] == DOCUMENTARY
|
|
@ -1 +1,118 @@
|
||||||
|
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().__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()
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Reporter(models.Model):
|
||||||
last_name = models.CharField(max_length=30)
|
last_name = models.CharField(max_length=30)
|
||||||
email = models.EmailField()
|
email = models.EmailField()
|
||||||
pets = models.ManyToManyField("self")
|
pets = models.ManyToManyField("self")
|
||||||
a_choice = models.CharField(max_length=30, choices=CHOICES, blank=True)
|
a_choice = models.IntegerField(choices=CHOICES, null=True, blank=True)
|
||||||
objects = models.Manager()
|
objects = models.Manager()
|
||||||
doe_objects = DoeReporterManager()
|
doe_objects = DoeReporterManager()
|
||||||
|
|
||||||
|
|
|
@ -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