如何在Django中建模单向一对一关系

4 投票
2 回答
7281 浏览
提问于 2025-04-15 12:53

我想在Django中建模一个有版本的文章:

在我的文章模型的models.py文件中,我有以下内容:

class Article(models.Model):
    title = models.CharField(blank=False, max_length=80)
    slug = models.SlugField(max_length=80)

    def __unicode__(self):
        return self.title

class ArticleRevision(models.Model):
    article = models.ForeignKey(Article)
    revision_nr = models.PositiveSmallIntegerField(blank=True, null=True)
    body = models.TextField(blank=False)

在文章模型中,我想有两个直接指向版本的引用,一个指向已发布的版本,另一个指向正在编辑的版本。不过根据我的理解,OneToOne和ForeignKey引用会在模型的另一侧生成一个反向引用,所以我想问的是,如何在Django中创建一个单向的一对一引用?

这有什么特别的方法吗?还是说我需要通过在版本中加入状态,以及自定义实现那些需要特定状态版本的字段来“伪造”这个引用?

编辑:我想我之前没有很好地解释我的意图。让我们试着从更高的抽象层次来看看:

我最初的想法是实现一个有版本的文章模型,每篇文章可以有多个版本,其中一个版本可能是“已发布”的,另一个是正在编辑的。

这意味着文章和版本之间是一个多对一的关系(在ArticleRevision类中用ForeignKey(Article)表示),同时从文章到版本有两个单向引用:published_revisionedited_revision

我主要想问的是,如何用Django的ORM来建模这个关系。

2 个回答

4

这个双向链接有什么问题呢?我觉得使用OneToOneField应该是个不错的选择。有没有什么特别的原因会对你的应用造成不利影响?如果你不需要反向引用,那为什么不直接忽略它呢?

5

Django生成的反向引用是程序内部的东西,不会影响数据库的基本结构。换句话说,如果你的文章(Article)有一个一对一或外键字段指向修订(Revision),那么在数据库的文章表中会增加一列,但修订表不会增加任何列。

因此,从修订到文章的反向关系是可以不去管的。如果你真的觉得有必要说明这个反向链接没有被使用,可以在代码中给这些字段加个related_name属性,比如 _unused_1。这样你的文章模型可能看起来像下面这样:

class Article(models.Model):
    title = models.CharField(blank=False, max_length=80)
    slug = models.SlugField(max_length=80)
    revision_1 = models.OneToOneField(ArticleRevision, related_name='_unused_1')
    revision_2 = models.OneToOneField(ArticleRevision, related_name='_unused_2')

    def __unicode__(self):
        return self.title

不过,实际上在应用中一对一的关系很少有用(除非你有特别的优化需求),我建议你仔细检查一下你的数据库结构,确保这真的是你想要的。可能更合理的是在文章修订(ArticleRevision)中保留一个指向文章的外键字段(因为修订通常需要和某篇文章关联),并在修订中增加另一列来表示它是否已发布。

撰写回答