flask中的操作数据库的插件Flask-SQLAlchemy

本文介绍了在Web开发中如何使用ORM框架如SQLAlchemy,以及在Flask框架中如何配置和操作SQLite数据库。涵盖了数据库配置、模型定义、数据操作(添加、查询、更新和删除)等内容。

1、ORM 框架

Web 开发中,一个重要的组成部分便是数据库了。Web 程序中最常用的莫过于关系型数据库了,也称 SQL 数据库。另外,文档数据库(如 mongodb)、键值对数据库(如 redis)近几年也逐渐在 web 开发中流行起来,我们习惯把这两种数据库称为 NoSQL 数据库。

大多数的关系型数据库引擎(比如 MySQL、Postgres 和 SQLite)都有对应的 Python 包。在这里,我们不直接使用这些数据库引擎提供的 Python 包,而是使用对象关系映射(Object-Relational Mapper, ORM)框架,它将低层的数据库操作指令抽象成高层的面向对象操作。也就是说,如果我们直接使用数据库引擎,我们就要写 SQL 操作语句,但是,如果我们使用了 ORM 框架,我们对诸如表、文档此类的数据库实体就可以简化成对 Python 对象的操作。

Python 中最广泛使用的 ORM 框架是 SQLAlchemy,它是一个很强大的关系型数据库框架,不仅支持高层的 ORM,也支持使用低层的 SQL 操作,另外,它也支持多种数据库引擎,如 MySQL、Postgres 和 SQLite 等

2、Flask-SQLAlchemy

在 Flask 中,为了简化配置和操作,我们使用的 ORM 框架是 Flask-SQLAlchemy,这个 Flask 扩展封装了 SQLAlchemy 框架。在 Flask-SQLAlchemy 中,数据库使用 URL 指定,下表列出了常见的数据库引擎和对应的 URL
在这里插入图片描述
上面的表格中,username 和 password 表示登录数据库的用户名和密码,hostname 表示 SQL 服务所在的主机,可以是本地主机(localhost)也可以是远程服务器,database 表示要使用的数据库。有一点需要注意的是,SQLite 数据库不需要使用服务器,它使用硬盘上的文件名作为 database。

3、一个最小的应用

创建数据库

首先,我们使用 pip 安装 Flask-SQLAlchemy:

pip install flask-sqlalchemy

接下来,我们配置一个简单的 SQLite 数据库:

$ cat app.py
# -*- coding: utf-8 -*-
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db/users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
class User(db.Model):
    """定义数据模型"""
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)
    def __init__(self, username, email):
        self.username = username
        self.email = email
    def __repr__(self):
        return '<User %r>' % self.username

这里有几点需要注意的是:

  • app 应用配置项 SQLALCHEMY_DATABASE_URI 指定了 SQLAlchemy 所要操作的数据库,这里我们使用的是 SQLite,数据库 URL 以 sqlite:/// 开头,后面的 db/users.db 表示数据库文件存放在当前目录的 db 子目录中的 users.db 文件。当然,你也可以使用绝对路径,如 /tmp/users.db 等。
  • db 对象是 SQLAlchemy 类的实例,表示程序使用的数据库。
  • 我们定义的 User 模型必须继承自 db.Model,这里的模型其实就对应着数据库中的表。其中,类变量__tablename__ 定义了在数据库中使用的表名,如果该变量没有被定义,Flask-SQLAlchemy 会使用一个默认名字。
    接着,我们创建表和数据库。为此,我们先在当前目录创建 db 子目录和新建一个 users.db 文件,然后在交互式 Python shell 中导入 db 对象并调用 SQLAlchemy 类的 create_all() 方法:
$ mkdir db
$ python
>>> from app import db
>>> db.create_all()

我们验证一下,”users” 表是否创建成功:

$ sqlite3 db/users.db    # 打开数据库文件
SQLite version 3.8.10.2 2015-05-20 18:17:19
Enter ".help" for usage hints.
sqlite> .schema users   # 查看 "user" 表的 schema
CREATE TABLE users (
        id INTEGER NOT NULL,
        username VARCHAR(80),
        email VARCHAR(120),
        PRIMARY KEY (id),
        UNIQUE (username),
        UNIQUE (email)
);

