如何在单个Django模型中存储任意类型的值?

3 投票
3 回答
578 浏览
提问于 2025-04-15 21:51

假设我有一些不确定数量的问题。例如:

  • 天空是蓝色的吗 [是/否]
  • 你出生的日期是什么 [日期]
  • π(圆周率)是多少 [3.14]
  • 什么是大整数 [100]

这些问题的答案类型各不相同,有的是布尔值(是/否),有的是日期,有的是浮点数,还有的是整数。在Django中,这些问题可以很方便地通过模型来处理。

class SkyModel(models.Model):
    question = models.CharField("Is the sky blue")
    answer = models.BooleanField(default=False)

class BirthModel(models.Model):
    question = models.CharField("What date were your born on")
    answer = models.DateTimeField(default=today)

class PiModel(models.Model)
    question = models.CharField("What is pi")
    answer = models.FloatField()

但是,这样就有一个明显的问题:每个问题都需要一个特定的模型。如果我想以后再添加一个问题,就必须修改数据库,这样很麻烦。所以我想要更高级一点的做法——如何设置一个模型,让答案的类型转换可以自动完成呢?

ANSWER_TYPES = (
    ('boolean', 'boolean'),
    ('date', 'date'),
    ('float', 'float'),
    ('int', 'int'),
    ('char', 'char'),
)

class Questions(models.model):
    question = models.CharField(()
    answer = models.CharField()
    answer_type = models.CharField(choices = ANSWER_TYPES)
    default = models.CharField()

理论上,这样做可以实现以下功能:

  • 当我构建视图时,我会查看答案的类型,确保只输入那个类型的值。
  • 但当我想要取出答案时,它会以答案类型指定的格式返回数据。例如,3.14会以浮点数的形式返回,而不是字符串。

我该如何实现这种自动转换?或者有没有人能建议一个更好的方法?

非常感谢!!

3 个回答

2

我建议你在你的 Questions 类里添加一个自定义的方法:

def get_converted_answer(self):
  if self.answer_type == 'int':
    return int(self.answer)
  if self.answer_type == 'bool':
    # ...
2

你只需要把答案存储为字符串就可以了。如果我们是在网上接收数据,你得到的输入本来就是字符串,所以把它存到数据库里作为字符串并不会失去精度。

另一种可能的做法是为每种可能的数据类型设置一列,这样这些列可以为空。

class Questions(models.model):
    question = models.CharField(()
    answer = models.CharField()
    answer_type = models.CharField(choices = ANSWER_TYPES)
    int_answer = models.IntegerField(null=True)
    bool_answer = models.NullBooleanField(null=True)
    ... etc. 

如果是我的话,我还是会选择用一个 CharField 来处理。

6

我最近遇到了一个关于可扩展用户设置的问题。我的解决办法是把类型存储在模型的一个 CharField 中,然后用一个获取器来进行类型转换,巧妙地使用了 __builtin__getattr。以下是我的代码(你可以根据自己的需要进行调整):

VALUE_TYPE_CHOICES = (
    ("unicode", "Unicode String"),
    ("int", "Integer"),
    ("bool", "Boolean"),
)

class Setting(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    type = models.CharField(max_length=50, choices=VALUE_TYPE_CHOICES)
    default_value = models.CharField(max_length=127)

def get_setting(user, setting_id):
    profile_setting = #get the user's specific setting value here, not relevant
    type = getattr(__builtin__, profile_setting.setting.type)
    if type is bool:
        return type(int(profile_setting.value))
    else:
        return type(profile_setting.value)

这里有一个小陷阱: bool('0') 实际上会返回 True,所以我选择先把它转换成 int,再转换成 bool。当然,还有其他方法可以实现这个,比如使用 ast 模块的 literal_eval 方法。总的来说,这个方法是有效的。

撰写回答