Skip to content

Commit

Permalink
added the formaters on Views, some refactoring, in the process of wri…
Browse files Browse the repository at this point in the history
…ting the tutorial.
  • Loading branch information
Yohann Gabory committed Oct 15, 2013
1 parent 9b8c044 commit 076dbe9
Show file tree
Hide file tree
Showing 36 changed files with 1,771 additions and 436 deletions.
Binary file not shown.
Binary file modified docs/build/.doctrees/environment.pickle
Binary file not shown.
Binary file added docs/build/.doctrees/first_step.doctree
Binary file not shown.
Binary file modified docs/build/.doctrees/references.doctree
Binary file not shown.
Binary file added docs/build/.doctrees/related_ressources.doctree
Binary file not shown.
Binary file added docs/build/.doctrees/representing_data.doctree
Binary file not shown.
Binary file modified docs/build/.doctrees/tutorial.doctree
Binary file not shown.
Binary file modified docs/build/.doctrees/using_user_endpoint.doctree
Binary file not shown.
56 changes: 56 additions & 0 deletions docs/build/_sources/adding_validator_datastore.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Adding validators to your DataStore
===================================

In this exemple, you want to check that a user with the same last_name
and same first_name does not exist in your datastore before creating a
new user.

For this you can use UniqueTogether:

UniqueTogether
--------------

Change your UserEndPoint to get:

.. code-block:: python

from rest_api_framework.datastore.validators import UniqueTogether

class UserEndPoint(Controller):
ressource = {
"ressource_name": "users",
"ressource": {"name": "adress_book.db", "table": "users"},
"model": UserModel,
"datastore": SQLiteDataStore,
"options":{"validators": [UniqueTogether("first_name", "last_name")]}
}

controller = {
"list_verbs": ["GET", "POST"],
"unique_verbs": ["GET", "PUT", "DElETE"]
}

view = {"response_class": JsonResponse}

each of ressource, controller and views can have various options to
add new functionality to them. The "validators" option of ressource
enable some datastore based validators. As you can see, validators are
a list. This meen that you can add many validators for a single datastore.

UniqueTogether will ensure that a user with first_name: John and
last_name: Doe cannot be created.

Let's try:

.. code-block:: python

curl -i -H "Content-type: application/json" -X POST -d '{"first_name": "John", "last_name": "Doe"}' http://localhost:5000/users/
HTTP/1.0 400 BAD REQUEST
Content-Type: application/json
Content-Length: 57
Server: Werkzeug/0.8.3 Python/2.7.2
Date: Mon, 14 Oct 2013 17:13:41 GMT

{"error": "first_name,last_name must be unique together"}

Next: :doc:`representing_data`
125 changes: 125 additions & 0 deletions docs/build/_sources/first_step.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
First Step Building a user endpoint
===================================

For this project we need users. Users will be helpfull for our adress
book and for our authentication process.

Users will be define with at least a first name and a last name. We
also need an unique identifier to retreive the user.

Define a model
~~~~~~~~~~~~~~

.. code-block:: python

from rest_api_framework import models

class UserModel(models.Model):

fields = [models.StringField(name="first_name", required=True),
models.StringField(name="last_name", required=True),
models.PkField(name="id", required=True)
]

The use of required_true will ensure that a user without this field
cannot be created

Chose a DataStore
~~~~~~~~~~~~~~~~~

We also need a datastore to get a place where we can save our
users. For instance we will use a sqlite3 database. The
SQLiteDataStore is what we need

.. code-block:: python

from rest_api_framework.datastore import SQLiteDataStore

Chose a view
~~~~~~~~~~~~

We want results to be rendered as Json. We use the JsonResponse view
for that:

.. code-block:: python

from rest_api_framework.views import JsonResponse

Create The user endpoint
~~~~~~~~~~~~~~~~~~~~~~~~

To create an endpoint, we need a controller. This will manage our
endpoint in a RESTFUL fashion.

.. code-block:: python

