Как эффективно исключить уже назначенные объекты из набора запросов Django?
Я работаю над проектом на Django, где мне нужно фильтровать объекты на основе их статуса, при этом исключая те, которые уже назначены в другой модели.
У меня есть две модели:
CartObject
– Сохраняет все объекты.OnGoingProcess
– Отслеживает объекты, которые назначены в данный момент.
Каждая запись OnGoingProcess
имеет отношение OneToOneField
к CartObject
, что означает, что каждый объект может быть назначен только один раз.
Моя цель состоит в том, чтобы получить все объекты с определенным статусом, но исключить те, которые уже назначены в OnGoingProcess
.
Модели:
class CartObject(models.Model):
object_id = models.CharField(max_length=100, unique=True)
status = models.CharField(max_length=50, choices=[("pending", "Pending")])
# Other fields...
class OnGoingProcess(models.Model):
user = models.OneToOneField(DeliveryProfile, on_delete=models.CASCADE, related_name="ongoing_process")
associated_object = models.OneToOneField(CartObject, on_delete=models.CASCADE, related_name="associated_process", blank=True, null=True)
# Other fields...
<время работы/>
Текущий код просмотра:
@user_passes_test(lambda user: user.is_staff)
def process_manager_view(request):
# Get objects that are already assigned in OnGoingProcess
assigned_objects = OnGoingProcess.objects.values_list('associated_object', flat=True)
# Exclude objects that are already assigned
available_objects = CartObject.objects.filter(status="pending").exclude(id__in=assigned_objects).order_by("-id")
context = {
"available_objects": available_objects,
}
return render(request, "useradmin/available-objects.html", context)
<время работы/>
Проблема:
- Я использую
values_list('associated_object', flat=True)
для извлечения назначенных идентификаторов объектов. - Затем я использую
exclude(id__in=assigned_objects)
, чтобы отфильтровать эти объекты. - Является ли это наиболее эффективным способом? Или есть лучший метод Django ORM для достижения того же результата?
- Должен ли я использовать
Subquery()
,isnull=False
, или любой другой подход для повышения производительности?
Альтернативные решения, которые Я рассматривал:
Вариант 1: Использование isnull=False
available_objects = CartObject.objects.filter(status="pending").exclude(associated_process__isnull=False)
Плюсы: Простота, позволяет избежать лишних запросов.
Минусы: Не уверен, что это лучший подход для повышения производительности.
Вариант 2: Использование Subquery
from django.db.models import Subquery
assigned_objects = OnGoingProcess.objects.values('associated_object')
available_objects = CartObject.objects.filter(status="pending").exclude(id__in=Subquery(assigned_objects))
Плюсы: Оптимизирован для работы с большими наборами данных.
Минусы: Более сложный.
Вариант 3: Использование необработанного SQL (при необходимости)
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("""
SELECT id FROM useradmin_cartobject
WHERE status='pending'
AND id NOT IN (SELECT associated_object FROM useradmin_ongoingprocess)
ORDER BY id DESC
""")
result = cursor.fetchall()
available_objects = CartObject.objects.filter(id__in=[row[0] for row in result])
Плюсы: Повышение производительности при обработке больших объемов данных.
Минусы: Менее удобочитаемы, зависят от базы данных.
Вопрос:
Каков наилучший и наиболее эффективный подход Django ORM к фильтрации объектов, исключая те, которые уже назначены?
Будет ли рекомендуемым способом values_list()
, isnull=False
, или Subquery()
?
Существуют ли какие-либо соображения по поводу производительности при работе с большими наборами данных?
Ваш запрос:
available_objects = (
CartObject.objects.filter(status='pending')
.exclude(id__in=assigned_objects)
.order_by('-id')
)
будет использоваться подзапрос, поэтому запустите его следующим образом:
AND id NOT IN (SELECT associated_object FROM useradmin_ongoingprocess)
Вы можете проверить его с помощью:
print(available_objects.query)
Но в таких базах данных, как MySQL, это не самый эффективный вариант, нет.
Для большинства баз данных:
available_objects = CartObject.objects.filter(status='pending').filter(
associated_process=None
)
мы можем переписать это следующим образом:
available_objects = CartObject.objects.filter(
status='pending', associated_process=None
)
при этом будет сгенерирован тот же запрос, но код будет немного короче.
даст эффективные результаты.
Это работает на основе того факта, что LEFT OUTER JOIN
содержит строку NULL
для элементов, для которых в таблице нет соответствующего элемента для модели OnGoingProcess
. Тогда мы, таким образом, извлекаем только те, у которых есть NULL
, поэтому сохраняем только CartObject
без OnGoingProcess
.
Что касается необработанных запросов, то их обычно используют, если нет доступного ORM-эквивалента или когда его очень сложно создать. Необработанные запросы имеют дополнительные недостатки: QuerySet
может быть отфильтрован, разбит на страницы и т.д. принимая во внимание, что необработанный запрос - это "конечный продукт": вы не можете манипулировать им дальше, что ограничивает то, что вы можете делать с ним с помощью Django. Это также означает, что если вы измените соответствующие модели, например, добавите столбец, вам, вероятно, придется переписать исходные запросы, которые также работают с этими моделями.