Skip to content

Commit

Permalink
update with images
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffknupp committed Aug 21, 2013
1 parent ca1f640 commit 15f2834
Show file tree
Hide file tree
Showing 22 changed files with 353 additions and 19 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,15 @@ Let's start our new service and make a request:
]
```

Oh, that's not enough? You also want a Django-style admin interface built
automatically? Fine. Add one more line to the list of models to get access to
this:

![improved admin interface screenshot](/docs/images/admin_tracks_improved.jpg)

With **sandman**, (almost) zero boilerplate code is required. Your existing database
structure and schema is introspected and your database tables magically get a
RESTful API. For each table, Sandman creates:
RESTful API and admin interface. For each table, Sandman creates:

* proper endpoints
* support for a configurable set of HTTP verbs
Expand Down
11 changes: 10 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,18 @@ Let's start our new service and make a request:
}
]
Oh, that's not enough? You also want a Django-style admin interface
built automatically? Fine. Add one more line to the list of models to
get access to this:

.. figure:: /docs/images/admin_tracks_improved.jpg
:alt: improved admin interface screenshot

improved admin interface screenshot
With **sandman**, (almost) zero boilerplate code is required. Your
existing database structure and schema is introspected and your database
tables magically get a RESTful API. For each table, Sandman creates:
tables magically get a RESTful API and admin interface. For each table,
Sandman creates:

- proper endpoints
- support for a configurable set of HTTP verbs
Expand Down
4 changes: 2 additions & 2 deletions docs/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ administered via the admin interface.
Here's a look at the interface generated for the ``chinook`` database's
``Track`` table, listing the information about various music tracks:

![admin interface screenshot](/images/admin_tracks.jpg)
.. 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
Expand Down Expand Up @@ -82,4 +82,4 @@ more useful information. After making the changes below::
Now, we get much more useful information in the columns mentioned, as you can
see here:

![improved admin interface screenshot](/images/admin_tracks_improved.jpg)
.. image:: images/admin_tracks_improved.jpg
Binary file added docs/generated/.doctrees/admin.doctree
Binary file not shown.
Binary file modified docs/generated/.doctrees/environment.pickle
Binary file not shown.
Binary file modified docs/generated/.doctrees/index.doctree
Binary file not shown.
Binary file modified docs/generated/.doctrees/installation.doctree
Binary file not shown.
Binary file modified docs/generated/.doctrees/sandman.doctree
Binary file not shown.
Binary file modified docs/generated/.doctrees/sandman.test.doctree
Binary file not shown.
Binary file added docs/generated/_images/admin_tracks.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/generated/_images/admin_tracks_improved.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 27 additions & 2 deletions docs/generated/_modules/sandman/model.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,12 @@ <h1>Source code for sandman.model</h1><div class="highlight"><pre>
<span class="kn">from</span> <span class="nn">..</span> <span class="kn">import</span> <span class="n">db</span><span class="p">,</span> <span class="n">app</span>
<span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">current_app</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.ext.declarative</span> <span class="kn">import</span> <span class="n">declarative_base</span><span class="p">,</span> <span class="n">DeferredReflection</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.engine</span> <span class="kn">import</span> <span class="n">reflection</span>
<span class="kn">from</span> <span class="nn">sqlalchemy.orm</span> <span class="kn">import</span> <span class="n">relationship</span>
<span class="kn">from</span> <span class="nn">flask.ext.admin</span> <span class="kn">import</span> <span class="n">Admin</span>
<span class="kn">from</span> <span class="nn">flask.ext.admin.contrib.sqlamodel</span> <span class="kn">import</span> <span class="n">ModelView</span>

<div class="viewcode-block" id="register"><a class="viewcode-back" href="../../sandman.html#sandman.model.register">[docs]</a><span class="k">def</span> <span class="nf">register</span><span class="p">(</span><span class="n">cls</span><span class="p">):</span>
<div class="viewcode-block" id="register"><a class="viewcode-back" href="../../sandman.html#sandman.model.register">[docs]</a><span class="k">def</span> <span class="nf">register</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">use_admin</span><span class="o">=</span><span class="bp">True</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Register with the API a :class:`sandman.model.Model` class and associated</span>
<span class="sd"> endpoint.</span>

Expand All @@ -68,20 +72,41 @@ <h1>Source code for sandman.model</h1><div class="highlight"><pre>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">current_app</span><span class="p">,</span> <span class="s">&#39;endpoint_classes&#39;</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
<span class="n">current_app</span><span class="o">.</span><span class="n">endpoint_classes</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">current_app</span><span class="o">.</span><span class="n">classes_by_name</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="p">(</span><span class="nb">list</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">)):</span>
<span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">cls</span><span class="p">:</span>
<span class="n">current_app</span><span class="o">.</span><span class="n">endpoint_classes</span><span class="p">[</span><span class="n">entry</span><span class="o">.</span><span class="n">endpoint</span><span class="p">()]</span> <span class="o">=</span> <span class="n">entry</span>
<span class="n">current_app</span><span class="o">.</span><span class="n">classes_by_name</span><span class="p">[</span><span class="n">entry</span><span class="o">.</span><span class="n">__name__</span><span class="p">]</span> <span class="o">=</span> <span class="n">entry</span>
<span class="n">entry</span><span class="o">.</span><span class="n">_use_admin</span> <span class="o">=</span> <span class="n">use_admin</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">current_app</span><span class="o">.</span><span class="n">endpoint_classes</span><span class="p">[</span><span class="n">cls</span><span class="o">.</span><span class="n">endpoint</span><span class="p">()]</span> <span class="o">=</span> <span class="n">cls</span>
<span class="n">current_app</span><span class="o">.</span><span class="n">classes_by_name</span><span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">__name__</span><span class="p">,</span> <span class="n">cls</span><span class="p">)</span>
<span class="n">cls</span><span class="o">.</span><span class="n">_use_admin</span> <span class="o">=</span> <span class="n">use_admin</span>
<span class="n">Model</span><span class="o">.</span><span class="n">prepare</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">engine</span><span class="p">)</span>
</div>
<span class="k">def</span> <span class="nf">prepare_relationships</span><span class="p">():</span>
<span class="n">inspector</span> <span class="o">=</span> <span class="n">reflection</span><span class="o">.</span><span class="n">Inspector</span><span class="o">.</span><span class="n">from_engine</span><span class="p">(</span><span class="n">db</span><span class="o">.</span><span class="n">engine</span><span class="p">)</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="k">for</span> <span class="n">class_name</span><span class="p">,</span> <span class="n">cls</span> <span class="ow">in</span> <span class="n">current_app</span><span class="o">.</span><span class="n">classes_by_name</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">for</span> <span class="n">foreign_key</span> <span class="ow">in</span> <span class="n">inspector</span><span class="o">.</span><span class="n">get_foreign_keys</span><span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">__tablename__</span><span class="p">):</span>
<span class="n">other</span> <span class="o">=</span> <span class="n">current_app</span><span class="o">.</span><span class="n">classes_by_name</span><span class="p">[</span><span class="n">foreign_key</span><span class="p">[</span><span class="s">&#39;referred_table&#39;</span><span class="p">]]</span>
<span class="nb">setattr</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">cls</span><span class="o">.</span><span class="n">__tablename__</span><span class="p">,</span> <span class="n">relationship</span><span class="p">(</span><span class="n">cls</span><span class="o">.</span><span class="n">__tablename__</span><span class="p">,</span> <span class="n">backref</span><span class="o">=</span><span class="n">other</span><span class="o">.</span><span class="n">__tablename__</span><span class="p">))</span>

<span class="k">def</span> <span class="nf">activate_admin_classes</span><span class="p">():</span>
<span class="n">prepare_relationships</span><span class="p">()</span>
<span class="n">admin</span> <span class="o">=</span> <span class="n">Admin</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>
<span class="k">with</span> <span class="n">app</span><span class="o">.</span><span class="n">app_context</span><span class="p">():</span>
<span class="k">for</span> <span class="n">cls</span> <span class="ow">in</span> <span class="p">(</span><span class="n">cls</span> <span class="k">for</span> <span class="n">cls</span> <span class="ow">in</span> <span class="n">current_app</span><span class="o">.</span><span class="n">classes_by_name</span><span class="o">.</span><span class="n">values</span><span class="p">()</span> <span class="k">if</span> <span class="n">cls</span><span class="o">.</span><span class="n">_use_admin</span> <span class="o">==</span> <span class="bp">True</span><span class="p">):</span>
<span class="n">admin</span><span class="o">.</span><span class="n">add_view</span><span class="p">(</span><span class="n">ModelView</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">db</span><span class="o">.</span><span class="n">session</span><span class="p">))</span>


<span class="c"># Redefine &#39;Model&#39; to be a sqlalchemy.ext.declarative.api.DeclarativeMeta</span>
<span class="c"># object which also derives from sandman.models.Model. The naming is done for</span>
<span class="c"># documentation/clarity purposes. Previously the line below had Model deriving</span>
<span class="c"># from DeferredReflection and &quot;Resource&quot;, which was the exact same class</span>
<span class="c"># as &quot;Model&quot; in models.py. It caused confusion in the documentation, however,</span>
<span class="c"># since it wasn&#39;t clear that the Model class and the Resource class were</span>
<span class="c"># actually the same thing.</span></div>
<span class="c"># actually the same thing.</span>
<span class="n">Model</span> <span class="o">=</span> <span class="n">declarative_base</span><span class="p">(</span><span class="n">cls</span><span class="o">=</span><span class="p">(</span><span class="n">Model</span><span class="p">,</span> <span class="n">DeferredReflection</span><span class="p">))</span>
</pre></div>

Expand Down
5 changes: 4 additions & 1 deletion docs/generated/_modules/sandman/model/models.html
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ <h1>Source code for sandman.model.models</h1><div class="highlight"><pre>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="k">for</span> <span class="n">column</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">__table__</span><span class="o">.</span><span class="n">columns</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">column</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">from_dict</span><span class="p">(</span><span class="n">dictionary</span><span class="p">)</span></div></div>
<span class="bp">self</span><span class="o">.</span><span class="n">from_dict</span><span class="p">(</span><span class="n">dictionary</span><span class="p">)</span>
</div>
<span class="k">def</span> <span class="nf">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">primary_key</span><span class="p">()))</span></div>
</pre></div>

</div>
Expand Down
85 changes: 85 additions & 0 deletions docs/generated/_sources/admin.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
===========================
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/)
and some SQLAlchemy, erm, alchemy to allow your resources to be
administered via the admin interface.

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
``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.

To activate the admin interface (which adds an ``/admin`` endpoint to your
service, accessible via a browser), you'll need to call one additional function:
``model.activate_admin_classes``. 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.

Getting Richer Information for Related Objects
----------------------------------------------

The sharp-eyed among you may have noticed that the information presented for
``Album``, ``Genre``, and ``MediaType`` are not very helpful. By default, the
value that will be shown is the value returned by ``__str__`` on the
associated table. Currently, ``__str__`` simply returns the value of a Model's
``primary_key()`` attribute. By overriding ``__str__``, however, we can display
more useful information. After making the changes below::

from sandman.model import register, activate_admin_classes, Model

class Track(Model):
__tablename__ = 'Track'

def __str__(self):
return self.Name

class Artist(Model):
__tablename__ = 'Artist'

def __str__(self):
return self.Name

class Album(Model):
__tablename__ = 'Album'

def __str__(self):
return self.Title

class Playlist(Model):
__tablename__ = 'Playlist'

def __str__(self):
return self.Id

class Genre(Model):
__tablename__ = 'Genre'

def __str__(self):
return self.Name

class MediaType(Model):
__tablename__ = 'MediaType'

def __str__(self):
return self.Name

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

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

.. image:: images/admin_tracks_improved.jpg
1 change: 1 addition & 0 deletions docs/generated/_sources/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Contents:

installation
using_sandman
admin
sandman


Expand Down
Loading

0 comments on commit 15f2834

Please sign in to comment.