2017-09-25 89 views
3

我想從python中的tiger.geocode函數中使用sqlalchemy提取組合列。
純SQL形式,這看起來像這樣:在postgres中使用sqlalchemy訪問複合數據類型

SELECT 
    g.rating 
    ,ST_X(g.geomout) As lon 
    ,ST_Y(g.geomout) As lat 
    ,(addy).address As stno 
    ,(addy).streetname As street 
    ,(addy).streettypeabbrev As styp 
    ,(addy).location As city 
    ,(addy).stateabbrev As st 
    ,(addy).zip 
FROM geocode(pagc_normalize_address('1 Capitol Square Columbus OH 43215')) As g 
; 

這將產生以下輸出:

# rating lon lat stno street styp city st zip 
1 17 -82.99782603089086 39.96172588526335 1 Capital St Columbus OH 43215 

我面對的問題是如何引用組合柱查詢從SQLAlchemy的對象時(評分,lon,lat,stno,street,styp,city,st,zip)?

請和謝謝。

+2

你有沒有看着['sqlalchemy_utils.types.pg_composite'(HTTP: //sqlalchemy-utils.readthedocs.io/en/latest/data_types.html#module-sqlalchemy_utils.types.pg_composite)? – univerio

+0

我可以看到,如果我的複合類型是一個靜態表上的列的情況下,將如何工作,但在我的情況下,它是一個函數返回的複合類型。在我的設置中,我們沒有爲任何函數(僅限於表格)定義sqlalchemy類。對這部分問題有什麼想法? – BrianEdwardHoover

回答

3

SQLAlchemy不支持直接設置返回函數,但其​​FunctionElement s被認爲是FromClause s,這意味着您已經可以將它們視爲表;我們只需要添加從函數中選擇特定列的功能。幸運的是,這是簡單的(雖然不是很明顯):從GenericFunction

from sqlalchemy.sql.base import ColumnCollection 
from sqlalchemy.sql.expression import column 
from sqlalchemy.sql.functions import FunctionElement 

NormAddy = CompositeType(
    "norm_addy", 
    [ 
     Column("address", Integer), 
     Column("predirAbbrev", String), 
     Column("streetName", String), 
     Column("streetTypeAbbrev", String), 
     Column("postdirAbbrev", String), 
     Column("internal", String), 
     Column("location", String), 
     Column("stateAbbrev", String), 
     Column("zip", String), 
     Column("parsed", Boolean), 
    ], 
) 

class geocode(GenericFunction): 
    columns = ColumnCollection(
     Column("rating", Integer), 
     column("geomout"), # lowercase column because we don't have the `geometry` type 
     Column("addy", NormAddy), 
    ) 

子類在全球擁有註冊geocode功能,以便爲預期func.geocode將工作的額外好處。

g = func.geocode(func.pagc_normalize_address("1 Capitol Square Columbus OH 43215")).alias("g") 
query = session.query(
    g.c.rating, 
    func.ST_X(g.c.geomout).label("lon"), 
    func.ST_Y(g.c.geomout).label("lat"), 
    g.c.addy.address.label("stno"), 
    g.c.addy.streetName.label("street"), 
    g.c.addy.streetTypeAbbrev.label("styp"), 
    g.c.addy.location.label("city"), 
    g.c.addy.stateAbbrev.label("st"), 
    g.c.addy.zip, 
).select_from(g) 

不幸的是,這並不奏效。似乎有一個錯誤,使得g.c.addy.address語法不適用於最新版本的SQLAlchemy。我們可以修復它真正的快速(雖然這應該真正固定在sqlalchemy_utils):

from sqlalchemy_utils.types.pg_composite import CompositeElement 
import sqlalchemy_utils 

class CompositeType(sqlalchemy_utils.CompositeType): 
    class comparator_factory(_CompositeType.comparator_factory): 
     def __getattr__(self, key): 
      try: 
       type_ = self.type.typemap[key] 
      except KeyError: 
       raise AttributeError(key) 
      return CompositeElement(self.expr, key, type_) 

    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     self.typemap = {c.name: c.type for c in self.columns} 

現在,它的工作原理:

print(query.statement.compile(engine)) 
# SELECT g.rating, ST_X(g.geomout) AS lon, ST_Y(g.geomout) AS lat, (g.addy).address AS stno, (g.addy).streetName AS street, (g.addy).streetTypeAbbrev AS styp, (g.addy).location AS city, (g.addy).stateAbbrev AS st, (g.addy).zip AS zip_1 
# FROM geocode(pagc_normalize_address(%(pagc_normalize_address_1)s)) AS g