如何在sqlalchemy中映射postgres EARTH列

los*_*rje 1 python sqlalchemy

我正在使用sqlalchemy,也使用alembic进行迁移(和flask-sqlalchemy)。我有一个使用EARTH数据类型的postgres表。

CREATE TABLE things
(
  id INTEGER PRIMARY KEY NOT NULL,
  name TEXT,
  earth_location EARTH
)
Run Code Online (Sandbox Code Playgroud)

这是我的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(???)
Run Code Online (Sandbox Code Playgroud)

如何映射接地柱?

Alembic迁移能够应付吗?

谢谢!

Ilj*_*ilä 5

可以通过以下方式使用earth来自Earthdistance扩展的类型列与SQLAlchemy:

A的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))
Run Code Online (Sandbox Code Playgroud)

为了产生column具有正确的_selectable的属性Thing,必须在创建类之后将其添加到类中,因为Table在类主体中创建时声明式创建的内容不可用。这样就可以查询

session.query(Thing.earth_location_longitude).scalar()
Run Code Online (Sandbox Code Playgroud)

按预期工作。没有_selectable,发出的SQL将是:

SELECT longitude(loc)
Run Code Online (Sandbox Code Playgroud)

不幸的是,这使映射表完全忽略了基础列things.earth_location,因此Alembic也不是更明智的选择。Alembic中的表创建必须通过执行原始SQL字符串来完成。

A UserDefinedType具有能够支持表创建的优点。原始earth值是在Python方面没什么用,所以一些来回ll_to_earthlatitude而且longitude是必需的功能。将a UserDefinedType与a 结合使用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))
Run Code Online (Sandbox Code Playgroud)

检查表创建:

In [21]: t = Table('test', meta, Column('loc', EARTH))

In [22]: print(CreateTable(t))                        

CREATE TABLE test (
        loc earth
)
Run Code Online (Sandbox Code Playgroud)

添加一个新的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()
Run Code Online (Sandbox Code Playgroud)

请注意,提供了对的绑定函数调用ll_to_earth作为值。

支持将lat和lon作为属性等访问的更复杂的自定义类型是完全可能的,但可能超出此q / a的范围。