Graphene-django: Add support for PrimaryKeyRelatedField for SerializerMutation

Created on 2 Feb 2018  路  6Comments  路  Source: graphql-python/graphene-django

It seems to return a type of String, where a graphene.ID would be more appropriate.

We probably need to check also the type of the PK of the related model, to return the proper type

wontfix

Most helpful comment

I haven't studied all the repo, but what seems missing is the proper field conversion.

In the file serializer_converter.py, I added the following:

@get_graphene_type_from_serializer_field.register(serializers.ManyRelatedField)
def convert_serializer_primary_key_related_field(field, is_input=True):
    return (graphene.List, graphene.ID)

This is a naive approach, and I don't know if there are some edge cases that I need to take, but this should allow the correct typing of the mutation input for ManyRelatedField.

Also, for handling Relay ID on mutations, I ended up implementing this:

class GlobalIDField(serializers.Field):
    """
    Defines a Relay GlobalID to numeric Django id conversion.

    Must use with RelaySerializerMutation
    """

    def __init__(self, required=False, **kwargs):
        super().__init__(required=required, **kwargs)

    def get_attribute(self, instance):
        return to_global_id(instance.__class__.__name__, getattr(instance, instance._meta.pk.name))

    def to_internal_value(self, data):
        _, value = from_global_id(data)
        return int(value)

I use this field on my serializers for handling the serialization/deserialization.

As for what @annshress mentions, I used the following approach:

class GlobalIDModelField(serializers.Field):
    model = None
    """
    Defines a Relay GlobalID to a Django model conversion.
    """

    def __init__(self, model, required=True, **kwargs):
        assert (model is not None), "{} model param cannot be None".format(
            self.__class__.__name__
        )
        self.model = model
        super().__init__(required=required, **kwargs)

    def get_attribute(self, instance):
        attribute_instance = getattr(instance, self.field_name, None)
        if not attribute_instance:
            return None
        return to_global_id(attribute_instance.__class__.__name__,
                            getattr(attribute_instance, attribute_instance._meta.pk.name))

    def to_internal_value(self, data):
        model, value = from_global_id(data)
        return self.model.objects.get(pk=value)

Yet, I don't know if it's the best, but it works, just use that field to override your serializer fields with the corresponding model and it should work.

EDIT: format

All 6 comments

I have a small question.

http://docs.graphene-python.org/projects/django/en/latest/tutorial-relay/#schema

If we are going to use relay nodes, it uses the concept of global_id in lieu of pk. But in the SerializerMutation, for the foreign keys, we need to be passing real pks rather than same global_ids. Hence, making clients require both the real and global id for that?

Please, shed some light on this. Thank you.

Hi, is there any news about this issue?

If normal queries use Relay, Why mutations does not use Relay too? I've wrote a lot of code lines trying to supply this issue

I haven't studied all the repo, but what seems missing is the proper field conversion.

In the file serializer_converter.py, I added the following:

@get_graphene_type_from_serializer_field.register(serializers.ManyRelatedField)
def convert_serializer_primary_key_related_field(field, is_input=True):
    return (graphene.List, graphene.ID)

This is a naive approach, and I don't know if there are some edge cases that I need to take, but this should allow the correct typing of the mutation input for ManyRelatedField.

Also, for handling Relay ID on mutations, I ended up implementing this:

class GlobalIDField(serializers.Field):
    """
    Defines a Relay GlobalID to numeric Django id conversion.

    Must use with RelaySerializerMutation
    """

    def __init__(self, required=False, **kwargs):
        super().__init__(required=required, **kwargs)

    def get_attribute(self, instance):
        return to_global_id(instance.__class__.__name__, getattr(instance, instance._meta.pk.name))

    def to_internal_value(self, data):
        _, value = from_global_id(data)
        return int(value)

I use this field on my serializers for handling the serialization/deserialization.

As for what @annshress mentions, I used the following approach:

class GlobalIDModelField(serializers.Field):
    model = None
    """
    Defines a Relay GlobalID to a Django model conversion.
    """

    def __init__(self, model, required=True, **kwargs):
        assert (model is not None), "{} model param cannot be None".format(
            self.__class__.__name__
        )
        self.model = model
        super().__init__(required=required, **kwargs)

    def get_attribute(self, instance):
        attribute_instance = getattr(instance, self.field_name, None)
        if not attribute_instance:
            return None
        return to_global_id(attribute_instance.__class__.__name__,
                            getattr(attribute_instance, attribute_instance._meta.pk.name))

    def to_internal_value(self, data):
        model, value = from_global_id(data)
        return self.model.objects.get(pk=value)

Yet, I don't know if it's the best, but it works, just use that field to override your serializer fields with the corresponding model and it should work.

EDIT: format

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ZuluPro picture ZuluPro  路  3Comments

amiyatulu picture amiyatulu  路  3Comments

BrianChapman picture BrianChapman  路  3Comments

timothyjlaurent picture timothyjlaurent  路  3Comments

artofhuman picture artofhuman  路  3Comments