插入数据

我们创建一些用户,通过使用 db.session.add()来添加数据:

@app.route('/adduser')
def add_user():
    user1 = User('ethan', 'ethan@example.com')
    user2 = User('admin', 'admin@example.com')
    user3 = User('guest', 'guest@example.com')
    user4 = User('joe', 'joe@example.com')
    user5 = User('michael', 'michael@example.com')
    db.session.add(user1)
    db.session.add(user2)
    db.session.add(user3)
    db.session.add(user4)
    db.session.add(user5)
    db.session.commit()
    return "<p>add succssfully!"

这里有一点要注意的是,我们在将数据添加到会话后,在最后要记得调用 db.session.commit() 提交事务,这样,数据才会被写入到数据库。

查询数据

查询数据主要是用 query 接口,例如 all() 方法返回所有数据,filter_by() 方法对查询结果进行过滤,参数是键值对,filter 方法也可以对结果进行过滤,但参数是布尔表达式,详细的介绍请查看这里。

>>> from app import User
>>> users = User.query.all()
>>> users
[<User u'ethan'>, <User u'admin'>, <User u'guest'>, <User u'joe'>, <User u'michael'>]
>>>
>>> user = User.query.filter_by(username='joe').first()
>>> user
<User u'joe'>
>>> user.email
u'joe@example.com'
>>>
>>> user = User.query.filter(User.username=='ethan').first()
>>> user
<User u'ethan'>

如果我们想查看 SQLAlchemy 为查询生成的原生 SQL 语句,只需要把 query 对象转化成字符串:

>>> str(User.query.filter_by(username='guest'))
'SELECT users.id AS users_id, users.username AS users_username, users.email AS users_email \nFROM users \nWHERE users.username = :username_1'

分页方法

分页方法可以采用 limit() 和 offset() 方法,比如从第 3 条记录开始取(注意是从 0 开开始算起),并最多取 1 条记录,可以这样:

users = User.query.limit(1).offset(3)

更新数据

更新数据也用 add() 方法,如果存在要更新的对象,SQLAlchemy 就更新该对象而不是添加。

>>> from app import db
>>> from app import User
>>>
>>> admin = User.query.filter_by(username='admin').first()
>>>
>>> admin.email = 'admin@hotmail.com'
>>> db.session.add(admin)
>>> db.session.commit()
>>>
>>> admin = User.query.filter_by(username='admin').first()
>>> admin.email
u'admin@hotmail.com'

删除数据

删除数据用 delete() 方法,同样要记得 delete 数据后,要调用 commit() 提交事务:

