Flask修炼——数据库!



内容概述
ORM,
Flak-SQLAlchemy 安装及配置,
数据库的基本操作,
综合案例-图书管理,
多对多演练


ORM

Object-Relation Mapping 对象-关系映射

主要实现模型对象到关系数据库数据的映射.

优点 :
  • 只需要面向对象编程, 不需要面向数据库编写代码.
    • 对数据库的操作都转化成对类属性和方法的操作.
    • 不用编写各种数据库的sql语句.
  • 实现了数据模型与数据库的解耦, 屏蔽了不同数据库操作上的差异.
    • 不在关注用的是mysqloracle…等.
    • 通过简单的配置就可以轻松更换数据库, 而不需要修改代码.
缺点 :
  • 相比较直接使用SQL语句操作数据库,有性能损失.
  • 根据对象的操作转换成SQL语句,根据查询的结果转化成对象, 在映射过程中有性能损失.



Flask-SQLAlchemy 安装及配置

windows 中安装 SQLAlchemy:

pip install mysqlclient==1.3.12

pip install flask-mysqldb

pip install flask-sqlalchemy

pip install mysql-connector


数据库连接设置
  • 在 Flask-SQLAlchemy 中,数据库使用URL指定,而且程序使用的数据库必须保存到Flask配置对象的 SQLALCHEMY_DATABASE_URI键中
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://root:[email protected]:3306/booktest"
  • 其他设置:
# 动态追踪修改设置,如未设置只会提示警告
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True

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

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



数据库的基本操作

  • 在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。
    • 会话用 db.session 表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 commit() 方法提交会话。
  • 在 Flask-SQLAlchemy 中,查询操作是通过 query 对象操作数据。
    • 最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。

常用的SQLAlchemy查询过滤器
过滤器 说明
filter() 把过滤器添加到原查询上,返回一个新查询
filter_by() 把等值过滤器添加到原查询上,返回一个新查询
limit 使用指定的值限定原查询返回的结果
offset() 偏移原查询返回的结果,返回一个新查询
order_by() 根据指定条件对原查询结果进行排序,返回一个新查询
group_by() 根据指定条件对原查询结果进行分组,返回一个新查询

常用的SQLAlchemy查询执行器
方法 说明
all() 以列表形式返回查询的所有结果
first() 返回查询的第一个结果,如果未查到,返回None
first_or_404() 返回查询的第一个结果,如果未查到,返回404
get() 返回指定主键对应的行,如不存在,返回None
get_or_404() 返回指定主键对应的行,如不存在,返回404
count() 返回查询结果的数量
paginate() 返回一个Paginate对象,它包含指定范围内的结果

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)

# 配置连接数据库
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://root:[email protected]:3306/test_04"
# 是否追踪数据库的修改
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

# 初始化 SQLAlchemy 对象
db = SQLAlchemy(app)


class Role(db.Model):
    # 定义表名
    __tablename__ = 'roles'
    # 定义列对象
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    us = db.relationship('User', backref='role')

    # repr()方法显示一个可读字符串
    def __repr__(self):
        return 'Role:%s' % self.name  # 在控制台进行查询操作时返回的数据


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True, index=True)
    email = db.Column(db.String(64), unique=True)
    password = db.Column(db.String(64))
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))

    def __repr__(self):
        return 'User:%d:%s' % (self.id, self.name)


@app.route('/')
def index():
    return 'Hello World!'


