Flask--02(flask中数据库相关+书籍管理案例)

一、数据库的设置

Web应用中普遍使用的是关系模型的数据库,关系型数据库把所有的数据都存储在表中,表用来给应用的实体建模,表的列数是固定的,行数是可变的。它使用结构化的查询语言。关系型数据库的列定义了表中表示的实体的数据属性。比如:商品表里有name、price、number等。 Flask本身不限定数据库的选择,你可以选择SQL或NOSQL的任何一种。也可以选择更方便的SQLALchemy,类似于Django的ORM。SQLALchemy实际上是对数据库的抽象,让开发者不用直接和SQL语句打交道,而是通过Python对象来操作数据库,在舍弃一些性能开销的同时,换来的是开发效率的较大提升。

SQLAlchemy是一个关系型数据库框架,它提供了高层的ORM和底层的原生数据库的操作。flask-sqlalchemy是一个简化了SQLAlchemy操作的flask扩展。

1.1数据库安装

安装服务端

sudo apt-get install mysql-server

安装客户端

sudo apt-get install mysql-client
sudo apt-get install libmysqlclient-dev

1.2 数据库的基本命令

登录数据库

mysql -u root -p

创建数据库,并设定编码

create database <数据库名> charset=utf8;

显示所有数据库

show databases;

1.3 在Flask中使用mysql数据库,需要安装一个flask-sqlalchemy的扩展。

pip install flask-sqlalchemy

要连接mysql数据库,仍需要安装flask-mysqldb

pip install flask-mysqldb

1.4 使用Flask-SQLAlchemy管理数据库

使用Flask-SQLAlchemy扩展操作数据库,首先需要建立数据库连接。数据库连接通过URL指定,而且程序使用的数据库必须保存到Flask配置对象的SQLALCHEMY_DATABASE_URI键中。

对比下Django和Flask中的数据库设置:

Django的数据库设置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydb',
        'HOST':'192.168.18.128',
        'PORT':3306,
        'USER':'root',
        'PASSWORD':'123456',
    }
}
​

Flask的数据库设置:

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysq+pymysqll://root:mysql@127.0.0.1:3306/mydb'

 

常用的SQLAlchemy字段类型

类型名python中类型说明
Integerint普通整数,一般是32位
SmallIntegerint取值范围小的整数,一般是16位
BigIntegerint或long不限制精度的整数
Floatfloat浮点数
Numericdecimal.Decimal普通整数,一般是32位
Stringstr变长字符串
Textstr变长字符串,对较长或不限长度的字符串做了优化
Unicodeunicode变长Unicode字符串
UnicodeTextunicode变长Unicode字符串,对较长或不限长度的字符串做了优化
Booleanbool布尔值
Datedatetime.date时间
Timedatetime.datetime日期和时间
LargeBinarystr二进制文件

常用的SQLAlchemy列选项

选项名说明
primary_key如果为True,代表表的主键
unique如果为True,代表这列不允许出现重复的值
index如果为True,为这列创建索引,提高查询效率
nullable如果为True,允许有空值,如果为False,不允许有空值
default为这列定义默认值

常用的SQLAlchemy关系选项

选项名说明
backref在关系的另一模型中添加反向引用
primary join明确指定两个模型之间使用的联结条件
uselist如果为False,不使用列表,而使用标量值
order_by指定关系中记录的排序方式
secondary指定多对多中记录的排序方式
secondary join在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件

代码演示

db_demo.py

# coding:utf-8
#db_demo.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
​
​
app = Flask(__name__)
​
​
class Config(object):
    """配置参数"""
    # sqlalchemy的配置参数
    SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/db_python04"
​
    # 设置sqlalchemy自动更跟踪数据库
    SQLALCHEMY_TRACK_MODIFICATIONS = True
