Итак, у меня есть система бронирования. Агенты (люди и организации, отправляющие заказы) разрешены только для бронирования в категориях, которые мы им назначаем. Многие агенты могут назначать одни и те же категории. Это простой для многих. Вот представление о том, как выглядят модели:
class Category(models.Model):
pass
class Agent(models.Model):
categories = models.ManyToManyField('Category')
class Booking(models.Model):
agent = models.ForeignKey('Agent')
category = models.ForeignKey('Category')
Итак, когда приходит заказ, мы динамически выделяем категорию, на основе которой доступны агенту. Агент обычно не указывает.
Можно ли выбрать "Заказы", в которых "Booking.kategory" не находится в Booking.agent.categories?
Мы только что заметили, что по милости глупой ошибки администратора некоторым агентам разрешалось отправлять заказы в любую категорию. Это оставило нас с тысячами заказов в неправильном месте.
Я могу исправить это, но я могу заставить его работать только с помощью поиска вложенности:
for agent in Agent.objects.all():
for booking in Booking.objects.filter(agent=agent):
if booking.category not in agent.categories.all():
# go through the automated allocation logic again
Это работает, но он очень медленный. Это много данных, которые летают между базой данных и Django. Это тоже не одноразовый. Я хочу периодически проверять новые заказы, чтобы убедиться, что они находятся в правильном месте. Не кажется невозможным, чтобы произошла другая проблема с администратором, поэтому после проверки базы данных агента я хочу запросить Заказы, которые не входят в их категории агентов.
Опять же, вложенные запросы будут работать не так, как только наши наборы данных вырастут до миллионов (и далее), я бы хотел сделать это более эффективно.
Мне кажется, что это можно сделать с помощью поиска F()
, примерно так:
from django.db.models import F
bad = Booking.objects.exclude(category__in=F('agent__categories'))
Но это не работает: TypeError: 'Col' object is not iterable
Я также пробовал .exclude(category=F('agent__categories'))
, и пока он более счастлив с синтаксисом, он не исключает "правильные" заказы.
Какова секретная формула для выполнения этого запроса F()
на M2M?
Чтобы скрыть то, что мне нужно, я создал репозиторий Github с этими моделями (и некоторые данные). Пожалуйста, используйте их для написания запроса. Нынешний единственный ответ на вопрос и проблема, которую я видел на моих "реальных" данных тоже.
git clone https://github.com/oliwarner/djangorelquerytest.git
cd djangorelquerytest
python3 -m venv venv
. ./venv/bin/activate
pip install ipython Django==1.9a1
./manage.py migrate
./manage.py shell
И в оболочке огонь в:
from django.db.models import F
from querytest.models import Category, Agent, Booking
Booking.objects.exclude(agent__categories=F('category'))
Это ошибка? Есть ли правильный способ достичь этого?