为分层模型创建高效的数据库查询(django)

时间:2011-04-13 17:13:23

标签: django

考虑这个(django)模型:

class Source(models.Model):
   # Some other fields
   type = models.ForeignKey('Type')

class Type(models.Model):
    # Some other fields
    parent = models.ForeignKey('self')

此模型具有自身的外键,从而创建层次结构。

假设我们有以下层次结构:

Website
    Blog
    News    
    Social Network
    Q&A
    Forum
Radio
    Government radio
    Pirate radio
    Commercial radio
    Internet radio

如何有效地进行查询,以便在Source之后选择Type时,我还会检索Sources Type,其中{{1}}是给定类型?

我试过遍历整棵树,但这显然不是很有效。

另一种选择是使用ManyToManyField并通过覆盖save()方法自动附加父类型。例如,如果选择“博客”,则还会创建“网站”的记录。但这对我来说似乎有点过分了。

3 个答案:

答案 0 :(得分:5)

django-mptt或django-treebeard是分层数据的好帮手。它们都为模型添加了额外的元数据,以便进行有效的查询。

如果您选择使用django-treebeard,您的模型可能如下所示:

from django.db import models
from treebeard.mp_tree import MP_Node

class Source(models.Model):
    # Some other fields
    type = models.ForeignKey('Type')

class Type(MP_Node):
    # Some other fields
    name = models.CharField(max_length=100)

    # parent gets added automatically by treebeard
    # parent = models.ForeignKey('self', blank=True, null=True)

可以像这样查询:

# get all Sources of Type type and descendants of type
type = Type.objects.get(name='Radio')
Source.objects.filter(type__in=type.get_descendants())

请参阅https://tabo.pe/projects/django-treebeard/docs/tip/api.html了解更多可能的查询

答案 1 :(得分:1)

使用递归公用表表达式可以轻松检索到这样的结构。

一个例子是例如在这里:http://www.postgresql.org/docs/current/static/queries-with.html

答案 2 :(得分:1)

  

如何高效查询以便if   我选择Source by Type,我也是   检索具有类型的源   那是给定类型的孩子?

对于给出的示例,设置查询相当容易,因为不需要进行递归调用,并且您的“层次结构”只有一层深度:

class Source(models.Model):
   # Some other fields
   type = models.ForeignKey('Type')

class Type(models.Model):
    # Some other fields
    name = models.CharField(max_length=100)
    parent = models.ForeignKey('self', blank=True, null=True)

#We want all sources under in the type = Radio tree
type = Type.objects.get(name='Radio')
qs = Source.objects.filter(type__parent=type)

#We want all sources that are `Forum` typed
type = Type.objects.get(name='Forum')
qs = Source.objects.filter(type=type)

这假设Source始终与“child”类型相关,而不是与“parent”相关。

如果源也可以与“父”类型相关,则可以将Q用于复杂查询:

>>> from django.db.models import Q
>>> type = Type.objects.get(name='Radio')
>>> qs = Source.objects.filter(Q(type=type)|Q(type_parent=type))
>>> #if Radio type id = 2
>>> print qs.query
SELECT `app_source`.`id`, `app_source`.`type_id` FROM `app_source` INNER JOIN `app_type` ON  (`app_source`.`type_id` = `app_type`.`id`) WHERE (`app_source`.`type_id` = 2  OR `app_type`.`parent_id` = 2 )
>>> 

如果表中有一个真正的分层树,这种方法的可用性要小得多,你应该找到另一个解决方案。