​
​
app.config.from_object(Config)
​
# 创建数据库sqlalchemy工具对象
db = SQLAlchemy(app)
​
​
class Role(db.Model):
    """用户角色/身份表"""
    __tablename__ = "tbl_roles"
​
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), unique=True)
    users = db.relationship("User", backref="role")
​
    def __repr__(self):
        """定义之后,可以让显示对象的时候更直观"""
        return "Role object: name=%s" % self.name
​
​
# 表名的常见规范
# ihome -> ih_user   数据库名缩写_表名
# tbl_user  tbl_表名
# 创建数据库模型类
class User(db.Model):
    """用户表"""
    __tablename__ = "tbl_users"  # 指明数据库的表名
​
    id = db.Column(db.Integer, primary_key=True)  # 整型的主键,会默认设置为自增主键
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(128), unique=True)
    password = db.Column(db.String(128))
    role_id = db.Column(db.Integer, db.ForeignKey("tbl_roles.id"))
​
    def __repr__(self):
        return "User object: name=%s" % self.name
​
​
if __name__ == '__main__':
    # 清除数据库里的所有数据
    db.drop_all()
​
    # 创建所有的表
    db.create_all()
​
    # 创建对象
    role1 = Role(name="admin")
    # session记录对象任务
    db.session.add(role1)
    # 提交任务到数据库中
    db.session.commit()
​
    role2 = Role(name="stuff")
    db.session.add(role2)
    db.session.commit()
​
    us1 = User(name='wang', email='wang@163.com', password='123456', role_id=role1.id)
    us2 = User(name='zhang', email='zhang@189.com', password='201512', role_id=role2.id)
    us3 = User(name='chen', email='chen@126.com', password='987654', role_id=role2.id)
    us4 = User(name='zhou', email='zhou@163.com', password='456789', role_id=role1.id)
​
    # 一次保存多条数据
    db.session.add_all([us1, us2, us3, us4])
    db.session.commit()

terminal

Role.query.all()
Out[2]: [<db_demo.Role at 0x10388d190>, <db_demo.Role at 0x10388d310>]
​
In [3]: li = Role.query.all()
​
In [4]: li
Out[4]: [<db_demo.Role at 0x10388d190>, <db_demo.Role at 0x10388d310>]
​
In [5]: r = li[0]
​
In [6]: type(r)
Out[6]: db_demo.Role
​
In [7]: r.name
Out[7]: u'admin'
​
In [8]: Role.query.first()
Out[8]: <db_demo.Role at 0x10388d190>
​
In [9]: r = Role.query.first()
​
In [10]: r.name
Out[10]: u'admin'
​
#  根据主键id获取对象
In [11]: r = Role.query.get(2)
​
In [12]: r
Out[12]: <db_demo.Role at 0x10388d310>
​
In [13]: r.name
Out[13]: u'stuff'
​
In [14]:
​
​
# 另一种查询方式
In [15]: db.session.query(Role).all()
Out[15]: [<db_demo.Role at 0x10388d190>, <db_demo.Role at 0x10388d310>]
​
In [16]: db.session.query(Role).get(2)
Out[16]: <db_demo.Role at 0x10388d310>
​
In [17]: db.session.query(Role).first()
Out[17]: <db_demo.Role at 0x10388d190>
​
In [18]:
​
In [18]: User.query.filter_by(name="wang")
Out[18]: <flask_sqlalchemy.BaseQuery at 0x1038c90d0>
​
In [19]: User.query.filter_by(name="wang").all()
Out[19]: [<db_demo.User at 0x1038c87d0>]
​
In [20]: User.query.filter_by(name="wang").first()
Out[20]: <db_demo.User at 0x1038c87d0>
​
In [21]: user = User.query.filter_by(name="wang").first()
​
In [22]: user.name
Out[22]: u'wang'
​
In [23]: user.email
Out[23]: u'wang@163.com'
​
In [24]: User.query.filter_by(name="wang", role_id=1).first()
Out[24]: <db_demo.User at 0x1038c87d0>
​
In [25]: User.query.filter_by(name="wang", role_id=2).first()
​
In [26]: user = User.query.filter_by(name="wang", role_id=2).first()
​
In [27]: type(user)
Out[27]: NoneType
​
In [28]:
​
​
In [28]: user = User.query.filter(User.name=="wang", User.role_id==1).first
    ...: ()
