ORM教程¶
(本教程的灵感来自 SQLAlchemy ORM Tutorial ,最终推荐阅读。)
GeoAlchemy不提供对象关系映射器(ORM),但可以很好地与SQLAlchemy ORM配合使用。本教程展示了如何将SQLAlchemy ORM与空间表一起使用,并使用GeoAlchemy。
连接到数据库¶
在本教程中,我们将使用PostGIS 2数据库。为了连接我们使用SQLAlchemy的 create_engine()
功能:
>>> from sqlalchemy import create_engine
>>> engine = create_engine('postgresql://gis:gis@localhost/gis', echo=True)
在本例中,数据库的名称、数据库用户和数据库密码是 gis
.
这个 echo
flag是设置SQLAlchemy日志的快捷方式,它是通过Python的标准日志模块实现的。启用后,我们将看到生成的所有SQL。
的返回值 create_engine
是一个 Engine
对象,它表示数据库的核心接口。
声明映射¶
当使用ORM时,配置过程首先描述我们要处理的数据库表,然后定义我们自己的类,这些类将映射到这些表。在现代的SQLAlchemy中,这两个任务通常一起执行,使用一个称为 Declarative
,这允许我们创建包含指令的类来描述它们将映射到的实际数据库表。
>>> from sqlalchemy.ext.declarative import declarative_base
>>> from sqlalchemy import Column, Integer, String
>>> from geoalchemy2 import Geometry
>>>
>>> Base = declarative_base()
>>>
>>> class Lake(Base):
... __tablename__ = 'lake'
... id = Column(Integer, primary_key=True)
... name = Column(String)
... geom = Column(Geometry('POLYGON'))
这个 Lake
类建立有关要映射的表的详细信息,包括由 __tablename__
,和三列 id
, name
和 geom
. 这个 id
列将是表的主键。这个 geom
列是 geoalchemy2.types.Geometry
其列 geometry_type
是 POLYGON
.
在数据库中创建表¶
这个 Lake
类有一个对应的 Table
对象表示数据库表。这个 Table
对象是由SQLAlchemy自动创建的,它被 Lake.__table__
属性:
>>> Lake.__table__
Table('lake', MetaData(bind=None), Column('id', Integer(), table=<lake>,
primary_key=True, nullable=False), Column('name', String(), table=<lake>),
Column('geom', Polygon(srid=4326), table=<lake>), schema=None)
创建 lake
数据库中的表:
>>> Lake.__table__.create(engine)
如果我们想放下我们要用的桌子:
>>> Lake.__table__.drop(engine)
创建映射类的实例¶
通过声明映射,我们可以创建 Lake
对象:
>>> lake = Lake(name='Majeur', geom='POLYGON((0 0,1 0,1 1,0 1,0 0))')
>>> lake.geom
'POLYGON((0 0,1 0,1 1,0 1,0 0))'
>>> str(lake.id)
'None'
一个作品被传给 Lake
其几何结构的构造函数。这幅画代表了我们湖的形状。因为我们还没有告诉SQLAlchemy坚持 lake
对象,其 id
是 None
.
也支持EWKT(扩展WKT)格式。例如,如果几何列的空间参照系是 4326
字符串 SRID=4326;POLYGON((0 0,1 0,1,0 1,0 0))
可用作几何表示。
创建会话¶
ORM通过 Session
. 让我们创建一个 Session
班级:
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker(bind=engine)
这个定制的 Session
类将创建新的 Session
绑定到数据库的对象。然后,每当我们需要与数据库进行对话时,我们将 Session
::
>>> session = Session()
以上 Session
与我们的PostgreSQL有关 Engine
,但它尚未打开任何连接。
添加新对象¶
坚持我们的 Lake
对象,我们 add()
它给 Session
::
>>> session.add(lake)
在这一点上 lake
对象已添加到 Session
,但尚未向数据库发出SQL。对象位于 悬而未决的 国家。持久化对象a 脸红 或 犯罪 必须执行操作(提交意味着刷新):
>>> session.commit()
我们现在可以查询数据库 Majeur
::
>>> our_lake = session.query(Lake).filter_by(name='Majeur').first()
>>> our_lake.name
u'Majeur'
>>> our_lake.geom
<WKBElement at 0x9af594c; '0103000000010000000500000000000000000000000000000000000000000000000000f03f0000000000000000000000000000f03f000000000000f03f0000000000000000000000000000f03f00000000000000000000000000000000'>
>>> our_lake.id
1
our_lake.geom
是一个 geoalchemy2.elements.WKBElement
,这是地球炼金术提供的一种类型。 geoalchemy2.elements.WKBElement
包装数据库返回的WKB值。
让我们添加更多的湖:
>>> session.add_all([
... Lake(name='Garde', geom='POLYGON((1 0,3 0,3 2,1 2,1 0))'),
... Lake(name='Orta', geom='POLYGON((3 0,6 0,6 3,3 3,3 0))')
... ])
>>> session.commit()
查询¶
A Query
对象是使用 query()
作用于 Session
. 例如这里有一个 Query
装载 Lake
按名称排序的实例:
>>> query = session.query(Lake).order_by(Lake.name)
任何 Query
是可以接受的:
>>> for lake in query:
... print lake.name
...
Garde
Majeur
Orta
另一种执行查询并获取 Lake
对象涉及调用 all()
上 Query
::
>>> lakes = session.query(Lake).order_by(Lake.name).all()
SQLAlchemy ORM教程 Querying section 提供更多查询示例。
进行空间查询¶
在SQL SELECT查询中使用空间过滤器非常常见。这些查询是通过使用空间关系函数或运算符在 WHERE
SQL查询的子句。
例如,要找到 Lake
包含要点的 POINT(4 1)
,我们可以用这个 Query
::
>>> from sqlalchemy import func
>>> query = session.query(Lake).filter(
... func.ST_Contains(Lake.geom, 'POINT(4 1)'))
...
>>> for lake in query:
... print lake.name
...
Orta
GeoAlchemy允许重写这个 Query
更简洁地说:
>>> query = session.query(Lake).filter(Lake.geom.ST_Contains('POINT(4 1)'))
>>> for lake in query:
... print lake.name
...
Orta
这里 ST_Contains
函数应用于 Lake.geom
列属性。在这种情况下,column属性实际上作为函数的第一个参数传递给函数。
下面是另一个空间过滤查询,基于 ST_Intersects
::
>>> query = session.query(Lake).filter(
... Lake.geom.ST_Intersects('LINESTRING(2 1,4 1)'))
...
>>> for lake in query:
... print lake.name
...
Garde
Orta
我们还可以将关系函数应用于 geoalchemy2.elements.WKBElement
. 例如::
>>> lake = session.query(Lake).filter_by(name='Garde').one()
>>> print session.scalar(lake.geom.ST_Intersects('LINESTRING(2 1,4 1)'))
True
session.scalar
允许执行子句并返回标量值(本例中为布尔值)。
地球炼金术的功能都是从 ST_
. 运算符也被称为函数,但函数名不包括 ST_
前缀。以PostGIS为例 &&
运算符,它允许测试几何体的边界框是否相交。地球炼金术提供 intersects
功能:
>>> query = session.query
>>> query = session.query(Lake).filter(
... Lake.geom.intersects('LINESTRING(2 1,4 1)'))
...
>>> for lake in query:
... print lake.name
...
Garde
Orta
在模型中设置空间关系¶
我们假设除了 lake
我们还有一张桌子, treasure
,其中包括宝藏位置。假设我们对发现藏在湖底的宝藏感兴趣。
这个 Treasure
类如下:
>>> class Treasure(Base):
... __tablename__ = 'treasure'
... id = Column(Integer, primary_key=True)
... geom = Column(Geometry('POINT'))
我们现在可以添加 relationship
到 Lake
自动加载每个湖泊中包含的宝藏的表:
>>> from sqlalchemy.orm import relationship, backref
>>> class Lake(Base):
... __tablename__ = 'lake'
... id = Column(Integer, primary_key=True)
... name = Column(String)
... geom = Column(Geometry('POLYGON'))
... treasures = relationship(
... 'Treasure',
... primaryjoin='func.ST_Contains(foreign(Lake.geom), Treasure.geom).as_comparison(1, 2)',
... backref=backref('lake', uselist=False),
... viewonly=True,
... uselist=True,
... )
注意使用 as_comparison
功能。它是使用SQL函数所必需的 (ST_Contains
这里)在 primaryjoin
条件。这只适用于SQLAlchemy 1.3,因为 as_comparison
此版本之前不存在函数。见 Custom operators based on SQL function 有关详细信息,请参阅SQLAlchemy文档的一节。
有关用于配置此 relationship
:
backref
用于提供要放置在另一方向处理此关系的类上的属性的名称,即Treasure
;viewonly=True
指定关系仅用于加载对象,而不用于持久性操作;uselist=True
指示属性应作为列表加载,而不是作为标量加载。
另外,请注意 treasures
属性对 lake
对象(和 lake
属性对 treasure
对象)在首次访问属性时“延迟”加载。另一个加载策略可以在 relationship
. 比如你会用 lazy='joined'
对于要在与父项相同的查询中“急切”加载的相关项,使用 JOIN
或 LEFT OUTER JOIN
.
见 Relationships API 有关 relationship
函数,以及可用于配置它的所有参数。
使用其他空间功能¶
这里有一个 Query
计算我们湖泊的缓冲区面积:
>>> from sqlalchemy import func
>>> query = session.query(Lake.name,
... func.ST_Area(func.ST_Buffer(Lake.geom, 2)) \
... .label('bufferarea'))
>>> for row in query:
... print '%s: %f' % (row.name, row.bufferarea)
...
Majeur: 21.485781
Garde: 32.485781
Orta: 45.485781
这个 Query
应用PostGIS ST_Buffer
函数的每一行的几何列 lake
桌子。返回值是一个行列表,其中每一行实际上是由两个值组成的元组:湖泊名称和湖泊缓冲区的面积。每个元组实际上是一个SQLAlchemy KeyedTuple
对象,它提供属性类型访问器。
再一次, Query
能写得更简洁些:
>>> query = session.query(Lake.name,
... Lake.geom.ST_Buffer(2).ST_Area().label('bufferarea'))
>>> for row in query:
... print '%s: %f' % (row.name, row.bufferarea)
...
Majeur: 21.485781
Garde: 32.485781
Orta: 45.485781
显然,处理和测量功能也可以用于 WHERE
条款。例如::
>>> lake = session.query(Lake).filter(
... Lake.geom.ST_Buffer(2).ST_Area() > 33).one()
...
>>> print lake.name
Orta
而且,与地球炼金术支持的任何其他功能一样,处理和测量功能可以应用于 geoalchemy2.elements.WKBElement
. 例如::
>>> lake = session.query(Lake).filter_by(name='Majeur').one()
>>> bufferarea = session.scalar(lake.geom.ST_Buffer(2).ST_Area())
>>> print '%s: %f' % (lake.name, bufferarea)
Majeur: 21.485781
Majeur: 21.485781
使用栅格函数¶
一些功能(比如 ST_Transform() , ST_Union() , ST_SnapToGrid() ,…)可用于两个 geoalchemy2.types.Geometry
和 geoalchemy2.types.Raster
类型。在GeoAlchemy2中,这些函数只为 Geometry
因为它不能同时为多个类型定义。因此在 Raster
通过传递 type_=Raster 函数的参数:
>>> query = session.query(Lake.raster.ST_Transform(2154, type_=Raster))