from rest_api_framework.controllers import Controller

class UserEndPoint(Controller):
ressource = {
"ressource_name": "users",
"ressource": {"name": "adress_book.db", "table": "users"},
"model": UserModel,
"datastore": SQLiteDataStore
}

controller = {
"list_verbs": ["GET", "POST"],
"unique_verbs": ["GET", "PUT", "DElETE"]
}

view = {"response_class": JsonResponse}

then we must run our application:

.. code-block:: python

if __name__ == '__main__':
from werkzeug.serving import run_simple
from rest_api_framework.controllers import WSGIDispatcher
app = WSGIDispatcher([UserEndPoint])
run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)

Summary
~~~~~~~

So far, all of the code should look like this:

.. code-block:: python

from rest_api_framework import models
from rest_api_framework.datastore import SQLiteDataStore
from rest_api_framework.views import JsonResponse
from rest_api_framework.controllers import Controller


class UserModel(models.Model):

fields = [models.StringField(name="first_name", required=True),
models.StringField(name="last_name", required=True),
models.PkField(name="id", required=True)
]


class UserEndPoint(Controller):
ressource = {
"ressource_name": "users",
"ressource": {"name": "adress_book.db", "table": "users"},
"model": UserModel,
"datastore": SQLiteDataStore
}

controller = {
"list_verbs": ["GET", "POST"],
"unique_verbs": ["GET", "PUT", "DElETE"]
}

view = {"response_class": JsonResponse}

if __name__ == '__main__':
from werkzeug.serving import run_simple
from rest_api_framework.controllers import WSGIDispatcher
app = WSGIDispatcher([UserEndPoint])
run_simple('127.0.0.1', 5000, app, use_debugger=True, use_reloader=True)

Next: :doc:`using_user_endpoint`
5 changes: 5 additions & 0 deletions docs/build/_sources/related_ressources.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Linking ressource together
==========================

Address Book need to link address to user as you can have multiple
users with a single address (a familliy for example)
82 changes: 82 additions & 0 deletions docs/build/_sources/representing_data.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
Show data to users
==================

The view you have used so far just added a ressource_uri. But preserve
the id attribut. As id is an internal representation of the data you
may wich to remove it.

Define a formater function
--------------------------

To do so you'll have to write a simple function to plug on the
view. This function is a formater. When the View instanciate the
formater, it give you access to the response object and the object to
be rendered.

Because you want to remove the id of the reprensentaion of your
ressource, you can write:

.. code-block:: python

def remove_id(response, obj):
obj.pop("id")
return obj

and change the view part of your UserEndPoint as follow:

.. code-block:: python

view = {"response_class": JsonResponse,
"options": {"formaters": ["add_ressource_uri",
remove_id]}}

add_ressource_uri is the default formatter for this View. You dont
need to remove it for now. But if you try, then it will work as
expected. The ressource_uri field will be removed.

The idea behind Python REST API Framework is to always get out of
your way.

You can check that it work as expected:

.. code-block:: bash

curl -i "http://localhost:5000/users/1/"
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 80
Server: Werkzeug/0.8.3 Python/2.7.2
Date: Mon, 14 Oct 2013 23:41:55 GMT

{"first_name": "Cap tain", "last_name": "America",
"ressource_uri": "/users/1/"}

Make things generics
--------------------

This implementation work on your endpoint because you each object has
an id. But, if later you create another endpoint with ressources
lacking the "id" key, you'll have to re-write your function.

Instead, you can take advantage of the response wich is part of the
parameters of your function.

response object carry the attribut model who define your ressources
fields. You can then get the name of the Pk field used with this
ressource with:

.. code-block:: python

response.model.pk_field.name

Your code then become:

.. code-block:: python

def remove_id(response, obj):
obj.pop(response.model.pk_field.name)
return obj

And reuse this formatter as long as you need

Next :doc:`related_ressources`
Loading

0 comments on commit 076dbe9

Please sign in to comment.