Override model serializer globally, big integer field #9722
-
First of all thanks for this project, i can't use Django without drf 😀 Im looking for a way to override serializers.ModelSerializer globally Little bit of context: My problem:
So the best solution in my opinion is to serialize bigint as string. class SnowflakeField(serializers.Field):
def to_representation(self, value):
return str(value)
def to_internal_value(self, data):
try:
return int(data)
except ValueError:
raise serializers.ValidationError("Invalid integer value")
class SnowflakeModelSerializer(serializers.ModelSerializer):
"""
Snowflake is a bigint so convert to string to avoid frontend memory handling problems
"""
def build_standard_field(self, field_name, model_field):
field_class, field_kwargs = super().build_standard_field(field_name, model_field)
# Use BigIntAsStringField per BigIntegerField
if isinstance(model_field, models.BigIntegerField):
field_class = SnowflakeField
return field_class, field_kwargs Anyway, since django uses bigint as id, serializing it as sting could be a better option in any case since the id can grow big Possible ways
class MyModelSerializer(SnowflakeModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
and do something like this with a lot of assumptions, spoiler bad programming class SnowflakeJSONRenderer(JSONRenderer):
def _convert_big_integers(self, obj):
if isinstance(obj, dict):
return {key: self._convert_big_integers(value) for key, value in obj.items()}
elif isinstance(obj, list):
return [self._convert_big_integers(item) for item in obj]
elif isinstance(obj, int) and obj > 2**53:
return str(obj)
return obj What im looking forAs far as i know there is no option to do this REST_FRAMEWORK = { Am i missing something? In your opinion what could be the best solution? I'm open to any suggestion |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
I'd recommend going with the first option. It doesn't look pleasant at first, but it's easily maintainable if you add a Django system check to validate that the correct class is used. Do you have a specific reason to use snowflake btw? If you are open to alternatives, I'd recommend UUID7 as a solid one. |
Beta Was this translation helpful? Give feedback.
-
Thx for the replay @ulgens Actually we are also considering uuid, for sure it's a simpler implementation and probably a better compromise in our situation I'm try to evaluate snowflake option for this reasons:
Probably it's overkill but i'm not committed to the switch yet I've made some tests with this code and it's working, it's still a fast implementation.
from django.db import models
from django.utils.encoding import smart_str
from rest_framework import serializers
class SnowflakeField(serializers.Field):
"""Convert snowflake data to string"""
def to_representation(self, value):
return smart_str(value)
def to_internal_value(self, data):
try:
return int(data)
except ValueError:
raise serializers.ValidationError("Invalid integer value")
class SnowflakePrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField, SnowflakeField):
"""Handle related primary keys as snowflake"""
def to_representation(self, value):
if self.pk_field is not None:
return self.pk_field.to_representation(value.pk)
return str(value.pk)
def to_internal_value(self, data):
data = super(SnowflakeField).to_representation(data)
return super(serializers.PrimaryKeyRelatedField).to_internal_value(data)
class SnowflakeSlugRelatedField(serializers.SlugRelatedField):
"""Implement if used"""
class SnowflakeModelSerializer(serializers.ModelSerializer):
"""
Snowflake is a bigint so convert to string to avoid frontend memory handling problems
"""
serializer_field_mapping = {
**serializers.ModelSerializer.serializer_field_mapping,
models.BigAutoField: SnowflakeField,
models.BigIntegerField: SnowflakeField
}
serializer_related_field = SnowflakePrimaryKeyRelatedField
serializer_related_to_field = SnowflakeSlugRelatedField My pk model from django.db import models
class SnowflakeBigAutoField(models.BigAutoField):
def pre_save(self, model_instance, add):
if add and not getattr(model_instance, self.attname, None):
value = self.get_next_value()
setattr(model_instance, self.attname, value)
return super().pre_save(model_instance, add)
@staticmethod
def get_next_value():
return next(sf)
# settings.py
DEFAULT_AUTO_FIELD = 'core.snowflake.SnowflakeBigAutoField' If we deoloy with snowfake i like to share the production code here, because i didn't find any useful resource about snowflake id with django DEFAULT_MODEL_SERIALIZER_CLASSIn any case, is it possible in your opinion try to introduce this setting? I can submit a pull request. |
Beta Was this translation helpful? Give feedback.
I'd recommend going with the first option. It doesn't look pleasant at first, but it's easily maintainable if you add a Django system check to validate that the correct class is used.
Do you have a specific reason to use snowflake btw? If you are open to alternatives, I'd recommend UUID7 as a solid one.