【问题标题】:class for model choices模型选择类
【发布时间】:2016-07-04 03:16:16
【问题描述】:

如何在 Django 中使用类来选择模型?

这是我想要的:

class ChoicesCls(object):
    def __init__(self):
        self.OPTION_ONE = 'O1'
        self.OPTION_ONE_SHORT = 'Opt 1'
        self.OPTION_ONE_LONG = 'Option 1'
        self.OPTION_TWO = 'O2'
        self.OPTION_TWO_SHORT = 'Opt 2'
        self.OPTION_TWO_LONG = 'Option 2'

    def get_choices(self):
        return (
            (self.OPTION_ONE, self.OPTION_ONE_LONG),
            (self.OPTION_TWO, self.OPTION_TWO_LONG)
        )


class TestModel(models.Model):
    choices_obj = ChoicesCls()
    field = models.CharField(choices=choices_obj.get_choices)

我想要这样的主要原因是在另一个地方我可以做这样的事情

option = "ONE"
getattr(TestModel.choices_obj, "OPTION_{}_SHORT".format(option))

【问题讨论】:

  • 我不确定您要解决什么问题。您的 getattr() 示例不起作用,因为您试图从元组中获取实例属性,而不是从原始选择类。也就是说,只要格式正确,Django 并不关心您如何生成选择元组。
  • @MadWombat 我正在尝试getattr 上的ChoicesCls 而不是元组。
  • 在您发布的代码中 TestModel.choices 是 get_choices() 方法的结果,而不是 ChoicesCls() 实例。
  • 也就是说,没有什么能阻止您定义自己的类并使其表现得像 Django 所期望的元组元组。
  • 我看不到你从哪里得到的,它是ChoicesCls 的一个实例。见choices = ChoicesCls()

标签: python django python-2.7 django-1.8


【解决方案1】:

试试这样的

FakeChoices 类: def __init__(self): self.OPTION_ONE = 'O1' self.OPTION_ONE_SHORT = '选择 1' self.OPTION_ONE_LONG = '选项 1' self.OPTION_TWO = 'O2' self.OPTION_TWO_SHORT = '选择 2' self.OPTION_TWO_LONG = '选项 2' def __get_tuple(self): 返回 ( (self.OPTION_ONE, self.OPTION_ONE_LONG), (self.OPTION_TWO,self.OPTION_TWO_LONG) ) def __getitem__(self, key): 返回 self.__get_tuple()[key] def __iter__(self): 返回迭代器(self.__get_tuple()) ... 类测试模型(模型。模型): 字段 = models.CharField(choices=FakeChoices())

正如您可能想象的那样,您可以将各种疯狂的逻辑放入 FakeChoices 类中。你应该能够做到

选项=“一个” getattr(TestModel._meta.get_field('field').choices, "OPTION_{}_SHORT".format(option))

【讨论】:

  • 我编辑了我的答案以合并您的 getattr() 示例
  • 我收到错误AttributeError: 'Creator' object has no attribute 'choices'
  • 看起来您无法直接访问模型类中的字段。一秒钟,我会更新答案。
  • 我将实例化另一个需要执行 getattr 的类实例。可以正常工作:)
  • 嗯,不,因为您需要实际的字段定义而不是字段值。我刚刚更新了 getattr() 代码以正确访问选项
【解决方案2】:

主要问题是你试图在类实例之外初始化一些东西,所以你可以这样做

field = models.CharField(choices=ChoicesCls().get_choices())

但这仍然不是很有用(在我看来)

我发现从模块中导入一个元组列表并将其用作选择会更好,不需要为此生成一个类。

这有一个额外的好处,它允许你做你想做的第二件事,因为你也在模块级别定义了选项

OPTION_ONE_WITH_A_GOOD_NAME = '1'
OPTION_TWO_WITH_A_GOOD_NAME = '2'

MY_GOOD_NAME_CHOICES = [(OPTION_ONE_WITH_A_GOOD_NAME,OPTION_ONE_WITH_A_GOOD_NAME),
                        (OPTION_TWO_WITH_A_GOOD_NAME,OPTION_TWO_WITH_A_GOOD_NAME)]

【讨论】:

  • 我可以在模块上做getattr 吗?如果是这样,你可能是对的,那会更好。但我需要的不仅仅是一个普通的旧元组,因为我需要更多信息。
  • @electrometro - 你根本不需要getattr,只需导入你要比较的选项
【解决方案3】:

要扩展Mad Wombat's answer,您可以创建一个自定义字段:

class ChoicesCharField(CharField):
    def contribute_to_class(self, cls, name, virtual_only=False):
        super().contribute_to_class(cls, name, virtual_only)

        def get_FOO_display_short(s):
            value = getattr(s, self.name)
            return self.choices.get_short_name(value)
        get_FOO_display_short.__name__ = 'get_{}_display_short'.format(self.name)
        get_FOO_display_short.short_description = self.verbose_name
        get_FOO_display_short.admin_order_field = self.name

        setattr(cls, get_FOO_display_short.__name__, get_FOO_display_short)

这样您就可以使用my_model_instance.get_field_display_short(),就像您通常使用my_model_instance.get_field_display() 一样。

【讨论】:

  • 很有趣,我实际上已经为这个特定的字段使用了一个自定义字段,但还有别的用途。但我真的想在这里争取可读性和可维护性,所以我会选择简单的答案。
  • 这不会阻止您将contribute_to_class 方法添加到您的自定义字段,或者创建一个扩展您的自定义字段而不是CharField 的自定义字段。 :)
  • 你就在那儿,但我觉得如果另一个开发人员想要找到这一切的功能,在现场寻找可能不是最好的地方。只是在我们需要 getattr 的地方实例化 FakeChoices 类的另一个实例要清楚得多。
猜你喜欢
  • 2020-06-17
  • 2018-08-23
  • 2018-10-25
  • 2016-01-07
  • 2016-05-31
  • 1970-01-01
  • 2017-03-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多