Python_Flask系列_2.模型 Model

前言

Flask-SQLAlchemy是Flask扩展,它将对SQLAlchemy的支持添加到Flask应用程序中。

一、准备

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

1.连接

pip install flask-sqlalchemy
pip install flask-mysqldb
pip install pymysql -i https://pypi.tuna.tsinghua.
edu.cn/simple

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:mysql@127.0.0.1:3306/falsk'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True

2.常用的SQLAlchemy字段类型

类型名 python中类型 说明
Integer int 普通整数,一般是32位
SmallInteger int 取值范围小的整数,一般是16位
BigInteger int或long 不限制精度的整数
Float float 浮点数
Numeric decimal.Decimal 普通整数,一般是32位
String str 变长字符串
Text str 变长字符串,对较长或不限长度的字符串做了优化
Unicode unicode 变长Unicode字符串
UnicodeText unicode 变长Unicode字符串,对较长或不限长度的字符串做了优化
Boolean bool 布尔值
Date datetime.date 时间
Time datetime.datetime 日期和时间
LargeBinary str 二进制文件

参数

选项名 说明
primary_key 如果为True,代表表的主键
unique 如果为True,代表这列不允许出现重复的值
index 如果为True,为这列创建索引,提高查询效率
nullable 如果为True,允许有空值,如果为False,不允许有空值
default 为这列定义默认值
backref 在关系的另一模型中添加反向引用
primary join 明确指定两个模型之间使用的联结条件
uselist 如果为False,不使用列表,而使用标量值
order_by 指定关系中记录的排序方式
secondary 指定多对多中记录的排序方式
secondary join 在SQLAlchemy中无法自行决定时,指定多对多关系中的二级联结条件

二、数据库基本操作

1.基本概念

  • 在Flask-SQLAlchemy中,插入、修改、删除操作,均由数据库会话管理。
    会话用db.session表示。在准备把数据写入数据库前,要先将数据添加到会话中然后调用 commit() 方法提交会话。

  • 在Flask-SQLAlchemy中,查询操作是通过query对象操作数据。
    最基本的查询是返回表中所有数据,可以通过过滤器进行更精确的数据库查询。

db.session.add(role)    添加到数据库的session中
db.session.add_all([user1, user2]) 添加多个信息到session中
db.session.commit()     提交数据库的修改(包括增/删/改)
db.session.rollback()   数据库的回滚操作
db.session.delete(user) 删除数据库(需跟上commit)
db.session.commit()

db.create_all() # 创建表:
db.drop_all()    # 删除表
#查询操作
#常用的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对象,它包含指定范围内的结果

2. 数据的增删改查

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 设置连接数据库的URL
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/test'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 查询时会显示原始SQL语句
app.config['SQLALCHEMY_ECHO'] = True
# 使用应用程序对象作为参数创建SQLAlchemy类的对象。该对象包含用于ORM操作的辅助函数。它还提供了一个父Model类,使用它来声明用户定义的模型。在下面的代码段中,创建了students模型。
db = SQLAlchemy(app)


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

    # repr()方法显示一个可读字符串
    def __repr__(self):
        return f'<Role: {self.name}-{self.id}>'


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

    def __repr__(self):
        return f'<User: {self.name}-{self.id}-{self.email}-{self.password}>'

# 数据的增删改查
@app.route('/a')
def index():
    db.create_all()
    return "创建表成功"

@app.route('/upd')
def index1():
    # 修改数据
    role = Role()
    role.name = 'lisi'
    db.session.commit()
    return "更新成功"

@app.route('/ins')
def index2():
    # role = Role(name='admin')
    # db.session.add(role)
    # db.session.commit()
    # 添加一条User数据, 数据有误可以使用回滚, 将add的对象从session移除
    # user = User(name='zhangsan')
    # db.session.add(user)
    # db.session.rollback()
    # user.role_id = 1
    # db.session.add(user)
    # db.session.commit()
    # 插入一条角色数据
    role = Role(name='dmin')
    db.session.add(role)
    db.session.commit()
    # 一次插入多条数据
    user1 = User(name='zs', role_id=role.id)
    user2 = User(name='ls', role_id=role.id)
    db.session.add_all([user1, user2])
    db.session.commit()
    # 通过角色直接查询到用户信息
    # 通过用户直接查询到角色信息
    print(role.user)
    print(user1.role)
    print(user2.role)
    return "添加成功"

@app.route('/del')
def index3():
    # 删除数据
    # db.session.delete(user)
    # db.session.commit()
    db.drop_all()
    return "删除表成功"

@app.route('/query')
def index4():
    # all()返回查询到的所有对象
    res=User.query.all()
    print(User.query.all())
    for i in res:
        print(i.name)
    # 查询有多少个用户User.query.count()
    # 查询第1个用户User.query.first()
    # 查询id为4的用户[3种方式]
    # filter_by直接用属性名,比较用=, filter用类名.属性名,比较用==
    # filter_by用于查询简单的列名,不支持比较运算符
    # filter比filter_by的功能更强大,支持比较运算符,支持or_、in_等语法。
    #User.query.get(3)
    #User.query.filter_by(id=3).first()
    #User.query.filter(User.id == 3).first()
    return "查询成功"

if __name__ == '__main__':
    app.run(debug=True)

3.表单验证1

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

app = Flask(__name__)
manager = Manager(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:123456@127.0.0.1:3306/test'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SECRET_KEY']='s'
db = SQLAlchemy(app)
# 第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app, db)
# manager是Flask-Script的实例,这条语句在flask-Script中添加一个db命令
manager.add_command('db', MigrateCommand)

