Django 新手在模型和可重用业务逻辑方面困难重重
我是一名 .net 开发者,之前一直在使用 asp.net mvc 框架,现在想尝试进入 Python 的世界,特别是想用 Django 编写一些网站,但我对 Django 的结构感到很困惑。
在 asp.net mvc 中,我把 asp.net mvc 框架当作一个展示层,我的业务逻辑和数据层是独立的。这是我所有 asp.net mvc 网站的基本结构:
这个网站是一个 asp.net mvc 项目,而业务逻辑和数据类型的项目是类库(dll 文件)。在业务逻辑项目中,我使用一个实体框架模型(它生成的所有类),并创建一些我称之为“管理器”的类,这些类包含系统的核心逻辑。例如,如果这个网站是一个博客网站,“UsersMgr” 会包含“注册用户、获取用户、确认用户账户”等操作,而“PostsMgr” 会包含“添加帖子、删除帖子、编辑帖子、给帖子添加评论”等操作。
这种方法的想法是,网站只是一个展示层,它使用业务逻辑,但并不是紧密耦合在一起。我通常会添加一个“管理员控制台”(另一个 asp.net mvc 网站给管理员使用)、一些合作伙伴的“报告”(比如想象一下这个博客有广告,我们给合作伙伴一个登录,让他们可以更新广告并查看广告展示次数的报告等)、一个“REST API”,这样如果我想创建一个移动应用,就可以通过 REST API 暴露业务逻辑,还有定时任务来运行维护任务或每天结束时发送报告到我的邮箱等。
在我之前的 .net 项目中,添加这些新东西的方式是这样的:
基本上,所有这些新添加的项目都是业务逻辑的“消费者”。
我很难理解如何在 Django 中实现类似的功能,因为 web 应用似乎与它们的模型紧密耦合,而这些模型是数据库表的映射。那么如果我想在一个非 Django 网站中重用业务逻辑,我应该把它放在哪里呢?
关于 Django 应用,我到处都看到说把网站拆分成 Django 应用是个好主意,但当概念通常是耦合在一起的时候,应该怎么做呢?例如,一个博客可能有用户、帖子、评论、标签等。我的问题是,这一切都是相互关联的,帖子属于某个用户,标签和评论与帖子相关联。你会怎么处理这些关系呢?
如果有人能帮我解决这个问题,我会非常感激。我为我的树莓派写了一些 Python 脚本,觉得这个语言真不错,但我在 Django 上遇到了困难。肯定有我遗漏的东西……
谢谢!
2 个回答
我看到很多开发者对Django有相同的疑问。其实,Django对MVC的理解有点不同。Django的ORM(对象关系映射)把和数据库的连接给抽象化了,让模型来处理业务逻辑。虽然Django允许你自己管理数据库结构,使用触发器、进程等等,但Django也能在应用层面上完成这些事情,这样你的项目就完全不依赖于具体的数据库了。
Django还提倡使用“胖模型”和管理器。Django中的管理器在某种程度上可以替代MVC中的控制器,因为它们的工作是处理模型实例的集合。不过,你可以扩展模型管理器,以适应你习惯的指令。
数据库独立性对某些人来说是个很大的好处,但这也意味着你的数据模型是和Django项目绑定在一起的,可能在其他项目中用处不大。不过,嘿,这就是接口的用处所在!
Django内置了一个管理应用,定制起来非常简单,可以根据你的需求进行调整。
说到接口,Django的视图就是这样的一个接口。它并不告诉你应该如何展示数据。Django有一个非常强大的模板框架,但这其实是可选的。如果你想实现一个REST接口,Django REST Framework或Django Tastypie可以在几个小时内搞定,因为你的业务逻辑已经用一种通用的语言定义好了,这些应用都能很好地利用这些逻辑并美观地展示出来。
接下来谈谈应用的部分。可以把你的项目想象成一些功能模块:用户、注册、博客、评论、标签管理。用户可以有用户、个人资料和组模型,注册不需要模型,博客有帖子和分类模型,标签管理则处理标签模型。
应用就是一些打包好的功能,它们有模型、视图、网址等等,能够一起工作是没问题的。你可以在应用之间建立关系(比如博客帖子有很多标签)。当然,如果你删除了标签应用,博客可能会出问题,所以要提前考虑这些事情(也许是标签有很多博客帖子)。
如果你还有其他问题,可以在评论里问我,我会尽量解释得清楚一些。
视图和模型
模型就像是你数据类型的部分,而视图则是网站的界面,通常会包含你“管理者”需要的代码。
我猜“业务逻辑”指的是一些规则,比如“只有符合特定条件的用户才能评论帖子”。你说把这些逻辑直接放在视图里不太好是对的,因为如果用户通过其他方式(比如本地运行的命令行工具)访问系统,那么你就得在那儿重复这些逻辑。
解决办法是把业务逻辑加到模型里,通过在模型类中添加自定义函数,这些函数相当于你管理者的“GetX”、“GetY”,并在模型的保存钩子中加入“这个用户能否做这个”的逻辑。
你完全可以在视图和模型之间创建一个管理层,比如:
models.py:
class Post(models.Model):
...
class Comment(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
text = models.CharField(...)
managers.py:
class PostManager(object):
@staticmethod
def getPost(id):
return Post.object.get(pk=id)
@staticmethod
def addComment(post, comment_text, user):
comment = Comment(post=post, user=user, text=comment_text)
# check stuff
comment.save()
...
views.py:
def add_comment(request, post_id):
...
PostManager.addComment(post=PostManager.getPost(post_id),
user=request.user,
request.POST['text'])
...
不过这样做有几个缺点:
- 模型实例的API仍然可以绕过这些检查
- 你会失去模型API的表达能力
- 代码会显得有点冗长
主要的好处是,你可以轻松地把整个Django模型替换成其他的ORM(对象关系映射)。但如果你不太可能这样做,那就不建议走这条路。
Django应用
把东西分成不同的应用是个好主意。应用应该根据功能进行划分,分开你整个项目中不同的部分。
在你的例子中(博客、帖子、评论、用户),其实你有两个应用。一个是处理用户的(登录/登出、注册、密码重置等),另一个是处理博客的(发帖、评论等)。
博客应用依赖于用户应用并不是坏事,利用用户应用提供的现有功能在博客应用中是合理的。
从长远来看,这样做可以方便重用(你可能还有其他项目需要用户功能,但不需要博客功能)。