Skip to content

Django Enum Type

冒菜略寡味 edited this page May 10, 2018 · 2 revisions

关于Django中的Enum类型

1. 官方Enum的实现

首先说明Django官方没有提供对Enum类型的支持,而是提供了一种CharField类型与Field.choices结合的间接方式实现Enum,如下:

from django.db import models

class Student(models.Model):
    FRESHMAN = 'FR'
    SOPHOMORE = 'SO'
    JUNIOR = 'JR'
    SENIOR = 'SR'
    YEAR_IN_SCHOOL_CHOICES = (
        (FRESHMAN, 'Freshman'),
        (SOPHOMORE, 'Sophomore'),
        (JUNIOR, 'Junior'),
        (SENIOR, 'Senior'),
    )
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

    def is_upperclass(self):
        return self.year_in_school in (self.JUNIOR, self.SENIOR)

这种方式如果在没有引入DRF之前,且不考虑接口枚举类型传参的可读性前提下,尚且可行; 但我们引入DRF之后,发现这种实现方式完全没办法容忍,接口参数层面无法直接使用枚举类型; 既然官方不支持,于是就转而考虑使用第三方提供的解决方式;

2. 第三方的实现方式

其实可供选择且靠谱的第三方实现并不多,最终仅锁定了两个:

对比了一下,最终采用了第二个,第一个感觉没人维护了,呃...

具体的使用步骤可以看一下:ChoiceField should serialize the human-readable form in to_native #1755

上面这位hacknaked老兄写了大致步骤,我抄了一遍,如下:

models.py

from enum import Enum
from django.db import models
from enumfields import EnumIntegerField

class GeoModel(models.Model):

    class LocationType(Enum):
        ROOFTOP = 1
        RANGE_INTERPOLATED = 2
        GEOMETRIC_CENTER = 3
        APPROXIMATE = 4
        UNRESOLVED = 5

    location_type = EnumIntegerField(
        enum=LocationType,
        default=LocationType.UNRESOLVED
    )
    ...

fields.py

from rest_framework import serializers

class EnumField(serializers.ChoiceField):
    def __init__(self, enum, **kwargs):
        self.enum = enum
        kwargs['choices'] = [(e.name, e.name) for e in enum]
        super(EnumField, self).__init__(**kwargs)

    def to_representation(self, obj):
        return obj.name

    def to_internal_value(self, data):
        try:
            return self.enum[data]
        except KeyError:
            self.fail('invalid_choice', input=data)

serializers.py

from rest_framework import serializers
from . import fields
from . import models

class GeoModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.GeoModel

    location_type = fields.EnumField(enum=models.GeoModel.LocationType)

大致就是酱紫,最后附上我的commit: 2c049a9beaf82dee3db21a32c1e4de33b2b45ad1

昨日擔當 昨日敢想

昨日轉眼 就跌撞

夏時夢長 秋時晝短

清冽途上 不遠望

Clone this wiki locally