Skip to content

Instantly share code, notes, and snippets.

@Ruslan-Skira
Last active February 16, 2024 10:38
Show Gist options
  • Save Ruslan-Skira/f4d370af1c35d6d0c1554fa3487a8d50 to your computer and use it in GitHub Desktop.
Save Ruslan-Skira/f4d370af1c35d6d0c1554fa3487a8d50 to your computer and use it in GitHub Desktop.
If data has not the same keys and they are with dots in names i customized the to_internal_value function
"""serializers"""
import logging
from collections import Mapping, OrderedDict
from typing import Dict
from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import get_error_detail, set_value, SkipField
from rest_framework.settings import api_settings
from pricefetch.models import CurrencyExchangeRate # Your model
logger = logging.getLogger(__name__)
class CurrencyExchangeRateSerializer(serializers.Serializer):
"""
Base serializer for Alphavantage api.
"""
from_currency_code = serializers.CharField(max_length=10)
from_currency_name = serializers.CharField(max_length=50)
to_currency_code = serializers.CharField(max_length=10)
to_currency_name = serializers.CharField(max_length=50)
exchange_rate = serializers.DecimalField(max_digits=20, decimal_places=10)
last_refreshed = serializers.DateTimeField()
time_zone = serializers.CharField(max_length=10)
bid_price = serializers.DecimalField(max_digits=20, decimal_places=10)
ask_price = serializers.DecimalField(max_digits=20, decimal_places=10)
def to_internal_value(self, data: Dict):
"""
Rewriting base function for mapping alphavantage dict keys and model fields.
"""
mapping_dict = {
'from_currency_code': '1. From_Currency Code',
'from_currency_name': '2. From_Currency Name',
'to_currency_code': '3. To_Currency Code',
'to_currency_name': '4. To_Currency Name',
'exchange_rate': '5. Exchange Rate',
'last_refreshed': '6. Last Refreshed',
'time_zone': '7. Time Zone',
'bid_price': '8. Bid Price',
'ask_price': '9. Ask Price'
}
if not isinstance(data, Mapping):
message = self.error_messages['invalid'].format(datatype=type(data).__name__)
raise ValidationError({api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='invalid')
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
for field in fields:
validate_method = getattr(self, 'validate_' + field.field_name, None)
primitive_value = data.get(mapping_dict[field.field_name]) # here is mapping model keys with alphavantage api
try:
validated_value = field.run_validation(primitive_value)
if validate_method is not None:
validated_value = validate_method(validated_value)
except ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = get_error_detail(exc)
except SkipField:
pass
else:
set_value(ret, [field.field_name], validated_value) # set_value takes keys in list.
if errors:
raise ValidationError(errors)
return ret
def create(self, validated_data: Dict) -> CurrencyExchangeRate:
"""
Saving model instance.
:param validated_data:
:type validated_data:
:return:
:rtype:
"""
return CurrencyExchangeRate.objects.create(**validated_data)
"""
Example of using serializer with not matching keys in data.
>>> class D(serializers.Serializer):
... name = serializers.CharField(max_length=20, source='wtf1 wtf2 wtf3')
... sername = serializers.CharField(max_length=12)
data = {'wtf1 wtf2 wtf3': "here is", 'sername': 'Hello'}
>>> serializer = D(data)
>>> serializer_main = D(data=serializer.data)
>>> serializer_main.is_valid()
True
>>> serializer_main.data
{'name': 'here is'}
Example of using serializer with key where you have dot
data = {'wtf1 wtf2. wtf3': "here is"}
>>> class D(serializers.Serializer):
... name = serializers.CharField(max_length=20, source='wtf1 wtf2. wtf3')
... sername = serializers.CharField(max_length=12)
...
>>> serializer = D(data)
>>> serializer.data
Traceback (most recent call last):
File "/home/user/PycharmProjects/pricefetch/.venv/lib/python3.8/site-packages/rest_framework/fields.py", line 457, in get_attribute
return get_attribute(instance, self.source_attrs)
File "/home/user/PycharmProjects/pricefetch/.venv/lib/python3.8/site-packages/rest_framework/fields.py", line 95, in get_attribute
instance = instance[attr]
KeyError: 'wtf1 wtf2'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/user/PycharmProjects/pricefetch/.venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 548, in data
ret = super().data
File "/home/user/PycharmProjects/pricefetch/.venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 246, in data
self._data = self.to_representation(self.instance)
File "/home/user/PycharmProjects/pricefetch/.venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 502, in to_representation
attribute = field.get_attribute(instance)
File "/home/user/PycharmProjects/pricefetch/.venv/lib/python3.8/site-packages/rest_framework/fields.py", line 490, in get_attribute
raise type(exc)(msg)
KeyError: "Got KeyError when attempting to get a value for field `name` on serializer `D`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'wtf1 wtf2'."
Because source attributes are parsed by dot
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment