SerializerMutation have output_fields with defination of return_field_name similar to DjangoModelFormMutation
sushilkhadka165 opened this issue · comments
Is your feature request related to a problem? Please describe.
A SerializerMutation provided by the package returns a output_fields as the serializer fields provided int serializer class. But as graphql we should be able to provided a DjangoObjectType of the required models. As looking into DjangoModelFormMutation and SerializerMutation I have found that DjangoModelFormMutation contains return_field_name which describes the output_fields but in SerializerMuatation the output_fields are generated by serializerfields.
Describe the solution you'd like
We could implement this by adding a new filed return_field_name in SerializerMutation and overriding the perform_mutate of the SerializerMutation.
original code
update code below
class SerializerMutationOptions(MutationOptions):
lookup_field = None
model_class = None
model_operations = ["create", "update"]
serializer_class = None
# add new field here for return_field_name
return_field_name = None
class DupSerializerMutation(ClientIDMutation):
class Meta:
abstract = True
errors = graphene.List(
ErrorType, description="May contain more than one error for same field."
)
# add new argument return_field_name
@classmethod
def __init_subclass_with_meta__(
cls,
lookup_field=None,
serializer_class=None,
model_class=None,
return_field_name=None,
model_operations=("create", "update"),
only_fields=(),
exclude_fields=(),
convert_choices_to_enum=True,
_meta=None,
**options
):
if not serializer_class:
raise Exception("serializer_class is required for the SerializerMutation")
if "update" not in model_operations and "create" not in model_operations:
raise Exception('model_operations must contain "create" and/or "update"')
serializer = serializer_class()
if model_class is None:
serializer_meta = getattr(serializer_class, "Meta", None)
if serializer_meta:
model_class = getattr(serializer_meta, "model", None)
if lookup_field is None and model_class:
lookup_field = model_class._meta.pk.name
input_fields = fields_for_serializer(
serializer,
only_fields,
exclude_fields,
is_input=True,
convert_choices_to_enum=convert_choices_to_enum,
lookup_field=lookup_field,
)
#Code change start
registry = get_global_registry()
model_type = registry.get_type_for_model(model_class)
if not model_type:
raise Exception("No type registered for model: {}".format(model_class.__name__))
if not return_field_name:
model_name = model_class.__name__
return_field_name = model_name[:1].lower() + model_name[1:]
output_fields = OrderedDict()
output_fields[return_field_name] = graphene.Field(model_type)
# Code Change end
if not _meta:
_meta = SerializerMutationOptions(cls)
_meta.lookup_field = lookup_field
_meta.model_operations = model_operations
_meta.serializer_class = serializer_class
_meta.model_class = model_class
# code change start
_meta.return_field_name = return_field_name
# code change end
_meta.fields = yank_fields_from_attrs(output_fields, _as=graphene.Field)
input_fields = yank_fields_from_attrs(input_fields, _as=graphene.InputField)
super().__init_subclass_with_meta__(
_meta=_meta, input_fields=input_fields, **options
)
@classmethod
def get_serializer_kwargs(cls, root, info, **input):
lookup_field = cls._meta.lookup_field
model_class = cls._meta.model_class
if model_class:
if "update" in cls._meta.model_operations and lookup_field in input:
instance = get_object_or_404(
model_class, **{lookup_field: input[lookup_field]}
)
partial = True
elif "create" in cls._meta.model_operations:
instance = None
partial = False
else:
raise Exception(
'Invalid update operation. Input parameter "{}" required.'.format(
lookup_field
)
)
return {
"instance": instance,
"data": input,
"context": {"request": info.context},
"partial": partial,
}
return {"data": input, "context": {"request": info.context}}
@classmethod
def mutate_and_get_payload(cls, root, info, **input):
kwargs = cls.get_serializer_kwargs(root, info, **input)
serializer = cls._meta.serializer_class(**kwargs)
if serializer.is_valid():
return cls.perform_mutate(serializer, info)
else:
errors = ErrorType.from_errors(serializer.errors)
return cls(errors=errors)
@classmethod
def perform_mutate(cls, serializer, info):
obj = serializer.save()
# code change start
kwargs = {cls._meta.return_field_name: obj}
# code change end
return cls(errors=None, **kwargs)