Tutorial: Creating an administration interface with FormAlchemy/GeoFormAlchemy
******************************************************************************
Many web applications require an administration interface to edit a website's
content. Using `FormAlchemy `_ and
`GeoFormAlchemy <#todo>`_ it is really easy to set up an automatic admin interface.
FormAlchemy reads your SQLAlchemy models and automatically creates ready-to-use HTML
forms, which can be customized to your needs.
This document will describe how to set up an administration interface using FormAlchemy
and GeoFormAlchemy in an existing MapFish application.
Setup
=====
Requirements
------------
The following chapters assume that you have installed the MapFish framework and that you have
created a MapFish application for which you want to set up an admin interface.
Installation
------------
First you will have to install FormAlchemy and GeoFormAlchemy. This can be done using ``easy_install``.
Run the following two commands in your virtual Python environment::
(venv) $ easy_install formalchemy
(venv) $ easy_install geoformalchemy
Also make sure that you have installed the latest release of the library WebHelpers::
(venv) $ easy_install -U webhelpers
Activate FormAlchemy/GeoFormAlchemy in your MapFish project
------------------------------------------------------------
FormAlchemy and GeoFormAlchemy both provide Pylons templates. The FormAlchemy template is intended for the
creation of new Pylons projects, but it can also be used to update existing projects. But you will have to
be careful that no files are overwritten.
Run the following command to apply the GeoFormAlchemy template to your MapFish application. It will first
apply the FormAlchemy template and then insert the files of GeoFormAlchemy. Replace
``tutorial`` with the actual name of your project::
(venv) $ paster create -t geo_fa tutorial
[..]
Enter admin_controller (Add formalchemy's admin controller) [False]: True
Enter template_engine (mako/genshi/jinja2/etc: Template language) ['mako']: mako
Enter sqlalchemy (True/False: Include SQLAlchemy 0.5 configuration) [False]: False
When asked if the FormAlchemy ``admin controller`` should be added, enter ``True``. As
``Template language`` use ``mako``. And on the prompt ``Include SQLAlchemy 0.5 configuration``,
reply with ``False``. The SQLAlchemy configuration was already installed when creating the
MapFish application.
After that you will be asked several times if a file should be overwritten. This is because
the FormAlchemy template assumes an empty project. Always deny with ``n``::
Overwrite ./tutorial/tutorial/config/deployment.ini_tmpl [y/n/d/B/?] n
Overwrite ./tutorial/tutorial/config/environment.py [y/n/d/B/?] n
Overwrite ./tutorial/tutorial/lib/base.py [y/n/d/B/?] n
Overwrite ./tutorial/tutorial/model/__init__.py [y/n/d/B/?] n
Overwrite ./tutorial/tutorial/websetup.py [y/n/d/B/?] n
Overwrite ./tutorial/development.ini [y/n/d/B/?] n
Overwrite ./tutorial/setup.py [y/n/d/B/?] n
Overwrite ./tutorial/tutorial/config/routing.py [y/n/d/B/?] n
Then you will have to define a route to the admin interface controller. Open the file
``tutorial/config/routing.py`` and add the following lines after the ``CUSTOM ROUTES HERE``
comment::
def make_map():
# [..]
# CUSTOM ROUTES HERE
# Map the /admin url to FA's AdminController
# Map static files
map.connect('fa_static', '/admin/_static/{path_info:.*}', controller='admin', action='static')
# Index page
map.connect('admin', '/admin', controller='admin', action='models')
map.connect('formatted_admin', '/admin.json', controller='admin', action='models', format='json')
# Models
map.resource('model', 'models', path_prefix='/admin/{model_name}', controller='admin')
The administration interface is now set up. Start your application with ``paster serve development.ini``
and open the URL ``_ in your web browser.
.. image:: _static/formalchemy-admin-empty.png
:align: center
:alt: FormAlchemy admin interface for Pylons
Configuration
=============
At the moment the admin interface is empty, you will have to tell FormAlchemy for which model classes
it should create forms. Open the file ``tutorial/model/__init__.py`` and add imports for your models. For example
like this::
"""The application's model objects"""
import sqlalchemy as sa
from sqlalchemy import orm
from tutorial.model import meta
from tutorial.model.places import Place
from tutorial.model.categories import Category
# [..]
.. hint::
See also `Pylons extensions: Administration interface `_
Then the GeoFormAlchemy extension has to be activated. Open the file ``tutorial/forms/__init__.py`` and
add the following lines at the end of the file::
# [..]
from geoformalchemy.base import GeometryFieldRenderer
from geoalchemy import geometry
FieldSet.default_renderers[geometry.Geometry] = GeometryFieldRenderer
Restart your application and open or reload the URL ``_ in your web browser.
You will now see a list of all your models that are parsed by FormAlchemy. Choose a model and a list of
existing objects for this model will be displayed.
.. image:: _static/formalchemy-admin-overview.png
:align: center
:alt: FormAlchemy admin interface: Overview of existing objects
If you click on the ``New ...`` button or on a ``edit`` symbol, the overview page for a single object
will be displayed. A `OpenLayers `_ map will be renderer for your geometry field by GeoFormAlchemy,
in which you can add, edit and delete features using OpenLayers controls.
.. image:: _static/geoformalchemy-admin-detail.png
:align: center
:alt: FormAlchemy/GeoFormAlchemy admin interface: Detail page
Customization
=============
FormAlchemy offers a number of ways to customize your forms, the following two chapters will describe two
of them. If you want to do advanced customizations like writing your `own field renderer
`_, using your `own template engine
`_ or creating your `own field validator
`_, please refer to the `FormAlchemy documentation
`_.
Field modifications
-------------------
Single fields of your model can easily be customized using the FormAlchemy field modification methods. For example
you can exclude fields, so that they are not shown in the form, set a label for a field, mark a field as ``required``
or render a string field in a ``textarea``.
These modifications can be set in the file ``tutorial/forms/__init__.py``. Open the file and add the following lines::
Place = FieldSet(model.places.Place)
Place.configure(options=[Place.the_geom.label('Geometry').required()])
Here we are creating a custom ``FieldSet`` for the model class ``Place``. Note that the custom ``FieldSet`` **must
have the same name** as your model class. Using the method `configure( )
`_ we now can customize the form
for our model.
In the above example we are setting a label for the field ``the_geom`` and are marking the field as
``required`` by calling the methods ``label()`` and ``required()`` on the field. These two modifications are *chained*,
you can also add further modifications. You can find a list of modifications in the FormAlchemy documentation in
chapter `Forms: Field Modification `_.
The rendering of geometry fields can be customized with additional options. For example the following statement
called on the field ``the_geom`` of the custom ``FieldSet`` changes the background map::
Place.the_geom.set(options=[
('map_srid', 900913),
('base_layer', 'new OpenLayers.Layer.OSM("OSM")')
])
.. _geoformalchemy-options:
**The following options are available in GeoFormAlchemy:**
``default_lat`` and ``default_lon``
If the geometry is ``None`` or when creating a new geometry, the map
is centered at (default_lon, default_lat). Otherwise the map is centered
at the centroid of the geometry.
``zoom``
The zoom-level on start-up.
``map_width`` and ``map_height``
The size of the ``DIV`` container in which the map is displayed.
``base_layer``
The OpenLayers layer which will be used as background map, for example::
('base_layer', 'new OpenLayers.Layer.OSM("OSM")')
``map_srid``
If the map uses a different CRS than the geometries, the geometries will be
reprojected to this CRS. For example::
('map_srid', 900913)
``openlayers_lib``
The path to the OpenLayers JavaScript library, for example to use the OpenLayers
library packaged with MapFish use the following path::
('openlayers_lib', '/mfbase/openlayers/lib/OpenLayers.js')
``show_map`` (default: ``True``)
If ``show_map`` is set to ``False``, the geometry will be displayed as WKT string
inside a text input field.
Template files
--------------
If you want to change the look of your forms, you can modify the template files used by
FormAlchemy and GeoFormAlchemy. The template files are located in the folder
``tutorial/templates/forms``.
``fieldset.mako`` and ``fieldset_readonly.mako`` are used for displaying
a single object, ``grid.mako`` and ``grid_readonly.mako`` are rendered as model overview page. The overall
design can be changed in the file ``restfieldset.mako``.
The CSS stylesheet and the images used in the admin interface are part of the FormAlchemy library and can not
be modified directly. If you want to change them, you can download the original files from the `FormAlchemy repository
`_ to ``tutorial/public``
and then adapt the paths in the template files.
GeoFormAlchemy uses the template files ``map_js.mako`` and ``map.mako``. In ``map.mako`` you can set most of the
options that you can also use as :ref:`field modification `. But unlike to field modifications,
the options set in the template file are used for the geometry fields of all models, whereas the options
set as field modification are only used for the field they were set on. ::
<%
# default configuration options that will be used when
# no field options were set
options = {}
options['default_lon'] = 10
options['default_lat'] = 45
options['zoom'] = 4
options['map_width'] = 512
options['map_height'] = 256
options['base_layer'] = 'new OpenLayers.Layer.WMS("WMS", "http://labs.metacarta.com/wms/vmap0", {layers: "basic"})'
options['openlayers_lib'] = 'http://openlayers.org/api/OpenLayers.js'
%>
If you want to customize the OpenLayers map, for example to add a further OpenLayers control or to add a
second background layer, modify the file ``map_js.mako``.