Django嵌套查询集

5

我有一个Django数据模型,像这样(省略了数据字段):

class Atom(Model):
    pass

class State(Model):
    atom = ForeignKey(Atom)

class Transition(Model):
    atom = ForeignKey(Atom)
    upstate = ForeignKey(State,related_name='uptrans')
    lostate = ForeignKey(State,related_name='lotrans')

当我查询时,需要限制的字段可以在任何一个模型中,所以最简单的方法是在Transition.objects.filter(...)上进行查询,因为其他模型中的所有字段都可以通过外键访问。我们将结果称为QuerySet t
现在,我想要的是一个Atom模型的QuerySet a,它对应于t,可以像这样完成:a = t.values('atom').distinct()。到目前为止没问题。
然而,我还希望a中的每个条目都有一个属性/字段,它保存了此Atom的States的QuerySet,仍然反映了原始选择t上的条件,可以通过upstatelostate之一的ForeignKey实现。
到目前为止,我已经通过循环t,将values('upstate_id')values('lostate_id')添加到Python set()中以去除重复项,并使用此列表查询States。但是,我无法实现States嵌套在Atoms中的结构。
如果可能的话,欢迎提出如何做到这一点的建议,使用未评估的QuerySet,因为我将它们传递给生成器(yield语句),这是一种流式传输大量数据的好方法。
2个回答

2
我认为以下函数实现了我上述描述的功能,但是我不确定通过对原始QuerySet进行进一步过滤的循环是否是正确的方法。
def getAtomsWithStates(t):
    atom_ids = set( t.values_list('atom_id',flat=True) )
    atoms = Atoms.objects.filter(pk__in=atom_ids)
    for atom in atoms:
        upstate_ids = t.filter(atom=atom).values_list('upstate_id',flat=True)
        lostate_ids = t.filter(atom=atom).values_list('lostate_id',flat=True)
        all_ids = set( upstate_ids + lostate_ids )

        # attach the new QuerySet to the entry in the outer one:
        atom.States = State.objects.filter(pk__in=all_ids)

    return atoms

现在我可以像这样完成所需的嵌套循环:
someAtoms = getAtomsWithStates( Transition.objects.filter(...) )
for atom in someAtoms:
    for state in atom.States:
        print state.field

但是,再次强调,可能有更智能的解决方案,我会非常感兴趣。


1
很高兴您了解“集合”(set)的概念。然而,使用SQL的“In”操作符不会导致数据重复。让我们来考虑一下这个问题。如果我说:“给我一个在这个列表中的原子:(1, 2, 3, 3, 3, 4)”,数据库将返回1、2、3和4这些原子。为了简化,我不会要求Python执行“集合”运算,因为数据库应该可以很好地处理它。虽然有时候需要使用“集合”(set),但是在您的场景下似乎不需要。
对于您的另一种选择:
states = State.objects.filter(
    Q(pk__in=t.values_list('upstate', flat=True)) |
    Q(pk__in=t.values_list('lostate', flat=True)
)

即便如此,似乎您的模型需要进行一些更改,但我并不完全理解您试图实现什么。请注意,在我的替代方案中,我没有对原子做任何处理。我使用Q对象来执行OR操作,但您可能可以向State模型添加一个标志来指示其高低状态。或者您可以使用带有通过表的M2M关系。而为什么您的转换和状态都与原子相关联?您可以从State中删除atom并像这样获取atom
atoms = Atom.objects.filter(
    pk__in=State.objects.filter(
        Q(pk__in=t.values_list('upstate', flat=True)) |
        Q(pk__in=t.values_list('lostate', flat=True)
    ).values_list('atom', flat=True)
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接