我使用sqlalchemy和alembic进行迁移(和flask-sqlalchemy)。我有一个使用EARTH数据类型的postgres表。
CREATE TABLE things
(
id INTEGER PRIMARY KEY NOT NULL,
name TEXT,
earth_location EARTH
)
这是我的sqlalchemy映射:
class Thing(db.Model):
__tablename__ = 'things'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.UnicodeText, nullable=False)
earth_location = db.Column(???)
如何映射地球栏?
alembic迁移是否能够处理它?</ p>
谢谢!
答案 0 :(得分:4)
使用earthdistance extension中带有SQLAlchemy的earth
类型的列可以使用:
column_property
,如果您只想将纬度和经度值映射到属性UserDefinedType
,如果您希望拥有支持例如创建表格的完整版本 column_property
实现起来有点简单,但如果声明列不可用,则仅限于只读且需要一些非正统的语法:
from sqlalchemy import Float, column, func
from sqlalchemy.orm import column_property
class Thing(db.Model):
__tablename__ = 'things'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.UnicodeText, nullable=False)
_earth_location = column('earth_location', _selectable=Thing.__table__)
Thing.earth_location_latitude = column_property(func.latitude(
_earth_location, type_=Float))
Thing.earth_location_longitude = column_property(func.longitude(
_earth_location, type_=Float))
为了使用正确的 _selectable 生成column
,必须在创建后将属性添加到类Thing
,因为声明性地创建了{{ 1}}在类体中创建时不可用。这允许像
Table
按预期工作。如果没有 _selectable ,则发出的SQL将是:
session.query(Thing.earth_location_longitude).scalar()
不幸的是,这使得映射表完全忘记了基础列SELECT longitude(loc)
,因此Alembic也不会更聪明。 Alembic中的表创建必须通过执行原始SQL字符串来完成。
things.earth_location
具有支持表创建的优势。原始UserDefinedType
值在python上下文中非常无用,因此需要来回使用earth
,ll_to_earth
和latitude
函数。将longitude
与UserDefinedType
结合起来可能会提供“两全其美”的解决方案:
column_property
检查表创建:
from sqlalchemy.types import UserDefinedType
from sqlalchemy.orm import deferred
class EARTH(UserDefinedType):
def get_col_spec(self, **kw):
return 'EARTH'
class Thing(db.Model)
__tablename__ = 'things'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.UnicodeText, nullable=False)
# defer loading, as the raw value is pretty useless in python
earth_location = deferred(db.Column(EARTH))
# A deferred column, aka column_property cannot be adapted, extract the
# real column. A bit of an ugly hack.
latitude = column_property(func.latitude(*earth_location.columns,
type_=Float))
longitude = column_property(func.longitude(*earth_location.columns,
type_=Float))
添加新的In [21]: t = Table('test', meta, Column('loc', EARTH))
In [22]: print(CreateTable(t))
CREATE TABLE test (
loc earth
)
:
Thing
请注意,对>>> latitude = 65.012089
>>> longitude = 25.465077
>>> t = Thing(name='some name',
earth_location=func.ll_to_earth(latitude, longitude))
>>> session.add(t)
>>> session.commit()
的绑定函数调用是作为值提供的。
支持访问lat和lon作为属性等的更复杂的自定义类型将是完全可能的,但可能超出此q / a的范围。