简体   繁体   English

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

[英]Automatically add constants for each of the choices in a Django model

I'm constantly doing the following pattern in Django: 我一直在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)

Is there a good pattern that other people follow that achieves the same effect (ie I have access to constants with names and dictionaries that go both ways) without so much code? 在没有太多代码的情况下,是否有其他人可以遵循的良好模式来实现相同的效果(即,我可以使用带有双向名称和字典的常量)?

卡尔·迈耶(Carl Meyer)的django-model-utils库为此提供了一个极好的解决方案-Choices类,该类使您可以声明选项列表,并可以通过人类可读的属性进行访问。

This is about the most compact way to initialize your constants: 这是初始化常量的最紧凑的方法:

# 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

In any case, I usually whip up a const.py and keep all the insanity in there (just looking at one that's 98 lines long), but definitely move them out of the model class and make them a part of the module data to conserve time and memory, unless your doing something dynamic. 无论如何,我通常会鞭打一个const.py并把所有的精神错乱保留在那里(只const.py长达98行的行),但是一定要将它们移出模型类,并使其成为模块数据的一部分以保存时间和记忆,除非您动态地做某事。

Otherwise, your doing it about as good as it can possibly get. 否则,您将尽其所能。 I also do a BLAH_TYPES , to conform to the Django choices structure every developer is accustomed to reading. 我还做了一个BLAH_TYPES ,以符合每个开发人员习惯阅读的Django选择结构。 That's to part you can't avoid and can't make any shortcuts. 那是您无法避免且无法做出任何捷径的部分。 And, if I need those transformations, such as ID_FOR_TYPE , I just define them right beneath the choices. 而且,如果我需要这些转换,例如ID_FOR_TYPE ,则只需在选择下方定义它们即可。 Readable, clean and compact. 可读,干净,紧凑。

I had the same itch, and that's what I wrote: 我有同样的痒,这就是我写的:

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

From there, you can rewrite MyModel as: 从那里,您可以将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)

And have all the constants automatically created for you in the model. 并在模型中为您自动创建所有常数。 From the Django shell: 在Django shell中:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM