如何在可重用的Django应用中建模外键?

17 投票
6 回答
3594 浏览
提问于 2025-04-15 14:18

在我的Django网站里,有两个应用,一个是博客(blog),另一个是链接(links)。博客里有一个叫做博客文章(blogpost)的模型,而链接里有一个叫做链接(link)的模型。这两个模型之间应该是“一对多”的关系。也就是说,每篇博客文章可以有很多链接,但每个链接只能对应一篇博客文章。简单来说,就是在链接模型里加一个指向博客文章的外键(ForeignKey)。

这听起来没问题,但我遇到了一个问题。我想让链接应用可以重复使用,不想让它依赖于博客应用。我希望能在其他网站上也用到这个链接应用,甚至可以把链接和其他非博客文章的应用和模型关联起来。

看起来使用通用外键(generic foreign key)可能是个解决办法,但其实并不是。我不想让链接可以和我网站里的任何模型关联,只想和我明确指定的那个模型关联。而且我之前的经验告诉我,使用通用外键在数据库使用上可能会有问题,因为你不能像使用普通外键那样对通用外键进行选择关联(select_related)。

那么,建模这种关系的“正确”方式是什么呢?

6 个回答

1

另一种解决这个问题的方法是参考 django-mptt 的做法:在一个可重用的应用中,只定义一个抽象模型(叫做MPTTModel),然后需要在其他模型中继承这个抽象模型,并定义一些字段(比如,parent=ForeignKey指向自己,或者根据你的应用需求来设置)。

2

我觉得TokenMacGuy的思路是对的。我会看看django-tagging是怎么处理类似的通用关系的,主要是通过内容类型、通用对象ID,还有generic.py这个文件。从models.py可以找到相关信息。

class TaggedItem(models.Model):
    """
    Holds the relationship between a tag and the item being tagged.
    """
    tag          = models.ForeignKey(Tag, verbose_name=_('tag'), related_name='items')
    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
    object_id    = models.PositiveIntegerField(_('object id'), db_index=True)
    object       = generic.GenericForeignKey('content_type', 'object_id')

    objects = TaggedItemManager()

    class Meta:
        # Enforce unique tag association per object
        unique_together = (('tag', 'content_type', 'object_id'),)
        verbose_name = _('tagged item')
        verbose_name_plural = _('tagged items')
24

如果你认为链接的应用程序总是指向一个单一的应用程序,那么一种方法是将外部模型的名称作为一个字符串传递,这个字符串包含应用程序的标签,而不是直接引用类(Django文档解释)。

换句话说,不要这样做:

class Link(models.Model):
    blog_post = models.ForeignKey(BlogPost)

而是这样做:

from django.conf import setings
class Link(models.Model):
    link_model = models.ForeignKey(settings.LINK_MODEL)

在你的settings.py文件中:

LINK_MODEL = 'someproject.somemodel'

撰写回答