【问题标题】:Accessing table variables in Flask-Sqlalchemy query during search在搜索期间访问 Flask-Sqlalchemy 查询中的表变量
【发布时间】:2019-04-03 07:12:59
【问题描述】:

我正在使用 Flask 和 Flask-SQLalchemy 以及 Flask-Login 编写一个 RESTful api。我有一个模型Users,它的初始化如下:

class Users(UserMixin, db.Model):
    __tablename__ = "users"
    # STATIC Standard required info
    id = db.Column("id", db.Integer, primary_key = True)
    public_id = db.Column("public_id", db.String(50), unique = True)
    email = db.Column("email", db.String(50), unique = True)
    username = db.Column("username", db.String(20), unique = True)
    password = db.Column("password", db.String(20))

    # DYNAMIC Standard required info
    lat = db.Column("lat", db.Float)
    lon = db.Column("lon", db.Float)

表格中还有其他元素,但纬度和经度最相关。我有一个方法distanceMath(),它接受当前登录用户的纬度和经度,以及表格中的纬度和经度来计算它们之间的距离。但我无法弄清楚如何在以下调用distanceMath 的查询期间访问表值而不会收到语法错误:

lat1 = current_user.lat
lon1 = current_user.lon
users = Users.query.filter(distanceMath(lat1, Users.lat, lon1, Users.lon)==1)
    output = []
    for user in users:
        data = {}
        data["username"] = user.username
        output.append(data)
    return str(output)

以防万一,这里是distanceMath 方法:

# Haversine Distance
def distanceMath(lat1, lat2, lon1, lon2):
    distance = math.acos(math.sin(math.radians(lat1))*math.sin(math.radians(lat2))) + math.cos(math.radians(lat1))*math.cos(math.radians(lat2))*math.cos(math.radians(lon1)-math.radians(lon2))
    return distance

错误的一个例子是:

TypeError: must be real number, not InstrumentedAttribute

在我看来,这本质上是说User.latUser.lon 不是指浮点数甚至是数字,而是表的属性。我的问题是如何实际使用纬度和经度等于(在数据库中它们分别等于 37.7 和 -122.4)。

【问题讨论】:

  • “没有收到语法错误”:请在帖子中包含错误。我冒昧地编辑了您的帖子,但保留了例如视图(?)代码中的缩进错误。
  • 另外,你也许可以从这个答案中得到一些想法:stackoverflow.com/a/51018368/2681632。诀窍是形成一个在数据库中求值的 SQL 表达式。
  • 添加了一个语法错误的例子。 ****EDIT**** 目前也在尝试来自该链接的想法,将更新。
  • 确切地说,这不是语法错误:)。编辑时也不需要包含“***** EDIT *****”。 SO 不是一个论坛,如果您可以从整体上改进您的问题(并使其对未来的读者有用),那就更好了。

标签: python sqlite sqlalchemy flask-sqlalchemy


【解决方案1】:

您的直觉是正确的,错误是将 SQLAlchemy 的检测属性传递给 Python 的 math 库函数的结果,这些函数不知道如何处理这些函数。

为了根据距离进行查询,您可以从数据库中获取所有数据并在 Python 中进行过滤(通常不需要),或者在查询中将操作发送到 DBMS 进行评估。诀窍是形成一个合适的 SQL 表达式。您需要创建 SQL function expressions using func,而不是调用 Python 的数学函数:

# Bind math as a keyword argument and provide Python's math module as the default
def distanceMath(lat1, lat2, lon1, lon2, math=math):
    distance = math.acos(math.sin(math.radians(lat1))*math.sin(math.radians(lat2))) + math.cos(math.radians(lat1))*math.cos(math.radians(lat2))*math.cos(math.radians(lon1)-math.radians(lon2))
    return distance

在视图中:

users = Users.query.filter(distanceMath(lat1, Users.lat, lon1, Users.lon, math=db.func) == 1)

您还可以在 hybrid attribute 中使用 wrap the distance calculations,它可以处理生成 SQL 表达式或在 Python 中对实例执行计算。

但是有一个问题:SQLite 默认没有所需的数学函数。不过,它确实允许 creating custom functions 轻松。此类操作应在池第一次创建连接时处理一次:

import math
from sqlalchemy import event

# Before anything else DB related is performed:
event.listens_for(db.get_engine(), 'connect')
def create_math_functions_on_connect(dbapi_connection, connection_record):
    dbapi_connection.create_function('sin', 1, math.sin)
    dbapi_connection.create_function('cos', 1, math.cos)
    dbapi_connection.create_function('acos', 1, math.acos)
    dbapi_connection.create_function('radians', 1, math.radians)

【讨论】:

    【解决方案2】:

    我通过使用 distanceMath 函数并将其放入我的用户模型类并传递“self”和“math”来解决它,并相应地引用表值:

        def distanceMath(self, lat1, lon1, math=math):
            distance = math.acos(math.sin(math.radians(lat1))*math.sin(math.radians(self.lat))) + math.cos(math.radians(lat1))*math.cos(math.radians(self.lat))*math.cos(math.radians(lon1)-math.radians(self.lon))
            return distance
    

    然后在执行查询时,只需将 current_user 作为 Users 实例传入,一切正常:

        users = Users.query.filter(current_user.distanceMath(lat1, lon1) == 1).all()
    

    【讨论】:

      猜你喜欢
      • 2017-07-23
      • 2015-08-10
      • 2021-12-12
      • 1970-01-01
      • 1970-01-01
      • 2014-02-15
      • 2020-02-17
      • 2013-07-24
      • 2016-05-03
      相关资源
      最近更新 更多