【问题标题】:How to setup a 3-tier web application project如何设置 3 层 Web 应用程序项目
【发布时间】:2013-09-19 08:58:13
【问题描述】:

编辑:

我添加了 [MVC] 和 [design-patterns] 标签来扩大这个问题的受众,因为它更像是一个通用编程问题,而不是与 Python 或 SQLalchemy 直接相关的问题。它适用于所有具有业务逻辑和 ORM 的应用程序。
基本问题是将业务逻辑保留在单独的模块中,还是将其添加到我们的 ORM 提供的类中更好:

我们有一个烧瓶/sqlalchemy 项目,我们必须为它设置一个结构来工作。关于如何设置有两种有效的意见,在项目真正开始之前,我们想下定决心在其中一个上。
如果你们中的任何人能给我们一些见解,说明两者中哪一个更有意义,为什么,以及优点/缺点是什么,将不胜感激。


我的示例是需要批量发送和/或显示给单个用户的 HTML 信件。这封信可以包含显示发票和/或收件人的用户的文章列表的部分。


方法一:
将代码分成 3 层 - 第 1 层:Web 界面,第 2 层:处理字母,第 3 层:来自 ORM (sqlalchemy) 的模型。
该网站将在第二层的一个类中调用服务器端方法,第二层将遍历需要获取此信函的用户,并且它将具有生成 HTML 并替换信函中的一些通用字段的内部方法,当前用户的信息。它还具有生成发票或要放在信中的文章列表的内部方法。

在这个方法中,第 3 层仅用于从数据库中获取数据,可能还有一些与数据库相关的逻辑,例如从用户的名字和姓氏生成全名。第二层执行大部分工作

方法二: 将代码拆分为相同的三层,但仅通过第二层中的用户集合执行循环。

生成 HTML、发票和文章列表的方法都作为方法添加到 ORM 提供的第 3 层中的模型定义中。第二层执行循环,但实际功能包含在第三层的模型类中

我们得出的结论是,这两种方法都行得通,并且各有利弊:

方法一:

  • 将业务逻辑与数据库访问完全分离
  • 防止导入 ORM 模型同时导入许多我们可能不需要的方法/功能,同时使模型类的代码更紧凑。
  • 在模拟 ORM 模型进行测试时可能更易于使用

方法二:

  • 似乎符合 Django 在 Python 中做事的方式
  • 允许对方法的简单访问:当模型实例存在时,它的任何函数 perform 可以立即被调用。 (在我的例子中:当我有一个可用的字母实例时,我可以直接在其上调用一个方法来生成该字母的 HTML)
  • 您可以传递实例,手头有所有适当的方法。

【问题讨论】:

    标签: python design-patterns model-view-controller orm sqlalchemy


    【解决方案1】:

    通常,您使用MVC 模式来处理这类东西,但大多数python 中的Web 框架都删除了“控制器”部分,因为他们认为这是一个不必要的组件。在我的发展过程中,我意识到这在某种程度上是正确的:没有它我也可以生活。这将给您留下两层:视图和模型。

    问题是现在将业务逻辑放在哪里。在实际意义上,有两种方法可以做到这一点,至少有两种方法让我遇到了将逻辑放在哪里的问题:

    • 创建处理逻辑的特殊内部视图方法,这可能在多个视图中都需要,例如_process_list_data
    • 创建与模型相关但不直接绑定到相应模型模块内的单个实例的函数,例如check_login

    详细说明:我将第一个用于严格与显示相关的方法,即它们以某种方式关注处理数据以用于显示目的。我上面的例子,_process_list_data 存在于视图类中(按目的对方法进行分组),但也可以是模块中的普通函数。它接收一些参数,例如数据列表并以某种方式对其进行格式化(例如,它可能会添加额外的视图参数,以便模板可以减少逻辑)。然后它将数据集返回给原始视图函数,该函数可以将其传递或进一步处理。

    第二个用于大多数其他逻辑,我不想直接查看代码以便于测试。我的check_login 示例是这样做的:它是一个不直接与显示输出相关联的函数,因为它的目的是检查用户登录凭据并决定返回用户或报告登录失败(通过抛出异常,返回False 或返回 None)。但是,此功能也不直接与模型相关联,因此它不能存在于 ORM 类中(它可以是 User 对象的 staticmethod)。相反,它只是模块内的一个函数(请记住,这是 Python,您应该使用最简单的方法,并且函数是用来做某事的)

    总结一下:在视图中显示逻辑,模型中的所有其他内容,因为大多数逻辑都以某种方式与特定模型相关联。如果不是,则为这种逻辑创建一个新模块或包。这可能是一个单独的模块,甚至是一个包。例如,我经常为辅助函数创建一个util 模块/包,这些函数不直接与任何视图、模型或其他函数相关联,例如一个格式化日期的函数,该函数从模板调用但包含这么多 python在模板中定义会很丑。

    现在我们将这个逻辑带到您的任务中:处理/创建字母。由于我不知道具体需要做什么处理,所以只能根据自己的假设给出一般性的建议。

    假设您有一些数据并想将其放入一封信中。例如,您有一个articles 和一个costumer 的列表,他们购买了这些文章。在这种情况下,您已经拥有数据。在将其传递给模板之前,唯一可能需要做的就是以模板可以轻松使用它的方式重新格式化它。例如,可能希望订购购买的物品,例如按数量、价格或物品编号。这是独立于模型的东西,订单现在只与显示相关(您可以在数据库查询中指定订单,但假设您没有)。在这种情况下,这是您的视图将执行的操作,因此您的模板已准备好格式化数据以显示。

    现在假设您想要获取数据来创建一个特定的字母,例如用户随时间购买的文章列表,以及购买日期和其他详细信息。这将是模型的工作,例如创建一个查询,获取数据并确保它具有此特定任务所需的所有属性。

    假设在这两种情况下,您都需要检索产品的价格,并且该价格由基值和基于其他属性的一些百分比确定:这作为模型是有意义的方法,因为它在单个产品或订单实例上运行。然后,您将模型传递给模板并在其中调用 price 方法。但是您不妨以这样的方式重新格式化它,即调用已经在视图中进行,并且模板只获取元组或字典。这样可以更轻松地将相同的数据作为 API 传递出去(见下文),但它不一定是最简单/最好的方式。

    这个决定的一个好规则是问自己如果我要在标准视图之外提供 JSON API,我需要如何修改我的代码以尽可能 DRY?。如果一开始理论还不够,请为模板构建一些 API,然后查看需要在哪些地方更改 API,这在视图本身旁边是有意义的。您可能永远不会使用此 API,因此它不需要完美,但它可以帮助您弄清楚如何构建代码。但是,正如您在上面看到的,这并不一定意味着您应该以只返回可以转换为 JSON 的内容的方式对数据进行预处理,而是您可能希望为 API 制作一些 JSON 特定格式查看。

    所以我比我预期的要长一点,但我想为你提供一些例子,因为这是我开始并通过反复试验发现这些事情时错过的。

    【讨论】:

    • 感谢您详尽的回答,javex,但不幸的是,这让我一点也不明智。 MVC 是我们或多或少遵循的一种模式,但即使在一种模式中,逻辑也可以分为多个层。如果需要,没有理由将控制器或模型拆分为多个模块。最基本的问题是,在模型中包含所有逻辑是否更聪明,甚至是特定任务所需的逻辑,或者将任务特定代码与通用模型特定代码分开是否更聪明。
    猜你喜欢
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 2013-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多