As usual this will be a hands-on tutorial. You can just read it or execute all steps in parallel. I recommend practicing in parallel to get an instant feeling for behaviour and you can easily play around and test some more cases that are relevant for you.
In this tutorial you will learn how to
The primary use-case for this is an API that you want to expose for other applications using your service. Often this will be useful even if your app has an HTML user interface. Think e.g. of a webshop that has a GUI but also offers an API where you can query the shop or place orders from other apps just by retrieving or sending JSON.
Why JSON, XML and CSV ?
Good question. In PythonOnWheels these three formats are the default formats (besides rendering HTML views) that any model can be serialized to by default. These are implemented because they are the most commonly these days. There will be many cases where you can access data as csv (think of all the Excel files out there) or retrive data from other services and APIs (mostly) as JSON these days. XML is very common in enterprise environments ...
But anyway. If you know how to respond one of those, you can easily write your own encoder to send anything you like.
python generate_model.py -n todo -t tinydb
As you know this generates a todo.py model file in your models\tinydb directory, which looks like this.
class Todo(TinyModel):
#
# Use the cerberus schema style
# which offer you immediate validation with cerberus
# http://docs.python-cerberus.org/en/stable/validation-rules.html
# types: http://docs.python-cerberus.org/en/stable/validation-rules.html#type
#
schema = {
'title' : { 'type' : 'string', 'maxlength' : 35 },
'text' : { 'type' : 'string' },
'tags' : { 'type' : 'list', "default" : [] },
"votes" : { "type" : "integer", "default" : 0 }
}
We will leave the schema definition unchanged in this case since the focus is to respond JSON, XML and CSV.
python generate_handler.py -n todo -t tinydb --rest
This will create a handler for us with the following nice features:
That's pretty nice for a one-liner !
We generated our model and access to the embedded tinyDB is preconfiguired for us in the todo/config.py file (section database.tinydb)
so open a python shell in your virtualenv and execute the following to produce 10 todos.
>>> from models.tinydb.todo import Todo
Create 10 todos in a loop:
>>> for x in range(0,10):
... t=Todo()
... t.title="todo #" +str(x)
... t.upsert()
...
insert, new eid: 1
insert, new eid: 2
insert, new eid: 3
insert, new eid: 4
insert, new eid: 5
insert, new eid: 6
insert, new eid: 7
insert, new eid: 8
insert, new eid: 9
insert, new eid: 10
>>>
In PythonOnwheels there are two ways where the server checks which format a clients request wants:
Both are absolutely valid. The .format is actually checked first.
Nothing easier than this. Every PythonOnWheels model instance can encode (or serialize) itself to JSON, CSV or XML. The flow is always the same. The model converts it's data to JSON and calls and encoder to transform into the format you want. Because PythonOnWheels altready includes encoders for XML and CSV you can use those out of the box (batteries included ;)
Before we go into the details of where the encoders are, where the definition is which encoder to choose for which format and how you can extend that with your own encoders let's first check that this really works.
python server.py
You will see that the server will wait for requests on localhost:8080
I will use curl to give you a more raw view and the superb Insomnia REST Client to show you the call and results in much a nicer formatted way.
curl -H "Accept: application/json" -X GET http://localhost:8080/todo
{"message": "todo, index", "http_status": 200, "prev": null, "next": null, "data": [{"title": "todo #0", "text": "", "tags": [], "votes": 0, "id": "0044e328-b544-4d5d-baa9-1d2343b6fe1e", "_uuid": "0044e328-b544-4d5d-baa9-1d2343b6fe1e", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #1", "text": "", "tags": [], "votes": 0, "id": "9f9769d6-ee50-4938-8633-c0b8452ebddf", "_uuid": "9f9769d6-ee50-4938-8633-c0b8452ebddf", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #2", "text": "", "tags": [], "votes": 0, "id": "aa057a26-5c87-4acf-9811-b8c8d514edee", "_uuid": "aa057a26-5c87-4acf-9811-b8c8d514edee", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #3", "text": "", "tags": [], "votes": 0, "id": "e9549346-50d4-4b28-bce9-5ce2064175aa", "_uuid": "e9549346-50d4-4b28-bce9-5ce2064175aa", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #4", "text": "", "tags": [], "votes": 0, "id": "7d976057-f125-4c6d-8336-3e6a8dcc557a", "_uuid": "7d976057-f125-4c6d-8336-3e6a8dcc557a", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #5", "text": "", "tags": [], "votes": 0, "id": "e5121361-cded-40c4-a34f-96d128884e7a", "_uuid": "e5121361-cded-40c4-a34f-96d128884e7a", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #6", "text": "", "tags": [], "votes": 0, "id": "ec796ee5-97d7-4f73-83a4-b70fe39337c1", "_uuid": "ec796ee5-97d7-4f73-83a4-b70fe39337c1", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #7", "text": "", "tags": [], "votes": 0, "id": "a329337e-e7a2-4720-8866-027ed71bd1db", "_uuid": "a329337e-e7a2-4720-8866-027ed71bd1db", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #8", "text": "", "tags": [], "votes": 0, "id": "2e8f3699-bd50-4681-9952-8bf2c32ca04c", "_uuid": "2e8f3699-bd50-4681-9952-8bf2c32ca04c", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}, {"title": "todo #9", "text": "", "tags": [], "votes": 0, "id": "3c3a96f3-8358-4064-a46d-d9c339f4b9f9", "_uuid": "3c3a96f3-8358-4064-a46d-d9c339f4b9f9", "created_at": "2019-08-15 14:37:31", "last_updated": "2019-08-15 14:37:31"}]}
Not very beutifully formatted but clearly a JSON response of our 10 todos.
Just change the Accept Header from application/json to
application/xml
Again: just change the Accept header to:
application/csv
Nice and simple. Just works.
For JSON and XML you can see that the actual format is not only responding with the pure data but with a schema like this:
{
"message": "todo, index",
"http_status": 200,
"prev": null,
"next": null,
"data": [...]
}
<root>
<message type="str">todo, index</message>
<http_status type="int">200</http_status>
<prev type="null"></prev>
<next type="null"></next>
<data type="list">
<item type="dict">
</item>
...
</data>
</root>
This makes no sense for CSV so you just get the data here.