>>> from app import db
>>> from app import User
>>>
>>> admin = User.query.filter_by(username='admin').first()
>>> db.session.delete(admin)
>>> db.session.commit()
### 实现登录功能并连接数据库Flask 应用程序中实现登录功能以及连接到数据库的过程涉及多个方面。对于登录功能而言,通常会涉及到路由设置、表单处理和身份验证逻辑。 针对提到的错误 `ValueError: urls must start with a leading slash` 和缺少前导斜杠的情况,在定义路由时应确保 URL 模式以正斜线开头[^1]: ```python @app.route('/login') def login(): return 'login' ``` 为了创建一个完整的登录系统并与数据库交互,可以采用如下方式构建应用程序结构,并利用扩展库简化操作流程。这里假设使用 SQLite 数据库作为例子来展示基本概念。 #### 安装依赖项 首先安装必要的 Python 包,比如用于 ORM 的 SQLAlchemy 及其工具包 Flask-SQLAlchemy 来帮助管理模型类;还有 WTForms 用来生成 HTML 表单字段及其验证器;另外还需要 PyJWT 或者其他类似的 JWT 工具来进行令牌编码解码工作以便支持基于 Token 的认证机制[^2]。 通过命令行执行 pip install 命令完成这些软件包的下载与配置: ```bash pip install flask sqlalchemy flask_sqlalchemy wtforms pyjwt ``` #### 初始化项目文件夹及核心组件 建立一个新的 Flask 项目目录树形结构,其中包含启动脚本 (`app.py`) 文件和其他辅助模块如配置参数(`config.py`)等资源文件夹。接着初始化 Flask 应用实例并将上述第三方插件集成进来形成基础框架环境。 ##### 配置数据库 URI 设置 (config.py) ```python import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config(object): SECRET_KEY = os.environ.get('SECRET_KEY') or 'you-will-never-guess' SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \ f'sqlite:///{os.path.join(basedir, "app.db")}' SQLALCHEMY_TRACK_MODIFICATIONS = False ``` ##### 创建应用工厂函数(app.py) ```python from flask import Flask from config import Config from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate db = SQLAlchemy() migrate = Migrate() def create_app(config_class=Config): app = Flask(__name__) app.config.from_object(config_class) db.init_app(app) migrate.init_app(app, db) # 注册蓝图为后续添加更多特性做准备 from .main import bp as main_bp app.register_blueprint(main_bp) return app ``` #### 设计用户数据模型(user_model.py) 根据业务需求设计相应的实体对象映射关系,此处仅提供简单的 User 类型表示法供参考: ```python from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from hashlib import md5 from app import db class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(64), index=True, unique=True) email = db.Column(db.String(120), index=True, unique=True) password_hash = db.Column(db.String(128)) def set_password(self, password): self.password_hash = generate_password_hash(password) def verify_password(self, password): return check_password_hash(self.password_hash, password) def get_reset_password_token(self, expires_in=600): s = Serializer(current_app.config['SECRET_KEY'], expires_in) return s.dumps({'reset_password': self.id}).decode('utf-8') @staticmethod def verify_reset_password_token(token): s = Serializer(current_app.config['SECRET_KEY']) try: data = s.loads(token.encode('utf-8')) except: return None return User.query.get(data['reset_password']) ``` #### 构建视图蓝图(main/views.py) 编写具体的 HTTP 请求处理器方法,负责接收客户端提交的数据并对之作出响应动作。下面给出了一部分关于用户注册、登陆页面渲染等方面的代码片段说明: ```python from flask import render_template, flash, redirect, url_for, request from app.main.forms import LoginForm, RegistrationForm from app.models.user_model import User from flask_login import current_user, login_user, logout_user, login_required from werkzeug.urls import url_parse from app.extensions import db from flask import Blueprint bp = Blueprint('main', __name__) @bp.route('/') @bp.route('/index') @login_required def index(): posts = [ {'author': {'username': 'John'}, 'body': 'Beautiful day in Portland!'}, {'author': {'username': 'Susan'}, 'body': 'The Avengers movie was so cool!'} ] return render_template('index.html', title='Home Page', posts=posts) @bp.route('/register', methods=['GET', 'POST']) def register(): if current_user.is_authenticated: return redirect(url_for('main.index')) form = RegistrationForm() if form.validate_on_submit(): user = User(username=form.username.data, email=form.email.data) user.set_password(form.password.data) db.session.add(user) db.session.commit() flash('Congratulations, you are now a registered user!') return redirect(url_for('main.login')) return render_template('register.html', title='Register', form=form) @bp.route('/login', methods=['GET', 'POST']) def login(): if current_user.is_authenticated: return redirect(url_for('main.index')) form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user is None or not user.verify_password(form.password.data): flash('Invalid email or password') return redirect(url_for('main.login')) login_user(user, remember=form.remember_me.data) next_page = request.args.get('next') if not next_page or url_parse(next_page).netloc != '': next_page = url_for('main.index') return redirect(next_page) return render_template('login.html', title='Sign In', form=form) @bp.route('/logout') def logout(): logout_user() return redirect(url_for('main.index')) @bp.route('/user/<username>') @login_required def user(username): user = User.query.filter_by(username=username).first_or_404() posts = [{'author': user, 'body': 'Test post #1'}, {'author': user, 'body': 'Test post #2'}] return render_template('user.html', user=user, posts=posts) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

javascript_good

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值