Django 知识库:ForeignKey关联不同模型

3849 views, 2020/05/15 updated   Go to Comments

有的时候我们需要将“一对多” ForeignKey 关联到多个不同的模型。什么意思?比如博客文章,它的作者既可以是独立的用户 Person,也可以是某一个群组 Group;但是 ForeignKey 显然只支持关联到单个模型的,怎么办?

解决方案有很多种,我比较倾向于这样:

# 群组
class Group(models.Model):
    name = models.CharField(max_length=100)

# 用户
class Person(models.Model):
    name = models.CharField(max_length=100)

# 起过渡作用的桥接模型
class Owner(models.Model):
    # 用户
    person = models.OneToOneField(
        Person,
        null=True,
        blank=True,
        on_delete=models.CASCADE
    )
    # 群组
    group = models.OneToOneField(
        Group,
        null=True,
        blank=True,
        on_delete=models.CASCADE
    )

# 文章
class Post(models.Model):
    owner = models.ForeignKey(Owner, on_delete=models.CASCADE)

关键点是 ForeignKey 关联了一个 Owner 桥接模型,再由 Owner 与实际的作者作“一对一”的关联。

在使用它时,可能还需要一个辅助函数确定 Owner 到底关联了谁:

class Owner(...):
    ...
    def get_owner(self):
        # 获取非空 Owner 对象
        if self.person is not None:
            return self.person
        elif self.group is not None:
            return self.group
        raise AssertionError("Neither is set")

在视图中可以这样取得实际的 owner

owner = post.owner.get_owner()

这种方式有一些缺点,比如多了一个 Owner 桥接表、需要写更多辅助函数保证 owner 的正确性,但我觉得它在简洁、效率上是个不错的折中。

在个人博客的开发中,需要用到这种技巧的主要地方就是评论模块了,尝试去应用吧。

实际上 Django 有一个专门处理对应不同模型的外键,叫 GenericForeignKey,但我不太喜欢,有兴趣的同学请读官方文档。有关这个话题还有一篇经典的文章为什么你应该避免使用GenericForeignKey,里面介绍了 5 种替代方案,值得一读。




本文作者: 杜赛
发布时间: 2020年05月15日 - 16:09
最后更新: 2020年05月15日 - 16:09
转载请保留原文链接及作者