Spoiler: My main point in this post is not given away by the title.
But first things first: What are all those words?
<span id="more-4316"></span>
* *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:
```bash
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):
```python
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):
```python
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:
```python
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'}
)
```
4 Replies to “Python: post JSON data containing a datetime object with requests to flask”