初学者:尝试理解Django中应用的互动方式
我刚刚完成了第二遍Django教程,现在对很多东西理解得更清楚了。不过,我还是不太明白一个网站里的不同应用是怎么相互作用的。
比如说,我在写一个博客应用(这似乎是个很流行的活动)。博客文章和评论通常是一起出现的,但它们又足够独立,所以应该分成不同的应用,这也是Django开发的一般理念。
考虑下面这个例子。实际上,我不会自己写评论应用,因为网上已经有很好的代码可以用了,但这个例子是为了演示和练习:
mysite/blog/models.py
from django.db import models
class post(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
content = models.TextField()
mysite/comments/models.py
from django.db import models
from mysite.blog.models import post
class comment(models.Model):
id = models.AutoField()
post = models.ForeignKey(post)
author = models.CharField(max_length=200)
text = models.TextField()
我上面写的内容,是不是通过从另一个应用导入模型并将其设置为外键,这就是Django应用之间的互动方式?还是说还有其他更好的方法让一个网站里的应用相互作用?
更新
根据一个回答的建议,我正在阅读关于contrib.contenttypes的文档。如果我理解得没错,我可以这样重写我的评论应用:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contentypes import generic
class comment(models.Model):
id = models.AutoField()
author = models.CharField(max_length=200)
text = models.TextField()
content_type = models.ForeignKey(ContentType)
content_object = generic.GenericForeignKey(content_type, id)
这样做对吗?
4 个回答
我觉得让一个应用依赖另一个应用并没有什么问题。毕竟,应用程序就是对一组模型进行操作。你只需要时刻注意哪个应用依赖于哪个应用(我想你可以称之为依赖关系图)。
你可以通过内容类型框架实现松耦合。这让一个应用可以真正地便携和可插拔,同时又能与其他应用集成。
我写了一个评论应用(是的,我是在重新发明轮子),它可以集成到任何其他应用中,只需在需要发布评论的页面模板中加几行代码(使用自定义标签)。
假设你想让一个模型“线程”可以插入到任何其他模型中。这个想法是创建一个通用外键(可以查看django的文档),然后写一个小函数,接受任何对象并返回一个对应的“线程”(如果没有则创建一个),再写一个自定义模板标签来使用这个功能,比如 {% get_thread for arbitrary_object as thread %}
。所有的帖子都与一个线程相关,而这个线程又与对象相关,这个对象可以是任何类型。
你可以把“线程”对象想象成一种代理,所以帖子不需要直接与某个“文章”或“博客帖子”相关,而是与一个线程相关,这个线程在某种意义上是抽象的,什么是线程呢?它就是一组帖子。这个线程可以与任何类型的对象相关联。(当然,它的功能不止这些,它还可以保存额外的信息,比如允许/禁止匿名帖子、关闭/开启页面评论等……)
编辑
下面是如何使用内容类型框架创建一个通用外键:
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
class Thread( models.Model ):
object_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = generic.GenericForeignKey('object_type', 'object_id')
你可以通过利用django假设所有对象都实现的隐式“公共”接口,使其变得更“透明”……
#inside the Thread class:
def __unicode__(self):
return unicode(self.object)
def get_absolute_url(self):
return self.object.get_absolute_url()
“我上面写的,导入另一个应用的模型并将其设置为外键,这就是Django应用之间的互动方式吗?”
没错,对我来说是有效的。
我们大约有10个应用程序,它们之间相互借用。
这导致我们的单元测试脚本中出现了一种依赖关系。
它看起来是这样的。
“ownership”(所有权)。我们有一个简单的数据所有权应用,它定义了一些其他应用依赖的核心所有权概念。这里有几个简单的表。
“thing”(东西)。[这不是实际名称]。我们的“东西”应用有不同用户组拥有的数据元素。实际上,这个应用的模型有几个复杂的表。它依赖于“ownership”。
“tables”(表格)。[这不是实际名称]。我们的一些用户创建相当复杂的离线模型(可能是用电子表格),然后将这些模型的结果上传到“tables”。这里有一组相当复杂的表。它依赖于“ownership”。
“result”(结果)。[这不是实际名称]。我们的结果是基于有所有者的东西。结果是基于“东西”和“表格”,并且是对客户请求的响应。这并不太复杂,可能只有两三个核心表。它依赖于“things”和“tables”。不,它并不是完全独立的。不过,它比其他依赖的东西更容易发生变化。这就是为什么它是单独的。
“processing”(处理)。我们安排和监控大型批处理作业。这在这个应用中。它非常通用,可以以多种方式使用。它完全独立。
“welcome”(欢迎)。我们有一个“欢迎”应用,展示了一些大部分是静态的页面。这个应用的表不多。但它已经经历了第二次改版,因为第一次的设计太复杂了。它完全独立。
这些依赖应用之间唯一的关系就是一些表的名称。只要我们保留这些表(及其键),我们就可以根据需要重新安排其他应用。
看看Django自带的内容类型框架:
django.contrib.contenttypes
这个框架让你可以把应用程序开发成独立的模块。Django的开发者就是用这个框架来实现它的内置评论系统,这样你就可以在项目中的任何模型上添加评论。
举个例子,如果你有一些内容对象,想把它们“连接”到其他不同类型的内容对象上,比如让每个用户可以在博客文章、图片或用户资料上留下一个“喜欢”的星星,你可以创建一个Favorite
模型,并添加一个通用关系字段,像这样:
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Favorite(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
这样一来,你就可以让任何用户在项目中的任何模型上添加一个Favorite
星星。如果你想通过接收模型类添加API访问,你可以在接收模型上添加一个反向通用关系字段(不过这样会把两个模型“耦合”在一起,而你说你想避免这样),或者通过Favorite
模型查找接收实例的content_type
和object_id
,具体可以参考官方文档中的例子。