自动为Django模型中的每个选项添加常量

5 投票
3 回答
1657 浏览
提问于 2025-04-16 12:21

我在使用Django的时候,总是会做以下这种操作:

class MyModel(models.Model):

    FOO = 1
    BAR = 2
    GOO = 3

    BLAH_TYPES = (
        (FOO, 'Foodally boogaly'),
        (BAR, 'Bar bar bar bar'),
        (GOO, 'Goo goo gaa gaa'),
    )

    TYPE_FOR_ID = dict(BLAH_TYPES)

    ID_FOR_TYPE = dict(zip(TYPE_FOR_ID.values(), TYPE_FOR_ID.keys()))

    blah = models.IntegerField(choices=BLAH_TYPES)

有没有其他人用的好方法,可以达到同样的效果(也就是说,我可以访问带名字的常量和可以双向使用的字典),而且代码量更少呢?

3 个回答

0

我也遇到过同样的问题,所以我写了这个:

from django.db import models
from django.db.models.base import ModelBase
import re

class ModelBaseWithChoices(ModelBase):
    def __new__(cls, name, bases, attrs):
        def format_label(label):
            return re.sub('[^A-Z]+', '_', label.upper()).strip('_')

        def get_choices(attrs):
            for attr, value in attrs.items():
                if attr.endswith('_CHOICES') and isinstance(value, tuple):
                    base = '_'.join(attr.split('_')[:-1])
                    for val, label in value:
                        yield '_'.join((base, format_label(label))), val

        attrs.update(list(get_choices(attrs)))
        return super(ModelBaseWithChoices, cls).__new__(cls, name, bases, attrs)

class ModelWithChoices(models.Model):
    __metaclass__ = ModelBaseWithChoices

    class Meta:
        abstract = True

接下来,你可以把 MyModel 改写成:

class MyModel(ModelWithChoices):
    BLAH_CHOICES = ((1, 'Foodally boogaly'),
                    (2, 'Bar bar bar bar'),
                    (3, 'Goo goo gaa gaa'))

    blah = models.IntegerField(choices = BLAH_CHOICES)

这样一来,模型中的所有常量就会自动生成。你可以在 Django 的命令行工具里这样做:

>>> MyModel.BLAH_FOODALLY_BOOGALY
1
>>> MyModel.BLAH_GOO_GOO_GAA_GAA
3
0

这里讲的是如何以最简洁的方式来初始化常量:

# foobar choices constants
FOO, BAR, BAZ = range(3) 

# bebop choices constants (if you prefer this to readability)
(BUU,
BAA,
DII,
DUU) = range(4)

# then, in a galaxy far away, much to your expectations:
>> FOO
0
>> DII
2

通常情况下,我会创建一个叫做 const.py 的文件,把所有的常量都放在里面(我现在看到的一个文件就有98行),但最好把这些常量从模型类中移出来,放到模块的数据部分,这样可以节省时间和内存,除非你在做一些动态的事情。

否则,你的做法已经算是相当不错了。我还会创建一个 BLAH_TYPES,这样可以符合Django的选择结构,这样每个开发者都能很容易理解。这部分是无法避免的,也不能省略。而且,如果我需要一些转换,比如 ID_FOR_TYPE,我就会把它们定义在选择项的下面。这样做既清晰又简洁。

5

Carl Meyer的django-model-utils库提供了一个很棒的解决方案——Choices类。这个类让你可以声明一个选择列表,并通过易于理解的属性来访问这些选择。

撰写回答