模型方法中的业务逻辑在 Django Rest Framework 中去哪里了?

Posted

技术标签:

【中文标题】模型方法中的业务逻辑在 Django Rest Framework 中去哪里了?【英文标题】:Where does business logic in a model method go in Django Rest Framework? 【发布时间】:2016-06-01 04:32:14 【问题描述】:

我有 3 个模型,它们通过一对多的关系相互关联。

模型 A 可以有多个模型 B 的实例。 模型 A 可以有多个模型 C 的实例。 模型 B 可以有多个模型 C 的实例。

这个想法是,用户将创建模型 A 的实例(如股票投资组合),然后输入股票持有量(模型 C)。模型 B 适合的地方是,我想根据投资组合(模型 A)中的股票(模型 C)运行计算/逻辑,并使用另一个类/模型来跟踪事情让生活更轻松,因此模型 B。

我最初在 Django 视图中拥有这些计算的逻辑,但在 Two Scoops of Django 中读到业务逻辑应该与视图分离。结果,我将逻辑移至模型 A(投资组合)的方法,现在从视图中调用该方法。此逻辑循环遍历股票持有量并创建模型 B 的新实例,即结果。

我现在有兴趣探索 django-rest-framework 以向 javascript 前端(如 Angular)提供 API。我猜我将无法在我的 REST 接口中进行这种数据操作。但是,此逻辑的结果(模型 B 中的数据)需要通过 REST 可见。因此,这种类型的计算/逻辑到哪里去了?

【问题讨论】:

【参考方案1】:

Django Rest Framework 的主要部分是视图(ViewSets、ApiViews 等)和序列化程序。这些都不是编写逻辑的理想场所。 正如您所提到的,在任何视图中编写逻辑都不好。为什么?

    无法从应用的其他部分使用相同的逻辑 没办法封装逻辑(需要调用视图为 运行逻辑) 无法对逻辑进行单元测试,因为与视图耦合

恕我直言,模型不是编写逻辑的好地方。将模型视为您的数据库定义。我会让它们尽可能简单。您可以覆盖“保存”和其他方法来完成琐碎的任务。任何其他高级功能都应该在它之外。

我可以想到两个更好的地方来满足您的需求:

其中一个是django signal

更好的是自定义类。 在您自己的类中封装/解耦逻辑(您可以使用静态或实例方法,没关系),然后您将能够:

    对这些方法进行单元测试/模拟该类 在其他地方重用这个逻辑 根据KISS原则简化你的代码 在保持信号代码简单的信号中使用它 在 celery 任务中使用它(如果您将来实现它),而无需修改任何一行代码

更新如何组织代码的示例。

从 cmets 可以清楚地看出,信号不起作用,因为分析操作将根据用户请求运行。如果该操作应在保存特定模型时自动运行,则信号将很有用。

我假设您知道如何使用 django-rest-framework api 视图或 视图集,序列化程序等。我不知道,最好问 另一个问题。这将比其他任何东西都更像是一个 python 解释

在您的应用模块中,创建一个文件 app_business_logic.py 或任何您想调用的文件。例如,您可以将其放在 models.py 的同一级别,但这不是强制性的:

class HoldingsAnalyser:
    # static method sample. Call it like this: "HoldingsAnalyser.run(..)"
    @staticmethod
    def run(holding_list):
        # do your model generation here or whatever you need
        return True # or whatever you need to return

    # instance method sample. Create an instance first and then call the method: 
    # analyser = HoldingsAnalyser()
    # analyser.run(...)
    def run(self, holding_list):
        # do your model generation here or whatever you need
        return True # or whatever you need to return

现在,在 django-rest-framework api 视图或视图集中,创建一个 POST 方法,以便在客户端应用用户按下按钮(该按钮生成分析)时调用:

from yourapp.app_business_logic import HoldingsAnalyser

class StockPortfolioViewSet(WhatEverMixingYouNeedToInheritFrom):
    serializer_class = whatever # look at the docs
    
    @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:
            # everything ok
            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 是 StockPortfolio 的 id

【讨论】:

感谢@xleon 的回复。我仍然不确定所有这些(信号或自定义类)适合项目的位置。我设想用户将在他们的投资组合(模型 A)中查看他们的资产(模型 C)。然后,他们将能够通过单击“分析”按钮来运行分析(循环通过每个持有并创建模型 B 实例)。最终,所有 3 个模型都可以通过 REST API 获得。如果用户尚未运行分析,则模型 B 的 API 将为空。如果分析逻辑在信号或自定义类中,何时调用它来填充 Model B API? 如果您通过用户操作运行分析,则不能选择 django 信号(仅在保存其他模型时应该自动运行该分析时才推荐使用)。我会用一些伪代码为您的工作流程更新答案 哇。很好的例子。我一直想知道将业务逻辑放在 models.py 中。一些流行的出版物表明这是放置它的地方,但我一直对此表示怀疑。 确实如此,许多 django 专家开发人员建议将 models.py 作为放置该逻辑的地方。我就是不明白 这是一个很好的答案,在 django 中创建了几个项目后,我意识到模型是应该放置数据库特定逻辑的地方,而应该为您的业务逻辑创建自定义类。业务逻辑类始终可以使用模型类方法来实现其逻辑。这提供了关注点的分离。 Django docs 以及他们建议将其放在models.py 中的其他任何地方,但我相信那是针对特定于数据库的逻辑,而不是业务逻辑。

以上是关于模型方法中的业务逻辑在 Django Rest Framework 中去哪里了?的主要内容,如果未能解决你的问题,请参考以下文章

Django简介

Django:将业务逻辑与视图逻辑分离

在Django中实现具有许多重叠字段的模型的最佳方法?

Django进阶篇

Django 简介

Django Rest 框架中的自定义用户模型注册