if __name__ == '__main__':
    db.drop_all()
    db.create_all()

    ro1 = Role(name='admin')
    db.session.add(ro1)
    db.session.commit()
    # 再次插入一条数据
    ro2 = Role(name='user')
    db.session.add(ro2)
    db.session.commit()

    us1 = User(name='wang', email='[email protected]', password='123456', role_id=ro1.id)
    us2 = User(name='zhang', email='[email protected]', password='201512', role_id=ro2.id)
    us3 = User(name='chen', email='[email protected]', password='987654', role_id=ro2.id)
    us4 = User(name='zhou', email='[email protected]', password='456789', role_id=ro1.id)
    us5 = User(name='tang', email='[email protected]', password='158104', role_id=ro2.id)
    us6 = User(name='wu', email='[email protected]', password='5623514', role_id=ro2.id)
    us7 = User(name='qian', email='[email protected]', password='1543567', role_id=ro1.id)
    us8 = User(name='liu', email='[email protected]', password='867322', role_id=ro1.id)
    us9 = User(name='li', email='[email protected]', password='4526342', role_id=ro2.id)
    us10 = User(name='sun', email='[email protected]', password='235523', role_id=ro2.id)
    db.session.add_all([us1, us2, us3, us4, us5, us6, us7, us8, us9, us10])
    db.session.commit()

    """
    查询操作
    
    查询所有用户数据
        User.query.all()
    查询有多少个用户
        User.query.count()
    查询第1个用户
        User.query.get(1)
        User.query.first()
    查询id为4的用户[3种方式]
        User.query.get(4)
        User.query.filter(User.id == 4).first()
        User.query.filter_by(id=4).first()
    查询名字结尾字符为g的所有数据[开始/包含]
        User.query.filter(User.name.endswith('g')).all()
    查询名字不等于wang的所有数据[2种方式]
        User.query.filter(User.name != 'wang').all()
        User.query.filter(not_(User.name == 'wang')).all()
    查询名字和邮箱都以 li 开头的所有数据[2种方式]
        User.query.filter(User.name.startswith('li'), User.email.startswith('li')).all()
        User.query.filter(and_(User.name.startswith('li'), User.email.startswith('li'))).all()
    查询password是 `123456` 或者 `email` 以 `itheima.com` 结尾的所有数据
        User.query.filter(or_(User.password == '123456', User.email.endswith('itheima.com'))).all()
    查询id为 [1, 3, 5, 7, 9] 的用户列表
        User.query.filter(User.id.in_([1, 3, 5, 7, 9])).all()
    查询name为liu的角色数据
        User.query.filter(User.name == 'liu').first().role
    查询所有用户数据,并以邮箱排序
        User.query.order_by(User.email.desc()).all()
    每页3个,查询第2页的数据
        paginate = User.query.paginate(2,3)
        paginate.items
        paginate.page
        paginate.pages
    """
    app.run(debug=True)



综合案例—图书管理

MVC 简介:

MVC 的全名是 Model View Controller,是模型 (model) -视图 (view) -控制器 (controller) 的缩写,是一种软件设计典范。它是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间。

MVC 开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器

V即 View 视图是指用户看到并与之交互的界面;

M即 model 模型是指模型表示业务规则。

C即 controller 控制器是指控制器接受用户的输入并调用模型和视图去完成用户的需求,控制器本身不输出任何东西和做任何处理。

在这里 M 就是指创建的几个模型类,数据表;

V 就是指 index 这个函数里的业务逻辑加模板渲染,一块构成了视图;

C 就是客户端发送过来的请求,进行判断来决定去调用哪个模型


  • 逻辑代码
from flask import Flask, render_template, flash, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import InputRequired

app = Flask(__name__)
app.secret_key = 'asdfasdf'

# 配置数据库
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://root:[email protected]:3306/booktest"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)


class AddBookForm(FlaskForm):
    """自定义添加书籍的表单"""
    author = StringField('作者: ', validators=[InputRequired('请输入作者')])
    book = StringField('书名: ', validators=[InputRequired('请输入书名')])
    submit = SubmitField('添加')


class Author(db.Model):
    """ 作者模型,一 的一方"""
    __tablename__ = 'authors'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    # 定义属性,以便作者模型可以直接通过该属性访问其多的一方的数据(书的数据)
    # backref 给 Book 也添加了一个 author 的属性,可以通过 book.author 获取 book 所对应的作者信息
    books = db.relationship('Book', backref='author')


class Book(db.Model):
    """书的模型,多的一方"""
    __tablename__ = 'books'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    # 记录 一 的一方的id作为外键
    author_id = db.Column(db.Integer, db.ForeignKey(Author.id))


@app.route('/delete_author/')
def delete_author(author_id):
    """删除作者以及作者所有的书籍"""
    try:
        author = Author.query.get(author_id)
    except Exception as e:
        print(e)
        return "查询错误"

    if not author:
        return '作者不存在'

    try:
        # 删除作者及其所有书籍
        Book.query.filter(Book.author_id == author_id).delete()
        db.session.delete(author)
        db.session.commit()
    except Exception as e:
        print(e)
        db.session.rollback()

    return redirect(url_for('index'))


