Найти все объекты определенного класса, которые не имеют активных связей с другими объектами
У меня есть класс A
, который используется как внешний ключ во многих других классах.
class A(models.Model):
pass
class B(models.Model):
a: A = ForeignKey(A)
class C(models.Model):
other_name: A = ForeignKey(A)
Сейчас у меня есть база данных с огромной таблицей объектов A
и множеством классов, таких как B
и C
, которые ссылаются на A
(скажем, потенциально десятки). В этой таблице много объектов (100k+), и я хочу очистить все объекты, на которые активно не ссылаются другие объекты с внешним ключом. Например, на object 1
из класса A
не ссылаются классы B
и C
.
Как мне это сделать? Я уже придумал следующий код:
a_list: list = list()
classes: list[tuple] = [(B, "a"), (C, "other_name")]
for cl, field in classes:
field_object: Field = cl._meta.get_field(field)
for obj in cl.objects.all():
a: A = field_object.value_from_object(obj)
a_list.append(a)
to_remove: list[A] = [a for a in A.objects.all() if a not in a_list]
for a in to_remove():
a.remove()
В связи с этим у меня возникает несколько вопросов:
- Что если я не знаю полного списка классов и полей (случай, так как это большая группа)?
- Является ли это наиболее эффективным способом для большой таблицы с большим количеством несвязанных объектов (скажем, 95%)? Я думаю, что я могу оптимизировать это намного больше.
Вы можете фильтровать с помощью:
A.objects.filter(b=None, c=None).delete()
Это позволит создать правильные JOIN и таким образом определить элементы за один запрос, без необходимости получения всех остальных записей модели из базы данных.
Но это в любом случае будет дорого, поскольку триггеры выполняются Django, который, таким образом, будет "собирать" все A
объекты.
Если вы не знаете, на что ссылается A
, вы можете работать с мета моделью, так:
from django.db.models.fields.reverse_related import OneToOneRel
fields = {
f.related_query_name: None
for f in A._meta.get_fields()
if isinstance(f, ManyToOneRel)
}
A.objects.filter(**fields).delete()
Это будет искать все ForeignKey
и OneToOneField
от других моделей, которые направлены (непосредственно) на модель A
, затем делать LEFT OUTER JOIN
и фильтровать на NULL
, а затем удалять их.
Я бы посоветовал сначала осмотреть A.objects.filter(**fields)
однако, и убедиться, что вы не убрали все необходимые предметы.