我有一个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"函数使选项的标签成为可用的函数名称):
def dynamic_add_checks(instance, field):
if hasattr(field, 'choices'):
choices = getattr(field, 'choices')
for (value,label) in choices:
def fun(instance):
return getattr(instance, field.name) == value
normalized_func_name = "is_%s_%s" % (field.name, normalize(label))
setattr(instance, normalized_func_name, fun(instance))
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
super(MyModel).__init__(*args, **kwargs)
dynamic_add_checks(self, self._meta.get_field('some_choicefield')
some_choicefield = models.IntegerField(choices=SOME_CHOICES)
现在,这有效,但我觉得有更好的方法。也许是在课堂创作时(使用元类或新方法)?你对此有什么想法/建议吗?
答案 0 :(得分:3)
嗯,我不确定如何以你的方式做到这一点,但在这种情况下,我认为要走的路是简单地创建一个新模型,在那里你保持选择,并将字段更改为ForeignKey。这更容易编码和管理。
您可以在Django docs: Models: Relationships的基本级别找到大量信息。在那里,有许多链接可以跟随各种主题进行扩展。 Beyong,我相信它只需要一点想象力,也许一开始就是反复试验。
答案 1 :(得分:3)
我遇到了类似的问题,我需要在运行时编写大量属性,以便在更改模型字段时提供向后兼容性。有两种标准方法可以解决这个问题 -
答案 2 :(得分:3)
我打赌你知道Django字段提供的选项会自动显示功能。
假设你有一个像这样定义的字段:
category = models.SmallIntegerField(choices=CHOICES)
您只需调用名为get_category_display()
的函数即可访问显示值。以下是此功能的Django源代码:
因此,我们可以按照这种方法来实现dynamically set property
目标。
这是我的情景,与你的情况略有不同,但到最后它是一样的:
我有两个类Course
和Lesson
,类Lesson
的ForeignKey字段为Course
,我想添加一个属性名称cached_course
到类Lesson
,它将尝试首先从缓存中获取Course
,并在缓存未命中时回退到数据库:
这是一个典型的解决方案:
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
,并使用contribute_to_class
方法覆盖BaseModel
方法和_cached_FIELD
类,每个CachedForeignKeyField
将自动拥有cached_FIELD
相应的1}}属性。
太好了,真实,勇敢!