关于python:Django自定义模型字段:to_python()未调用

Django custom model fields: to_python() not called

我对python和django非常陌生,对堆栈溢出也非常陌生,所以我希望我不会打破任何规则,并且尊重问题的格式。

我在使用django(python 3.3.0,django 1.5a 1)实现自定义模型字段时遇到了一个问题,我找不到任何类似的主题,实际上我非常关注这个主题……

所以有一个球员,他有一手牌。这只手继承了cardcontainer,它基本上是一个包含一些(隐藏在这里)助手功能的卡片列表。以下是相应的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from django.db import models


class Card:
    def __init__(self, id):
        self.id = id


class CardContainer:
    def __init__(self, cards=None):
        if cards is None:
            cards = []
        self.cards = cards


class Hand(CardContainer):
    def __init__(self, cards=None):
        super(Hand, self).__init__(cards)


class CardContainerField(models.CommaSeparatedIntegerField):
    __metaclass__ = models.SubfieldBase

    def __init__(self, cls, *args, **kwargs):
        if not issubclass(cls, CardContainer):
            raise TypeError('{} is not a subclass of CardContainer'.format(cls))

        self.cls = cls
        kwargs['max_length'] = 10
        super(CardContainerField, self).__init__(*args, **kwargs)

    def to_python(self, value):
        if not value:
            return self.cls()

        if isinstance(value, self.cls):
            return value

        if isinstance(value, list):
            return self.cls([i if isinstance(i, Card) else Card(i) for i in value])

        # String: '1,2,3,...'
        return self.cls([Card(int(i)) for i in value.split(',')])

    def get_prep_value(self, value):
        if value is None:
            return ''

        return ','.join([str(card.id) for card in value.cards])


class Player(models.Model):
    hand = CardContainerField(Hand)

但是当我得到一个玩家时,我们可以这样说:Player.objects.get(id=3).hand,而不是得到Hand实例(甚至是CardContainer实例!),我只是得到一个逗号分隔的整数字符串,如"1,2,3",这在数据库中很好(这是我希望在数据库中看到的格式)。

在我看来,to_python没有被调用,所以返回的数据是原始值,因此是字符串。当我搜索这类问题时,人们错过了__metaclass__ = models.SubfieldBase。我希望我也能错过,但是,嘿,那太简单了!我错过了一些琐碎的事情,还是我对整个事情都错了?D

谢谢!!


在python 3中,不再支持模块global __metaclass__变量。必须使用:

1
2
class CardContainerField(models.CommaSeparatedIntegerField, metaclass=models.SubfieldBase):
   ...


适用于Django 1.10及最新版本

1
2
3
class CardContainerField(models.CommaSeparatedIntegerField):
    def from_db_value(self,value, expression, connection, context):
      .......