Flask / SQL Alchemy 原始查询而不是类方法

Sco*_*oks -1 python sqlalchemy flask flask-sqlalchemy

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

长话短说,我和我的队友正在参加数据库设计课程,我们现在处于实施阶段,我们正在编写基于我们的数据库架构设计的应用程序。我们想让事情变得简单,所以我们选择在 Python 中使用 Flask。我们正在关注Flask Mega 教程,这是一个解释如何像我们所做的那样构建基本站点的精彩教程。我们刚刚完成了第 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)
Run Code Online (Sandbox Code Playgroud)

该行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
Run Code Online (Sandbox Code Playgroud)

还有一些关于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'}
Run Code Online (Sandbox Code Playgroud)

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

这可能吗?

Mar*_*ers 7

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

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

例如,使用 ORM 层,您可以告诉Query对象不要为您生成 SQL,而是获取文本。这在使用文本 SQL部分下的 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):
    # ...
Run Code Online (Sandbox Code Playgroud)

您还可以阅读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)
Run Code Online (Sandbox Code Playgroud)

并使用该方法执行此类对象Session.execute()

results = db.session.execute(s)
Run Code Online (Sandbox Code Playgroud)

如果你真的想用脚射自己,你也可以db.session.execute()直接将字符串传递给:

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

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

另外,要知道 Flask-Login不需要您使用 ORM。正如项目文档所述

但是,它不会:

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

因此,您可以手动创建每次查询数据库时实例化的子类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)
Run Code Online (Sandbox Code Playgroud)

就是这样。扩展希望看到情况下实现4种基本方法UserMixin类提供所有这些,你只需要提供的id属性。如何验证用户 ID 和处理登录取决于您。