1
0
mirror of https://github.com/django/django.git synced 2025-06-14 16:09:12 +00:00

PoC to replace SQL with ORM queries

This commit is contained in:
Hisham Mahmood 2024-03-21 19:52:56 +05:00
parent 6a37e9bfae
commit 5f38a7d7a6

View File

@ -3,40 +3,39 @@ import base64
import pickle import pickle
from datetime import datetime, timezone from datetime import datetime, timezone
from django.apps import apps
from django.conf import settings from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache from django.core.cache.backends.base import DEFAULT_TIMEOUT, BaseCache
from django.db import DatabaseError, connections, models, router, transaction from django.db import DatabaseError, connections, models, router, transaction
from django.db.models import Q
from django.utils.timezone import now as tz_now from django.utils.timezone import now as tz_now
class Options:
"""A class that will quack like a Django model _meta class.
This allows cache operations to be controlled by the router
"""
def __init__(self, table):
self.db_table = table
self.app_label = "django_cache"
self.model_name = "cacheentry"
self.verbose_name = "cache entry"
self.verbose_name_plural = "cache entries"
self.object_name = "CacheEntry"
self.abstract = False
self.managed = True
self.proxy = False
self.swapped = False
class BaseDatabaseCache(BaseCache): class BaseDatabaseCache(BaseCache):
def __init__(self, table, params): def __init__(self, table, params):
super().__init__(params) super().__init__(params)
self._table = table self._table = table
self.cache_model_class = self._get_cache_entry_model(self._table)
class CacheEntry: def _get_cache_entry_model(self, table):
_meta = Options(table) try:
model = apps.get_registered_model("django_cache", "cacheentry")
if table == model._meta.db_table:
return model
del apps.all_models["django_cache"]["cacheentry"]
except (LookupError, KeyError):
pass
self.cache_model_class = CacheEntry class CacheEntry(models.Model):
cache_key = models.CharField(max_length=255, unique=True, primary_key=True)
value = models.TextField()
expires = models.DateTimeField(db_index=True)
class Meta:
app_label = "django_cache"
db_table = table
return CacheEntry
class DatabaseCache(BaseDatabaseCache): class DatabaseCache(BaseDatabaseCache):
@ -61,33 +60,15 @@ class DatabaseCache(BaseDatabaseCache):
db = router.db_for_read(self.cache_model_class) db = router.db_for_read(self.cache_model_class)
connection = connections[db] connection = connections[db]
quote_name = connection.ops.quote_name rows = (
table = quote_name(self._table) self.cache_model_class.objects.using(db)
.filter(cache_key__in=list(key_map))
with connection.cursor() as cursor: .values_list("cache_key", "value", "expires")
cursor.execute( )
"SELECT %s, %s, %s FROM %s WHERE %s IN (%s)"
% (
quote_name("cache_key"),
quote_name("value"),
quote_name("expires"),
table,
quote_name("cache_key"),
", ".join(["%s"] * len(key_map)),
),
list(key_map),
)
rows = cursor.fetchall()
result = {} result = {}
expired_keys = [] expired_keys = []
expression = models.Expression(output_field=models.DateTimeField())
converters = connection.ops.get_db_converters(
expression
) + expression.get_db_converters(connection)
for key, value, expires in rows: for key, value, expires in rows:
for converter in converters:
expires = converter(expires, expression, connection)
if expires < tz_now(): if expires < tz_now():
expired_keys.append(key) expired_keys.append(key)
else: else:
@ -233,25 +214,14 @@ class DatabaseCache(BaseDatabaseCache):
def has_key(self, key, version=None): def has_key(self, key, version=None):
key = self.make_and_validate_key(key, version=version) key = self.make_and_validate_key(key, version=version)
db = router.db_for_read(self.cache_model_class) db = router.db_for_read(self.cache_model_class)
connection = connections[db] now = tz_now().replace(microsecond=0)
quote_name = connection.ops.quote_name
now = tz_now().replace(microsecond=0, tzinfo=None) return (
self.cache_model_class.objects.using(db)
with connection.cursor() as cursor: .filter(Q(cache_key=key) & Q(expires__gt=now))
cursor.execute( .exists()
"SELECT %s FROM %s WHERE %s = %%s and %s > %%s" )
% (
quote_name("cache_key"),
quote_name(self._table),
quote_name("cache_key"),
quote_name("expires"),
),
[key, connection.ops.adapt_datetimefield_value(now)],
)
return cursor.fetchone() is not None
def _cull(self, db, cursor, now, num): def _cull(self, db, cursor, now, num):
if self._cull_frequency == 0: if self._cull_frequency == 0: