Flask數據庫

一 數據庫的設置html

Web應用中廣泛使用的是關係模型的數據庫,關係型數據庫把全部的數據都存儲在表中,表用來給應用的實體建模,表的列數是固定的,行數是可變的。它使用結構化的查詢語言。關係型數據庫的列定義了表中表示的實體的數據屬性。好比:商品表裏有name、price、number等。 Flask自己不限定數據庫的選擇,你能夠選擇SQL或NOSQL的任何一種。也能夠選擇更方便的SQLALchemy,相似於Django的ORM。SQLALchemy其實是對數據庫的抽象,讓開發者不用直接和SQL語句打交道,而是經過Python對象來操做數據庫,在捨棄一些性能開銷的同時,換來的是開發效率的較大提高。python

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

在Flask中使用mysql數據庫,須要安裝一個flask-sqlalchemy的擴展。sql

pip3 install flask-sqlalchemy

要鏈接mysql數據庫,仍須要安裝flask-mysqldb數據庫

pip3 install flask-mysqldb

 

使用Flask-SQLAlchemy管理數據庫flask

使用Flask-SQLAlchemy擴展操做數據庫,首先須要創建數據庫鏈接。數據庫鏈接經過URL指定,並且程序使用的數據庫必須保存到Flask配置對象的SQLALCHEMY_DATABASE_URI鍵中。安全

Flask的數據庫設置:服務器

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mysql@127.0.0.1:3306/test'

 

經常使用的SQLAlchemy字段類型session

類型名 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關係選項app

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

 

二 自定義模型類

模型表示程序使用的數據實體,在Flask-SQLAlchemy中,模型通常是Python類,繼承自db.Model,db是SQLAlchemy類的實例,表明程序使用的數據庫。

類中的屬性對應數據庫表中的列。id爲主鍵,是由Flask-SQLAlchemy管理。db.Column類構造函數的第一個參數是數據庫列和模型屬性類型。

以下示例:定義了兩個模型類,用戶和角色。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)


class Config(object):
    """配置參數"""
    # sqlalchemy的配置參數
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/db_flask"
    # 設置成 True,SQLAlchemy 將會追蹤對象的修改而且發送信號。這須要額外的內存, 若是沒必要要的能夠禁用它。
    SQLALCHEMY_TRACK_MODIFICATIONS = True

# 實例化SQLAlchemy對象
app.config.from_object(Config)

# 建立數據庫sqlalchemy工具對象
db = SQLAlchemy(app)

# 表名常見規範
# ihome --> ih_user     數據庫縮寫_表名
# tbl_user  -->  tbl_表名

# 建立數據庫模型類

class Role(db.Model):
    """用戶表"""
    __tablename__ = "tbl_roles"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship("User", backref="role")


class User(db.Model):
    """用戶表"""
    __tablename__ = "tbl_users"     # 指明數據庫的表名

    id = db.Column(db.Integer, primary_key=True)    # 整型的主鍵, 會默認設置爲自增主鍵
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(128))
    password = db.Column(db.String(128))
    role_id = db.Column(db.Integer, db.ForeignKey("tbl_roles.id"))
View Code

 

三 數據庫基本操做

在Flask-SQLAlchemy中,插入、修改、刪除操做,均由數據庫會話管理。會話用db.session表示。在準備把數據寫入數據庫前,要先將數據添加到會話中而後調用commit()方法提交會話。

數據庫會話是爲了保證數據的一致性,避免因部分更新致使數據不一致。提交操做把會話對象所有寫入數據庫,若是寫入過程發生錯誤,整個會話都會失效。

數據庫會話也能夠回滾,經過db.session.rollback()方法,實現會話提交數據前的狀態。

在Flask-SQLAlchemy中,查詢操做是經過query對象操做數據。最基本的查詢是返回表中全部數據,能夠經過過濾器進行更精確的數據庫查詢。

建立表:

db.create_all()

刪除表:

db.drop_all()

