• Flask:数据库管理


    为什么不使用SQL语句,而使用ORM框架管理数据库?首先,在python程序中嵌入原生SQL语句,不方便维护,ORM框架使用面向对象思想,使用较方便;第二,如果更换底层数据库引擎,ORM框架不需要修改代码。ORM框架也有其弊端,如有一定的性能损耗,且高级查询编写复杂,有一定的学习成本。


    1、配置数据库链接

    Flask-SQLAlchemy 中,数据库使用 URL 指定。一般开发环境使用SQLite,生产环境使用MySQL,使用MySQL时,还需要再安装相应的依赖包。

    几种最流行的数据库引擎使用的 URL 格式:

    数据库引擎 URL
    MySQL mysql://username:password@hostname/database
    SQLite(Linux,macOS) sqlite:////absolute/path/to/database
    SQLite(Windows) sqlite:///c:/absolute/path/to/database

    配置数据库:
    app.config['SQLALCHEMY_DATABASE_URI'] ='sqlite:///' + os.path.join(basedir, 'data.sqlite')
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    db = SQLAlchemy(app)
    
    # SQLALCHEMY_TRACK_MODIFICATIONS 参数决定是否追踪对象的修改,SQLAlchemy建议配置此变量,不然会有警告。
    

    2、定义模型


    模型这个术语表示应用使用的持久化实体。在ORM中,模型一般是一个表示数据表的类,类的实例对应一条记录,类中的属性对应于数据库表中的列。

    所以模型定义,类似原生SQL的CREATE TABLE语句:

    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 %r>' % self.name
    

    2.1 常用列类型:

    类型名 Python类型 说明
    Integer int 普通整数,通常是 32 位
    SmallInteger int 取值范围小的整数,通常是 16 位
    BigInteger intlong 不限制精度的整数
    Float float 浮点数
    Numeric decimal.Decimal 定点数
    String str 变长字符串
    Text str 变长字符串,对较长或不限长度的字符串做了优化
    Unicode unicode 变长 Unicode 字符串
    UnicodeText unicode 变长 Unicode 字符串,对较长或不限长度的字符串做了优化
    Boolean bool 布尔值
    Date datetime.date 日期
    Time datetime.time 时间
    DateTime datetime.datetime 日期和时间
    Interval datetime.timedelta 时间间隔
    Enum str 一组字符串
    PickleType 任何 Python 对象 自动使用 Pickle 序列化
    LargeBinary str 二进制 blob

    2.2 常用列选项

    选项名 说明
    primary_key 如果设为 True,列为表的主键
    uniquey 如果设为 True,列不允许出现重复的值
    indexy 如果设为 True,为列创建索引,提升查询效率
    nullabley 如果设为 True,列允许使用空值;如果设为 False,列不允许使用空值
    defaulty 为列定义默认值

    3、关系

    关系型数据库的表和表之间需要建立“一对多”,“多对一”和“一对一”的关系,这样才能够按照应用程序的逻辑来组织和存储数据。在关系型数据库中,关系是通过主键和外键来维护,其中外键既可以通过数据库来约束,也可以不设置约束,仅依靠应用程序的逻辑来保证,这里讨论使用数据库来约束的情况。


    3.1 一对多

    例如角色和用户是一对多关系,一个用户只对用一个角色,而一个角色可以对应多个用户:

    class Role(db.Model):
        # ...
        users = db.relationship('User', backref='role', lazy='dynamic')
    
    class User(db.Model):
        # ...
        role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    
    • 添加到 User 模型中的 role_id 列被定义为外键,就是这个外键建立起了关系。传给 db.ForeignKey() 的参数 'roles.id' 表明,这列的值是 roles 表中相应行的 id值。

    • 添加到 Role 模型中的 users 属性代表这个关系的面向对象视角,对于一个 Role 类的实例,其 users 属性将返回与角色相关联的用户组成的列表。
      • db.relationship() 的第一个参数表明这个关系的另一端是哪个模型

      • backref 参数向 User 模型中添加一个 role 属性,从而定义反向关系。通过 User 实例的这个属性可以获取对应的 Role 模型对象,而不用再通过 role_id 外键获取。

      • lazy参数会告诉SQLAlchemy如何去加载我们指定的关联对象。如果设为子查询方式 (subquery),则会在加载完Post对象的时候,就立即加载与其关联的对象。这样会让总查询数量减少,但如果返回的条目数量很多,就会比较慢。另外,也可以设置为动态方式 (dynamic),这样关联对象会在被使用的时候再进行加载,并且在返回前进行过滤。如果返回的对象数很多,或者未来会变得很多,那最好采用这种方式


    3.2 一对一

    一对一关系可以用前面介绍的一对多关系表示,但调用 db.relationship() 时要把 uselist 属性设为 False,把“多”变成“一”。


    3.3 多对一

    多对一关系从“多”这一侧看,就是一对多关系,对调两个表即可。


    3.4 多对多

    上述关系至少都有一侧是单个实体,所以记录之间的联系通过外键实现,让外键指向那个实体。但是,可能两侧都是“多”的关系,如有学生表和课程表,学生可能选多个课程,课程也可能被多个学生选。这种时候一般使用第三张表(即关联表)来表示关系:

    class Student(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
        classes = db.relationship('Class',
                                  secondary=registrations,
                                  backref=db.backref('students', lazy='dynamic'),
                                  lazy='dynamic')
    
    class Class(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String)
    
    registrations = db.Table('registrations',
        db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
        db.Column('class_id', db.Integer, db.ForeignKey('classes.id'))
    )
    

    多对多关系仍使用定义一对多关系的 db.relationship()方法定义,但在多对多关系中,必须把 secondary 参数设为关联表。多对多关系可以在任何一个类中定义,backref参数会处理好关系的另一侧。关联表就是一个简单的表,不是模型,SQLAlchemy 会自动接管这个表。

    Studentclasses 关系使用列表语义,这样处理多对多关系特别简单:

    # 查看Student实例所选课程、Class实例被选情况
    >>> s.classes.all()
    >>> c.students.all()
    
    # 增加课程
    >>> s.classes.append(c)
    >>> db.session.add(s)
    >>> db.session.commit()
    
    # 取消选课
    >>> s.classes.remove(c)
    >>> db.session.add(c)
    >>> db.session.commit()
    

    3.5 自引用

    多对多有种特殊情况,即关系中的两侧都在同一个表中,这种关系称为自引用关系。如用户表之间的关注关系,


    4、数据库操作

    对数据库的改动通过数据库会话管理,在 Flask-SQLAlchemy 中,数据库会话由 db.session 表示。调用 db.session.commit()提交事务,db.session.rollback() 回滚事务。


    4.1 创建表、删除表

    db.create_all()
    db.drop_all()
    

    4.2 CREATE

    admin_role = Role(name='Admin')
    db.session.add(admin_role)
    db.session.add_all([admin_role, mod_role])
    db.session.commit()
    

    4.3 UPDATE

    # 方法一
    admin_role.name = 'Administrator'
    db.session.add(admin_role)
    
    # 方法二
    User.query.filter_by(username='Alice').update({'password': 'test123'})
    
    # 修改后commit
    db.session.commit()
    

    4.4 DELETE

    db.session.delete(mod_role)
    db.session.commit()
    

    4.5 READ

    <模型类>.query.<过滤方法>.<查询方法>

    基本查询:

    #------------------基础查询-----------------------------
    # 返回所有数据集合
    >>> User.query.all()
    
    # 返回前十条数据集合
    >>> users = User.query.limit(10).all()
    
    # 返回查到的第一条记录
    >>> users = User.query.first()
    
    # 返回主键为1的记录
    >>> user = User.query.get(1)
    
    # 对查询结果排序
    >>> users = User.query.order_by(User.username).all()
    
    # 逆序
    >>> users = User.query.order_by(User.username.desc()).all()
    
    # 链式调用
    >>> users = User.query.order_by(
    
            User.username.desc()
    
        ).limit(10).first()
    
    #------------------条件查询:filter_by(精确条件)-----------------------------
    # 单条件
    >>> User.query.filter_by(role=user_role).all()
    >>> User.query.filter_by(role=user_role).first()
    
    # 多条件
    >>> User.query.filter_by(password='123',username='test0').all()
    >>> User.query.filter_by(password='123').filter_by(username='test0').all()
    
    # 带条件的链式调用
    >>> users = User.query.order_by(User.username.desc())
    
            .filter_by(username='fake_name')
    
            .limit(2)
    
            .all()
    
    #------------------条件查询:filter(模糊条件>、<、==...)-----------------------------
    >>> user = User.query.filter(User.id > 1).all()
    
    #------------------分页查询:paginate-----------------------------
    # 获取前十个虚构的User对象
    >>> page = User.query.paginate(1, 10)
    
    # 返回这一页包含的数据对象
    >>> page.items
    
    # 返回这一页的页数
    >>> page.page
    
    # 返回总页数
    >>> pages
    
    # 上一页和下一页是否有对象可以显示
    >>> page.has_prev, page.has_next
    
    # 返回上一页和下一页的 pagination 对象如果不存在的话则返回当前页
    >>> page.prev(), page.next()
    
    

    一些复杂的SQL查询可以转为用SQLAlchemy的函数来表示。例如,可以像下面这样实现SQL中IN、OR和NOT的比较操作:

    >>> from sqlalchemy.sql.expression import not_, or_
    
    >>> user = User.query.filter(
    
               User.username.in_(['fake_name']),
    
               User.password == None
    
           ).first()
    
    # 找出拥有密码的用户
    
    >>> user = User.query.filter( 
    
               not_(User.password == None)
    
           ).first()
    
    # 这些方法都可以被组合起来
    
    >>> user = User.query.filter(
    
               or_(not_(User.password == None), User.id >= 1)
    
           ).first()
    

    5、数据库迁移

    配置flask-migrate:

    # ...
    from flask_migrate import Migrate, MigrateCommand
    
    migrate = Migrate(app, db)
    
    manager.add_command('db', MigrateCommand)
    # ...
    

    开始跟踪数据库变更,使用init命令:

    $ python manage.py db init
    

    这会在项目目录中创建一个叫作migrations的文件夹,所有的记录文件会被保存在里面。现在可以开始进行首次迁移:

    $ python manage.py  db migrate -m "initial migration"
    

    执行迁移脚本

    $ python manage.py db upgrade
    

    要返回以前的版本,则可以根据history命令找到版本号,然后传给downgrade命令:

    $ python manage.py db history
    
    <base> -> 7ded34bc4fb (head), initial migration
    
    $ python manage.py db downgrade 7ded34bc4fb
    

    6、集成到flask shell

    @app.shell_context_processor
    def make_shell_context():
        return dict(db=db, User=User, Role=Role)
    

    参考

    sqlalchemy中文文档:https://www.osgeo.cn/sqlalchemy/
    sqlalchemy英文文档:https://docs.sqlalchemy.org/
    flask-sqlalchemy文档:http://www.pythondoc.com/flask-sqlalchemy/index.html

  • 相关阅读:
    【转】C#常用的集合类型(ArrayList类、Stack类、Queue类、Hashtable类、SortedList类)
    【转】第三方控件安装方法
    【jQuery插件】chackTextarea类似于新浪腾讯微博文本域字符数判断
    【转】关于wBox iFrame回调关闭问题的解决方案
    【转】jquery插件
    db4o助手 do4oHelper
    【转】C# 中文URL编码
    【转】C#集合之Hashtable
    【转】Web页面导出标准的Excel/Word/txt的代码实现
    jQuery图片居中裁切效果
  • 原文地址:https://www.cnblogs.com/bqzzz/p/14407451.html
Copyright © 2020-2023  润新知