我在Django Model.save()方法中传入什么数据?
假设我们在Django中收到了一个这样的表单提交:
rate=10
items= [23,12,31,52,83,34]
这些项目是一个名为Item的模型的主键。我有一些业务逻辑会根据这些数据、数据库查询的结果和一些业务逻辑来创建更多的项目。我想把这些逻辑放到一个保存信号或者另一个模型(我们称之为Inventory)的Model.save()
方法中。当我使用这个表单数据创建一个新的Inventory
对象时,这些业务逻辑就会运行。Inventory看起来会是这样的:
class Inventory(models.Model):
picked_items = models.ManyToManyField(Item, related_name="items_picked_set")
calculated_items = models.ManyToManyField(Item, related_name="items_calculated_set")
rate = models.DecimalField()
... other fields here ...
新的calculated_items
将根据传入的项目创建,并存储为picked_items
。
我的问题是:这个模型的save()
方法接受什么参数比较好:
- 请求对象(我不太喜欢这种耦合)
- 作为参数或关键字参数传入的表单数据(主键列表和其他表单字段)
- 一组Items(调用者的表单或视图会查找Items列表并创建一个列表,同时传入其他表单字段)
- 其他方法?
我知道这个问题有点主观,但我想知道一般的想法是什么。我看了很多代码,但很难找到我喜欢的模式。
澄清:
好的,大家的共识是应该把它放到模型的一个不同函数中,比如inventory.calculate(...)
,然后在这个函数中创建所有内容,执行业务逻辑等等……这很好。我的问题仍然是:在哪里查找表单数据对应的数据库对象比较好?这个函数的调用者应该把主键转换为数据库模型,还是模型方法应该接受主键并自己处理?这是我希望在整个项目中保持一致的做法。
澄清 2:
好的,现在关于是否可以重写save
方法有一些分歧。
当你收到一个简单的CRUD类型操作的表单提交时,你会将模型和数值作为参数传递给Model.objects.create(...)
,或者重写save
,或者使用信号等等。
我认为我问题的核心是:
如果表单提交中有用于业务逻辑的相关模型,那么你需要在模型层中写一些业务逻辑。当你这样做时,这些逻辑应该放在哪里?这个方法应该接受对象列表还是ID列表?模型的API应该接受对象还是ID?
1 个回答
好吧,我之前得到的前两个答案现在被其他人推翻了。我一直在研究这个问题,决定自己来试着回答一下。如果你觉得我的说法对,请投票支持;如果你不同意我的理由,请留言告诉我。
模型中的方法应该接受对象和对象列表,而不是像整数/长整型的ID或ID列表之类的东西。这是因为这些方法通常会从视图或表单中调用,而它们可以直接访问来自
cleaned_data
字典的完整对象。比如,管理类中的create()
方法就是Django本身接受对象的另一个例子。调用模型层方法的人应该先查找并将ID转换为完整的对象。
你可以重写
save()
方法,但如果这样做,要小心接受args
和**kwargs
。如果模型跨越多个应用程序,考虑使用信号而不是重写
save
。不要试图通过重写模型管理器的
create
方法来聪明地处理问题。如果视图层创建了一个新对象并保存,它是不会调用这个方法的。如果你需要在保存之前进行额外处理,可以重写save
或__init__
,或者捕获一个信号。如果你重写__init__
,可以检查主键(pk)来判断它是否已经存在于数据库中。
我打算把我的创建代码放在一个单独的方法中,直到我弄清楚最喜欢哪种技术。
我觉得这是为模型层添加方法的一套不错的指导原则。我还有什么遗漏的吗?