搜索期间访问Flask-Sqlalchemy查询中的表变量

时间:2018-10-30 09:24:28

标签: python sqlite sqlalchemy flask-sqlalchemy

我正在使用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)

表中还有其他元素,但是lat和lon最相关。我有一个方法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)。

2 个答案:

答案 0 :(得分:0)

您的直觉是正确的,而错误是由于将SQLAlchemy的检测属性传递给Python的math库函数而导致的,而这些线索不知道如何处理。

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

# 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)

您还可以在wrap the distance calculations中使用hybrid attribute来处理生成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)

答案 1 :(得分:-1)

我通过采用distanceMath函数并将其放置到我的Users模型类中,并将其传递给“ 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()