Flask入門到放棄(四)—— 數據庫

轉載請在文章開頭附上原文連接地址:http://www.javashuo.com/article/p-dabsgyih-hx.htmlhtml

數據庫操做

ORM

ORM 全拼Object-Relation Mapping,中文意爲 對象-關係映射。主要實現模型對象到關係數據庫數據的映射python

優勢 :mysql

  • 只須要面向對象編程, 不須要面向數據庫編寫代碼.
    • 對數據庫的操做都轉化成對類屬性和方法的操做.
    • 不用編寫各類數據庫的sql語句.
  • 實現了數據模型與數據庫的解耦, 屏蔽了不一樣數據庫操做上的差別.
    • 再也不須要關注當前項目使用的是哪一種數據庫。
    • 經過簡單的配置就能夠輕鬆更換數據庫, 而不須要修改代碼.

缺點 :git

  • 相比較直接使用SQL語句操做數據庫,有性能損失.
  • 根據對象的操做轉換成SQL語句,根據查詢的結果轉化成對象, 在映射過程當中有性能損失.

Flask-SQLAlchemy

flask默認提供模型操做,可是並無提供ORM,因此通常開發的時候咱們會採用flask-SQLAlchemy模塊來實現ORM操做。github

SQLAlchemy是一個關係型數據庫框架,它提供了高層的 ORM 和底層的原生數據庫的操做。flask-sqlalchemy 是一個簡化了 SQLAlchemy 操做的flask擴展。redis

SQLAlchemy: https://www.sqlalchemy.org/sql

安裝 flask-sqlalchemy數據庫

pip install flask-sqlalchemy

若是鏈接的是 mysql 數據庫,須要安裝 mysqldb 驅動django

pip install flask-mysqldb

安裝flask-mysqldb時,注意編程

安裝 flask-mysqldb的時候,python底層依賴於一個底層的模塊 mysql-client模塊
若是沒有這個模塊,則會報錯以下:

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-21hysnd4/mysqlclient/

解決方案:

apt-get install libmysqlclient-dev python3-dev

運行上面的安裝命令若是報錯:
   dpkg 被中斷,您必須手工運行 ‘sudo dpkg --configure -a’ 解決此問題。
則根據提示執行命令如下命令,再次安裝mysqlclient
    sudo dpkg --configure -a
    apt-get install libmysqlclient-dev python3-dev

解決了mysqlclient問題之後,從新安裝 flask-mysqldb便可。
pip install flask-mysqldb

數據庫鏈接設置

  • 在 Flask-SQLAlchemy 中,數據庫使用URL指定,並且程序使用的數據庫必須保存到Flask配置對象的 SQLALCHEMY_DATABASE_URI 鍵中

    config.py,配置文件代碼:

class Config(object):
    DEBUG = True
    SECRET_KEY = "*(%#4sxcz(^(#$#8423"
    # 數據庫連接配置:
    #數據類型://登陸帳號:登陸密碼@數據庫主機IP:數據庫訪問端口/數據庫名稱
    SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/flask_students"
  • 其餘設置:
# 動態追蹤修改設置,如未設置只會提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = True
#查詢時會顯示原始SQL語句
SQLALCHEMY_ECHO = True
  • 配置完成須要去 MySQL 中建立項目所使用的數據庫
$ mysql -uroot -p123
mysql > create database flask_students charset=utf8mb4;

經常使用的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 二進制文件

經常使用的SQLAlchemy列選項

選項名 說明
primary_key 若是爲True,表明表的主鍵
unique 若是爲True,表明這列不容許出現重複的值
index 若是爲True,爲這列建立索引,提升查詢效率
nullable 若是爲True,容許有空值,若是爲False,不容許有空值
default 爲這列定義默認值

經常使用的SQLAlchemy關係選項

選項名 說明
backref 在關係的另外一模型中添加反向引用,用於設置外鍵名稱,在1查多的
primary join 明確指定兩個模型之間使用的連表條件
uselist 若是爲False,不使用列表,而使用標量值
order_by 指定關係中記錄的排序方式
secondary 指定多對多關係中關係表的名字
secondary join 在SQLAlchemy中沒法自行決定時,指定多對多關係中的二級連表條件

數據庫基本操做

  • 在Flask-SQLAlchemy中,插入、修改、刪除操做,均由數據庫會話管理。
    • 會話用 db.session 表示。在準備把數據寫入數據庫前,要先將數據添加到會話中而後調用 commit() 方法提交會話。
  • 在 Flask-SQLAlchemy 中,查詢操做是經過 query 對象操做數據。
    • 最基本的查詢是返回表中全部數據,能夠經過過濾器進行更精確的數據庫查詢。

