Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question]How about leveraging djangorestframework serializer from models to json to protobuf? #22

Open
rangababur opened this issue Apr 4, 2022 · 3 comments

Comments

@rangababur
Copy link

Experts,
Please let me know, if I am going in right direction?
If not provide some examples on using grpc serializers.

Thank you in advance

@gluk-w
Copy link
Owner

gluk-w commented Apr 4, 2022

I think you can build an extension (middleware) on the top of django rest framework that would use ParseDict to convert JSON generated by DRF to protobuf. There is special flag that can help you to map properties with different field name called "json_name" flag:

message MyObject {
    int32 name = 1 [json_name = "title"]; 
}

@rangababur
Copy link
Author

Thanks for the immediate response Stan.
I will work on the solution provided :)

@yswtrue
Copy link

yswtrue commented Aug 11, 2023

you can write like this, this code is copy from https://github.com/socotecio/django-socio-grpc

from google.protobuf.json_format import MessageToDict, ParseDict
from rest_framework import serializers

from news_grpc import news_pb2

from .models import News


def _is_field_optional(field):
    """
    Checks if a field is optional.

    Under the hood, Optional fields are OneOf fields with only one field with the name of the OneOf
    prefixed with an underscore.
    """

    if not (co := field.containing_oneof):
        return False

    return len(co.fields) == 1 and co.name == f"_{field.name}"


def message_to_dict(message, **kwargs):
    """
    Converts a protobuf message to a dictionary.
    Uses the default `google.protobuf.json_format.MessageToDict` function.
    Adds None values for optional fields that are not set.
    """

    kwargs.setdefault("including_default_value_fields", True)
    kwargs.setdefault("preserving_proto_field_name", True)

    result_dict = MessageToDict(message, **kwargs)
    optional_fields = {field.name: None for field in message.DESCRIPTOR.fields if _is_field_optional(field)}

    return {**optional_fields, **result_dict}


def parse_dict(js_dict, message, **kwargs):
    kwargs.setdefault("ignore_unknown_fields", True)
    return ParseDict(js_dict, message, **kwargs)


class GrpcSerializer(serializers.BaseSerializer):
    @property
    def message(self):
        if not hasattr(self, "_message"):
            self._message = self.data_to_message(self.data)
        return self._message

    def data_to_message(self, data):
        """Protobuf message <- Dict of python primitive datatypes."""
        assert hasattr(self, "Meta"), 'Class {serializer_class} missing "Meta" attribute'.format(
            serializer_class=self.__class__.__name__
        )
        assert hasattr(
            self.Meta, "proto_class"
        ), 'Class {serializer_class} missing "Meta.proto_class" attribute'.format(
            serializer_class=self.__class__.__name__
        )
        return parse_dict(data, self.Meta.proto_class())


class NewsSerializer(serializers.ModelSerializer, GrpcSerializer):
    is_liked = serializers.BooleanField(read_only=True)

    class Meta:
        model = News
        proto_class = news_pb2.News
        fields = "__all__"

s = NewsSerializer(News())
s.message

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants