flasksqlalchemy 多数据库 flask与数据库交互

admin2024-05-30  13


文章目录

  • 一、Python数据库连接方式
  • 什么是Flask_SQLAlchemy?
  • 如何配置数据库?
  • 安装第三方模块
  • 数据库配置
  • 定义模型
  • 模型列类型
  • 模型列属性
  • 数据查询
  • 分页实现
  • 数据库关系
  • 一对一关系
  • 模型定义与基本操作
  • 一对多关系
  • 模型定义与基本操作
  • 多对多关系
  • 模型定义与基本操作
  • 二、Flask-Script
  • 为什么使用Falsk-Script?
  • 安装Flask-Script
  • 如何配置Flask-Script?
  • 添加自定义命令的三种方式:
  • 命令行拓展开发
  • 三、Flask-Migrate数据库迁移
  • 为什么使用Flask-Migrate?
  • 实现步骤
  • 数据库迁移命令行操作
  • 创建迁移仓库
  • 自动创建迁移脚本
  • 更新数据库


一、Python数据库连接方式

python中的数据库连接方式有两种:

  • Python标准数据库接口,使用SQL域居正常操作数据库,eg:MySQL关系型数据库的pymysql模块
  • 用ORM来进行数据库连接,python中使用sqlalchemy,flask中典型的flask_sqlalchemy,已面向对象的方式进行数据库的连接和操作
  • ORM是什么?有什么优势?
    ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复杂的SQL语句打交道,只需要简单的操作对象的属性和方法

什么是Flask_SQLAlchemy?

  Flask-SQLAlchemy 是一个 Flask 扩展,简化了在 Flask 程序中使用SQLAlchemy 的操作。SQLAlchemy 是一个很强大的关系型数据库框架,支持多种数据库后台。 SQLAlchemy 提供了高层 ORM ,也提供了使用数据库原生 SQL 的低层功能。

如何配置数据库?

安装第三方模块

pip install flask-sqlalchemy

数据库配置

官方配置信息网址

# app.py文件
from flask_sqlalchemy import SQLAlchemy
# 获取当前绝对路径
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
# SQLALCHEMY_DATABASE_URI: 用于连接数据的数据库。
app.config['SQLALCHEMY_DATABASE_URI'] =
           'sqlite:///' + os.path.join(basedir, 'data.sqlite')   
# sqlchemy将会追踪对象的修改并且发送信号
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# 每次请求结束之后都会提交数据库的变动
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
# 数据库与app项目关联, 返回SQLAlchemy实例对象,后面可以使用对象调用数据
db = SQLAlchemy(app)
  • 流行的数据库引擎采用的数据库URL格式
  • 连接mysql数据库的报错解决
import pymysql
pymysql.install_as_MySQLdb()

定义模型

  • 模型(Model)这个术语表是程序使用的持久化实体
  • 模型列类型
  • 模型列属性
class Role(db.Model):
   # __tablename__类变量定义数据库中表的名称。不指定默认为模型名称。
   # Flask-SQLAlchemy需要给所有的模型定义主键列,通常命名为id。
   id = db.Column(db.Integer, primary_key=True)
   name = db.Column(db.String(64), unique=True)
   # repr()显示一个可读字符串,用于调试和测试
   def __repr__(self):
       return '<Role %r>' % 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 %r>' % self.username

模型列类型

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_sqlalchemy,第1张

模型列属性

选项名

说明

primary_key

如果设为True,这列就是表的主键

unique

如果设为True,这列不允许出现重复的值

index

如果设为True,为这列创建索引,提升查询效率

nullable

如果设为True,这列允许使用空值;如果设为False,这列不允许使用空值

default

为这列设置默认值

数据查询

  • 查询过滤器

过滤器

说明

filter( )

把过滤器添加到原查询,返回新查询

filter_by( )

把等值过滤器添加到原查询,返回新查询

limit( )

使用指定值限制原查询返回的结果数量,返回新查询

offset( )

偏移原查询返回的结果,返回新查询

order_by( )

排序返回结果,返回新查询

groupby()

原查询分组,返回新查询

  • 查询执行函数

方法

说明

all( )

以列表形式返回结果

first()

返回第一个结果,如果没有返回None

first_or_404( )

返回第一个结果,如果没有抛出404异常

get( )

返回主键对应记录,没有则返回None

get_or_404( )

返回主键对应记录,如果没有抛出404异常

count( )

返回查询结果数量

paginate( )

返回paginate对象,此对象用于分页

分页实现

  • 分页对象拥有的属性

属性

说明

items

当前页面中的记录

query

分页的源查询

page

当前页数

prev_num

上一页的页数

next_num

下一页的页数

has_next

如果存在下一页,返回True

has_prev

如果有上一页,返回True

pages

查询得到的总页数

per_page

每页显示的记录总数

total

查询返回的记录总数

  • 分页对象拥有的方法

方法

说明

iter_pages(left_edge=2,left_current=2,right_current=5,right_edge=2)

一个迭行代器,返回一个在分页导航栏中显示的页数列表。这个列表的最左边显示left_edge页,当前页的左边显示left_current页,当前页的右边显示right_current页,最右边显示right_edge页。例如:在一个100页的列表中,当前页为第50页,使用默认配置,这个方法会返回以下页数:1、2、None、48、49、50、51、52、53、54、55、None、99、100。None表示页数之间的间隔

prev( )

上一页的分页对象

next( )

下一页的分页对象

数据库关系

数据库实体间有三种对应关系:一对一,一对多,多对多。

一对一关系

一个学生对应一个学生档案材料,或者每个人都有唯一的身份证编号。

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_分页_02,第2张

模型定义与基本操作
# one_to_one.py文件

from app import db

class People(db.Model):
    __tablename__ = 'peoples'
    id = db.Column(db.Integer,autoincrement=True,primary_key=True)
    name = db.Column(db.String(15),unique=True)
    age = db.Column(db.Integer,default=18)
    # gender = db.Column(db.String(10))
    """
    关系使用:relationship()函数表示;
            'Card':People类对应的表与Card类对应的表关联;
            backref='people':反向引用,在Card类中添加新属性people;
            userlist=False:默认为True,如果为False代表一对一关系;
            
    """
    # 公民表和身份证表关联起来了,uselist=False代表一个对象不对应一个列表,所以是一对一关系
    # backref = 'people'给Card类里面添加属性people,可以根据身份证获取公民信息
    card = db.relationship('Card', backref='people', uselist=False)
    def __init__(self,name):
        self.name = name

    def __repr__(self):
        return '<People:%s>' %(self.name)

class Card(db.Model):
    __tablename__ = 'cards'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    # nullable=False是这个字段在保存时不能为空,必须要有值
    cardID = db.Column(db.String(30), nullable=False)
    # 限制身份证对应的公民必须是存在的公民
    is_vaild = db.Column(db.Boolean, default=True)

    # 外键必须用类sqlalchemy.schema.ForeignKey来单独声明
    # peoples指的是表名
    people_id = db.Column(db.Integer,db.ForeignKey('peoples.id'))
    def __repr__(self):
        return '<Card:%s>' %(self.cardID)

if __name__ == '__main__':
    db.create_all()
    #
    # # 增加元素
    # p1 = People(name='Tom')
    # p2 = People(name='Jack')
    #
    # c1 = Card(cardID='001')
    # c2 = Card(cardID='002')
    #
    # c1.people = p1
    # c2.people = p2
    #
    # # p1.card = c1
    # # p2.card = c2
    #
    # db.session.add_all([p1,p2,c1,c2])
    # db.session.commit()

    # 删除
    # print("删除前:",Card.query.all(),People.query.all())
    # card = Card.query.filter_by(cardID='002').first()
    # db.session.delete(card)
    # db.session.commit()
    # print("删除后: ",Card.query.all(),People.query.all())
    # c1 = Card.query.get(1)
    # db.session.delete(c1)
    # db.session.commit()

    # 查找
    # card_people2 = Card.query.filter_by(cardID='002').first().people
    # print(card_people2)
    #
    # people_card2 = People.query.filter_by(id=2).first().card
    # print(people_card2)

一对多关系

一个学生只属于一个班,但是一个班级有多名学生。

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_分页_03,第3张

  • 设计数据库表:只需在学生表中多添加一个班级号的ID;
  • 通过添加主外键约束,避免删除数据时造成数据混乱!
模型定义与基本操作
#one_to_many.py
# 数据库模型

from app import db

# 一对多:外键写在多的一端
# Grade:Student == 1:n

class Student(db.Model):
    __tablename__ = 'students'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(20), unique=True,nullable=False)
    # 写入外键
    grade_id = db.Column(db.Integer,db.ForeignKey('grade.id'))

    def __repr__(self):
        return "<student:%s %s %s>" %(self.name,self.id,self.grade_id)


class Grade(db.Model):
    __tablename__ = 'grade'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    name = db.Column(db.String(20),unique=True,nullable=False)
    # 反向引用,让Student类中添加属性grade
    students = db.relationship('Student',backref='grade',uselist=True)
    def __repr__(self):
        return "<grade %s %s %s>"  %(self.name,self.id,self.students)

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

    # 增元素
    s1 = Student(name='Tom')
    s2 = Student(name='Jack')
    s3 = Student(name='Alice')

    grade1 = Grade(name='Python开发')
    grade2 = Grade(name='Java开发')

    # s1.grade = grade1
    # s2.grade = grade1

    grade1.students = [s1]
    grade2.students = [s2,s3]

    # db.session.add_all([s1,s2,s3,grade1,grade2])
    # db.session.commit()
    # print("添加成功: ", Student.query.all(),Grade.query.all())

    # 查找
    student_grade = Student.query.filter_by().first().grade
    print(student_grade)
    grade_student = Grade.query.filter_by(name='Java开发').first().students
    print(grade_student)

    # 删除
    print("删除前: ", Student.query.all(),Grade.query.all())
    student1 = Student.query.filter_by(name = 'Jack').first()
    db.session.delete(student1)
    db.session.commit()
    print("删除后: ",Student.query.all(),Grade.query.all())

多对多关系

一个学生可以选择多门课,一门课也有多名学生。

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_分页_04,第4张

  • 对于多对多表,通过关系表就建立起了两张表的联系!多对多表时建立主外键后,要先删除约束表内容再删除主表内容。
模型定义与基本操作
# many_to_many.py
# 数据库模型
# 数据库连接,db.Model
from app import db

# 第三方表

tags = db.Table('tags',
                db.Column('student_id',db.Integer,db.ForeignKey('math_students.id')),
                db.Column('course_id',db.Integer,db.ForeignKey('courses.id')))

class MathStudent(db.Model):
    __tablename__ = 'math_students'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    name = db.Column(db.String(20),unique=True,nullable=False)
    # secondary指定关系表对象
    courses = db.relationship('Course',secondary=tags)

    def __repr__(self):
        return '<MathStudent: %s>' %(self.name)

class Course(db.Model):
    __tablename__ = 'courses'
    id = db.Column(db.Integer,primary_key=True,autoincrement=True)
    name = db.Column(db.String(20),nullable=False)
    # secondary指定关系表对象
    students = db.relationship('MathStudent',secondary=tags)

    def __repr__(self):
        return '<Course: %s>' %(self.name)

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

    # 创建
    # s1 = MathStudent(name='拉格朗日')
    # s2 = MathStudent(name='牛顿')
    # s3 = MathStudent(name='罗尔')
    #
    # course1 = Course(name='高等数学')
    # course2 = Course(name='线形代数')

    # 高等数学: 拉格朗日,牛顿,罗尔
    # 线形代数: 拉格朗日,牛顿
    # s1.courses.append(course1)
    # s2.courses.append(course1)
    # s3.courses.append(course1)
    # s1.courses.append(course2)
    # s2.courses.append(course2)
    #
    # db.session.add_all([s1,s2,s3,course1,course2])
    # db.session.commit()

    # 删除
    courseObj = Course.query.filter_by(name='高等数学').first() # <Course: 高等数学>
    # courseObj = Course.query.filter_by(name='线形代数').first().students # [<MathStudent: 牛顿>, <MathStudent: 拉格朗日>,<MathStudent: 罗尔>]
    # print(courseObj)
    studentObj = MathStudent.query.filter_by(name='罗尔').first() # <MathStudent: 罗尔>
    # studentObj = MathStudent.query.filter_by(name='罗尔').first().courses # [<Course: 高等数学>]
    # print(studentObj)
    print("删除前选择高等数学的学生: ",courseObj.students)
    courseObj.students.remove(studentObj)
    print("删除后选择线形代数的学生 ",courseObj.students)
    db.session.commit()

