Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Knupp committed Apr 11, 2014
1 parent 3ed66ee commit f1f20f6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 31 deletions.
17 changes: 10 additions & 7 deletions docs/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,31 @@ The sandman Admin Interface
Activating the sandman Admin Interface
--------------------------------------

sandman supports an admin interface, much like the default Django admin
interface. sandman currently uses [Flask-Admin](https://flask-admin.readthedocs.org/en/latest/)
sandman supports an admin interface, much like the Django admin
interface. ``sandman`` currently uses [Flask-Admin](https://flask-admin.readthedocs.org/en/latest/)
and some SQLAlchemy, erm, alchemy to allow your resources to be
administered via the admin interface.
administered via the admin interface. Note, though, that the admin
interface may drastically change in the future.

Here's a look at the interface generated for the ``chinook`` database's
``Track`` table, listing the information about various music tracks:

.. image:: images/admin_tracks.jpg

Pretty nice! From here you can directly create, edit, and delete resources. In
the creation and editing forms, objects related via foreign key (e.g. a
the "create" and "edit" forms, objects related via foreign key (e.g. a
``Track``'s associated ``Album``) are auto-populated in a dropdown based on
available values. This ensures that all database constraints are honored when
making changes via the admin.

The admin interface (which adds an ``/admin`` endpoint to your
service, accessible via a browser), is enabled by default. To disable it, pass
`admin=False` as an argument in your call to `activate`.
``admin=False`` as an argument in your call to ``activate``.
By default, calling this function will make _all_ Models accessible in the admin.
If you'd like to prevent this, simply call ``register()`` with ``use_admin=False``
for whichever Model/Models you don't want to appear.
for whichever Model/Models you don't want to appear. Alternatively, you can
control if a model is viewable, editable, creatable, etc in the admin by
setting your class's ``__view__`` attribute to your own ``Admin`` class.

Getting Richer Information for Related Objects
----------------------------------------------
Expand Down Expand Up @@ -78,7 +81,7 @@ more useful information. After making the changes below::

register((Artist, Album, Playlist, Genre, Track, MediaType))

Now, we get much more useful information in the columns mentioned, as you can
we get much more useful information in the columns mentioned, as you can
see here:

.. image:: images/admin_tracks_improved.jpg
66 changes: 42 additions & 24 deletions docs/using_sandman.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ Here's what's required to create a RESTful API service from an existing database

*That's it.* ``sandman`` will then do the following:

* connect to your database and introspect it's contents
* create and launch a REST API service
* create an HTML admin interface
* *open your browser to the admin interface*
* Connect to your database and introspect it's contents
* Create and launch a RESTful API service
* Create an HTML admin interface
* *Open your browser to the admin interface*

That's right. Given a legacy database, ``sandman`` not only gives you a REST API,
it gives you a beautiful admin page and *opens your browser to the admin page*.
Expand All @@ -39,8 +39,8 @@ SQLAlchemy (http://www.sqlalchemy.org). As of this writing, that includes:
* SAP Sybase SQL Anywhere
* MonetDB

Behind the Scenes
-----------------
Beyond `sandmanctl`
-------------------

``sandmanctl`` is really just a simple wrapper around the following::

Expand All @@ -50,22 +50,23 @@ Behind the Scenes

from sandman.model import activate

activate()
activate(browser=True)

app.run()

**You don't even need to tell ``sandman`` what tables your database contains.**
Just point ``sandman`` at your database and let it do all the heavy lifting
**Notice you don't even need to tell ``sandman`` what tables your database contains.**
Just point ``sandman`` at your database and let it do all the heavy lifting.

Let's start our new service and make a request. While we're at it, lets make use
If you put the code above into a file named ``runserver.py``, You can start this new
service and make a request. While we're at it, lets make use
of ``sandman``'s awesome filtering capability by specifying a filter term::

$ python runserver.py &
* Running on http://127.0.0.1:5000/

> curl GET "http://localhost:5000/artists?Name=AC/DC"

and we get::
you should see the following::

{
"resources": [
Expand All @@ -82,6 +83,11 @@ and we get::
]
}

If you were to leave off the filtering term, you would get **all** results from
the ``Artist`` table. You can also *paginate* these results by specifying ``?page=2``
or something similar. The number of results returned per page is controlled by
the config value ``RESULTS_PER_PAGE``, which defualts to 20.

A Quick Guide to REST APIs
~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -91,13 +97,25 @@ Resources are sources of information, and the API is an interface to this inform
That is, resources are the actual "objects" manipulated by the API. In ``sandman``, each
row in a database table is considered a resource.

Groups of resources are called *collections*. In ``sandman``, each table in your
database is a collection. Collections can be queried and added to using the
appropriate *HTTP method*. ``sandman`` supports the following HTTP methods::

* GET
* POST
* PUT
* DELETE
* PATCH

Support for the ``HEAD`` and ``OPTIONS`` methods is underway.

Creating Models
---------------

A ``Model`` represents a table in your database. You control which tables to
expose in the API through the creation of classes which inherit from
:class:`sandman.model.models.Model`. The only attribute you must define in your
class is the ``__tablename__`` attribute. ``sandman`` uses this to map your
:class:`sandman.model.models.Model`. If you create a ``Model``, the only attribute you
must define in your class is the ``__tablename__`` attribute. ``sandman`` uses this to map your
class to the corresponding database table. From there, ``sandman`` is able to divine
all other properties of your tables. Specifically, ``sandman`` creates the
following:
Expand All @@ -116,7 +134,7 @@ following:
`models.py`
============

Creating a `models.py` file allows you to get even *more* out of ``sandman``. In the file,
Creating a `models.py` file allows you to get *even more* out of ``sandman``. In the file,
create a class that derives from `sandman.models.Model` for each table you want to
turn into a RESTful resource. Here's a simple example using the Chinook test database
(widely available online)::
Expand All @@ -143,7 +161,7 @@ turn into a RESTful resource. Here's a simple example using the Chinook test dat


``sandman`` Advanced Usage
---------------------------
--------------------------

Hooking up Models
~~~~~~~~~~~~~~~~~
Expand All @@ -154,10 +172,10 @@ this class is modeling. It has *no default* and is *required* for all classes.
Providing a custom endpoint
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In the code above, we created 4 :class:`sandman.model.models.Model` classes that
correspond to tables in our database. If we wanted to change the endpoint for a
class (the default endpoint is simply the class's name pluralized), we would do
so by setting the ``__endpoint__`` attribute in the definition of the class::
In the code above, we created four :class:`sandman.model.models.Model` classes that
correspond to tables in our database. If we wanted to change the HTTP endpoint for
one of the models (the default endpoint is simply the class's name pluralized in lowercase),
we would do so by setting the ``__endpoint__`` attribute in the definition of the class::

class Genre(Model):
__tablename__ = 'Genre'
Expand All @@ -172,8 +190,8 @@ Restricting allowable methods on a resource
Many times, we'd like to specify that certain actions can only be carried out
against certain types of resources. If we wanted to prevent API users from
deleting any ``Genre`` resources, for example, we could specify this implicitly
by defining the ``__methods__`` attribute and leaving it out, like so::

by defining the ``__methods__`` attribute and leaving out the ``DELETE`` method,
like so::

class Genre(Model):
__tablename__ = 'Genre'
Expand All @@ -188,7 +206,7 @@ Performing custom validation on a resource

Specifying which HTTP methods are acceptable gives rather coarse control over
how a user of the API can interact with our resources. For more granular
control, custom validation functions can be specified. To do so, simply define a
control, custom a validation function can be specified. To do so, simply define a
static method named ``validate_<METHOD>``, where ``<METHOD>`` is the HTTP method
the validation function should validate. To validate the ``POST`` method on
``Genres``, we would define the method ``validate_POST``, like so::
Expand Down Expand Up @@ -225,7 +243,7 @@ admin interface for a model by setting the `__view__` attribute to a class that
from `flask.ext.admin.contrib.sqla.ModelView`. The Flask-Admin's documentation should be
consulted for the full list of attributes that can be configured.

Below, we create a model and, additionally, tell ``sandman`` that we want the tables
Below, we create a model and, additionally, tell ``sandman`` that we want the table's
primary key to be displayed in the admin interface (by default, a table's primary keys
aren't shown)::

Expand Down Expand Up @@ -261,4 +279,4 @@ Project Layout

In a "real" project, you should divide the code into at least two files: one with the
``Model`` definitions (``models.py``) and the other with the configuration
and the ``app.run()`` call (``runserver.py``).
and the ``app.run()`` which I call (``runserver.py``).

0 comments on commit f1f20f6

Please sign in to comment.