如何在sqlalchemy中映射postgres EARTH列

时间:2016-05-16 02:37:07

标签: python sqlalchemy

我使用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>

谢谢!

1 个答案:

答案 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上下文中非常无用,因此需要来回使用earthll_to_earthlatitude函数。将longitudeUserDefinedType结合起来可能会提供“两全其美”的解决方案:

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的范围。