定義模型類

咱們後面會把模型建立到單獨的文件中,可是如今咱們先把模型類寫在manage.py文件中。

from flask import Flask
from config import Config

app = Flask(__name__,template_folder='templates')
app.config.from_object(Config)


"""模型的建立"""
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)

class Course(db.Model):
    # 定義表名
    __tablename__ = 'tb_course'
    # 定義字段對象
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    price = db.Column(db.Numeric(6,2))
    # repr()方法相似於django的__str__,用於打印模型對象時顯示的字符串信息
    def __repr__(self):
        return 'Course:%s'% self.name

class Student(db.Model):
    __tablename__ = 'tb_student'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(64),unique=True)
    age = db.Column(db.SmallInteger)
    sex = db.Column(db.Boolean,default=1)

    def __repr__(self):
        return 'Student:%s' % self.name

class Teacher(db.Model):
    __tablename__ = 'tb_teacher'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)

    def __repr__(self):
        return 'Teacher:%s' % self.name

@app.route("/")
def index():
    return "ok"

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

模型之間的關聯

一對多

class Course(db.Model):
    ...
    teacher_id = db.Column(db.Integer, db.ForeignKey('tb_teacher.id'))

class Teacher(db.Model):
    ...
    # 課程與老師之間的關聯
    courses = db.relationship('Course', backref='teacher', lazy='subquery')
    ...
  • 其中realtionship描述了Course和Teacher的關係。第一個參數爲對應參照的類"Course"
  • 第二個參數backref爲類Teacher申明新屬性的方法
  • 第三個參數lazy決定了何時SQLALchemy從數據庫中加載數據
    • 若是設置爲子查詢方式(subquery),則會在加載完Teacher對象後,就當即加載與其關聯的對象,這樣會讓總查詢數量減小,但若是返回的條目數量不少,就會比較慢
      • 設置爲 subquery 的話,teacher.courses 返回全部當前老師關聯的課程列表
    • 另外,也能夠設置爲動態方式(dynamic),這樣關聯對象會在被使用的時候再進行加載,而且在返回前進行過濾,若是返回的對象數不少,或者將來會變得不少,那最好採用這種方式
      • 設置爲 dynamic 的話,Teacher.courses返回查詢對象,並無作真正的查詢,能夠利用查詢對象作其餘邏輯,好比:先排序再返回結果

多對多

achievement = db.Table('tb_achievement',  
    db.Column('student_id', db.Integer, db.ForeignKey('tb_student.id')),  
    db.Column('course_id', db.Integer, db.ForeignKey('tb_course.id'))  
)

class Course(db.Model):
    ...
    students = db.relationship('Student',secondary=achievement,  
                                    backref='courses',  
                                    lazy='dynamic')
class Student(db.Model):
    ...

經常使用的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對象,它包含指定範圍內的結果

建立和刪除表

建立表

db.create_all()  # 注意,create_all()方法執行的時候,須要放在模型的後面
# 上面這段語句,後面咱們須要轉移代碼到flask-script的自定義命令中。
# 執行了一次之後,須要註釋掉。

刪除表

db.drop_all()

數據操做

添加一條數據

student1 = Student(name='xiaoming')
db.session.add(student1)
db.session.commit()
#再次插入一條數據
student2 = Role(name='xiaohong')
db.session.add(student2)
db.session.commit()

一次插入多條數據

st1 = Student(name='wang',email='wang@163.com',age=22)
st2 = Student(name='zhang',email='zhang@189.com',age=22)
st3 = Student(name='chen',email='chen@126.com',age=22)
st4 = Student(name='zhou',email='zhou@163.com',age=22)
st5 = Student(name='tang',email='tang@163.com',age=22)
st6 = Student(name='wu',email='wu@gmail.com',age=22)
st7 = Student(name='qian',email='qian@gmail.com',age=22)
st8 = Student(name='liu',email='liu@163.com',age=22)
st9 = Student(name='li',email='li@163.com',age=22)
st10 = Student(name='sun',email='sun@163.com',age=22)
db.session.add_all([st1,st2,st3,st4,st5,st6,st7,st8,st9,st10])
db.session.commit()

filter_by精確查詢

例如:返回名字等於wang的全部人

