Django:动态地将查询集变为另一个模型

时间:2015-09-24 11:28:23

标签: python django

有更优雅/更快的方法吗?

以下是我的模特:

class X(Model):
    (...)

class A(Model):
    xs = ManyToManyField(X)

class B(A):
    (...)

class C(A): # NOTE: not relevant here
    (...)

class Y(Model):
    b = ManyToOneField(B)

这是我想要做的:

def foo(x):
    # NOTE: b's pk (b.a_ptr) is actually a's pk (a.id)
    return Y.objects.filter(b__in=x.a_set.all())

但它返回以下错误:

<repr(<django.db.models.query.QuerySet at 0x7f6f2c159a90>) failed:
django.core.exceptions.FieldError: Cannot resolve keyword u'a_ptr'
into field. Choices are: ...enumerate all fields of A...>

以下是我现在正在做的事情,以尽量减少查询:

def foo(x):
    a_set = x.a_set
    a_set.model = B # NOTE: force the model to be B
    return Y.filter(b__in=a_set.all())

它有效,但它不是一条线。有这样的东西会很酷:

def foo(x):
    return Y.filter(b__in=x.a_set._as(B).all())

Django中更简单的方法似乎如下:

def foo(x):
    return Y.filter(b__in=B.objects.filter(pk__in=x.a_set.all()))

...但这会在SQL中产生无用的子查询:

SELECT Y.* FROM Y WHERE Y.b IN (
    SELECT B.a_ptr FROM B WHERE B.a_ptr IN (
        SELECT A.id FROM A WHERE ...));

这就是我想要的:

SELECT Y.* FROM Y WHERE Y.b IN (SELECT A.id FROM A WHERE ...);

(这个SQL示例有点简化,因为A和X之间的关系实际上是一个ManyToMany表,为了清楚起见,我将其替换为SELECT A.id FROM A WHERE ...。)

2 个答案:

答案 0 :(得分:1)

如果您再次关注关系,而不是使用in,则可能会获得更好的结果。

Y.objects.filter(b__xs=x)

答案 1 :(得分:0)

我对数据库几乎一无所知,但Django文献中有很多关于具体继承class B(A)的警告,由于重复的INNER JOIN操作导致数据库效率低下。

根本问题是关系数据库不是面向对象的。

如果你使用Postgres,那么Django对Hstore字段有本机支持(来自1.8),这些字段是可搜索的,但是无模式的关键字 - 值对集合。我打算在我的基类(你的A)中使用其中一个来消除对派生类(你的B和C)的任何需求。相反,我会在A中维护一个类型字段(等于&#34; B&#34;或者&#34; C&#34;在您的示例中)并使用该字段和/或仔细的密钥存在检查来访问子字段Hstore字段的保证&#34;保证&#34;如果(例如)instance.type ==&#34; C&#34;存在(该约束不是在DB完整性级别强制执行的)。

我还发现了在Django 1.8之前存在的 django-hstore 模块,它具有更强大的功能。我还没有做任何事情,所以无法进一步评论。

我还在文献中找到了对宽表的引用,在这里您只需定义一个具有潜在大量字段的模型A,只有当对象是B类时才包含有用数据,而C类,D类更多的字段,...我没有数据库知识来评估附加到表格每一行的大量空白或空字段的成本。很明显,如果你有一百万只绵羊记录和一条血统 - 赛马记录和一条粘虫记录,那么就像#34;子类&#34;在同一张宽大的桌子里,动物很傻。

如果您对DB缺乏理解,我会很感激您对这两种想法的反馈。

哦,为了完整起见,还有 django-polymorphic 模块,但它建立在具体的继承基础之上,伴随着效率低下。