向 Django 模型添加动态字段
Posted
技术标签:
【中文标题】向 Django 模型添加动态字段【英文标题】:Adding dynamic fields to Django models 【发布时间】:2012-06-05 08:39:21 【问题描述】:如何在模型上创建动态字段?
假设我正在编写一个与股票市场相关的应用程序。我在某一天进行了购买,稍后我想根据今天的价格检查收益(或损失)。我会有这样的模型:
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
我想做的是定义一个类似这样的模型:
class PurchaseGain(Purchase):
gain = models.DecimalField(max_digits=20, decimal_places=3)
class Meta:
proxy = True
所以我可以这样做:
todays_price = get_price_from_webservice(ticker)
for p in PurchaseGain.objects.get_purchase_gain(todays_price):
print '%s bought on %s for a gain of %s' % (p.ticker, p.date, p.gain)
其中 p.gain 是根据 get_purchase_gain 的输入动态计算的。我想使用模型而不只是即时构建字典,因为我想传递它并从实例中生成表单、保存更改等。
我尝试创建派生的 QuerySet,但这导致了循环依赖,因为 Purchase 需要了解 QuerySet(通过自定义管理器)并且 QuerySet 返回了一个迭代器,该迭代器需要实例化从 Purchase 派生的 PurchaseGain .
我有什么选择?
谢谢, 克雷格
【问题讨论】:
【参考方案1】:为什么不在模型中添加 gain() 方法?
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
def gain(self, todays_price=None):
if not todays_price:
todays_price = get_price_from_webservice(self.ticker)
result_gain = todays_price - self.price
return result_gain
然后你几乎可以做你想做的事:
for p in Purchase.objects.all():
print '%s bought on %s for a gain of %s' % (p.ticker, p.date, p.gain())
【讨论】:
您甚至可以使用@property
装饰器,因此您可以将其称为 p.gain
。
谢谢。那会起作用,但不会扩展。我可以为一个股票代码购买数百或数千次。我正在寻找一种可以预先计算并将其传递给查询的方法。尽管也许我可以只看一下缓存远程调用。我得考虑一下。
我不应该一醒来就反应过来。我没有这样做的原因是现在 HTTP 调用被硬编码到我的模型中,这使得单元测试几乎不可能。我想要一些东西,我可以依赖注入一个真正的 HTTP 客户端或一个模拟客户端进行测试。这就是我喜欢上面提出的模型的原因。
好的,我在增益方法中添加了一个可选的“new_price”参数,这将允许测试。我同意你上面的评论,如果你关心性能,你应该以某种方式考虑缓存。您仍然可以将缓存检索放在 gain() 方法中。【参考方案2】:
创建代理类让我很困惑。只需向购买添加属性,我就能完成我想要的。
class PurchaseQuerySet(QuerySet):
def __init__(self, *args, **kwargs):
super(PurchaseQuerySet, self).__init__(*args, **kwargs)
self.todays_price = None
def get_with_todays_price(self, todays_price):
self.todays_price = todays_price
cloned = self.all()
cloned.todays_price = todays_price
return cloned
def iterator(self):
for p in super(PurchaseQuerySet, self).iterator():
p.todays_price = self.todays_price
yield p
class PurchaseManager(models.Manager):
def get_query_set(self):
return PurchaseQuerySet(self.model)
def __getattr__(self, name)
return getattr(self.get_query_set(), name)
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
objects = PurchaseManager()
@property
def gain(self):
return self.todays_price - self.price
现在我可以做:
for p in Purchase.objects.filter(ticker=ticker).get_with_todays_price(100):
print p
print p.gain
【讨论】:
以上是关于向 Django 模型添加动态字段的主要内容,如果未能解决你的问题,请参考以下文章