Graphene: Django: Map Form to Mutation

Created on 9 Apr 2016  Â·  12Comments  Â·  Source: graphql-python/graphene

Currently it's possible to map Django models to ObjectTypes using DjangoNode. I think it would be nice to have a similar feature that allows mapping forms to Mutation with a DjangoMutation class.

This would provide several great improvements:

  • No need to duplicate form fields in Input classes anymore.
  • There would be a simple and obvious way to handle validation.
  • The code could map form.errors to info.context.errors (It took a while until I figured out how to do that) on it's own.

In the simple case you'd only have to define the outputs and a method that takes the validated data and returns an instance of the mutation class. I believe that even that would be unnecessary, if you're working with ModelForms. In that case a default method could just save the form and return the model instance wrapped in the corresponding DjangoNode.

Code that gets us some of the way to such a class already appears to exist, which was written for the django-filter integration.

I'm not that familiar with the code base itself but I have read quite a bit of it and I' think it might be able to implement this. I'm interested to know what you think of it and whether this has a chance to be accepted.

🙋 help wanted

Most helpful comment

Hi what is the status on this?

I need something like a UserInputType for a IntroduceUser mutation and i don't want to manually map all the fields of the Django User model.

I would like if there would be a DjangoInputObjectType that could be created similar to the DjangoObjectType. Passing it through Django Forms is a brilliant solution to handling field validation, but being able to bypass validation would be appreciated for bulk data imports from a trusted system.

All 12 comments

Hi @DasIch,
I think it's a great proposal!

Should be good to start a discussion about how we want to use it!
I was thinking of something like:

class ArticleInput(DjangoInputObjectType):
    class Meta:
        form = ArticleForm

I like mapping form.errors to the context. I think would be good if is done explicitly.

Thoughts?

Hi! Here is my proof of concept code to map DRF serializers to mutations: https://gist.github.com/gcheshkov/555b3e300b45f451b5b0fe79c73531ae

@gcheshkov thanks for the gist. I noticed you're importing from .types import ErrorType. Could you post the code to that too?

@stefanfoulis ErrorType is simple graphene object type for representing errors:

class ErrorType(graphene.ObjectType):
    field = graphene.String()
    message = graphene.String()

However now I took different approach to error handling. Just raising ValidationError in mutation handler and adding error messages to errors object in overridden GraphQLView.format_error method:

def format_error(error):
    data = {
        'message': str(error),
    }

    if isinstance(error, GraphQLLocatedError):
        data.update(format_graphql_error(error))
        if isinstance(error.original_error, Exception):
            error = error.original_error
        else:
            return data

    if isinstance(error, APIException):
        if isinstance(error, ValidationError):
            data.update({
                'message': _('Validation error'),
                'code': 'validation_error',
                'fields': error.detail
            })
        else:
            if getattr(error, 'error_code', None):
                data['code'] = error.error_code

            if getattr(error, 'extra', None):
                data.update(error.extra)

    elif isinstance(error, DjangoPermissionDenied):
        data.update({
            'message': _('Permission denied'),
            'code': 'permission_denied'
        })

    elif isinstance(error, GraphQLError):
        pass

    elif isinstance(error, HttpError):
        pass

    else:
        data['code'] = 'unhandled_exception'
        if not settings.DEBUG:
            data['message'] = _('Server error')
        else:
            data['message'] = '{}: {}'.format(error.__class__.__name__, error)
            data['traceback'] = itertools.chain(
                *[[l for l in ll.split('\n') if l.strip() != '']
                  for ll in traceback.format_exception(
                        error.__class__, error, error.__traceback__)])

    return data

@gcheshkov thanks. that helped me a lot :-)

Hi what is the status on this?

I need something like a UserInputType for a IntroduceUser mutation and i don't want to manually map all the fields of the Django User model.

I would like if there would be a DjangoInputObjectType that could be created similar to the DjangoObjectType. Passing it through Django Forms is a brilliant solution to handling field validation, but being able to bypass validation would be appreciated for bulk data imports from a trusted system.

i can see form.py and form_convert.py in graphene-django , but never found any how to use.
Django: Map Form to Mutation how to use?

@fangaofeng this is not implemented yet. I'm working on something similar for django rest framework's serialisers in https://github.com/graphql-python/graphene-django/pull/186

I'm happy to do the same for the Forms, but right now I don't have much time, and I'd like to finish the other PR first :)

Hi @DasIch . We're currently going through old issues that appear to have gone stale (ie. not updated in about the last 6 months) to try and clean up the issue tracker. If this is still important to you please comment and we'll re-open this.

Thanks!

@jkimbo this feature request is still valid if it is not implemented yet. Such a future would be very helpful for projects migrating from an existing Django infrastructure.

@lig could you open an issue on graphene-django then since that is where we track any issues with the Django integration

Just as a reference for who will implement this feature in the future, I found something that seems could help in another OS project: https://github.com/mirumee/saleor/blob/99d45583985eb64e62966b3411bee54ba5052b1a/saleor/dashboard/graphql/core/mutations.py
I'm too new to graphQL and I don't have the skills to take this task but I hope this serves as an inspiration to someone.
@jkimbo sorry about commenting here, I didn't find this topic in graphene-django issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

romaia picture romaia  Â·  3Comments

Glyphack picture Glyphack  Â·  3Comments

jloveric picture jloveric  Â·  4Comments

defrex picture defrex  Â·  3Comments

mandx picture mandx  Â·  4Comments