29

我刚刚完成了第二次 Django 教程的工作,现在我对事情的理解更加清楚了。但是,我仍然不清楚站点内的应用程序如何相互交互。

例如,假设我正在编写一个博客应用程序(显然是一个相当流行的活动)。博客文章和评论往往会一起出现,但它们又足够不同,以至于它们应该被构建到单独的应用程序中,这也是 Djano 开发的一般理念。

考虑以下示例。实际上,我实际上不会自己编写评论应用程序,因为网络上已经存在很好的代码,但这是出于演示/实践目的:

我的网站/博客/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()

我的网站/评论/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

4 回答 4

22

看看 django 内置的contenttypes 框架

django.contrib.contenttypes

它允许您将应用程序开发为独立单元。这是 django 开发人员用来允许 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

于 2008-12-09T18:01:25.200 回答
4

“是我上面写的,从另一个应用程序导入模型并将其设置为外键,Django 应用程序如何交互?”

是的。为我工作。

我们有大约 10 个应用程序在它们之间来回借用。

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

它看起来像这样。

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

  • “事物”。[不是真实姓名]。我们的事物应用程序具有不同用户组拥有的数据元素。这个应用程序的模型实际上有几个复杂的表。这取决于“所有权”。

  • “表”。[不是真实姓名]。我们的一些用户创建了相当复杂的离线模型(可能使用电子表格)并将该建模的结果上传到“表格”中。这有一组相当复杂的表。这取决于“所有权”。

  • “结果”。[不是真实姓名]。我们的结果基于拥有所有者的事物。结果基于事物和表格,并且是对客户请求的响应。这并不太复杂,可能只有两三个核心表。这取决于“事物”和“桌子”。不,它并不完全独立。但是,与它所依赖的其他事物相比,它会发生更多的变化。这就是为什么它是分开的。

  • “加工”。我们安排和监控大批量作业。这是在这个应用程序中。它非常通用,可以以多种方式使用。它完全独立。

  • “欢迎”。我们有一个“欢迎”应用程序,它展示了一堆大部分是静态的页面。这没有太多的桌子。但它是第二个化身,因为第一个太复杂了。它完全独立。

依赖应用程序之间的唯一关系是一些表名。只要我们保留这些表(及其键),我们就可以按照我们认为合适的方式重新排列其他应用程序。

于 2008-12-09T18:06:38.637 回答
3

使某些应用程序依赖于另一个应用程序没有任何问题(恕我直言)。毕竟,应用程序只是对一组模型的操作。您只需要始终了解哪个应用程序依赖于哪个应用程序(我想您可以将其称为依赖关系图)。

您可以使用 contenttypes 框架实现松散耦合。它允许应用程序真正可移植/可插入,但仍与其他应用程序集成。

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

假设您希望模型“线程”可以插入任何其他模型。这个想法是创建一个通用外键(参见 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()
于 2008-12-09T18:35:09.047 回答
2

您的代码似乎正确。不过,我会将帖子和评论保留在博客应用程序中。我不是说这是Django 的方式,但这些模型足够接近,可以在同一个应用程序中。

如何划分项目

如果出现以下情况,我会分开一个应用程序;

  • 我计划将其设计为可重复使用的。(并尝试松散耦合)
  • (对于大型项目)它由项目的主要部分组成。

另一方面; 拥有许多小型应用程序(例如具有单个模型和两个视图的应用程序)很难阅读和维护恕我直言。

应用程序应如何交互

这又取决于项目的类型和应用程序的类型。例如,如果一个应用程序隐式依赖于另一个应用程序(即非通用),那么导入和使用来自另一个应用程序的引用是可以接受的。在这种情况下,可能会单独安装第二个应用程序,但第一个应用程序需要第二个应用程序的存在。

如果您想让应用程序具有高度可重用性和通用性,例如评论应用程序,您可能需要集成一些设置机制。也许一些新设置或额外的 URL 配置,或模型上的特殊指令/方法......django.contrib.admin就是一个很好的例子。

如果没有必要,应用程序不应交互。设计应用程序以避免不必要的耦合非常有用。它提高了您的应用程序的灵活性并使其更易于维护(但可能需要更高的集成成本)。

于 2008-12-09T17:45:34.750 回答