This section provides information for upgrading a MapFish application from MapFish 1.2 to MapFish 2.x.
MapFish 1.2 is based on Pylons 0.9.7, and MapFish 2.x is based on Pylons 1.0. So to upgrade an application from MapFish 1.2 to MapFish 2.x it is first needed to upgrade that application from Pylons 0.9.7 to Pylons 1.0. For this refer to the Pylons 1.0 Upgrading page, and follow the provided indications.
The Pylons 1.0 Uprading page doesn’t include information for upgrading the model and base controller. Here’s additional information:
The model.meta module no longer stores reference to the engine and metadata objects. Edit model/meta.py and remove these files:
from sqlalchemy import MetaData
engine = None
metadata = MetaData()
The model.meta module now creates the scoped session and the SQLAlchemy declarative base class. Edit model/meta.py, replace the Session = None line with:
Session = scoped_session(sessionmaker())
and add the following line at the end of the file:
Base = declarative_base()
The init_model function of the model module is no longer responsible for creating the Session. Edit model/__init__.py and remove these three lines from the body of the init_model function:
sm = orm.sessionmaker(bind=engine)
meta.engine = engine
meta.Session = orm.scoped_session(sm)
The init_model function now just configures the (already-created) Session. Add the following statement to the body of init_model:
Session.configure(bind=engine)
The application should now be upgraded to Pylons 1.0, and it is now time to do MapFish-specific adjusments.
MapFish 2.x is based on GeoAlchemy. GeoAlchemy provides extensions to SQLAlchemy for use with spatial databases. MapFish 2.x no longer defines the Geometry type, the one defined by GeoAlchemy is to be used instead.
With MapFish 1.2 the paster mf-layer command generated model files that looked like this:
from sqlalchemy import Column, Table, types
from sqlalchemy.orm import mapper
from mapfish.sqlalchemygeom import Geometry
from mapfish.sqlalchemygeom import GeometryTableMixIn
from blonay.model.meta import metadata, engine
users_table = Table(
'users', metadata,
Column('the_geom', Geometry(4326)),
schema='my_schema',
autoload=True, autoload_with=engine)
class User(GeometryTableMixIn):
# for GeometryTableMixIn to do its job the __table__ property
# must be set here
__table__ = users_table
mapper(User, users_table)
With MapFish 2.x the same user model looks like this:
from sqlalchemy import Column, types
from geoalchemy import GeometryColumn, Point
from mapfish.sqlalchemygeom import GeometryTableMixIn
from sample.model.meta import Session, Base
class User(Base, GeometryTableMixIn):
__tablename__ = 'users'
__table_args__ = {
"schema": 'my_schema',
"autoload": True,
"autoload_with": Session.bind
}
the_geom = GeometryColumn(Point(srid=4326))
The Filter abstraction is no longer. With MapFish 2.x only SQLAlchemy filters (ClauseElement) are manipulated.
The create_default_filter(), create_attr_filter(), and create_geom_filter() functions all return SQLAlchemy ClauseElement objects.
Concretely it means that you change your code not to rely on Spatial, Comparison, Logical and FeatureId filters but on regular SQLAlchemy ClauseElement objects. For example you would use and_ instead of Logical, etc.
With the removal of the Filter abstraction the lib module is entirely gone, and the protocol module is now located in mapfish. This means your files that import protocol, typically controllers, should be changed. Typical controllers must be changed from:
from mapfish.lib.protocol import Protocol, create_default_filter
to:
from mapfish.protocol import Protocol, create_default_filter
MapFish 2.x introduces the geojsonify decorator for generating GeoJSON.
With MapFish 1.2 the paster mf-layer command generated controller files that looked like this:
from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect_to
from myproject.lib.base import BaseController
from myproject.model.users import User
from myproject.model.meta import Session
from myproject.lib.decorators import geojsonify
from mapfish.protocol import Protocol, create_default_filter
class UsersController(BaseController):
readonly = False # if set to True, only GET is supported
def __init__(self):
self.protocol = Protocol(Session, User, self.readonly)
def index(self, format='json'):
"""GET /: return all features."""
return self.protocol.index(request, response, format=format)
def show(self, id, format='json'):
"""GET /id: Show a specific feature."""
return self.protocol.show(request, response, id)
def create(self):
"""POST /: Create a new feature."""
return self.protocol.create(request, response)
def update(self, id):
"""PUT /id: Update an existing feature."""
return self.protocol.update(request, response, id)
def delete(self, id):
"""DELETE /id: Delete an existing feature."""
return self.protocol.delete(request, response, id)
With MapFish 2.x the same user controller looks like this:
from pylons import request, response, session, tmpl_context as c
from pylons.controllers.util import abort, redirect
from test.lib.base import BaseController
from test.model.users import User
from test.model.meta import Session
from mapfish.protocol import Protocol, create_default_filter
from mapfish.decorators import geojsonify
class UsersController(BaseController):
readonly = False # if set to True, only GET is supported
def __init__(self):
self.protocol = Protocol(Session, User, self.readonly)
@geojsonify
def index(self, format='json'):
"""GET /: return all features."""
if format != 'json':
abort(404)
return self.protocol.read(request)
@geojsonify
def show(self, id, format='json'):
"""GET /id: Show a specific feature."""
if format != 'json':
abort(404)
return self.protocol.read(request, response, id=id)
@geojsonify
def create(self):
"""POST /: Create a new feature."""
return self.protocol.create(request, response)
@geojsonify
def update(self, id):
"""PUT /id: Update an existing feature."""
return self.protocol.update(request, response, id)
def delete(self, id):
"""DELETE /id: Delete an existing feature."""
return self.protocol.delete(request, response, id)
Several things to note here: