Best practices for using @property with Enum values on a Django model for DRF serialization

Question: I'm looking for guidance on using @property on a Django model, particularly when the property returns an Enum value and needs to be exposed in a Django REST Framework (DRF) serializer. Here’s my setup:

I’ve defined an Enum, AccountingType, to represent the possible accounting types:

from enum import Enum

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

On my Account model, I use a @property method to determine the accounting_type based on existing fields:

# 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

In Django views, I can use this property directly without issues. For example:

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

Problem: When trying to expose accounting_type in DRF, using serializers.ReadOnlyField() does not include the property in the serialized output:

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

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

I found that switching to serializers.SerializerMethodField() resolves the issue, allowing me to return the Enum value as a string:

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

Questions:

  1. Is there a reason serializers.ReadOnlyField() doesn’t work with @property when it returns an Enum? Does DRF handle @property fields differently based on the return type?
  2. Is SerializerMethodField the recommended approach when a property returns a complex type, like an Enum, that needs specific serialization? Are there best practices for exposing Enum values via model properties in DRF?

Any insights would be appreciated.

Is there a reason serializers.ReadOnlyField() doesn’t work with @property when it returns an Enum?

An Enum can not be JSON serialized. Make the AccountingType json serializable by making it a subclass of str as well:

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

then it is sufficient to work with a ReadOnlyField:

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

    # …

Alternatively, we can just obtain the .value from the AccountingType:

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

    # …
Вернуться на верх