自定义 Django 字段不 return 查询中的枚举实例
Custom Django field does not return Enum instances from query
我实现了一个简单的自定义字段以利用 Python 3 个枚举实例。将枚举实例分配给我的模型属性,并保存到数据库工作正常。但是,使用 QuerySet 获取模型实例会导致枚举属性成为字符串,而不是相应的枚举实例。
如何获取以下 EnumField
到 return 有效的 Enum
实例,而不是字符串?
fields.py:
from enum import Enum
from django.core.exceptions import ValidationError
from django.db import models
class EnumField(models.CharField):
description = 'Enum with strictly typed choices'
def __init__(self, enum_class, *args, **kwargs):
self._enum_class = enum_class
choices = []
for enum in self._enum_class:
title_case = enum.name.replace('_', ' ').title()
entry = (enum, title_case)
choices.append(entry)
kwargs['choices'] = choices
kwargs['blank'] = False # blank doesn't make sense for enum's
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
args.insert(0, self._enum_class)
del kwargs['choices']
return name, path, args, kwargs
def from_db_values(self, value, expression, connection, context):
return self.to_python(value)
def to_python(self, value):
if value is None or isinstance(value, self._enum_class):
return value
else:
return self._parse_enum(value)
def _parse_enum(self, value):
try:
enum = self._enum_class[value]
except KeyError:
raise ValidationError("Invalid type '{}' for {}".format(
value, self._enum_class))
else:
return enum
def get_prep_value(self, value):
if value is None:
return None
elif isinstance(value, Enum):
return value.name
else:
msg = "'{}' must have type {}".format(
value, self._enum_class.__name__)
if self.null:
msg += ', or `None`'
raise TypeError(msg)
def get_choices(self, **kwargs):
kwargs['include_blank'] = False # Blank is not a valid option
choices = super().get_choices(**kwargs)
return choices
经过大量挖掘,我能够回答我自己的问题:
SubfieldBase
已被弃用,并将在 Django 1.10 中删除;这就是为什么我将它排除在上面的实现之外的原因。但是,它所做的似乎仍然很重要。添加以下方法以替换 SubfieldBase
会添加的功能。
def contribute_to_class(self, cls, name, **kwargs):
super(EnumField, self).contribute_to_class(cls, name, **kwargs)
setattr(cls, self.name, Creator(self))
Creator
描述符是对属性调用 to_python
的内容。如果这没有发生,对模型的查询将导致模型实例中的 EnumField
字段只是字符串,而不是我想要的 Enum 实例。
我实现了一个简单的自定义字段以利用 Python 3 个枚举实例。将枚举实例分配给我的模型属性,并保存到数据库工作正常。但是,使用 QuerySet 获取模型实例会导致枚举属性成为字符串,而不是相应的枚举实例。
如何获取以下 EnumField
到 return 有效的 Enum
实例,而不是字符串?
fields.py:
from enum import Enum
from django.core.exceptions import ValidationError
from django.db import models
class EnumField(models.CharField):
description = 'Enum with strictly typed choices'
def __init__(self, enum_class, *args, **kwargs):
self._enum_class = enum_class
choices = []
for enum in self._enum_class:
title_case = enum.name.replace('_', ' ').title()
entry = (enum, title_case)
choices.append(entry)
kwargs['choices'] = choices
kwargs['blank'] = False # blank doesn't make sense for enum's
super().__init__(*args, **kwargs)
def deconstruct(self):
name, path, args, kwargs = super().deconstruct()
args.insert(0, self._enum_class)
del kwargs['choices']
return name, path, args, kwargs
def from_db_values(self, value, expression, connection, context):
return self.to_python(value)
def to_python(self, value):
if value is None or isinstance(value, self._enum_class):
return value
else:
return self._parse_enum(value)
def _parse_enum(self, value):
try:
enum = self._enum_class[value]
except KeyError:
raise ValidationError("Invalid type '{}' for {}".format(
value, self._enum_class))
else:
return enum
def get_prep_value(self, value):
if value is None:
return None
elif isinstance(value, Enum):
return value.name
else:
msg = "'{}' must have type {}".format(
value, self._enum_class.__name__)
if self.null:
msg += ', or `None`'
raise TypeError(msg)
def get_choices(self, **kwargs):
kwargs['include_blank'] = False # Blank is not a valid option
choices = super().get_choices(**kwargs)
return choices
经过大量挖掘,我能够回答我自己的问题:
SubfieldBase
已被弃用,并将在 Django 1.10 中删除;这就是为什么我将它排除在上面的实现之外的原因。但是,它所做的似乎仍然很重要。添加以下方法以替换 SubfieldBase
会添加的功能。
def contribute_to_class(self, cls, name, **kwargs):
super(EnumField, self).contribute_to_class(cls, name, **kwargs)
setattr(cls, self.name, Creator(self))
Creator
描述符是对属性调用 to_python
的内容。如果这没有发生,对模型的查询将导致模型实例中的 EnumField
字段只是字符串,而不是我想要的 Enum 实例。