【问题标题】:Flask / SQL Alchemy raw query instead of class methodFlask / SQLAlchemy 原始查询而不是类方法
【发布时间】:2018-10-23 11:43:05
【问题描述】:

在我问这个问题之前,我承认这是一个奇怪的问题。 我想知道是否可以使用原始 SQL 而不是使用方法本身来复制 Flask / SQL Alchemy 类方法?

长话短说,我和我的队友正在学习数据库设计课程,我们现在正处于实施阶段,我们正在编写基于我们的数据库架构设计的应用程序。我们想让事情变得简单,所以我们选择在 Python 中使用 Flask。我们正在关注Flask Mega Tutorial,这是一个kickass-tic 教程,解释了如何像我们一样构建一个基本站点。我们刚刚完成了第 5 章:用户登录,并且正在继续。

app/routes.py 脚本中,教程做了一些事情来获取用户信息。这是示例应用程序的示例登录路径:

from flask_login import current_user, login_user
from app.models import User

# ...

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)
        return redirect(url_for('index'))
    return render_template('login.html', title='Sign In', form=form)

user = User.query.filter_by(username=form.username.data).first() 行是我感兴趣的。基本上,该行实例化了User 类,这是来自 SQL Alchemy 的数据库模型,并从用户输入的电子邮件地址中获取有关用户的信息。调用这些方法会生成如下 SQL 语句:

SELECT `User`.`userID` AS `User_userID`,
       `User`.user_email AS `User_user_email`,
       `User`.user_first_name AS `User_user_first_name`,
       `User`.user_last_name AS `User_user_last_name`,
       `User`.user_password AS `User_user_password`
FROM `User`
WHERE `User`.user_email = 'test@test.com'
LIMIT 1

还有一些关于user 变量本身的信息:

>>> print(type(user))
<class 'myapp.models.User'>

>>> pp(user.__dict__)
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f5a026a8438>,
 'userID': 1,
 'user_email': 'test@test.com',
 'user_first_name': 'SomeFirstName',
 'user_last_name': 'SomeLastName',
 'user_password': 'somepassword'}

在我们的项目中,我们不应该使用生成的 SQL 语句,例如在实例化的 User 类上调用 query.filter_by(username=form.username.data).first() 产生的 SQL 语句;我们应该自己编写原始 SQL,这通常没有意义,但在我们的例子中确实如此。

这可能吗?

【问题讨论】:

  • 但在我们的例子中确实如此。为什么它有意义?是的,您可以执行原始查询。然后,您可以根据数据创建自己的 User 实例。但是您为什么要这样做
  • 请注意,User.query.filter_by(username=form.username.data).first() 发出一个非常非常简单的 SELECT 查询,带有 LIMIT 1。使用手动“手工”查询不会改善该查询。 SQLAlchemy 所做的一切都是可自省和可调整的,您可以使用 core API 构建相同的查询,或者只是将原始 SQL 传递给数据库连接。但这 a) 会增加开发成本,并且 b) 如果您不小心处理不受信任的数据,则会给您带来安全问题。
  • @MartijnPieters 哈,我告诉过你这很奇怪:D。通常你不会那样做,我知道这一点。我担心该项目的评分者会使用自动生成 SQL 查询的类方法严厉地看待我们,无论多么简单。
  • 然后去和你的教授或助教谈谈,看看真正的要求是什么。如果您应该编写自己的 SQL,那么不要使用 SQLAlchemy 开始
  • @ScottCrooks:那么也许您需要寻找不同的教程。 Mega 教程正在教授最佳实践,但如果这些不符合您的课程要求,请查找其他内容。您在询问是否可以绕过专门为您处理 SQL 的工具而不为您处理 SQL。是的,它可以,但是为什么要使用该工具开始呢?

标签: python flask sqlalchemy flask-sqlalchemy


【解决方案1】:

首先:与您的教授或助教交谈。通过不对如此重大的事情做出假设,您将节省自己的时间。如果课程的目标是考虑数据库模式设计,那么使用 ORM 可能就可以了。如果您需要编写自己的 SQL,那么不要使用 ORM 开头

回答技术问题:是的,您可以将 SQLAlchemy 纯粹用作数据库连接池,用作从 Python 对象创建有效 SQL 语句的工具,以及用作成熟的 ORM,以及介于两者之间的每一个层次。

例如,使用 ORM 层,您可以告诉 Query 对象不为您生成 SQL,而是获取文本。这在Using Textual SQL section 下的 SQLAlchemy ORM 教程中有介绍:

文字字符串可以灵活地与 Query 一起使用,通过指定它们与 text() 构造的使用,大多数适用的方法都接受该构造

对于您的登录示例,仅查询密码可能如下所示:

user = User.query.from_statement(
    db.text("SELECT * FROM User where user_email=:email LIMIT 1")
).params(email=form.username.data).first()

if user is None or user.check_password(form.password.data):
    # ...

您还可以阅读SQL Expression API(SQLAlchemy 库的核心 API)以使用 Python 代码构建查询; Python 对象和结果查询之间的关系几乎是一对一的;您通常会首先生成表模型,然后从那里构建 SQL,或者您可以使用文字:

s = select([
    literal_column("User.password", String)
]).where(
    literal_column("User.user_email") == form.username.data
).select_from(table("User")).limit(1)

并使用Session.execute() method 执行此类对象

results = db.session.execute(s)

如果你真的想在脚上开枪,你也可以直接将字符串传递给db.session.execute()

results = db.session.execute("""
    SELECT user_password FROM User where user_email=:email LIMIT 1
    """, {'email': form.username.data})

只知道Session.execute() 返回一个ResultProxy() instance,而不是ORM 实例。

另外,要知道 Flask-Login 不需要您使用 ORM。作为project documentation states

但是,它没有:

  • 将特定数据库或其他存储方法强加于您。 您完全负责用户的加载方式。

因此,您可以在每次查询数据库时手动创建一个 subclass of UserMixin 实例化。

class User(flask_login.UserMixin):
    def __init__(self, id):    # add more attributes as needed
        self.id = id

@login_manager.user_loader
def load_user(user_id):
    # perhaps query the database to confirm the user id exists and
    # load more info, but all you basically need is:
    return User(user_id)

# on login, use

user = User(id_of_user_just_logged_in)
login_user(user)

就是这样。扩展想要查看implement 4 basic methods 的实例,而UserMixin 类提供了所有这些实例,您只需要提供id 属性。如何验证用户 ID 和处理登录取决于您。

【讨论】:

  • 谢谢,这确实回答了我的问题。我并不是想用我糟糕的要求冒犯任何人,我知道这很奇怪(请原谅我),但我只是需要潜在地找到解决方法。作为记录,我已经要求我的助教对此进行澄清:D
  • 你没有冒犯,别担心。但是,如果您需要编写自己的查询,那么使用 ORM 确实会让您自己变得更加困难。
  • @ScottCrooks:我添加了一个关于如何使用 Flask-Login 的简短部分。这真的很简单
  • @IljaEverilä:好吧,是的,这完全错了。那是我在某处被打断并以不完整状态返回的结果。我会解决的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
  • 2015-05-26
  • 1970-01-01
  • 2017-01-07
  • 2017-04-03
  • 2015-01-31
  • 2019-04-23
相关资源
最近更新 更多