mirror of https://github.com/django/django.git
Fixed #21146 - DatabaseCache converts expires to python value
DatabaseCache uses raw cursors to bypass the ORM. This prevents it from being used by database backends that require special handling of datetime values. There is no easy way to test this, so no tests added.
This commit is contained in:
parent
209de62d22
commit
d5606b5763
|
@ -11,6 +11,7 @@ except ImportError:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache.backends.base import BaseCache, DEFAULT_TIMEOUT
|
from django.core.cache.backends.base import BaseCache, DEFAULT_TIMEOUT
|
||||||
from django.db import connections, transaction, router, DatabaseError
|
from django.db import connections, transaction, router, DatabaseError
|
||||||
|
from django.db.backends.utils import typecast_timestamp
|
||||||
from django.utils import timezone, six
|
from django.utils import timezone, six
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
|
|
||||||
|
@ -65,8 +66,13 @@ class DatabaseCache(BaseDatabaseCache):
|
||||||
if row is None:
|
if row is None:
|
||||||
return default
|
return default
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
|
expires = row[2]
|
||||||
if row[2] < now:
|
if connections[db].features.needs_datetime_string_cast and not isinstance(expires, datetime):
|
||||||
|
# Note: typecasting is needed by some 3rd party database backends.
|
||||||
|
# All core backends work without typecasting, so be careful about
|
||||||
|
# changes here - test suite will NOT pick regressions here.
|
||||||
|
expires = typecast_timestamp(str(expires))
|
||||||
|
if expires < now:
|
||||||
db = router.db_for_write(self.cache_model_class)
|
db = router.db_for_write(self.cache_model_class)
|
||||||
cursor = connections[db].cursor()
|
cursor = connections[db].cursor()
|
||||||
cursor.execute("DELETE FROM %s "
|
cursor.execute("DELETE FROM %s "
|
||||||
|
@ -112,12 +118,21 @@ class DatabaseCache(BaseDatabaseCache):
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
b64encoded = b64encoded.decode('latin1')
|
b64encoded = b64encoded.decode('latin1')
|
||||||
try:
|
try:
|
||||||
|
# Note: typecasting for datetimes is needed by some 3rd party
|
||||||
|
# database backends. All core backends work without typecasting,
|
||||||
|
# so be careful about changes here - test suite will NOT pick
|
||||||
|
# regressions.
|
||||||
with transaction.atomic(using=db):
|
with transaction.atomic(using=db):
|
||||||
cursor.execute("SELECT cache_key, expires FROM %s "
|
cursor.execute("SELECT cache_key, expires FROM %s "
|
||||||
"WHERE cache_key = %%s" % table, [key])
|
"WHERE cache_key = %%s" % table, [key])
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
|
if result:
|
||||||
|
current_expires = result[1]
|
||||||
|
if (connections[db].features.needs_datetime_string_cast and not
|
||||||
|
isinstance(current_expires, datetime)):
|
||||||
|
current_expires = typecast_timestamp(str(current_expires))
|
||||||
exp = connections[db].ops.value_to_db_datetime(exp)
|
exp = connections[db].ops.value_to_db_datetime(exp)
|
||||||
if result and (mode == 'set' or (mode == 'add' and result[1] < now)):
|
if result and (mode == 'set' or (mode == 'add' and current_expires < now)):
|
||||||
cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
|
cursor.execute("UPDATE %s SET value = %%s, expires = %%s "
|
||||||
"WHERE cache_key = %%s" % table,
|
"WHERE cache_key = %%s" % table,
|
||||||
[b64encoded, exp, key])
|
[b64encoded, exp, key])
|
||||||
|
|
Loading…
Reference in New Issue