Запрос левого соединения в Django с несколькими объединениями к одной и той же таблице - PullRequest
0 голосов
/ 02 ноября 2010

Не уверен, как это сделать в Джанго.

Модель:

class LadderPlayer(models.Model):
  player = models.ForeignKey(User, unique=True)
  position = models.IntegerField(unique=True)

class Match(models.Model):
  date = models.DateTimeField() 
  challenger = models.ForeignKey(LadderPlayer)
  challengee = models.ForeignKey(LadderPlayer)

Хотел бы запросить, чтобы получить всю информацию об игроке за один выстрел, включая любые вызовы, которые он выполнил или вызовы против них. Этот SQL работает:

select lp.position,
lp.player_id,
sc1.challengee_id challenging,
sc2.challenger_id challenged_by
from ladderplayer lp left join challenge sc1 on lp.player_id = sc1.challenger_id
left join challenge sc2 on lp.player_id = sc2.challengee_id

Что возвращает примерно так, если игрок 3 бросил вызов игроку 2:

position    player_id   challenging  challenged_by
----------  ----------  -----------  -------------
1           1
2           2                        3
3           3           2

Не знаю, как это сделать в Django ORM .... есть ли способ сделать это?

Ответы [ 3 ]

2 голосов
/ 02 ноября 2010

На самом деле вам, вероятно, следует немного изменить свои модели, поскольку существует отношение «многие ко многим» от LadderPlayer до самого себя с использованием Match в качестве промежуточной таблицы .Ознакомьтесь с документацией django по этой теме.Тогда вы сможете делать запросы, которые хотите, используя django orm!Также взгляните на симметричные / асимметричные отношения «многие ко многим» !

1 голос
/ 02 ноября 2010

@ Лазерная наука абсолютно верна. Вы должны настроить свои модели, поскольку вы устанавливаете фактические отношения «многие ко многим»; это позволит вам использовать больше возможностей интерфейса администратора и т. д.

Кроме того, независимо от того, нет необходимости переходить на raw(), поскольку это можно сделать полностью с помощью обычного использования Django ORM.

Что-то вроде:

class LadderPlayer(models.Model):
    player = models.ForeignKey(User, unique=True)
    position = models.IntegerField(unique=True)
    challenges = models.ManyToManyField("self", symmetrical=False, through='Match')

class Match(models.Model):
    date = models.DateTimeField() 
    challenger = models.ForeignKey(LadderPlayer)
    challengee = models.ForeignKey(LadderPlayer)

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

player_of_interest = LadderPlayer.objects.filter(pk=some_id)
matches_of_interest = \
        Match.objects.filter(Q(challenger__pk=some_id)|Q(challengee__pk=some_id))

, чтобы получить всю интересующую информацию о данном игроке. Обратите внимание, что для этого вам понадобится from django.db.models import Q.

Если вы хотите точно ту же информацию, которую вы предоставляете в своем примере запроса, я считаю, что было бы проще разделить запросы на отдельные для получения списков претендентов и претендентов - например, что-то вроде:

challengers = LadderPlayer.objects.filter(challenges__challengee__pk=poi_id)
challenged_by = LadderPlayer.objects.filter(challenges__challenger__pk=poi_id)

получит два соответствующих набора запросов для интересующего игрока (с первичным ключом poi_id).

Если есть какая-то конкретная причина, по которой вы не хотите, чтобы фактические отношения «многие ко многим» стали де-юре, вы можете изменить их на что-то вроде

challenger = LadderPlayer.objects.filter(match__challengee__pk=poi_id)
challenged_by = LadderPlayer.objects.filter(match__challenger_pk=poi_id)

Таким образом, предложение для изменения модели состоит просто в том, чтобы помочь использовать существующие инструменты и сделать явным отношение, которое у вас в настоящее время происходит неявно.

В зависимости от того, как вы хотите его использовать, вы можете сделать что-то вроде

pl_tuple = ()
for p in LadderPlayer.objects.all():
    challengers = LadderPlayer.objects.filter(challenges__challengee__pk=p.id)
    challenged_by = LadderPlayer.objects.filter(challenges__challenger__pk=p.id)
    pl_tuple += (p.id, p.position, challengers, challenged_by)
context_dict['ladder_players'] = pl_tuple

по вашему мнению, чтобы подготовить данные для вашего шаблона.

В любом случае, вы, вероятно, должны выполнять запрос через ORM Django, вместо использования raw() в этом случае.

1 голос
/ 02 ноября 2010

Что ж, я больше копался, и, похоже, в Django 1.2 это можно сделать с помощью метода raw () в Query Manager. Так что это код, использующий мой запрос выше:

ladder_players = LadderPlayer.objects.raw("""select lp.id, lp.position,lp.player_id,
sc1.challengee_id challenging,
sc2.challenger_id challenged_by
from ladderplayer lp left join challenge sc1 on lp.player_id = sc1.challenger_id
left join challenge sc2 on lp.player_id = sc2.challengee_id order by position""")

А в шаблоне вы можете обратиться к «рассчитанным» полям объединения:

{% for p in ladder_players %}
{{p.challenging}} {{p.challenged_by}}
...

и т.д.

Кажется, работает так, как мне нужно ....

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...