Student.query.filter_by(name='xiaoming').all()

first()返回查詢到的第一個對象【first獲取一條數據,all獲取多條數據】

Student.query.first()

all()返回查詢到的全部對象

Student.query.all()

filter模糊查詢,返回名字結尾字符爲g的全部數據。

Student.query.filter(Student.name.endswith('g')).all()

get():參數爲主鍵,若是主鍵不存在沒有返回內容

Student.query.get()

邏輯非,返回名字不等於wang的全部數據

Student.query.filter(Student.name!='wang').all()

not_ 至關於取反

from sqlalchemy import not_
Student.query.filter(not_(Student.name=='wang')).all()

邏輯與,須要導入and,返回and()條件知足的全部數據

from sqlalchemy import and_
Student.query.filter(and_(Student.name!='wang',Student.email.endswith('163.com'))).all()

邏輯或,須要導入or_

from sqlalchemy import or_
Student.query.filter(or_(Student.name!='wang',Student.email.endswith('163.com'))).all()

查詢數據後刪除

student = Student.query.first()
db.session.delete(student)
db.session.commit()

更新數據

student = Student.query.first()
student.name = 'dong'
db.session.commit()

關聯查詢

假設:老師和課程的關係是一對多的關係,一個老師能夠授課多個課程,一個課程只由一個老師授課。

  • 查詢老師授課的全部課程
#查詢講師表id爲1的老師
teacher = Teacher.query.get(1)
#查詢當前老師的全部課程, 根據模型中關聯關係來查詢數據
print(teacher.courses)
  • 查詢課程所屬講師
course = Course.query.get(2)

print(course)

# 根據外鍵只能查詢到ID數值, SQLAlchemy不會幫咱們把ID轉換成模型
print( course.teacher_id )

# 要獲取外鍵對應的模型數據,須要找到主鍵模型裏面的  db.relationship 裏面的 backref
print( course.teacher.name )

數據庫遷移

  • 在開發過程當中,須要修改數據庫模型,並且還要在修改以後更新數據庫。最直接的方式就是刪除舊錶,但這樣會丟失數據。
  • 更好的解決辦法是使用數據庫遷移框架,它能夠追蹤數據庫模式的變化,而後把變更應用到數據庫中。
  • 在Flask中可使用Flask-Migrate擴展,來實現數據遷移。而且集成到Flask-Script中,全部操做經過命令就能完成。
  • 爲了導出數據庫遷移命令,Flask-Migrate提供了一個MigrateCommand類,能夠附加到flask-script的manager對象上。

首先要在虛擬環境中安裝Flask-Migrate。

pip install flask-migrate

代碼文件內容:

from flask import Flask
from config import Config
from flask_migrate import Migrate,MigrateCommand
from flask_script import Manager,Command

app = Flask(__name__,template_folder='templates')
app.config.from_object(Config)

manage = Manager(app)

"""模型的建立"""
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)



#第一個參數是Flask的實例,第二個參數是Sqlalchemy數據庫實例
migrate = Migrate(app,db)

#manager是Flask-Script的實例,這條語句在flask-Script中添加一個db命令
manage.add_command('db',MigrateCommand)

# 多對多的關係
# 關係表的聲明方式
achieve = db.Table('tb_achievement',
    db.Column('student_id', db.Integer, db.ForeignKey('tb_student.id')),
    db.Column('course_id', db.Integer, db.ForeignKey('tb_course.id'))
)


class Course(db.Model):
    # 定義表名
    __tablename__ = 'tb_course'
    # 定義字段對象
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    price = db.Column(db.Numeric(6,2))
    teacher_id = db.Column(db.Integer, db.ForeignKey('tb_teacher.id'))
    students = db.relationship('Student', secondary=achieve, backref='courses', lazy='subquery')
    # repr()方法相似於django的__str__,用於打印模型對象時顯示的字符串信息
    def __repr__(self):
        return 'Course:%s'% self.name

class Student(db.Model):
    __tablename__ = 'tb_student'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(64),unique=True)
    age = db.Column(db.SmallInteger,nullable=False)
    sex = db.Column(db.Boolean,default=1)

    def __repr__(self):
        return 'Student:%s' % self.name

class Teacher(db.Model):
    __tablename__ = 'tb_teacher'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    # 課程與老師之間的多對一關聯
    courses = db.relationship('Course', backref='teacher', lazy='subquery')

    def __repr__(self):
        return 'Teacher:%s' % self.name