​
In [29]: user
Out[29]: <db_demo.User at 0x1038c87d0>
​
In [30]: user.name
Out[30]: u'wang'
​
In [31]: from sqlalchemy import or_
​
In [32]: User.query.filter(or_(User.name=="wang", User.email.endswith("163.com")
    ...: )).all()
Out[32]: [<db_demo.User at 0x1038c87d0>, <db_demo.User at 0x1038ef310>]
​
In [33]: li = User.query.filter(or_(User.name=="wang", User.email.endswith("163.
    ...: com"))).all()
​
In [34]: li[0].name
Out[34]: u'wang'
​
In [35]: li[1].name
Out[35]: u'zhou'
​
In [36]:
​
​
# offset偏移  跳过几条
In [36]: User.query.offset(2).all()
Out[36]: [<db_demo.User at 0x1038c0950>, <db_demo.User at 0x1038ef310>]
​
In [37]: li = User.query.offset(2).all()
​
In [38]: li[0].name
Out[38]: u'chen'
​
In [39]: li[1].name
Out[39]: u'zhou'
​
In [40]:
​
​
In [42]: li = User.query.offset(1).limit(2).all()
​
In [43]: li
Out[43]: [<db_demo.User at 0x1038fd990>, <db_demo.User at 0x1038c0950>]
​
In [44]: li[0].name
Out[44]: u'zhang'
​
In [45]: li[1].name
Out[45]: u'chen'
​
In [46]:
​
In [50]: User.query.order_by("-id").all()
Out[50]:
[<db_demo.User at 0x1038ef310>,
 <db_demo.User at 0x1038c0950>,
 <db_demo.User at 0x1038fd990>,
 <db_demo.User at 0x1038c87d0>]
​
In [51]:
​
In [51]: li = User.query.order_by(User.id.desc()).all()
​
In [52]: li
Out[52]:
[<db_demo.User at 0x1038ef310>,
 <db_demo.User at 0x1038c0950>,
 <db_demo.User at 0x1038fd990>,
 <db_demo.User at 0x1038c87d0>]
​
In [53]: li[0].name
Out[53]: u'zhou'
​
In [54]: li[3].name
Out[54]: u'wang'
​
In [55]:
​
​
In [55]: from sqlalchemy import func
​
In [56]: db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_i
    ...: d)
Out[56]: <flask_sqlalchemy.BaseQuery at 0x103a38050>
​
In [57]: db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_i
    ...: d).all()
Out[57]: [(1L, 2L), (2L, 2L)]
​
In [58]:
​
​
In [61]: ro = Role.query.get(1)
​
In [62]: type(ro)
Out[62]: db_demo.Role
​
In [63]: ro.users
Out[63]: [<db_demo.User at 0x1038c87d0>, <db_demo.User at 0x1038ef310>]
​
In [64]: ro.users[0].name
Out[64]: u'wang'
​
In [65]: ro.users[1].name
Out[65]: u'zhou'
​
In [66]:
​
​
​
In [67]: user
Out[67]: <db_demo.User at 0x1038c87d0>
​
In [68]: user.role_id
Out[68]: 1L
​
In [69]: Role.query.get(user.role_id)
Out[69]: <db_demo.Role at 0x10388d190>
​
In [70]: user.role
Out[70]: <db_demo.Role at 0x10388d190>
​
In [71]: user.role.name
Out[71]: u'admin'
​
In [72]:
​
​
# 更新
In [14]: User.query.filter_by(name="zhou").update({"name": "python", "emai
    ...: l": "python@itast.cn"})
Out[14]: 1L
​
In [15]: db.session.commit()
​
In [16]:
​
​
# 删除
In [16]: user = User.query.get(3)
​
In [17]: db.session.delete(user)
​
In [18]: db.session.commit()
​
In [19]:

图书管理

(删除函数:注释代码部分为post请求写法)

后端部分

# coding:utf-8

from flask import Flask, render_template, request, redirect, url_for, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired


app = Flask(__name__)


class Config(object):
    # sqlalchemy的配置参数
    SQLALCHEMY_DATABASE_URI = "mysql://root:mysql@127.0.0.1:3306/author_book_py04"

    # 设置sqlalchemy自动更跟踪数据库
    SQLALCHEMY_TRACK_MODIFICATIONS = True

    SECRET_KEY = "doiso7fd89fyd9^(fsd"


app.config.from_object(Config)

db = SQLAlchemy(app)


# 定义数据库的模型
class Author(db.Model):
    """作者"""
    __tablename__ = "tbl_authors"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), unique=True)
    books = db.relationship("Book", backref="author")


class Book(db.Model):
    """书籍"""
    __tablename__ = "tbl_books"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    author_id = db.Column(db.Integer, db.ForeignKey("tbl_authors.id"))


# 创建表单模型类
class AuthorBookForm(FlaskForm):
    """作者数据表单模型类"""
    author_name = StringField(label=u"作者", validators=[DataRequired(u"作者必填")])
    book_name = StringField(label=u"书籍", validators=[DataRequired(u"书籍必填")])
    submit = SubmitField(label=u"保存")


@app.route("/", methods=["GET", "POST"])
def index():
    # 创建表单对象
    form = AuthorBookForm()

    if form.validate_on_submit():
        # 验证表单成功
        # 提取表单数据
        author_name = form.author_name.data
        book_name = form.book_name.data
        # 保存数据库
        author = Author(name=author_name)
        db.session.add(author)
        db.session.commit()

        book = Book(name=book_name, author_id=author.id)
        # book = Book(name=book_name, author=author)
        db.session.add(book)
        db.session.commit()

    # 查询数据库
    author_li = Author.query.all()
    return render_template("author_book.html", authors=author_li, form=form)


# post  /delete_book  json
# {"book_id":x}

# @app.route("/delete_book", methods=["POST"])
# def delete_book():
#     """删除数据"""
#     # 提取参数
#     # 如果前端发送的请求体数据是json格式,get_json会解析成字典
#     # get_json 要求前端传送的数据的Content-Type: application/json
#     req_dict = request.get_json()
#     book_id = req_dict.get("book_id")
#
#     # 删除数据
#     book = Book.query.get(book_id)
#     db.session.delete(book)
#     db.session.commit()
#
#     # "Content-Type": "application/json"
#     return jsonify(code=0, message="OK")


# /delete_book?book_id=xx
@app.route("/delete_book", methods=["GET"])
def delete_book():
    """删除数据"""
    # 提取参数
    book_id = request.args.get("book_id")

    # 删除数据
    book = Book.query.get(book_id)
    db.session.delete(book)
    db.session.commit()

    return redirect(url_for("index"))


if __name__ == '__main__':
    #测试数据
    # db.drop_all()
    # db.create_all()
    # au_xi = Author(name='我吃西红柿')
    # au_qian = Author(name='萧潜')
    # au_san = Author(name='唐家三少')
    # db.session.add_all([au_xi, au_qian, au_san])
    # db.session.commit()
    #
    # bk_xi = Book(name='吞噬星空', author_id=au_xi.id)
    # bk_xi2 = Book(name='寸芒', author_id=au_qian.id)
    # bk_qian = Book(name='飘渺之旅', author_id=au_qian.id)
    # bk_san = Book(name='冰火魔厨', author_id=au_san.id)
    # db.session.add_all([bk_xi, bk_xi2, bk_qian, bk_san])
    # db.session.commit()
    app.run(debug=True)

前端部分 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post">
        {{ form.csrf_token }}

        {{ form.author_name.label }}
        <p>{{form.author_name}}</p>
        {% for msg in form.author_name.errors %}
            <p>{{msg}}</p>
        {% endfor %}

        {{ form.book_name.label }}
        <p>{{form.book_name}}</p>
        {% for msg in form.book_name.errors %}
            <p>{{msg}}</p>
        {% endfor %}

        {{ form.submit }}
    </form>
    <hr/>
    <ul>
        {% for author in authors %}
        <li>作者:{{ author.name }}</li>
            <ul>
                {% for book in author.books%}
               <li>书籍:{{ book.name }}</li>
                <!--<a href="javascript:;" book-id="{{book.id}}">删除</a>-->
                <a href="/delete_book?book_id={{book.id}}">GET删除</a>
                {% endfor %}
            </ul>
        {% endfor %}
    </ul>
    <script type="text/javascript" src="/static/js/jquery.min.js"></script>
    <script>
//        $("a").click(
//            function () {
//                var data = {
//                    book_id: $(this).attr("book-id")
//                };
//                // 将js中的对象转换为 json字符串
//                var req_json = JSON.stringify(data);
//    //            $.post("/delete_book", req_json, function (resp) {
//    //                if (resp.code == 0) {
//    //                    location.href = "/";
//    //                }
//    //            })
//                $.ajax({
//                    url: "/delete_book", // 请求的后端url
//                    type: "post",  // 请求方式
//                    data: req_json,  // 向后端发送的请求体数据
//                    contentType: "application/json", // 指明向后端发送的数据格式
//                    dataType: "json",  // 指明后端返回的数据格式
//                    success: function (resp) {
//                        if (resp.code == 0) {
//                            alert("oK");
//                            location.href = "/";
//                        }
//                    }
//                })
//            }
//        )
    </script>
</body>
</html>

1.6数据库迁移

在开发过程中,需要修改数据库模型,而且还要在修改之后更新数据库。最直接的方式就是删除旧表,但这样会丢失数据。

更好的解决办法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。

在Flask中可以使用Flask-Migrate扩展,来实现数据迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。

为了导出数据库迁移命令,Flask-Migrate提供了一个MigrateCommand类,可以附加到flask-script的manager对象上。

首先要在虚拟环境中安装Flask-Migrate。

pip install flask-migrate

文件:database.py

#coding=utf-8
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate,MigrateCommand
from flask_script import Shell,Manager
​
app = Flask(__name__)
manager = Manager(app)
​
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/Flask_test'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
​
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db) 
​
#manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db',MigrateCommand)
​
#定义模型Role
class Role(db.Model):
    # 定义表名
    __tablename__ = 'roles'
    # 定义列对象
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    def __repr__(self):
        return 'Role:'.format(self.name)
​
#定义用户
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    def __repr__(self):
        return 'User:'.format(self.username)
if __name__ == '__main__':
    manager.run()

创建迁移仓库

#这个命令会创建migrations文件夹,所有迁移文件都放在里面。
python database.py db init

创建迁移脚本

自动创建迁移脚本有两个函数,upgrade()函数把迁移中的改动应用到数据库中。downgrade()函数则将改动删除。自动创建的迁移脚本会根据模型定义和数据库当前状态的差异,生成upgrade()和downgrade()函数的内容。对比不一定完全正确,有可能会遗漏一些细节,需要进行检查

#创建自动迁移脚本
python database.py db migrate -m 'initial migration'

更新数据库

python database.py db upgrade

回退数据库

回退数据库时,需要指定回退版本号,由于版本号是随机字符串,为避免出错,建议先使用python database.py db history命令查看历史版本的具体版本号,然后复制具体版本号执行回退。

python database.py db downgrade 版本号

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值