Python 向django模型动态添加属性

Python 向django模型动态添加属性,python,django,django-models,metaclass,Python,Django,Django Models,Metaclass,我有一个Django模型,其中有很多字段是可选择的。所以我必须编写很多类的“is_something”属性来检查实例值是否等于某个选择值。大致如下: class MyModel(models.Model): some_choicefield = models.IntegerField(choices=SOME_CHOICES) @property def is_some_value(self): return self.some_choicefield

我有一个Django模型,其中有很多字段是可选择的。所以我必须编写很多类的“is_something”属性来检查实例值是否等于某个选择值。大致如下:

class MyModel(models.Model):
    some_choicefield = models.IntegerField(choices=SOME_CHOICES)

    @property
    def is_some_value(self):
        return self.some_choicefield == SOME_CHOICES.SOME_CHOICE_VALUE

    # a lot of these...
为了实现自动化并节省大量冗余代码,我考虑在创建时使用一个函数来修补实例,该函数添加了一系列执行检查的方法。 代码如下所示(我假设有一个“normalize”函数,使选择的标签成为可用的函数名):


现在,这是可行的,但我觉得有更好的方法。可能是在类创建时(使用元类或使用新的方法)?你对此有什么想法/建议吗?

我不知道如何以你的方式做到这一点,但在这种情况下,我认为应该简单地创建一个新模型,保留你的选择,并将字段更改为ForeignKey。这更易于编码和管理


您可以在中找到许多基本级别的信息。在那里,有许多关于各种主题的链接。除此之外,我相信它只需要一点想象力,也许在开始时需要反复尝试。

我遇到了一个类似的问题,我需要在运行时编写大量属性,以便在更改模型字段时提供向后兼容性。有两种标准方法来处理此问题-

  • 首先是在模型中使用自定义元类,它继承了模型的默认元类
  • 第二,是使用类装饰器。类装饰器有时为元类提供了一种简单的替代方法,除非您必须在创建类之前做一些事情,在这种情况下,您必须使用元类

  • 我打赌你知道Django字段提供的选项将自动具有显示功能

    假设您有一个定义如下的字段:

    category = models.SmallIntegerField(choices=CHOICES)
    
    您只需调用名为
    get\u category\u display()
    的函数即可访问显示值。以下是此功能的Django源代码:

    因此,我们可以采用这种方法来实现
    动态设置属性的目标

    这是我的情景,与你的有点不同,但归根结底都是一样的:

    我有两个类,
    课程
    课程
    ,类
    课程
    有一个外键字段
    课程
    ,我想在类
    课程
    中添加一个属性名
    cached\u课程
    ,它将首先尝试从缓存中获取
    课程
    ,如果缓存未命中,则返回到数据库:

    以下是一个典型的解决方案:

    from django.db import models
    
    
    class Course(models.Model):
        # some fields
    
    
    class Lesson(models.Model):
        course = models.ForeignKey(Course)
    
        @property
        def cached_course(self):
            key = key_func()
            course = cache.get(key)
            if not course:
                course = get_model_from_db()
                cache.set(key, course)
            return course
    
    原来我有这么多的ForeignKey字段要缓存,下面的代码与Django get_FIELD_display特性的方法类似:

    from django.db import models
    from django.utils.functional import curry
    
    
    class CachedForeignKeyField(models.ForeignKey):
    
        def contribute_to_class(self, cls, name, **kwargs):
            super(models.ForeignKey, self).contribute_to_class(cls, name, **kwargs)
            setattr(cls, "cached_%s" % self.name,
                    property(curry(cls._cached_FIELD, field=self)))
    
    
    class BaseModel(models.Model):
    
        def _cached_FIELD(self, field):
            value = getattr(self, field.attname)
            Model = field.related_model
            return cache.get_model(Model, pk=value)
    
        class Meta:
            abstract = True
    
    
    class Course(BaseModel):
        # some fields
    
    
    class Lesson(BaseModel):
        course = CachedForeignKeyField(Course)
    
    通过自定义
    CachedForeignKeyField
    ,覆盖
    contribution\u to\u class
    方法,以及带有
    \u cached\u FIELD
    方法的
    BaseModel
    类,每个
    CachedForeignKeyField
    将自动具有相应的
    cached\u FIELD
    属性


    好得难以置信,好极了

    每个字段的选择都不同。您的意思是创建一个新模型,而不是创建包含选项的每个字段吗?就性能而言,这不是过分了吗?这种行为也可以建模,可能更容易。过度杀戮?是和否,这真的取决于很多事情。谢谢,这是一个有趣的方法,我可能应该读一下。你能给我指一下对这种模式有更深入解释的资源吗?每个类都是一个叫做元类的特殊类的实例。默认情况下,Python中的所有类都是类类型的实例。但是,您可以通过从类型继承ypur自己的元类来创建它。事实上,django模型已经使用了自定义元类。如果你想要一个代码示例,我现在无法访问我的笔记本电脑,我会在有机会时提供一个。嘿,我认为你的方法是正确的。你介意添加一个例子吗?@jsmedmar我现在不能这样做,请查看我的另一个答案,看看它是否有帮助我没有从你的answe中了解如何动态地将属性添加到我的模型对象中。。。
    from django.db import models
    from django.utils.functional import curry
    
    
    class CachedForeignKeyField(models.ForeignKey):
    
        def contribute_to_class(self, cls, name, **kwargs):
            super(models.ForeignKey, self).contribute_to_class(cls, name, **kwargs)
            setattr(cls, "cached_%s" % self.name,
                    property(curry(cls._cached_FIELD, field=self)))
    
    
    class BaseModel(models.Model):
    
        def _cached_FIELD(self, field):
            value = getattr(self, field.attname)
            Model = field.related_model
            return cache.get_model(Model, pk=value)
    
        class Meta:
            abstract = True
    
    
    class Course(BaseModel):
        # some fields
    
    
    class Lesson(BaseModel):
        course = CachedForeignKeyField(Course)