整合代码:

#app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_pyfile('config.py')
# 将app与AQLAlchemy绑定在一起
db = SQLAlchemy(app)

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


if __name__ == '__main__':
    app.run(host='0.0.0.0',port=5000,debug=True)



##################
# config.py
import os


# 获取当前绝对路径
basedir = os.path.abspath(os.path.dirname(__file__))

# SQLALCHEMY_DATABASE_URI:用于连接数据的数据库
# sqlite:home/kiosk/Desktop/PycharmProjects1/Flask与数据库操作的整合/data.sqlite
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir,'data.sqlite')
# print(SQLALCHEMY_DATABASE_URI)

# sqlchemy 将会追踪对象的修改并且发送信号
SQLALCHEMY_TRACK_MODIFICATIONS = False

# 每次请求结束之后都会提交数据库的变动
SQLALCHEMY_COMMIT_ON_TEARDOWN = True

二、Flask-Script

Flask-Script用来生成shell命令;为在Flask里编写额外的脚本提供了支持。

  • 这包括运行一个开发服务器,一个定制的Python命令行,用于执行初始化数据库、定时任务和其他属于web应用之外的命令行任务的脚本。
  • Flask-Script和Flask本身的工作方式类似。只需要定义和添加能从命令行中被Manager实例调用的命令即可。

为什么使用Falsk-Script?

Flask的开发Web服务器支持很多启动设置选项,但只能在脚本中作为参数传给app.run()函数。这种方式很不方便,传递设置选项的理想方式是使用命令行参数。

  • Flask-Scrip就是这么一个Flask扩展,为Flask程序添加一个命令行解析器
  • Flask-Script自带了一组常用选项,而且还支持自定义命令

安装Flask-Script

pip install flask_script

如何配置Flask-Script?

创建文件manage.py作为项目的入口文件。

  • 无需把所有的命令都放在同一个文件中,例如,在一个大型项目中,可以把相关联的命令放在不同的文件里。
# **** manage.py文件
from flask_script import Manager
app = Flask(__name__)
# Manager类将追踪所有的在命令行中调用的命令和处理过程的调用运行情况;
# configure your app
manager = Manager(app)
if __name__ == "__main__":
   # 将启动Manger实例接收命令行中的命令。
   manager.run()
  • 实现功能

python manage.py
python manage.py runserver
python manage.py runserver -h
python manage.py runserver -h ‘0.0.0.0’ -p 8089

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_分页_05,第5张

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_sqlalchemy_06,第6张

添加自定义命令的三种方式:

  • 参考网站:https://flask-script.readthedocs.io/en/latest/   1、 使用command装饰器
      2、定义Command的子类
      3、使用option装饰器
    通常使用第一和第三种方法。
# **** manage.py文件
# 方法一:
@manager.command
def hello():
   return "hello"
 
# 方法二:
from flask_script import Command
class Hello(Command):
   "prints hello world"
   def run(self):
       return "hello world"    
manager.add_command('hello', Hello())    
   
# 方法三:
@manager.option('-n', '--name', help='Your name')
def hello(name):
   return  "hello", name

命令行拓展开发

# 添加其他的命令到manager里面来
from app.managerUtil.database import  database_manager
# 自定义的数据库操作
manager.add_command('database', database_manager)
# flask-script集成的数据库操作
manager.add_command('db', MigrateCommand)

整合代码:

from flask_script import Manager
from app import app,db

# Manager类将追踪所有的在命令行中调用的命令和处理过程的调用运行情况
# configure your app
from models import User