@app.route('/delete_book/')
def delete_book(book_id):
    """ 删除书籍"""
    book = None
    try:
        book = Book.query.get(book_id)
    except Exception as e:
        print(e)
        return "查询错误"

    if not book:
        return '书籍不存在'

    try:
        db.session.delete(book)
        db.session.commit()
    except Exception as e:
        print(e)
        db.session.rollback()
        return '删除失败'

    return redirect(url_for('index'))


@app.route('/', methods=['GET', 'POST'])
def index():
    """返回首页"""

    book_form = AddBookForm()

    # 如果 book_form 可以被提交
    if book_form.validate_on_submit():
        # 1.取出表单中数据
        author_name = book_form.author.data
        book_name = book_form.book.data

        # 2.做具体业务逻辑代码实现
        # 2.1.查询指定名字的作者
        author = Author.query.filter(Author.name == author_name).first()
        # if 指定名字的作者不存在:
        if not author:
            try:
                # 添加作者信息到数据库
                # 初始化作者的模型对象
                author = Author(name=author_name)
                db.session.add(author)
                db.session.commit()
                # 添加书籍信息到数据库(指定其作者)
                book = Book(name=book_name, author_id=author.id)
                # book.author = author
                db.session.add(book)
                db.session.commit()
            except Exception as e:
                db.session.rollback()
                print(e)
                flash('添加失败')
        else:
            book = Book.query.filter(Book.name == book_name).first()
            if not book:
                try:
                    # 添加书籍信息到数据库(指定其作者)
                    book = Book(name=book_name, author_id=author.id)
                    # book.author = author
                    db.session.add(book)
                    db.session.commit()
                except Exception as e:
                    print(e)
                    flash('添加失败')
            else:
                flash('当前数据已存在')
    else:
        if request.method == 'POST':
            flash('参数错误')

    # 1.查询数据
    authors = Author.query.all()

    # 2.将数据传入到模板中进行渲染返回
    return render_template('demo1_bookDemo.html', authors=authors, form=book_form)


if __name__ == '__main__':
    db.drop_all()
    db.create_all()

    # 生成数据
    au1 = Author(name='老王')
    au2 = Author(name='老尹')
    au3 = Author(name='老刘')
    # 把数据提交给用户会话
    db.session.add_all([au1, au2, au3])
    # 提交会话
    db.session.commit()
    bk1 = Book(name='老王回忆录', author_id=au1.id)
    bk2 = Book(name='我读书少,你别骗我', author_id=au1.id)
    bk3 = Book(name='如何才能让自己更骚', author_id=au2.id)
    bk4 = Book(name='怎样征服美丽少女', author_id=au3.id)
    bk5 = Book(name='如何征服英俊少男', author_id=au3.id)
    # 把数据提交给用户会话
    db.session.add_all([bk1, bk2, bk3, bk4, bk5])
    # 提交会话
    db.session.commit()

    app.run(debug=True)


  • 模板代码

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
<h1>图书管理h1>
<form method="post">
    {{ form.csrf_token() }}<br/>
    {{ form.author.label }}{{ form.author }}<br/>
    {{ form.book.label }}{{ form.book }}<br/>
    {{ form.submit }}<br/>

    {% for message in get_flashed_messages() %}
        {{ message }}
    {% endfor %}
form>
<hr/>
<ur>
    {% for author in authors %}
        <li>{{ author.name }}<a href="/delete_author/{{ author.id }}">删除a>li>
        <ul>
            {% for book in author.books %}
                <li>{{ book.name }}<a href="/delete_book/{{ book.id }}">删除a>li>
            {% endfor %}
        ul>
    {% endfor %}
ur>
body>
html>



多对多演练

这块不需要记,用到了回来复制即可

在项目开发过程中,会遇到很多数据之间多对多关系的情况

在开发过程中需要使用 ORM 模型将表与表的多对多关联关系使用代码描述出来。多对多关系描述有一个唯一的点就是:需要添加一张单独的表去记录两张表之间的对应关系

tb_student_course = db.Table('tb_student_course',
                             db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
                             db.Column('course_id', db.Integer, db.ForeignKey('courses.id'))
                             )


class Student(db.Model):
    __tablename__ = "students"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    courses = db.relationship('Course', secondary=tb_student_course,
                              backref='student',
                              lazy='dynamic')


class Course(db.Model):
    __tablename__ = "courses"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

你可能感兴趣的:(flask,每日笔记)