#定义模型类-作者
class Author(db.Model):
    __tablename__ = 'author'
    id = db.Column(db.Integer,primary_key=True)
    name = db.Column(db.String(32),unique=True)
    email = db.Column(db.String(64))
    au_book = db.relationship('Book',backref='author')
    def __str__(self):
        return f'Author:{self.name}'
#定义模型类-书名
class Book(db.Model):
    __tablename__ = 'books'
    id = db.Column(db.Integer,primary_key=True)
    info = db.Column(db.String(32),unique=True)
    leader = db.Column(db.String(32))
    au_book = db.Column(db.Integer,db.ForeignKey('author.id'))
    def __str__(self):
        return f'Book:{self.info},{self.leader}'
#创建表单类,用来添加信息
class Append(FlaskForm):
    au_info = StringField(validators=[DataRequired()])
    au_email = EmailField(validators=[DataRequired()])
    bk_info = StringField(validators=[DataRequired()])
    bk_leader = StringField(validators=[DataRequired()])
    # au_bk_id = IntegerField(validators=[DataRequired()])
    submit = SubmitField(u'添加')
@app.route('/',methods=['GET','POST'])
def index():
    # db.create_all()
    #查询所有作者和书名信息
    author = Author.query.all()
    book = Book.query.all()
    #创建表单对象
    form = Append()
    if form.validate_on_submit():
        #获取表单输入数据
        wtf_au_info= form.au_info.data
        wtf_au_email = form.au_email.data
        wtf_bk_info = form.bk_info.data
        wtf_bk_leader = form.bk_leader.data
        # wtf_au_bk_id= form.au_bk_id.data

        #把表单数据存入模型类
        auid = Author.query.filter_by(name=wtf_au_info).first()
        if auid:
            # db_au = Author(name=wtf_au_info,email=wtf_au_email)
            print(auid.id)
            db_bk = Book(info=wtf_bk_info,leader=wtf_bk_leader,au_book=auid.id)
            #提交会话
            # db.session.add_all([db_au,db_bk])
            db.session.add(db_bk)
            db.session.commit()
        else:

            db_au = Author(name=wtf_au_info,email=wtf_au_email)
            db_bk = Book(info=wtf_bk_info, leader=wtf_bk_leader)
            # 提交会话
            db.session.add(db_au)
            db.session.commit()
            db.session.add(db_bk)
            db.session.commit()
            #添加数据后,再次查询所有作者和书名信息
        author = Author.query.all()
        book = Book.query.all()
        return render_template('index.html',author=author,book=book,form=form)
    else:
        if request.method=='GET':
            render_template('index.html', author=author, book=book,form=form)
    return render_template('index.html',author=author,book=book,form=form)
#删除作者
@app.route('/delete_author<id>')
def delete_author(id):
    #精确查询需要删除的作者id
    au = Author.query.filter_by(id=id).first()
    db.session.delete(au)
    #直接重定向到index视图函数
    return redirect(url_for('index'))
#删除书名
@app.route('/delete_book<id>')
def delete_book(id):
    #精确查询需要删除的书名id
    bk = Book.query.filter_by(id=id).first()
    db.session.delete(bk)
    #直接重定向到index视图函数
    return redirect(url_for('index'))
if __name__ == '__main__':
    app.run(debug=True)


<form method="post">
    {{ form.csrf_token }}
    <p>作者:{{ form.au_info }}</p>
    <p>电子邮件:{{ form.au_email }}</p>
    <hr>
    <p>书名:{{ form.bk_info }}</p>
    <p>主角:{{ form.bk_leader }}</p>
{#        <p>关联id:{{ form.au_bk_id }}</p>#}
    <p>{{ form.submit }}</p>
</form>
    <ul>
        {% for x in author %}
        <li>作者:{{ x.name }} 电子邮件:{{ x.email }} <a href='/delete_author{{ x.id }}'> 删除</a></li>
        {% endfor %}
    </ul>
    <hr>
    <ul>
        {% for x in book %}
        <li>书名:{{ x.info }} 主角:{{ x.leader }} 关联id:{{ x.au_book }} <a href='/delete_book{{ x.id }}'>删除</a></li>
        {% endfor %}
    </ul>

4.表单验证2

#  1. 调用wtf的函数实现验证
    if book_form.validate_on_submit():
        # 2. 验证通过获取数据
        author_name = book_form.author.data
        book_name = book_form.book.data
        # 3. 判断作者是否存在
        author = Author.query.filter_by(name=author_name).first()
        if author:
            # 4. 判断书籍是否存在, 没有重复书籍就添加数据. 如果重复就提示错误
            book = Book.query.filter_by(name=book_name).first()
            if book:
                # 如果重复就提示错误
                flash('已存在同名书籍')
            else:
                # 没有重复书籍就添加数据
                try:
                    new_book = Book(name=book_name, author_id=author.id)
                    db.session.add(new_book)
                    db.session.commit()
                except Exception as e:
                    print e
                    flash('书籍添加失败')
                    db.session.rollback()
        else:
            # 5. 如果作者不存在, 添加作者和书籍
            try:
                new_author = Author(name=author_name)
                db.session.add(new_author)
                db.session.commit()
                new_book = Book(name=book_name, author_id=new_author.id)
                db.session.add(new_book)
                db.session.commit()
            except Exception as e:
                print e
                flash('添加失败')
                db.session.rollback()
    else:
        # 6. 验证不通过提示错误
        if request.method == 'POST':
            flash('参数不完整')

总结

有关于model层的操作,很是重要。

posted @ 2022-01-15 09:57  cactus9  阅读(15)  评论(0编辑  收藏  举报