manager = Manager(app)

@manager.command
def create_superuser():
    """创建超级用户"""
    try:
        name = input('请输入超级用户的名称: ')
        user = User(name=name)
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        return "超级用户创建失败: ",e
    else:
        return '超级用户创建成功'

@manager.option('-n','--name',help='Your name')
def del_admin(name):
    """删除超级用户"""
    user = User.query.filter_by(name=name).first()
    if not user:
        return "删除的用户不存在"
    else:
        try:
            db.session.delete(user)
            db.session.commit()
        except Exception as e:
            return "超级用户删除失败: ",e
        else:
            return "超级用户删除成功"


if __name__ == '__main__':

    # 将启动Manager实例接收命令行中的命令
    manager.run()

三、Flask-Migrate数据库迁移

为什么使用Flask-Migrate?

在我们用 sqlchemy 模块创建完几个表时,如果在实际生产环境中,需要对表结构进行更改,应该怎么办呢?总不能把表删除了吧,这样数据就会丢失了。

更好的解决方法是使用数据库迁移框架,它可以追踪数据库模式的变化,然后把变动应用到数据库中。在Flask中可以使用Flask-Migrate扩展,来实现数据库的迁移。并且集成到Flask-Script中,所有操作通过命令就能完成。

实现步骤

  • 安装flask-migrate和flask-script模块。

pip3 install flask-migrate
pip3 install flask-script

  • 配置代码
manager = Manager(app)
db = SQLAlchemy(app)
#第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app,db)
#manager是Flask-Script的实例,添加一个db命令
manager.add_command('db',MigrateCommand)

数据库迁移命令行操作

创建迁移仓库

这个命令会创建 migirations 文件夹,所有迁移文件都放在里面

python3 xxx.py db init

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_sqlalchemy_07,第7张

自动创建迁移脚本

数据库迁移工作由迁移脚本完成。这个脚本有两个函数,分别叫做upgrade()和downgrade()。upgrade()函数实施数据库更改,是迁移的一部分,downgrade()函数则删除它们。通过添加和删除数据库变化的能力,Alembic可以重新配置数据库从历史记录中的任何时间点。

python3 xxx.py db migrate -m “版本名(注释)”

flasksqlalchemy 多数据库 flask与数据库交互,flasksqlalchemy 多数据库 flask与数据库交互_sqlalchemy_08,第8张

更新数据库

一旦迁移脚本被审查且接受,就可以使用db upgrade命令更新到数据库中:

python3 xxx.py db upgrade

当我们需要修改表结构时,如何实现数据库迁移呢?

  • 直接在 xxx.py 里直接增删相应的代码,
  • 修改完成后,继续创建新的迁移脚本

python 文件 db migrate -m"新版本名(注释)"

  • 更新数据库

python3 xxx.py db upgrade

更新完之后,其实就是提交操作,类似于 git 添加一个新的版本。但是,如果我们想返回历史的版本,应该怎么操作呢?

  • 先查版本号

python3 xxx.py db history

  • 想要返回的版本号。返回指定的版本

python3 xxx.py db downgrade(upgrade) 版本号

然后打开你的代码,可以发现他自动复原了!

代码整合:

# 入口脚本
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager

from models import User
from app import app, db

# Manager类将追踪所有的在命令行中调用的命令和处理过程中调用运行情况
manager = Manager(app)
# 第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
migrate = Migrate(app, db)

# manager是Flask-Script的实例,添加一个db命令
manager.add_command('db', MigrateCommand)


@manager.command
def create_superuser():
    """创建超级用户"""
    try:
        name = input('请输入超级用户的名称: ')
        user = User(name=name)
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        return "创建超级用户失败: ",e
    else:
        return "创建超级用户成功"

@manager.option('-n','--name',help='Your name')
def del_admin(name):
    """删除超级用户"""
    try:
        user = User.query.filter_by(name=name).first()
        db.session.delete(user)
        db.session.commit()
    except Exception as e:
        return '超级用户删除失败: ',e
    else:
        return '超级用户删除成功'


if __name__ == '__main__':
    manager.run()


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!