@app.route("/")
def index():
    return "ok"

if __name__ == '__main__':
    manage.run()
建立遷移版本倉庫
#這個命令會建立migrations文件夾,全部遷移文件都放在裏面。
python main.py db init
建立遷移版本
  • 自動建立遷移版本有兩個函數
    • upgrade():函數把遷移中的改動應用到數據庫中。
    • downgrade():函數則將改動刪除。
  • 自動建立的遷移腳本會根據模型定義和數據庫當前狀態的差別,生成upgrade()和downgrade()函數的內容。
  • 對比不必定徹底正確,有可能會遺漏一些細節,須要進行檢查
python main.py db migrate -m 'initial migration'

# 這裏等同於django裏面的 makemigrations,生成遷移版本文件
升級版本庫的版本
python main.py db upgrade
降級版本庫的版本
python main.py db downgrade

版本庫的歷史管理

能夠根據history命令找到版本號,而後傳給downgrade命令:

python manage.py db history

輸出格式:<base> ->  版本號 (head), initial migration

回滾到指定版本

python manage.py db downgrade # 默認返回上一個版本
python manage.py db downgrade 版本號   # 返回到指定版本號對應的版本

數據遷移的步驟:

1. 初始化數據遷移的目錄
python manage.py db init

2. 數據庫的數據遷移版本初始化
python manage.py db migrate -m 'initial migration'

3. 升級版本[建立表/建立字段/修改字段]
python manage.py db upgrade 

4. 降級版本[刪除表/刪除字段/恢復字段]
python manage.py db downgrade

模塊推薦

文檔: https://faker.readthedocs.io/en/master/locales/zh_CN.html

github: https://github.com/joke2k/faker

flask-session

容許設置session到指定存儲的空間中, 文檔:

安裝命令: https://pythonhosted.org/Flask-Session/

pip install flask-Session

使用session以前,必須配置一下配置項:

SECRET_KEY = "*(%#4sxcz(^(#$#8423" # session祕鑰

redis保存session的基本配置

配置文件信息:

import redis
class Config(object):
    DEBUG = True
    SECRET_KEY = "*(%#4sxcz(^(#$#8423"
    # 數據庫連接配置:
    #數據類型://登陸帳號:登陸密碼@數據庫主機IP:數據庫訪問端口/數據庫名稱
    SQLALCHEMY_DATABASE_URI = "mysql://root:123@127.0.0.1:3306/flask_students"
    # 設置mysql的錯誤跟蹤信息顯示
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    # 打印每次模型操做對應的SQL語句
    SQLALCHEMY_ECHO = True
    # 把session保存到redis中
    # session存儲方式爲redis
    SESSION_TYPE="redis"
    # 若是設置session的生命週期是不是會話期, 爲True,則關閉瀏覽器session就失效
    SESSION_PERMANENT = False
    # 是否對發送到瀏覽器上session的cookie值進行加密
    SESSION_USE_SIGNER = False
    # 保存到redis的session數的名稱前綴
    SESSION_KEY_PREFIX = "session:"
    # session保存數據到redis時啓用的連接對象
    SESSION_REDIS = redis.Redis(host='127.0.0.1', port='6379')  # 用於鏈接redis的配置

主文件信息main.py,代碼:

from flask import Flask
from config import Config
from flask_session import Session
from flask import session
app = Flask(__name__,template_folder='templates')
app.config.from_object(Config)

Session(app)

@app.route("/")
def index():
    return "ok"

@app.route("/set_session")
def set_session():
    """設置session"""
    session["username"] = "小明"
    return "ok"

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

SQLAlchemy存儲session的基本配置

須要手動建立session表,在項目第一次啓動的時候,使用db.create_all()來完成建立。

db = SQLAlchemy(app)

app.config['SESSION_TYPE'] = 'sqlalchemy'  # session類型爲sqlalchemy
app.config['SESSION_SQLALCHEMY'] = db # SQLAlchemy對象
app.config['SESSION_SQLALCHEMY_TABLE'] = 'session' # session要保存的表名稱
app.config['SESSION_PERMANENT'] = True  # 若是設置爲True,則關閉瀏覽器session就失效。
app.config['SESSION_USE_SIGNER'] = False  # 是否對發送到瀏覽器上session的cookie值進行加密
app.config['SESSION_KEY_PREFIX'] = 'session:'  # 保存到session中的值的前綴

Session(app)
相關文章
相關標籤/搜索