Django - 以一对多关系更改相关对象的值
Posted
技术标签:
【中文标题】Django - 以一对多关系更改相关对象的值【英文标题】:Django - altering the values on related objects in a one-to-many relationship 【发布时间】:2016-05-23 20:28:58 【问题描述】:我有一个代表财务模型的 Django 模型。该模型与另一组也包含财务数据的模型具有一对多的关系。在我的数据库中,我以美元 ($) 存储值,但是当我开始使用模型时,我希望能够在欧元和英镑之间进行转换。
因此,我在我的模型中添加了一个名为 convert_to_ccy(rate) 的方法,如果我调用它,我希望它也转换相关模型(通过这种一对多关系)。这是一个非常简单的例子来说明我想要做什么。
class main_model_example(models.Model):
INCLUDE_IN_CCY_CONVERSION = ['value_field_1', 'value_field_2' etc...]
# FIELD DEFINITIONS
date = models.DateField()
value_field_1 = models.DecimalField(max_digits=20, decimal_places=2, default=0.00)
value_field_2 = models.DecimalField(max_digits=20, decimal_places=2, default=0.00)
...etc...
# METHODS
def convert_to_ccy(self, rate):
"""Converts $ fields"""
for field in self._meta.get_all_field_names():
if field in self.INCLUDE_IN_CCY_CONVERSION:
value = getattr(self, field)
if value != None:
setattr(self, field, float(value) / rate)
# NOW CONVERT THE RELATED MODELS
for position in self.the_related_model_set.all():
position.convert_to_ccy_rate(rate)
# THE RELATED MODEL
class the_related_model(models.Model):
INCLUDE_CCY_CONVERSION = ['yet_another_finacial_field']
main_model_example = models.ForeignKey(main_model_example, on_delete=models.CASCADE)
yet_another_financial_field = models.DecimalField(max_digits=20, decimal_places=2, default=0.00)
...and so on....
def convert_to_ccy(self, rate):
"""Converts $ fields"""
for field in self._meta.get_all_field_names():
if field in self.INCLUDE_IN_CCY_CONVERSION:
value = getattr(self, field)
if value != None:
setattr(self, field, float(value) / rate)
这有效,并转换正确的字段。在我的代码(views.py 等)中,我传递了保留我设置的新值(即欧元)的主模型。
但是,当我再次从相关模型中获取信息时(通过使用 model.the_related_model_set.all() 的主模型,值又恢复为美元。我认为这是因为 xxx_set.all() 方法再次从数据库中检索模型(尚未更新)。我想要的是一旦我更新了相关模型,让它们保留在内存中,所以当我再次使用它们时,保留我设置的更新值. 我已经阅读了文档并浏览了问题,但似乎找不到任何类似的东西。
所以我有两个问题:
-
我想要实现的目标可能吗?即更新内存中的相关(一对多)模型,传递主模型(进行更多计算),然后当我稍后再次引用相关模型时取回更新的值。
或者我是否需要在偏移处从主模型中取出所有相关模型并将它们分配给我必须与主模型一起传递的变量?
-
我这样做是否被认为是坏事? IE。让主要模型方法调用相关模型方法?我对我的实施并不太满意,这通常表明有更好的方法。
任何关于实现这一目标的更好方法的建议将不胜感激。
谢谢。
【问题讨论】:
不确定错误,但您不想以各自的货币保持价值,并且只在您想向用户显示时进行转换吗?如果您今天有一个欧元的项目并将其转换为美元,那么明天当美元的价值发生变化时,您的数据库信息不正确。 是的,好问题。对不起,如果我不清楚。所以我只在向用户显示时才进行转换 - 例如,当用户单击“查看报告”时,我将值从数据库中提取出来(以美元为单位,均已预先计算),获取当前汇率,转换为欧元,然后显示给用户(通过我标记的几个额外计算)。该数据库仅以美元填充。现在,我可以编写一个自定义过滤器来在模板端进行转换,但是由于我在报告中显示的字段数量(几十个和几十个),我觉得在视图端这样做会更好。 值得一提的是,当我进行货币转换时,我不希望它传播到数据库。 【参考方案1】:很抱歉,我很难效仿你的榜样;虽然我认为这就是你想要的。
django Queriest 有一个 .update() 方法,可以将新属性传递给更新,并且值将保持不变。
my_django_class.objects.all().update(my_class_attr=new_value)
要获取查询集,您可以在查询中使用相关名称。 Django 默认生成它们,但我发现明确定义它们会更好。这个This stack question addresses them well。
这样,您的方法将如下所示(如果我没看错的话):
def _update_currancies(self):
Some_other_model(_the_related_name__field = main_model).update(attr = new_value)
我发现当请求被触发时最好不要更新表。通常我只会添加另外两个字段,并且每次输入值发生变化时(在这种情况下为美元),使用方法来更新其他字段(可以通过覆盖 save 方法来完成)。
对于你的情况,它看起来像这样:
class main_model_example(models.Model):
us_number = models.DecimalField(max_digits=20, decimal_places=2, default=0.00)
eur_number = models.DecimalField(max_digits=20, decimal_places=2, default=0.00)
ccy_number = models.DecimalField(max_digits=20, decimal_places=2, default=0.00)
def _update_currancies(self):
#update and set new currency values
#e.g. self.eur_number = self.us_number * .96
def save(self, *args, **kwargs):
self._update_currancies()
super(main_model_example, self).save(*args, **kwargs)
【讨论】:
嗨尼克。感谢您的回答。如果您发现很难遵循我的示例,那么只会加强这样一个事实,即我可能做错了/使事情变得过于复杂。我理解您的回复,但问题是当我从美元转换为另一种货币时,我不想保留更改。转换为另一种货币只是为了我呈现给用户的最终报告。所以我想要做的就是更新主模型实例和相关模型上的值(通过一对多关系),因为我将它从视图传递到模板,但不触及底层数据。 我明白了。在这种情况下。我会使用 django-Tastypie。它是一个常用于为 django 项目等编写 API 的库。您可以为您的模型覆盖资源的脱水方法以动态生成新货币,而无需接触您的实际数据库。以上是关于Django - 以一对多关系更改相关对象的值的主要内容,如果未能解决你的问题,请参考以下文章