初学者:尝试理解Django中应用的互动方式

29 投票
4 回答
7697 浏览
提问于 2025-04-11 19:51

我刚刚完成了第二遍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 个回答

3

我觉得让一个应用依赖另一个应用并没有什么问题。毕竟,应用程序就是对一组模型进行操作。你只需要时刻注意哪个应用依赖于哪个应用(我想你可以称之为依赖关系图)。

你可以通过内容类型框架实现松耦合。这让一个应用可以真正地便携和可插拔,同时又能与其他应用集成。

我写了一个评论应用(是的,我是在重新发明轮子),它可以集成到任何其他应用中,只需在需要发布评论的页面模板中加几行代码(使用自定义标签)。

假设你想让一个模型“线程”可以插入到任何其他模型中。这个想法是创建一个通用外键(可以查看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()
4

“我上面写的,导入另一个应用的模型并将其设置为外键,这就是Django应用之间的互动方式吗?”

没错,对我来说是有效的。

我们大约有10个应用程序,它们之间相互借用。

这导致我们的单元测试脚本中出现了一种依赖关系。

它看起来是这样的。

  • “ownership”(所有权)。我们有一个简单的数据所有权应用,它定义了一些其他应用依赖的核心所有权概念。这里有几个简单的表。

  • “thing”(东西)。[这不是实际名称]。我们的“东西”应用有不同用户组拥有的数据元素。实际上,这个应用的模型有几个复杂的表。它依赖于“ownership”。

  • “tables”(表格)。[这不是实际名称]。我们的一些用户创建相当复杂的离线模型(可能是用电子表格),然后将这些模型的结果上传到“tables”。这里有一组相当复杂的表。它依赖于“ownership”。

  • “result”(结果)。[这不是实际名称]。我们的结果是基于有所有者的东西。结果是基于“东西”和“表格”,并且是对客户请求的响应。这并不太复杂,可能只有两三个核心表。它依赖于“things”和“tables”。不,它并不是完全独立的。不过,它比其他依赖的东西更容易发生变化。这就是为什么它是单独的。

  • “processing”(处理)。我们安排和监控大型批处理作业。这在这个应用中。它非常通用,可以以多种方式使用。它完全独立。

  • “welcome”(欢迎)。我们有一个“欢迎”应用,展示了一些大部分是静态的页面。这个应用的表不多。但它已经经历了第二次改版,因为第一次的设计太复杂了。它完全独立。

这些依赖应用之间唯一的关系就是一些表的名称。只要我们保留这些表(及其键),我们就可以根据需要重新安排其他应用。

22

看看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_typeobject_id,具体可以参考官方文档中的例子。

撰写回答