Python 向django模型动态添加属性
我有一个Django模型,其中有很多字段是可选择的。所以我必须编写很多类的“is_something”属性来检查实例值是否等于某个选择值。大致如下: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
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)