"""
Serializers.
The following code is used in the usage examples of the ``ModelSerializer``
and ``HyperlinkedModelSerializer`` classes.
>>> from rest_framework import serializers
>>>
>>> from .models import Book
>>>
>>>
>>> class PublishingInformationSerializer(serializers.ModelSerializer):
>>>
>>> publication_date = serializers.DateField(required=False)
>>> isbn = serializers.CharField(required=False)
>>> pages = serializers.IntegerField(required=False)
>>>
>>> class Meta:
>>>
>>> model = Book
>>> fields = (
>>> 'publication_date',
>>> 'isbn',
>>> 'pages',
>>> )
>>> nested_proxy_field = True
>>>
>>>
>>> class StockInformationSerializer(serializers.ModelSerializer):
>>>
>>> class Meta:
>>>
>>> model = Book
>>> fields = (
>>> 'stock_count',
>>> 'price',
>>> 'state',
>>> )
>>> nested_proxy_field = True
"""
from rest_framework import serializers
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2017-2022 Artur Barseghyan"
__license__ = "GPL-2.0-only OR LGPL-2.1-or-later"
__all__ = (
"extract_nested_serializers",
"HyperlinkedModelSerializer",
"is_nested_proxy_field",
"ModelSerializer",
"NestedProxyFieldIdentifier",
"set_instance_values",
)
[docs]def is_nested_proxy_field(field):
"""Check if field is nested proxy field.
:param field:
:type field:
:return: True or False
:rtype: bool
"""
return isinstance(field, NestedProxyFieldIdentifier) or (
getattr(field, "Meta", False)
and getattr(field.Meta, "nested_proxy_field", False)
)
[docs]def set_instance_values(nested_serializers, nested_serializers_data, instance):
"""Set values on instance.
Does not perform any save actions.
:param nested_serializers: Nested serializers.
:param nested_serializers_data: Nested serializers data.
:param instance: Instance (not yet saved)
:type nested_serializers:
:type nested_serializers_data:
:type instance:
:return: Same instance with values set.
:rtype:
"""
for __serializer_name, __serializer in nested_serializers_data.items():
for __field_name, __field_value in __serializer.items():
if is_nested_proxy_field(
nested_serializers[__serializer_name][__field_name]
):
set_instance_values(
{
__field_name: nested_serializers[__serializer_name][
__field_name
]
},
{__field_name: __field_value},
instance,
)
else:
setattr(instance, __field_name, __field_value)
[docs]class NestedProxyFieldIdentifier:
"""NestedProxyField identifier."""
[docs]class ModelSerializer(serializers.ModelSerializer):
"""ModelSerializer for models with NestedProxyField fields.
Example:
>>> from rest_framework_tricks.serializers import ModelSerializer
>>>
>>>
>>> class BookSerializer(ModelSerializer):
>>>
>>> publishing_information = PublishingInformationSerializer(
>>> required=False
>>> )
>>> stock_information = StockInformationSerializer(required=False)
>>>
>>> class Meta:
>>>
>>> model = Book
>>> fields = (
>>> 'url',
>>> 'id',
>>> 'title',
>>> 'description',
>>> 'summary',
>>> 'publishing_information',
>>> 'stock_information',
>>> )
"""
[docs] def create(self, validated_data):
"""Create.
:param validated_data:
:return:
"""
# Collect information on nested serializers
(
__nested_serializers,
__nested_serializers_data,
) = extract_nested_serializers(
self,
validated_data,
)
# Create instance, but don't save it yet
instance = self.Meta.model(**validated_data)
# Assign fields to the `instance` one by one
set_instance_values(
__nested_serializers, __nested_serializers_data, instance
)
# Save the instance and return
instance.save()
return instance
[docs] def update(self, instance, validated_data):
"""Update.
:param instance:
:param validated_data:
:return:
"""
# Collect information on nested serializers
(
__nested_serializers,
__nested_serializers_data,
) = extract_nested_serializers(
self,
validated_data,
)
# Update the instance
instance = super(ModelSerializer, self).update(
instance, validated_data
)
# Assign fields to the `instance` one by one
set_instance_values(
__nested_serializers, __nested_serializers_data, instance
)
# Save the instance and return
instance.save()
return instance
[docs]class HyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
"""HyperlinkedModelSerializer for models with NestedProxyField fields.
Example:
>>> from rest_framework_tricks.serializers import (
>>> HyperlinkedModelSerializer,
>>> )
>>>
>>>
>>> class BookSerializer(HyperlinkedModelSerializer):
>>>
>>> publishing_information = PublishingInformationSerializer(
>>> required=False
>>> )
>>> stock_information = StockInformationSerializer(required=False)
>>>
>>> class Meta:
>>>
>>> model = Book
>>> fields = (
>>> 'url',
>>> 'id',
>>> 'title',
>>> 'description',
>>> 'summary',
>>> 'publishing_information',
>>> 'stock_information',
>>> )
"""
[docs] def create(self, validated_data):
"""Create.
:param validated_data:
:return:
"""
# Collect information on nested serializers
(
__nested_serializers,
__nested_serializers_data,
) = extract_nested_serializers(
self,
validated_data,
)
# Create instance, but don't save it yet
instance = self.Meta.model(**validated_data)
# Assign fields to the `instance` one by one
set_instance_values(
__nested_serializers, __nested_serializers_data, instance
)
# Save the instance and return
instance.save()
return instance
[docs] def update(self, instance, validated_data):
"""Update.
:param instance:
:param validated_data:
:return:
"""
# Collect information on nested serializers
(
__nested_serializers,
__nested_serializers_data,
) = extract_nested_serializers(
self,
validated_data,
)
# Update the instance
instance = super(HyperlinkedModelSerializer, self).update(
instance, validated_data
)
# Assign fields to the `instance` one by one
set_instance_values(
__nested_serializers, __nested_serializers_data, instance
)
# Save the instance and return
instance.save()
return instance