Джанго вложенные QuerySets - PullRequest
       13

Джанго вложенные QuerySets

5 голосов
/ 01 февраля 2011

У меня есть такая модель данных 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.

Теперь, в дополнение к этому, мне нужен QuerySet a модели Atom, который соответствует t, что можно сделать как a = t.values('atom').distinct(). Пока все хорошо.

Однако я также хочу, чтобы каждая из записей в a имела один атрибут / поле , содержащий QuerySet для состояний этого атома, по-прежнему отражая критерии исходного выбора t через один из upstate или lostate ForeignKeys.

Я до сих пор создавал свой QuerySet для состояний, зацикливая t, добавляя values('upstate_id') и values('lostate_id') к Python set() для выбрасывания дубликатов, а затем запрашивая состояния с этим списком. Но тогда я не могу достичь вложенной структуры состояний внутри атомов.

Любые предложения о том, как это сделать, приветствуются, если возможно, с неоцененными QuerySet с, поскольку я передаю их не в шаблон, а в генератор (операторы yield), который является хорошим способом потоковой передачи большого количества данные.

Ответы [ 2 ]

2 голосов
/ 01 февраля 2011

Я думаю, что следующая функция делает то, что я описал выше, но я не уверен, является ли цикл с дальнейшей фильтрацией исходного 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

Но, опять же, могло бы быть более разумное решение для этого, и я, несомненно, был бы заинтересован.

0 голосов
/ 06 января 2017

Очень приятно, что вы понимаете set с.Однако использование SQL In не будет дублировать ваши данные.Давайте рассмотрим это на мгновение.Если я скажу: «Дайте мне атом, который находится в этом списке: (1, 2, 3, 3, 3, 4)», база данных вернет атомы 1, 2, 3 и 4. Для упрощения я бы не сталпопросите Python выполнить арифметику set, так как база данных должна быть в состоянии справиться с ней очень хорошо.Есть времена, чтобы использовать set, но ваш сценарий не похож на один из них.

Альтернатива для вас:

states = State.objects.filter(
    Q(pk__in=t.values_list('upstate', flat=True)) |
    Q(pk__in=t.values_list('lostate', flat=True)
)

Несмотря на это, кажется, что ваша модель могла быиспользовать некоторые изменения, но я не совсем понимаю, чего вы пытаетесь достичь.Обратите внимание, что в моей альтернативе я ничего не делаю с атомами.Я использую объект Q, чтобы иметь возможность выполнять операцию ИЛИ, но вы можете добавить флаг в модель состояния, чтобы указать, является ли он высоким или низким.Или вы можете использовать отношение M2M со сквозной таблицей.И почему ваш переход и ваше состояние связаны с атомом?Вы можете просто исключить atom из Transition и получить atom из State следующим образом:

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)
)
...