Reputation: 2800
I have a class A
which is used as a Foreign Key in many other classes.
class A(models.Model):
pass
class B(models.Model):
a: A = ForeignKey(A)
class C(models.Model):
other_name: A = ForeignKey(A)
Now I have a database with a huge table of A
objects and many classes like B
and C
who reference A
(say potentially dozens). In this table, there are many objects (100k+) and I want to clean up all objects that are not actively referenced by other objects with a Foreign Key. For example, object 1
of class A
is not referenced by class B
and C
.
How would I do this? I already came up with the following code:
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()
This leaves me with a few questions:
Upvotes: 1
Views: 25
Reputation: 477533
You can filter with:
A.objects.filter(b=None, c=None).delete()
This will make proper JOINs and thus determine the items in a single querying, without having to fetch all other model records from the database.
But this will be expensive anyway, since the triggers are done by Django that will thus "collect" all A
objects.
If you do not know what is referencing A
, you can work with the meta of the model, so:
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()
This will look for all ForeignKey
s and OneToOneField
s from other models that target (directly) the A
model, then make LEFT OUTER JOIN
s and filter on NULL
, and then delete those.
I would advise to first inspect A.objects.filter(**fields)
however, and make sure you do not remove any items that are still necessary.
Upvotes: 1