5

I'm trying to save a date in my sessions. I always receive the error Object of type 'datetime' is not JSON serializable. I found this here in the Django documentation: stored as seconds since epoch since datetimes are not serializable in JSON.

How can I save my expiry_date as seconds instead of datetime?

code = social_ticketing_form.cleaned_data['a']
expiry_date = timezone.now() + timezone.timedelta(days=settings.SOCIAL_TICKETING_ATTRIBUTION_WINDOW)
request.session[social_ticketing_cookie_name(request.event)] = {'code': code, 'expiry_date': expiry_date}
4
  • you need to decide on a serialization method. You could transform it in unixtimestamp (number of seconds since EPOCH long/int) or you could transform it in a string using strftime and afterwards use strptime to transform it back in datetime Commented Jan 31, 2019 at 19:52
  • expiry_date.timestamp()? Commented Jan 31, 2019 at 19:53
  • check this for indications Commented Jan 31, 2019 at 19:54
  • check this post Commented Jan 31, 2019 at 19:58

1 Answer 1

8

Either write your own session serialiser to allow you to serialise datetime objects directly, or store the datetime value in some other form.

If you want to save it as seconds, then use the datetime.timestamp() method:

request.session[social_ticketing_cookie_name(request.event)] = {
    'code': code, 
    'expiry_date': expiry_date.timestamp()
}

Your own SESSION_SERIALIZER class only needs to provide loads and dumps methods, directly analogous to json.loads() and json.dumps() (which is how the standard JSON serializer is implemented).

If you want to encode datetime objects and be able to transparently turn those back into datetime objects again, I'd use a nested object format to flag such values as special:

from datetime import datetime

class JSONDateTimeSerializer:
    @staticmethod
    def _default(ob):
        if isinstance(ob, datetime):
            return {'__datetime__': ob.isoformat()}
        raise TypeError(type(ob))

    @staticmethod
    def _object_hook(d):
        if '__datetime__' in d:
            return datetime.fromisoformat(d['__datetime__'])
        return d

    def dumps(self, obj):
        return json.dumps(
            obj, separators=(',', ':'), default=self._default
        ).encode('latin-1')

    def loads(self, data):
        return json.loads(
            data.decode('latin-1'), object_hook=self._object_hook
        )

and set SESSION_SERIALIZER to the full qualified name of the above module (path.to.module.JSONDateTimeSerializer).

The above uses the datetime.fromisoformat() method, new in Python 3.7.

Sign up to request clarification or add additional context in comments.

4 Comments

Dear Martijn, thanks a lot for the long answer. I try to go with the JSONDateTimeSerializer. I currently struggle with the code you wrote. I always receive 'EventDetail' object has no attribute '_default'. I used JSONDateTimeSerializer.dumps(self, expiry_date) Do you have any idea what's going wrong?
@JoeyCoder you need to register the class in your Django configuration, so it is used as the serialiser for sessions. Don’t use it directly, or if you must, then at least create an instance of the class: JSONDateTimeSerializer().dumps(expiry_date).
@JoeyCoder But really, don’t use it for manual serialisation. Just store expirely_date.isoformat() directly if you don’t want to set SESSION_SERIALIZER.
Just struggled for the last hour until I realised I'm running on Python 3.6. That explains why it didn't work. At least I know what to do now! Thanks a lot, great help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.