Django Rest Framework的主要部分是视图(ViewSets、ApiViews等)和序列化器(serializers),但这些都不是编写逻辑的理想场所。
正如你所提到的,在任何视图中编写逻辑都不好。为什么?
- 无法从应用程序的另一部分使用相同的逻辑
- 无法封装该逻辑(您需要调用该视图才能运行该逻辑)
- 无法对逻辑进行单元测试,因为它与视图耦合
在我看来,模型并不是编写逻辑的好地方。将模型视为数据库定义。我会尽可能保持它们简单。您可以重写“save”和其他方法来执行微不足道的任务。任何其他高级功能都应存储在其外部。
我可以想到两个更好的适合您需要的位置:
其中之一是Django信号(signal)
更好的一个是自定义类。将逻辑封装/解耦在您自己的类中(您可以使用静态或实例方法,没有关系),然后您将能够:
- 单元测试这些方法/模拟该类
- 在其他地方重用此逻辑
- 根据KISS原则简化您的代码
- 从信号中使用它,使信号代码简单
- 使用celery任务(如果您将来实施)而无需修改您的代码的任何一行
更新 代码组织示例。
从评论中可以清楚地看出,信号不起作用,因为分析操作将在用户请求时运行。如果该操作应在保存特定模型时自动运行,则可以使用信号。我假设您知道如何使用Django Rest Framework API视图或视图集、序列化器等。如果不知道,请提出另一个问题。这将更多是关于Python的解释。
在您的应用程序模块中,创建一个名为app_business_logic.py
或其他您想要命名的文件。例如,您可以将其放置在与models.py相同的级别上,但这不是必须的:
class HoldingsAnalyser:
@staticmethod
def run(holding_list):
return True
def run(self, holding_list):
return True
现在,在django-rest-framework的api视图或视图集中,创建一个POST方法,当客户端应用程序用户按下按钮(生成分析的按钮)时调用该方法:
from yourapp.app_business_logic import HoldingsAnalyser
class StockPortfolioViewSet(WhatEverMixingYouNeedToInheritFrom):
serializer_class = whatever
@detail_route(methods=['post'])
def run_analysis(self, request, pk):
stock_portfolio = get the object based on the pk
holdings = make your query to get the holdings
analysis_result = HoldingsAnalyser.run(holdings)
if analysis_result:
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response(a useful error for your client)
这个链接的URL应该类似于http://server.com/api_path/stockportfolio/21/run_analysis/
,其中的21是指股票组合的ID。