2016-01-20 55 views
1

我有一个简单的自定义字段实现利用Python 3枚举实例。将枚举实例分配给我的模型属性,并保存到数据库可正常工作。但是,使用QuerySet获取模型实例会导致enum属性是一个字符串,而不是相应的Enum实例。自定义Django字段不返回来自查询的枚举实例

如何获取下面的EnumField以返回有效的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 

回答

1

大量挖掘后,我能回答我的问题:

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实例。

0

不是Django的人,但我可以看到两种可能性:

  • from_db_values不会被调用,所以你的字符串不是再造一个枚举
  • self._enum_class是一个{str: str}字典,而不是一个实际的Enum

尝试在一些print()log()调用喷洒以更好地了解什么是(不)发生。

噢,还有一种可能性是你在3.4之前运行Python,而没有使用enum34 backport