Last active
December 11, 2019 10:34
-
-
Save oldnote/0f58012ffb5be5e229e70dd44baa4258 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import rest_framework.serializers | |
| import graphql.execution.executor | |
| from django.db.models import Model | |
| from graphene.utils import str_converters | |
| from functools import lru_cache | |
| @lru_cache(maxsize=None) | |
| def get_serializer(model_class, model_fields): | |
| """Create and cache serializer object for given model with given fields.""" | |
| class ModelSerializer(rest_framework.serializers.ModelSerializer): | |
| class Meta: | |
| model = model_class | |
| fields = model_fields | |
| return ModelSerializer() | |
| def complete_value(exe_context, return_type, field_asts, info, path, result): | |
| if isinstance(result, Model): | |
| camel_case_fields = exe_context.get_sub_fields( | |
| return_type, field_asts | |
| ).keys() | |
| snake_case_fields = tuple( | |
| map(str_converters.to_snake_case, camel_case_fields) | |
| ) | |
| serializer = get_serializer(result.__class__, snake_case_fields) | |
| serializer.instance = result | |
| return serializer.data | |
| return _complete_value( | |
| exe_context, return_type, field_asts, info, path, result | |
| ) | |
| _complete_value = graphql.execution.executor.complete_value | |
| graphql.execution.executor.complete_value = complete_value |
Author
Author
Bugfixes with __typename, id fields support and more speed up by caching:
class DecimalWithFloatReprField(rest_framework.serializers.DecimalField):
"""In graphql decimals represented as floats."""
def to_representation(self, *a, **kw):
return float(super().to_representation(*a, **kw))
@lru_cache(maxsize=None)
def get_serializer(model_class, model_fields):
"""Create and cache serializer object for given model with given fields."""
class ModelSerializer(rest_framework.serializers.ModelSerializer):
class Meta:
model = model_class
fields = model_fields
serializer_field_mapping = (
rest_framework.serializers.ModelSerializer.serializer_field_mapping.copy() # noqa
)
serializer_field_mapping[ # noqa
DecimalField
] = DecimalWithFloatReprField
@lru_cache(maxsize=None)
def to_representation(self, instance):
return super().to_representation(instance)
return ModelSerializer()
def complete_value(exe_context, return_type, field_asts, info, path, result):
"""
Following functin used to speedup graphql response time.
XXX: Nested queried objects will be represented as id's, because
restframework serializer has default depth. For our current usecase
(candles) we're fine with that.
"""
# Use default behaviour if not Candle instance.
if not isinstance(result, games_models.Candle):
return _complete_value(
exe_context, return_type, field_asts, info, path, result
)
# Get camel cased field names list specified in graphql query.
camel_case_fields = exe_context.get_sub_fields(
return_type, field_asts
).keys()
# Transform field names to snake case excluding optional graphql's
# '__typename' field.
snake_case_fields = tuple(
filter(
lambda field_name: field_name != '__typename',
map(str_converters.to_snake_case, camel_case_fields),
)
)
# Create or receive cached serializer object.
serializer = get_serializer(result.__class__, snake_case_fields)
# Serialize instance to dict.
serialized_data = serializer.to_representation(result)
# Transform snake case fields to camel case for graphql.
serialized_data = funcy.walk_keys(to_camel_case, dict(**serialized_data))
# Append __typename if was provided.
if '__typename' in camel_case_fields:
serialized_data['__typename'] = str(return_type)
# Transform django's id to relay's if was provided.
if 'id' in serialized_data:
serialized_data['id'] = to_global_id(
str(return_type), serialized_data['id']
)
return serialized_data
_complete_value = graphql.execution.executor.complete_value
graphql.execution.executor.complete_value = complete_value
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Based on https://gist.github.com/ktosiek/a309f772399482a47cf2c4ed219ff1af