Django的select_related()和GenericForeignKey

4 投票
1 回答
3279 浏览
提问于 2025-04-16 21:53

我有这样的模型:

class Comment(models.Model):
    text = models.TextField(max_length = 250, blank = False)
    author = models.ForeignKey(User)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

class Product(models.Model):
    name = models.CharField(max_length = 40)
    comments = generic.GenericRelation(Comment)

在这个模板中,我展示了所有产品的最新5条评论:

<ul>
    {% for comment in last_comments %}
    <li><a href="/user/{{ comment.author }}/">{{ comment.author }}</a> on <a href="/product/{{ comment.content_object.name }}/">{{ comment.content_object }}</a>
    <br>{{ comment.text }}</li>
    {% endfor %}
</ul>

如果我用 last_comments = Comment.objects.all().order_by('-id')[:5] 来获取 last_comments,django调试工具显示执行了25个查询。

如果我用 last_comments = Comment.objects.select_related().all().order_by('-id')[:5] 来获取 last_comments,django调试工具显示执行了20个查询。

但是为什么 select_related 不能同时选择相关的内容对象呢?在django调试工具中,我看到获取产品的查询有5个。这显然是因为 {{ comment.content_object }} 的结果。

可能的原因是因为我在 Comment 模型中使用了GenericForeignKey

你有什么想法吗?

1 个回答

-1

你可以尝试把你的数据库结构调整成下面这个样子:

class Comment(models.Model):
    ...
    content_object = models.ForeignKey(Content)

class Content(models.Model):
    text = models.CharField(max_length=123)

class SomeSpecificContent(models.Model):
    ...
    content = models.ForeignKey(Content)

class OtherSpecificContent(models.Model):
    ...
    content = models.ForeignKey(Content)

在Django中,这个结构其实和下面这个很相似:

class Comment(models.Model):
    ...
    content_object = models.ForeignKey(Content)

class Content(models.Model):
    text = models.TextField()

class SomeSpecificContent(Content):
    ...

class OtherSpecificContent(Content):
    ...

因为这就是Django处理继承关系的方式。不过后者可能不太灵活,如果SomeSpecificContent和OtherSpecificContent实际上代表完全不同的概念,理解起来可能会有点困难。

另一方面,通用关系(Generic relations)之所以处理起来不太高效,就是因为它们可以和你想要的任何表连接。所以如果你有5个对象,可能每个对象都和不同类型的实体有关。我不太确定Django是怎么处理100个对象和5种实体之间的关系的。它会生成5+1个查询吗?

撰写回答