插入一條數據:

  # 建立對象
    ro1 = Role(name='admin')
    # session記錄對象任務
    db.session.add(ro1)
    # 提交任務到數據庫
    db.session.commit()

    ro2 = Role(name='user')
    db.session.add(ro2)
    db.session.commit()
View Code

一次插入多條數據

    us1 = User(name='wang', email='wang@163.com', password='123456', role_id=ro1.id)
    us2 = User(name='zhang', email='zhang@163.com', password='201512', role_id=ro2.id)
    us3 = User(name='chen', email='chen@126.com', password='987654', role_id=ro2.id)
    us4 = User(name='zhou', email='zhou@163.com', password='456789', role_id=ro1.id)

    # 一次保存多條數據
    db.session.add_all([us1, us2, us3, us4])
    db.session.commit()
View Code

 

3.1 查詢:

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

filter_by精確查詢:

>>> User.query.filter_by(name="wang").all()
[<User 1>]
>>> user = User.query.filter_by(name="wang").all()
>>> user[0].name
'wang'
View Code

first()返回查詢到的第一個對象

>>> user = User.query.first()
>>> user
<User 1>
>>> user.name
'wang'
View Code

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

>>> User.query.all()
[<User 1>, <User 2>, <User 3>, <User 4>]
View Code

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

>>> users = User.query.filter(User.name.endswith("g")).all()
>>> users
[<User 1>, <User 2>]
>>> users[0].name
'wang'
View Code

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

>>> User.query.get(1)
<User 1>
>>> user = User.query.get(1)
>>> user.name
'wang'
View Code

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

>>> user.query.filter(User.name!="wang").all()
[<User 2>, <User 3>, <User 4>]
View Code

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

>>> from sqlalchemy import and_
>>> user = User.query.filter(and_(User.name!="wang",User.email.endswith("163.com"))).all()
>>> user
[<User 2>, <User 4>]
View Code

邏輯或,須要導入or_

>>> from sqlalchemy import or_
>>> User.query.filter(or_(User.name!="wang", User.email.endswith("163.com"))).all()
[<User 1>, <User 2>, <User 3>, <User 4>]
View Code

not_ 至關於取反

>>> from sqlalchemy import not_
>>> User.query.filter(not_(User.name=="chen")).all()
[<User 1>, <User 2>, <User 4>]
View Code

使用db.session查詢

>>> db.session.query(Role).all()
[<Role 1>, <Role 2>]
>>> db.session.query(Role).get(2)
<Role 2>
>>> db.session.query(Role).first()
<Role 1>
View Code

取不到數據返回None

>>> user= User.query.get(5)
>>> user
>>> type(user)
<class 'NoneType'>
View Code

offset偏移

>>> users = User.query.offset(2).all()
>>> users[0].name
'chen'
View Code

limit

>>> users = User.query.offset(1).limit(2).all()
>>> users[0].name
'zhang'
>>> users[1].name
'chen'
View Code

order_by

>>> users = User.query.order_by(User.id.desc()).all()
>>> users
[<User 4>, <User 3>, <User 2>, <User 1>]
>>> users[0].name
'zhou'
View Code

group_by

>>> from sqlalchemy import func
>>> db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id)
<flask_sqlalchemy.BaseQuery object at 0x0000025DBA5B0CC0>
>>> db.session.query(User.role_id, func.count(User.role_id)).group_by(User.role_id).all()
[(1, 2), (2, 2)]
View Code

在每一個模型類中添加__repr__:

class Role(db.Model):
    """用戶表"""
    __tablename__ = "tbl_roles"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship("User", backref="role")

    def __repr__(self):
        return "Role object:%s" % self.name


class User(db.Model):
    """用戶表"""
    __tablename__ = "tbl_users"     # 指明數據庫的表名

    id = db.Column(db.Integer, primary_key=True)    # 整型的主鍵, 會默認設置爲自增主鍵
    name = db.Column(db.String(64), unique=True)
    email = db.Column(db.String(128))
    password = db.Column(db.String(128))
    role_id = db.Column(db.Integer, db.ForeignKey("tbl_roles.id"))

    def __repr__(self):
        return "User object:%s" % self.name
