the flask mega tutorial自学记录 之 第四章 数据库迁移

在实际网站开发中,开发者在本地进行开发,测试成功后再把新版本的功能发布的正式应用环境。不断更新升级的过程,怎么能快速的在本地及正式环境中同步数据呢?那就需要下边的知识——数据库迁移技术。


1. Flask架构中的数据库
其实在FLASK中,是没有自带数据库功能,但是,它可以与各种流行的数据库软件轻易结合,它把这种选择留给开发人员,这正是Flask的灵活支持。
虽然可供选择的数据库很多,比如关系型数据:mysql、PostgreSQL、mssql、oracle等,还有非关系型数据库。但是,我们示例还是选用的是轻巧的关系型数据库sqlite。

为了更好的管理数据库操作,需要一个第三方的库flask-sqlalchemy。

介绍下ORM
ORM 全称 Object Relational Mapping, 翻译过来叫对象关系映射。简单的说,ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系。这样,我们要操作数据库,数据库中的表或者表中的一条记录就可以直接通过操作类或者类实例来完成。
SQLAlchemy 是Python 社区最知名的 ORM 工具之一,为高效和高性能的数据库访问设计,实现了完整的企业级持久模型。

安装flask-sqlalchemy。 注意:先要激活虚拟环境再安装。

pip install flask-sqlalchemy

2、DATABASE Migrate数据库迁移(个人理解:这里的迁移不是仅指数据库的整体迁移,也包含数据的微小数据结构改变操作,实际应该就是所有数据库操作动作对应的语言脚本,通过这些脚本可以重复同样的操作)
很多的数据教程会提到怎么新建、使用数据库,但是当APP需要更改或扩展的时候,教程很好会提及。
关系型数据库的关系结构复杂,一旦结构改变,相应的数据就需要对应改变。
安装flask-migrate

pip install flask-migrate    注意:先要激活虚拟环境再安装。

3、Flask-SQLAlchemy设置
如前边的章节介绍一样,把此处的设置内容,增添到config.py文件中。

import os
basedir = os.path.abspath(os.path.dirname(__file__))  #__file__用来获得模块所在的路径

class Config(object):
    # ...
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \    #获取虚拟变量为DATABASE_URL值
        'sqlite:///' + os.path.join(basedir, 'app.db')   #拼接一个sqlite接口引擎
    SQLALCHEMY_TRACK_MODIFICATIONS = False    #设置为False后,不会记录数据的变更记录。

4、初始化DB对象及迁移对象(app/init.py)

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)                  #创建DB
migrate = Migrate(app, db)            #创建Migrate

from app import routes, models    #models是下边将建立的模块

5、创建数据库结构
这里写图片描述
新建一个模块:app/models.py,增加两个类(表结构):
这里请注意,如果完全按照教程中的代码,会遇到一个坑:在提交的时候,后台语句会报错,提示sqlite数据库中没有user表。所以,先用db.create_all()启动。

from datetime import datetime
from app import db              #db是上边创建

class User(db.Model):
    db.create_all()     #这个语句是教程中没有,必须添加
    id = db.Column(db.Integer, primary_key=True)    #db.Column定义列字段:第一个参数字段类型,第二个是主键
    username = db.Column(db.String(64), index=True, unique=True)  #unique代表唯一性
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    #relationship生成一个关系,posts不是一个数据库的字段,仅是一个结构关系
    #在一对多的模式下,第一个参数是代表多方的类(表)名称
    #第二个参数backref,将向post类中添加一个author属性,从而定义反向关系。这一属性可替代user_id 访问user模型,此时获取的是模型对象,而不是外键的值(类似表的一行或一个实例)。
    #u = User.query.get(1)
    #author使用的例子:p = Post(body='my first post!', author=u)
    posts = db.relationship('Post', backref='author', lazy='dynamic')  

    #__repr__主要是自定义一种输出格式,重组print的打印效果。另外一种__str__是更上一层的输出格式。
    #终端用户显示使用__str__,而程序员在开发期间则使用底层的__repr__来显示
    def __repr__(self):
        return '<User {}>'.format(self.username)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String(140))
    timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return '<Post {}>'.format(self.body)

6、创建一个迁移库
Flask-Migrate可以记录数据库操作的脚本,这些脚本按照操作顺序依次记录下来。
6.1第一次使用,创建迁移库(类似一个文件夹)
像第一章运行flask run那样,首先得设置一个环境变量:set FLASK_APP=microblog.py
然后就是初始化建立迁移库

flask db init

6.2 建立一个数据库迁移
原理:flask migrate中的 Alembic 会自动比较我们在models.py中定义的数据库模式与实际数据的模式(sqlite中的schema),针对有差异的地方,会自动生成一下脚本。

flask db migrate -m "users and posts table"     # -m 参数制定一个迁移描述性名称
结果如下:
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'user'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_email' on '['email']'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_user_username' on '['username']'
  Generating /home/miguel/microblog/migrations/versions/e517276bb1c2_users_table.py ... done

versions/e517276bb1c2_users_table.py 中的e517276bb1c2就是一个唯一性的版本号了**
**注意:**flask db migrate操作仅仅是生成了一些数据库操作脚本,根本没有改变数据库内容。

6.3 数据库升级

flask db upgrade

6.4 数据库回滚

flask db downgrade e517276bb1c2   #回滚到版本号

7、基础的数据操作

>>> from app import db
>>> from app.models import User, Post
>>> u = User(username='john', email='[email protected]')
>>> db.session.add(u)    #新增实例
>>> db.session.commit()    #执行操作
>>> u = User(username='susan', email='[email protected]')
>>> db.session.add(u)
>>> db.session.commit()
>>> users = User.query.all()    #查询user所有记录
>>> users
[<User john>, <User susan>]
>>> for u in users:
...     print(u.id, u.username)
...
1 john
2 susan

>>> u = User.query.get(1)      #查询主键是1的记录
>>> u
<User john>
>>> u = User.query.get(1)
>>> p = Post(body='my first post!', author=u)   #按照author赋值POST
>>> db.session.add(p)
>>> db.session.commit()
>>> # get all posts written by a user
>>> u = User.query.get(1)
>>> u
<User john>
>>> posts = u.posts.all()
>>> posts
[<Post my first post!>]

>>> # same, but with a user that has no posts
>>> u = User.query.get(2)
>>> u
<User susan>
>>> u.posts.all()
[]

>>> # print post author and body for all posts 
>>> posts = Post.query.all()
>>> for p in posts:
...     print(p.id, p.author.username, p.body)
...
1 john my first post!

# get all users in reverse alphabetical order
>>> User.query.order_by(User.username.desc()).all()
[<User susan>, <User john>]
>>> users = User.query.all()
>>> for u in users:
...     db.session.delete(u)
...
>>> posts = Post.query.all()

>>> for p in posts:
...     db.session.delete(p)
...
>>> db.session.commit()

猜你喜欢

转载自blog.csdn.net/hawk_2016/article/details/81221327