Skip to content

Commit

Permalink
Merge pull request #856 from ivelum/master
Browse files Browse the repository at this point in the history
Optimize deleted objects query
  • Loading branch information
etianen committed Jan 13, 2021
2 parents 1c0eeaa + 84972f0 commit cf7f58e
Showing 1 changed file with 46 additions and 14 deletions.
60 changes: 46 additions & 14 deletions reversion/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections import defaultdict
from itertools import chain, groupby

import django
from django.apps import apps
from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
Expand Down Expand Up @@ -133,20 +134,51 @@ def get_deleted(self, model, model_db=None):
model_db = model_db or router.db_for_write(model)
connection = connections[self.db]
if self.db == model_db and connection.vendor in ("sqlite", "postgresql", "oracle"):
model_qs = (
model._default_manager
.using(model_db)
.annotate(_pk_to_object_id=Cast("pk", Version._meta.get_field("object_id")))
.filter(_pk_to_object_id=models.OuterRef("object_id"))
)
subquery = (
self.get_for_model(model, model_db=model_db)
.annotate(pk_not_exists=~models.Exists(model_qs))
.filter(pk_not_exists=True)
.values("object_id")
.annotate(latest_pk=models.Max("pk"))
.values("latest_pk")
)
pk_field_name = model._meta.pk.name
object_id_cast_target = model._meta.get_field(pk_field_name)
if django.VERSION >= (2, 1):
# django 2.0 contains a critical bug that doesn't allow the code below to work,
# fallback to casting primary keys then
# see https://code.djangoproject.com/ticket/29142
if django.VERSION < (2, 2):
# properly cast autofields for django before 2.2 as it was fixed in django itself later
# see https://github.com/django/django/commit/ac25dd1f8d48accc765c05aebb47c427e51f3255
object_id_cast_target = {
"AutoField": models.IntegerField(),
"BigAutoField": models.BigIntegerField(),
}.get(object_id_cast_target.__class__.__name__, object_id_cast_target)
casted_object_id = Cast(models.OuterRef("object_id"), object_id_cast_target)
model_qs = (
model._default_manager
.using(model_db)
.filter(**{pk_field_name: casted_object_id})
)
else:
model_qs = (
model._default_manager
.using(model_db)
.annotate(_pk_to_object_id=Cast("pk", Version._meta.get_field("object_id")))
.filter(_pk_to_object_id=models.OuterRef("object_id"))
)
# conditional expressions are being supported since django 3.0
# DISTINCT ON works only for Postgres DB
if connection.vendor == "postgresql" and django.VERSION >= (3, 0):
subquery = (
self.get_for_model(model, model_db=model_db)
.filter(~models.Exists(model_qs))
.order_by("object_id", "-pk")
.distinct("object_id")
.values("pk")
)
else:
subquery = (
self.get_for_model(model, model_db=model_db)
.annotate(pk_not_exists=~models.Exists(model_qs))
.filter(pk_not_exists=True)
.values("object_id")
.annotate(latest_pk=models.Max("pk"))
.values("latest_pk")
)
else:
# We have to use a slow subquery.
subquery = self.get_for_model(model, model_db=model_db).exclude(
Expand Down

0 comments on commit cf7f58e

Please sign in to comment.