View Code
>>> User.query.get(1)
User object:wang
View Code

關聯查詢

>>> ro = Role.query.get(1)
>>> type(ro)
<class 'db_demo.Role'>
>>> ro.users
[User object:wang, User object:zhou]

>>> user = User.query.get(1)
>>> user
User object:wang
>>> user.role_id
1
>>> Role.query.get(user.role_id)
Role object:admin
>>> user.role
Role object:admin
View Code

 

3.2 更新數據

第一種方法:

>>> user = User.query.get(4)
>>> user
User object:zhou
>>> user.name = "test"
>>> db.session.add(user)
>>> db.session.commit()
>>> User.query.get(4)
User object:test
View Code

第二種方法:

>>> user = User.query.filter_by(id=1).update({"name":"test1"})
>>> db.session.commit()
>>> User.query.get(1)
User object:test1
View Code

 

3.3 刪除

>>> user =User.query.get(1)
>>> db.session.delete(user)
>>> db.session.commit()
View Code

 

四 圖書案例

視圖

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

app = Flask(__name__)

class Config(object):
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/db_flask"
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    SECRET_KEY = "sahq28y1qhihsd0-121ewq"

# 實例化SQLAlchemy對象
app.config.from_object(Config)

# 建立數據庫sqlalchemy工具對象
db = SQLAlchemy(app)

# 定義數據庫的模型
class Author(db.Model):
    """做者"""
    __tablename__ = "tbl_authors"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32), unique=True)
    books = db.relationship("Book", backref="author")


class Book(db.Model):
    """書籍"""
    __tablename__ = "tbl_books"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    author_id = db.Column(db.Integer, db.ForeignKey("tbl_authors.id"))

# 建立表單模型類
class AuthorBookForm(FlaskForm):
    """做者數據表單模型類"""
    author_name = StringField(label="做者", validators=[DataRequired("做者必填")])
    book_name = StringField(label="書籍", validators=[DataRequired("書籍必填")])
    submit = SubmitField(label="保存")

@app.route("/index", methods=["POST", "GET"])
def index():
    form = AuthorBookForm()
    if form.validate_on_submit():
        author_name = form.author_name.data
        book_name = form.book_name.data

        author = Author(name=author_name)
        db.session.add(author)
        db.session.commit()

        book = Book(name=book_name, author_id=author.id)
        # book = Book(name=book_name, author=author)
        db.session.add(book)
        db.session.commit()

    authors = Author.query.all()
    return render_template("author_book.html", form=form, authors=authors)

@app.route("/delete_book")
def delete_book():
    book_id = request.args.get("book_id")
    book = Book.query.get(book_id)
    author_id = book.id
    author = Author.query.get(author_id)
    db.session.delete(book)
    db.session.commit()

    db.session.delete(author)
    db.session.commit()
    return redirect(url_for("index"))

if __name__ == '__main__':
    # db.drop_all()
    # db.create_all()
    # au_xi = Author(name='我吃西紅柿')
    # au_qian = Author(name='蕭潛')
    # au_san = Author(name='唐家三少')
    # db.session.add_all([au_xi, au_qian, au_san])
    # db.session.commit()
    #
    # bk_xi = Book(name='吞噬星空', author_id=au_xi.id)
    # bk_xi2 = Book(name='寸芒', author_id=au_qian.id)
    # bk_qian = Book(name='飄渺之旅', author_id=au_qian.id)
    # bk_san = Book(name='冰火魔廚', author_id=au_san.id)
    # db.session.add_all([bk_xi, bk_xi2, bk_qian, bk_san])
    db.session.commit()

    app.run(debug=True)
View Code

