Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql Django-同一个表上的多个联接-结果不正确?_Sql_Django_Django Orm - Fatal编程技术网

Sql Django-同一个表上的多个联接-结果不正确?

Sql Django-同一个表上的多个联接-结果不正确?,sql,django,django-orm,Sql,Django,Django Orm,我有以下型号: class Document(models.Model): ... class DocumentAttributes(models.Model): document = models.ForeignKey(Document) key = models.TextField() value = models.TextField() 我想根据属性查询文档。指定的键必须与其中一个值匹配 最好举个例子: self.d1 = document_fa

我有以下型号:

class Document(models.Model):
    ...

class DocumentAttributes(models.Model):
    document = models.ForeignKey(Document)
    key = models.TextField()
    value = models.TextField()
我想根据属性查询文档。指定的键必须与其中一个值匹配

最好举个例子:

    self.d1 = document_factory(attributes={'a': '1', 'b': '1'})
    self.d2 = document_factory(attributes={'a': '2', 'b': '2'})
    self.d3 = document_factory(attributes={'a': '2', 'b': '1'})
    self.d4 = document_factory(attributes={'a': '3', 'b': '4'})
    self.d5 = document_factory(attributes={'a': '3', 'b': '2'})
    self.d6 = document_factory(attributes={'a': '1', 'b': '4'})
    self.d7 = document_factory(attributes={'a': '2', 'b': '4'})

 docs = whitelist_keyvalue_in({'a': ['1', '3'], 'b': ['1', '4']}, doc_qs).all()
文档现在应该包含d1、d4、d6

以下是我的实现:

def whitelist_keyvalue_in(json_obj, doc_qs):
    qs = doc_qs
    for key in json_obj:
        values = [json_obj[key]] if isinstance(json_obj[key], basestring) else json_obj[key]
        q_values = Q()
        for v in values:
            q_values |= Q(value=v)
        qs = qs.filter(attributes=DocumentAttributes.objects.filter(key=key).filter(q_values))
    print(qs.query)
    return qs
出于某种原因,这只返回d1?而且生成的查询并不十分漂亮

你能发现错误吗?有没有更好的方法写这个

SELECT ... FROM "document_document"
INNER JOIN "document_documentattributes" ON ("document_document"."id" = "document_documentattributes"."document_id")
INNER JOIN "document_documentattributes" T3 ON ("document_document"."id" = T3."document_id")
WHERE
("document_documentattributes"."id" = ( SELECT U0."id"
                       FROM "document_documentattributes" U0
                       WHERE (U0."key" = 'a' AND (U0."value" = '1' OR U0."value" = '3')))
                       AND T3."id" = ( SELECT U0."id"
                                FROM "document_documentattributes" U0
                                WHERE (U0."key" = 'b' AND (U0."value" = '1' OR U0."value" = '4'))))
如果我自己使用原始查询,则一切正常:

def whitelist_keyvalue_in(json_obj, doc_qs):
    names = {key: 'da{}'.format(k_index) for k_index, key in enumerate(json_obj)}
    raw_sql = "SELECT da0.document_id as id FROM document_documentattributes as da0 "
    for key in json_obj:
        if names[key] == 'da0':
            continue
        raw_sql += ("JOIN document_documentattributes as {0} ON {0}.document_id = da0.document_id "
                    "".format(names[key]))
    for key in json_obj:
        where_and = 'WHERE' if names[key] == 'da0' else ' AND'
        values = [json_obj[key]] if isinstance(json_obj[key], basestring) else json_obj[key]
        values_opts = ' OR '.join("{}.value = '{}'".format(names[key], value) for value in values)
        raw_sql += "{} {}.key = '{}' AND ({})".format(where_and, names[key], key, values_opts)
    return doc_qs.filter(id__in=(d.id for d in doc_qs.raw(raw_sql)))
其中:

SELECT da0.document_id as id 
FROM document_documentattributes as da0 
JOIN document_documentattributes as da1 ON da1.document_id = da0.document_id 
WHERE da0.key = 'a' AND (da0.value = '1' OR da0.value = '3') 
  AND da1.key = 'b' AND (da1.value = '1' OR da1.value = '4')

SELECT ... FROM "document_document" WHERE "document_document"."id" IN (1, 4, 6)
我更希望避免中的id__,但无法确定如何从原始查询集获取到常规查询集

如果必须使用原始sql进行此操作,是否有办法避免这两个选择以返回正常查询集?

qs=qs.filter(attributes=DocumentAttributes.objects.filter(key=key).filter(q_值))
位于for循环内,每次应用筛选器时,其行为类似于
条件。因此,最终的查询将是获取
a为1或3,b为1或4的文档。这里d1匹配条件并返回,因为它具有
a=1
b=1

在每个键周围使用
Q()
|
可以解决此问题


这一行
qs=qs.filter(attributes=DocumentAttributes.objects.filter(key=key.filter(q_值))
将触发两个查询,因为
DocumentAttributes.objects.filter(key=key.filter)(q_值)
将被评估以用于
qs
查询

确定最终找到了正确的方法。生成的原始查询甚至很好看

def whitelist_keyvalue_in(attributes, doc_qs):
    qs = doc_qs
    for key, values in attributes.iteritems():
        values = [values] if isinstance(values, basestring) else values
        qs = qs.filter(attributes__key=key, attributes__value__in=values)
    return qs

为什么要在“筛选器”中避免使用“id_uu?”@MevinBabu-我已经有了我关心的文档列表。中的id__执行第二个不必要的查询,这也可能是一个缓慢的查询,具体取决于有多少文档。您的意思是说您已经有一个查询返回文档列表,然后要再次筛选此列表以返回与属性匹配的文档?所以你不想做两个不同的查询?对吗?但是你的
函数中的
白名单\u keyvalue\u无论如何都在向db启动一个新的查询?@MevinBabu-不完全是这样。QuerySets存储执行SQL查询所需的信息,直到必须时才会访问数据库。当我使用原始sql查询时,它会立即命中数据库,给出一个PK:s列表。但是,我想返回一个QuerySet(便于后续操作)。在给定PK列表的情况下,返回QuerySet的唯一方法是在中执行id__。现在,我将总是击中数据库两次。我希望有某种方法可以获取一个提供PKs的原始sql,并将其转换为QuerySet,而不必点击db获取PKs(即Django将其用作sql子查询)。希望澄清。你的分析是不正确的。注意:qs.filter返回一个新的过滤器,它以迭代方式构建查询集。您的建议也不正确-每当查询多个属性时,它将返回空集。(请注意,“if key in filter_dic:”永远不会是这种情况。)这部分
DocumentAttributes.objects.filter(key=key).filter(q_值)
将得到评估,以用于
qs.filter
查询您的右侧。我把事情搞混了。我已经编辑了答案。仍然不正确。“它的行为就像一个条件”这正是我想要的。“a是1或3,b是1或4”也正是我想要的,但请注意,d1不是唯一符合此标准的。它真的应该返回:d1、d4、d6。”将引发两个查询,因为“不,不会”。查询集的好处(如我前面所述)是,您可以处理抽象查询,而无需向数据库触发sql语句,直到您需要它们为止。例如:qs=Documents.objects.all()。不会执行任何查询。如果我接着执行qs2=qs.filter(something=something)——它仍然不会执行任何查询。只有当我实际获取数据时,才会发送查询。例如doc=qs2.all()[0]。