Encode/decode date and datetime in redis sessions

In several places, odoo sets a datetime object directly in the
session. It works with the default session handler of odoo which
uses pickle.

But datetime and date are not json serializable by default.

Add custom encoder / decoder to handle them (from
https://github.com/OCA/queue/blob/dc12a6a20ecfd15c5b90f9b089c9a64cf9d8bbe4/queue_job/fields.py#L57-L99)

See the discussion raised by @PCatinean on https://github.com/camptocamp/odoo-cloud-platform/pull/176
This commit is contained in:
Guewen Baconnier
2020-05-04 16:09:55 +02:00
parent 323158763b
commit 35fb727d6c
2 changed files with 47 additions and 2 deletions
+39
View File
@@ -0,0 +1,39 @@
# Copyright 2016-2020 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import json
from datetime import date, datetime
import dateutil
class SessionEncoder(json.JSONEncoder):
"""Encode date/datetime objects
So that we can later recompose them if they were stored in the session
"""
def default(self, obj):
if isinstance(obj, datetime):
return {"_type": "datetime_isoformat", "value": obj.isoformat()}
elif isinstance(obj, date):
return {"_type": "date_isoformat", "value": obj.isoformat()}
return json.JSONEncoder.default(self, obj)
class SessionDecoder(json.JSONDecoder):
"""Decode json, recomposing recordsets and date/datetime"""
def __init__(self, *args, **kwargs):
super().__init__(object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, obj):
if "_type" not in obj:
return obj
type_ = obj["_type"]
if type_ == "datetime_isoformat":
return dateutil.parser.parse(obj["value"])
elif type_ == "date_isoformat":
return dateutil.parser.parse(obj["value"]).date()
return obj
+8 -2
View File
@@ -6,6 +6,8 @@ import logging
from werkzeug.contrib.sessions import SessionStore
from . import json_encoding
# this is equal to the duration of the session garbage collector in
# odoo.http.session_gc()
DEFAULT_SESSION_TIMEOUT = 60 * 60 * 24 * 7 # 7 days in seconds
@@ -57,7 +59,9 @@ class RedisSessionStore(SessionStore):
"expiration of %s seconds for %s",
key, expiration, user_msg)
data = json.dumps(dict(session)).encode('utf-8')
data = json.dumps(
dict(session), cls=json_encoding.SessionEncoder
).encode('utf-8')
if self.redis.set(key, data):
return self.redis.expire(key, expiration)
@@ -79,7 +83,9 @@ class RedisSessionStore(SessionStore):
"returning a new one", key)
return self.new()
try:
data = json.loads(saved.decode('utf-8'))
data = json.loads(
saved.decode('utf-8'), cls=json_encoding.SessionDecoder
)
except ValueError:
_logger.debug("session for key '%s' has been asked but its json "
"content could not be read, it has been reset", key)