模板

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta http-equiv='Content-type' content='text/htm'>
</head>
<body>
<form method="post">
<p>添加做者和書籍</p>
{{ form.csrf_token }}
{{ form.author_name.label }}
{{ form.author_name }}
{{ form.author_name.errors.0 }}
<br/>
{{ form.book_name.label }}
{{ form.book_name }}
{{ form.book_name.errors.0 }}
<br/>
{{ form.submit }}
<br/>
</form>
<hr/>

{% for author in authors %}
{{ author.name }}
    <ul>
    {% for book in author.books %} <a href="/delete_book?book_id={{ book.id }}">刪除</a>
    <li>{{ book.name }}</li>
    {% endfor %}
    </ul>
{% endfor %}


</body>
</html>
View Code

 

五 數據庫遷移

在Flask中可使用Flask-Migrate擴展,來實現數據遷移。而且集成到Flask-Script中,全部操做經過命令就能完成。

爲了導出數據庫遷移命令,Flask-Migrate提供了一個MigrateCommand類,能夠附加到flask-script的manager對象上。

pip3 install flask-migrate

文件: _migration.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

app = Flask(__name__)


class Config(object):
    SQLALCHEMY_DATABASE_URI = "mysql://root:123456@127.0.0.1:3306/db_flask"
    SQLALCHEMY_TRACK_MODIFICATIONS = True

app.config.from_object(Config)

# 建立sqlalchemy的數據庫鏈接對象
db = SQLAlchemy(app)

# 建立flask腳本管理工具對象
manager = Manager(app)

# 建立數據庫遷移工具對象
Migrate(app, db)

# 向manager對象中添加數據庫的操做命令
manager.add_command("db", MigrateCommand)


# 定義模型Role
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:'.format(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:'.format(self.username)


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

 

建立遷移倉庫

#這個命令會建立migrations文件夾,全部遷移文件都放在裏面。
python  _migrate.py db init

 

建立遷移腳本

自動建立遷移腳本有兩個函數,upgrade()函數把遷移中的改動應用到數據庫中。downgrade()函數則將改動刪除。自動建立的遷移腳本會根據模型定義和數據庫當前狀態的差別,生成upgrade()和downgrade()函數的內容。對比不必定徹底正確,有可能會遺漏一些細節,須要進行檢查

#建立自動遷移腳本
python  _migrate.py db migrate -m 'initial migration'     # -m 表示備註

 

更新數據庫

python  _migrate.py db upgrade

此時數據庫裏已經存在數據表了,若是須要回到以前的遷移版本,使用回退命令

 

回退數據庫

回退數據庫時,須要指定回退版本號,因爲版本號是隨機字符串,爲避免出錯,建議先使用python _migrate.py db history命令查看歷史版本的具體版本號,而後複製具體版本號執行回退。

python _migrate.py db downgrade 版本號

 

六 發送郵件

在開發過程當中,不少應用程序都須要經過郵件提醒用戶,Flask的擴展包Flask-Mail經過包裝了Python內置的smtplib包,能夠用在Flask程序中發送郵件。

Flask-Mail鏈接到簡單郵件協議(Simple Mail Transfer Protocol,SMTP)服務器,並把郵件交給服務器發送。

以下示例,經過開啓QQ郵箱SMTP服務設置,發送郵件。

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)
#配置郵件:服務器/端口/傳輸層安全協議/郵箱名/密碼
app.config.update(
    DEBUG = True,
    MAIL_SERVER='smtp.qq.com',
    MAIL_PROT=465,
    MAIL_USE_TLS = True,
    MAIL_USERNAME = 'xxxxxxxx@qq.com',
    MAIL_PASSWORD = 'xxxxxx',
)

mail = Mail(app)

@app.route('/')
def index():
 # sender 發送方,recipients 接收方列表
    msg = Message("This is a test ",sender='11111@qq.com', recipients=['aaaaaa@163.com','11111@qq.com'])
    #郵件內容
    msg.body = "Flask test mail"
    #發送郵件
    mail.send(msg)
    print "Mail sent"
    return "Sent Succeed"

if __name__ == "__main__":
    app.run()
View Code
相關文章
相關標籤/搜索