Как выполнять запросы в Django, следуя отношениям двойного соединения (или: Как обойти ограничения Django в отношении сквозных моделей ManyToMany?) - PullRequest
5 голосов
/ 27 января 2011

Должен быть способ выполнить этот запрос через ORM, но я его не вижу.

Настройка

Вот то, что я моделирую: один Арендатор может занимать несколько комнат, а один Пользователь может иметь несколько комнат. Таким образом, номера имеют FK для арендатора и FK для пользователя. Номера также обслуживаются (возможно, отдельным пользователем).

То есть у меня есть эти (упрощенные) модели:

class Tenant(models.Model):
    name = models.CharField(max_length=100)

class Room(models.Model):
    owner = models.ForeignKey(User)
    maintainer = models.ForeignKey(User)
    tenant = models.ForeignKey(Tenant)

Проблема

Учитывая Арендатора, я хочу, чтобы пользователи владели комнатой, которую они занимают.

Соответствующим SQL-запросом будет:

SELECT auth_user.id, ...
FROM tenants_tenant, tenants_room, auth_user
WHERE tenants_tenant.id = tenants_room.tenant_id
AND tenants_room.owner_id = auth_user.id;

Получение любого отдельного значения из связанных объектов User может быть выполнено, например, с помощью my_tenant.rooms.values_list('owner__email', flat=True), но получение полного набора запросов пользователей приводит в замешательство.

Обычно одним из способов решения этой проблемы является установка поля ManyToMany в моей модели Tenant, указывающего на User с TenantRoom в качестве сквозной модели. Это не будет работать в этом случае, потому что модель TenantRoom имеет вторую (не связанную) ForeignKey до User ( см. «Ограничения» ). Плюс, это кажется ненужным беспорядком на модели Арендатора.

Выполнение my_tenant.rooms.values_list('user', flat=True) приближает меня, но возвращает ValuesListQuerySet идентификаторов пользователей, а не набор запросов реальных объектов User.

Вопрос

Итак: есть ли способ получить набор запросов фактических экземпляров модели через ORM, используя только один запрос?


Редактировать

Если, на самом деле, нет способа сделать это непосредственно в одном запросе через ORM, то какой это лучший (некоторая комбинация наиболее производительного, наиболее идиоматического, наиболее читаемого и т. Д.) Способа выполнить то, что я находясь в поиске? Вот варианты, которые я вижу:

  1. подвыбор

    users = User.objects.filter(id__in=my_tenant.rooms.values_list('user'))
    
  2. Подвыбрать через Python (см. Рекомендации по производительности для обоснования этого)

    user_ids = id__in=my_tenant.rooms.values_list('user')
    users = User.objects.filter(id__in=list(user_ids))
    
  3. Необработанный SQL:

    User.objects.all("""SELECT auth_user.*
    FROM tenants_tenant, tenants_room, auth_user
    WHERE tenants_tenant.id = tenants_room.tenant_id
    AND tenants_room.owner_id = auth_user.id""")
    
  4. Другие ...

1 Ответ

3 голосов
/ 27 января 2011

Правильный способ сделать это с помощью related_name:

class Tenant(models.Model):
    name = models.CharField(max_length=100)

class Room(models.Model):
    owner = models.ForeignKey(User, related_name='owns')
    maintainer = models.ForeignKey(User, related_name='maintains')
    tenant = models.ForeignKey(Tenant)

Тогда вы можете сделать это:

jrb = User.objects.create(username='jrb')
bill = User.objects.create(username='bill')
bob = models.Tenant.objects.create(name="Bob")
models.Room.objects.create(owner=jrb, maintainer=bill, tenant=bob)

User.objects.filter(owns__tenant=bob)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...