如何在可重用的Django应用中建模外键?
在我的Django网站里,有两个应用,一个是博客(blog),另一个是链接(links)。博客里有一个叫做博客文章(blogpost)的模型,而链接里有一个叫做链接(link)的模型。这两个模型之间应该是“一对多”的关系。也就是说,每篇博客文章可以有很多链接,但每个链接只能对应一篇博客文章。简单来说,就是在链接模型里加一个指向博客文章的外键(ForeignKey)。
这听起来没问题,但我遇到了一个问题。我想让链接应用可以重复使用,不想让它依赖于博客应用。我希望能在其他网站上也用到这个链接应用,甚至可以把链接和其他非博客文章的应用和模型关联起来。
看起来使用通用外键(generic foreign key)可能是个解决办法,但其实并不是。我不想让链接可以和我网站里的任何模型关联,只想和我明确指定的那个模型关联。而且我之前的经验告诉我,使用通用外键在数据库使用上可能会有问题,因为你不能像使用普通外键那样对通用外键进行选择关联(select_related)。
那么,建模这种关系的“正确”方式是什么呢?
6 个回答
另一种解决这个问题的方法是参考 django-mptt 的做法:在一个可重用的应用中,只定义一个抽象模型(叫做MPTTModel),然后需要在其他模型中继承这个抽象模型,并定义一些字段(比如,parent=ForeignKey指向自己,或者根据你的应用需求来设置)。
我觉得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')
如果你认为链接的应用程序总是指向一个单一的应用程序,那么一种方法是将外部模型的名称作为一个字符串传递,这个字符串包含应用程序的标签,而不是直接引用类(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'