【问题标题】:Flask-SQLAlchemy ORM/GeoAlchemy2 results to a dictionary and ultimately JSONFlask-SQLAlchemy ORM/GeoAlchemy2 结果到字典,最终是 JSON
【发布时间】:2018-10-29 02:57:26
【问题描述】:

我正在使用 Flask/SQLAlchemy 创建一个包含地图的 Web 应用程序,所以我自然使用的是 PostGIS 数据库。 geom 列需要 ST_Transform 并且我需要以某种方式将此列和所有其他列转换为 JSON。数据库的一般结构是:

from app import login, db
from datetime import datetime
from geoalchemy2 import Geometry
from time import time
from flask import current_app
from sqlalchemy import func

class Streets(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    street = db.Column(db.String(50))
    geom = db.Column(Geometry(geometry_type='LINESTRING'))

    def to_dict(self):
        data = {
            'id': self.id,
            'street': self.street,
            '_geom': func.ST_AsGeoJSON(func.ST_Transform(self.geom, 4326))
        }
    return data

我的 api 路由把这个结果变成了一个 api:

return jsonify(Streets.query.get_or_404(id).to_dict())

但我不断收到此错误:NameError: name 'ST_AsGeoJSON' is not defined

我还尝试像这样创建我的_geom 值:

    data['_geom'] = db.session.query(func.ST_AsGeoJSON(func.ST_Transform(self.geom, 4326)))

错误信息是:TypeError: Object of type 'BaseQuery' is not JSON serializable

最后,我尝试了这样的 api 路由:

    data = Streets.to_dict(
        db.session.query(
            func.ST_AsGeoJSON(
                func.ST_Transform(
                    Streets.geom, 4326
                )
            )
        )
        .filter(Streets.id==id))

    return jsonify(data)

我得到一个不同的错误: AttributeError: 'BaseQuery' object has no attribute 'id'

如果我在 flask shell 中运行它,它可以工作:

streets = db.session.query(
        Streets.id,
        Streets.street,
        func.ST_AsGeoJSON(func.ST_Transform(Streets.geom, 4326)))

如何执行ST_Transform 并将 JSON 获取到我的 api 路由?

更新

我在 SQLALchemy 文档中发现了这一点,这让我取得了一些进展:“orm.column_property() 可用于映射 SQL 表达式”。所以我尝试将此添加到我的class Streets(db.Model)

coords = db.column_property(func.ST_AsGeoJSON(func.ST_Transform(geom, 4326)))

然后我像这样将它添加到data

def to_dict(self):
    data = {
        'id': self.id,
        'street': self.street,
        'coords': self.coords
        }
    return data

但现在我对我的结果进行双重编码,一次输入 GeoJSON,然后我 jsonify 它:

return jsonify(Streets.query.get_or_404(id).to_dict())

所以我的api插入了\的:

{"coords": "{\"type\":\"MultiLineString\",\"coordinates\":[[[-80.8357132798193,35.2260689001034],[-80.8347602582754,35.2252424284259]]]}"}

而使用ST_AsText 只是将其转换为文本:

{"coords": "MULTILINESTRING((-80.8357132798193 35.2260689001034,-80.8347602582754 35.2252424284259))"}

我想我已经接近这个更新了,但是有没有人建议使用我的数据库其他字段的 JSON 来获取正确的 GeoJSON?

【问题讨论】:

    标签: python json postgresql flask sqlalchemy


    【解决方案1】:

    第一个错误

    NameError: name 'ST_AsGeoJSON' is not defined
    

    意味着您的示例代码不是您实际使用的。您忘记通过func 访问它。修复后它也不起作用,因为您将混合 SQL 世界和 Python 世界。 func.ST_AsGeoJSON(...) 创建一个 SQL 函数表达式对象,该对象应该被编译为 SQL 并在查询中发送到 DB,而不是传递给 jsonify()

    第二个错误

    TypeError: Object of type 'BaseQuery' is not JSON serializable
    

    应该有点明显。

    data['_geom'] = db.session.query(func.ST_AsGeoJSON(func.ST_Transform(self.geom, 4326)))
    

    创建一个Query,以及一个过于宽泛的查询,因为您没有将它限制为获取当前对象的数据。 Query 对象不是 JSON 可序列化的。

    data = Streets.to_dict(db.session.query(...)...)
    

    您将Query 对象作为self 传递给Streets.to_dict(),然后它会尝试访问其id 中的属性

    'id': self.id,
    

    失败的原因很明显——即将一个不相关的对象作为实例传递给一个方法。

    column_property() 方法生成双重编码的 JSON,因为默认情况下 SQLAlchemy 不期望 ST_AsGeoJSON 返回 JSON 并将其视为文本,而是实际返回。尝试手动解码:

    def to_dict(self):
        data = {
            'id': self.id,
            'street': self.street,
            'coords': json.loads(self.coords)
            }
        return data
    

    【讨论】:

      猜你喜欢
      • 2018-12-30
      • 1970-01-01
      • 1970-01-01
      • 2014-06-26
      • 1970-01-01
      • 1970-01-01
      • 2021-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多