Spoiler: My main point in this post is not given away by the title.
But first things first: What are all those words?
* *Flask* is a Microframework for web applications often used for REST(-like) APIs.
* *requests* is simply the best library to make HTTP requests there is.
* *JSON* (JavaScript Object Notation) is a file format I prefer to XML in almost all of the case.
* And finally *POST* is a HTTP verb often used in the context of REST to create new entry in a database.
In Flask one can simply call
request.get_json()
if POST
ed content is encoded in JSON. On the client side of things, one can query such a POST
endpoint for example with the following shell command:
curl -X POST https://example.com/ -d @some-file.json --header 'Content-Type: application/json'
The requests library has the post
function which accepts data through the json
kwarg. The library then takes care of a few things (like setting the content type for example):
some_data = {'foo': 123}
requests.post('https://example.com/api', json=some_data)
All dandy so far. But things break when the some_data
contains a datetime.datetime
object. You will get the well-googled exception TypeError: datetime.datetime(1985, 1, 26, 0, 0, 0) is not JSON serializable
. The behavior is exactly the same as if you would call json.dumps
on such data. Of course there is a way to tell dumps
how to encode this in a sane way (copying & pasting from StackOverflow here):
from datetime import datetime
import json
class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return o.isoformat()
return json.JSONEncoder.default(self, o)
json.dumps({'foo': datetime(1985, 1, 26, 0, 0, 0)}, cls=DateTimeEncoder)
outputs {"foo": "1985-01-26T00:00:00"}
which looks perfect.
The naive "enterprise software engineering" way would now of course be to make requests.post
accept a similar argument as cls
and pass that to the json.dumps
call that probably is buried somewhere deep in requests. There also is an issue on github requesting such a feature ((https://github.com/requests/requests/issues/3947)). But the developer of the library politely asked the OP to dumps
himself. And I really like that! It saves the requests library from bloating away and I strongly think more developers should reason like this more often.
All right. You got it. That was the point. You can go now.
Oh you landed here because of a google search for the title? Here you go:
import json
import requests
class DateTimeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime):
return o.isoformat()
return json.JSONEncoder.default(self, o)
r = requests.post(
'https://example.com/api',
data=json.dumps(out, cls=DateTimeEncoder),
headers={'Content-type': 'application/json'}
)
import datetime
instead offrom datetime import datetime
maybe?