Лучшие практики использования @property со значениями Enum в модели Django для сериализации DRF

Вопрос: Я ищу руководство по использованию @property в модели Django, особенно когда свойство возвращает значение Enum и должно быть представлено в сериализаторе Django REST Framework (DRF). Вот моя установка:

Я определил Enum, AccountingType, для представления возможных типов учета:

from enum import Enum

class AccountingType(Enum):
    ASSET = "Asset"
    LIABILITY = "Liability"
    UNKNOWN = "Unknown"

В моей модели счета я использую метод @property для определения типа_бухгалтерии на основе существующих полей:

# Account fields ...

@property
def accounting_type(self) -> AccountingType:
    """Return the accounting type for this account based on the account sub type."""
    if self.account_sub_type in constants.LIABILITY_SUB_TYPES:
        return AccountingType.LIABILITY

    if self.account_sub_type in constants.ASSET_SUB_TYPES:
        return AccountingType.ASSET

    return AccountingType.UNKNOWN

В представлениях Django я могу использовать это свойство напрямую без проблем. Например:

account = Account.objects.get(id=some_id)
if account.accounting_type == AccountingType.LIABILITY:
    print("This account is a liability.")

Проблема: При попытке выставить accounting_type в DRF, использование serializers.ReadOnlyField() не включает свойство в сериализованный вывод:

class AccountDetailSerializer(serializers.ModelSerializer):
    accounting_type = serializers.ReadOnlyField()

    class Meta:
        model = Account
        fields = ['accounting_type', 'account_id', ...]

Я обнаружил, что переход на serializers.SerializerMethodField() решает проблему, позволяя мне возвращать значение Enum в виде строки:

class AccountDetailSerializer(serializers.ModelSerializer):
    accounting_type = serializers.SerializerMethodField()

    class Meta:
        model = Account
        fields = ['accounting_type', 'account_id', ...]

    def get_accounting_type(self, obj):
        return obj.accounting_type.value  # Return the Enum value as a string

Вопросы:

  1. Есть ли причина, по которой serializers.ReadOnlyField() не работает с @property, когда он возвращает Enum? DRF по-разному обрабатывает поля @property в зависимости от возвращаемого типа?
  2. Является ли SerializerMethodField рекомендуемым подходом, когда свойство возвращает сложный тип, такой как Enum, который нуждается в специфической сериализации? Существуют ли лучшие практики для раскрытия значений Enum через свойства модели в DRF?

Любые соображения будут оценены по достоинству.

Есть ли причина, по которой serializers.ReadOnlyField() не работает с @property, когда он возвращает Enum?

Enum не может быть сериализован в JSON. Сделайте AccountingType сериализуемым в JSON, сделав его подклассом str, а также:

class AccountingType(str, Enum):
    ASSET = 'Asset'
    LIABILITY = 'Liability'
    UNKNOWN = 'Unknown'

, то достаточно работать с ReadOnlyField:

class AccountDetailSerializer(serializers.ModelSerializer):
    accounting_type = serializers.ReadOnlyField()

    # …

Альтернативно, мы можем просто получить .value из AccountingType:

class AccountDetailSerializer(serializers.ModelSerializer):
    accounting_type = serializers.ReadOnlyField(source='accountingtype.value')

    # …

Если вы часто работаете с Enum, вы можете создать пользовательское поле DRF для обработки сериализации Enum:

class EnumField(serializers.Field):
def to_representation(self, value):
    return value.value if isinstance(value, Enum) else value

def to_internal_value(self, data):
    raise NotImplementedError("EnumField is read-only.")

Случай использования:

class AccountDetailSerializer(serializers.ModelSerializer):
accounting_type = EnumField()

class Meta:
    model = Account
    fields = ['accounting_type', 'account_id', ...]
Вернуться на верх