flask-sqlalchemy組件


一、簡介

  flask本身沒有內置orm框架,需要依賴第三方模塊,這里介紹flask-sqlalchemy,而flask-sqlalchemy是一個flask的擴展,本質上是對sqlalchemy的進一步封裝,當然也需要sqlalchemy的支持,使用起來和本來的sqlalchemy的orm是一樣的。本文主要介紹sqlalchemy的使用以及如何在flask中是用orm。 

二、sqlalchemy

  SQLAlchemy是Python編程語言下的一款ORM框架,該框架建立在數據庫API之上,使用關系對象映射進行數據庫操作,簡言之便是:將對象轉換成SQL,然后使用數據API執行SQL並獲取執行結果。圖示:

基本使用

安裝:

pip3 install sqlalchemy

注:SQLAlchemy無法修改表結構,如果需要可以使用SQLAlchemy開發者開源的另外一個軟件Alembic來完成,官網doc:http://docs.sqlalchemy.org/en/latest/core/expression_api.html

原生SQL

使用 Engine/ConnectionPooling/Dialect 進行數據庫操作,Engine使用ConnectionPooling連接數據庫,然后再通過Dialect執行SQL語句

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import create_engine
engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student", max_overflow=5)#創建連接,允許溢出5個連接
result = engine.execute('select * from student')#使用excute執行原生sql
print(result.fetchall())#獲取所有結果,與pymyql類似

事務

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import create_engine
engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student", max_overflow=5)#創建連接,允許溢出5個連接
result = engine.execute('select * from student')#使用excute執行原生sql
with engine.begin() as conn: #事務操作
    conn.execute("insert into student (name, age, res_date) values ('weikang', 33, '1992-11-11')")
    
print(result.fetchall())#獲取所有結果,與pymyql類似

建表

  定義數據表,才能進行sql表達式的操作,畢竟sql表達式的表的確定,是sqlalchemy制定的,如果數據庫已經存在了數據表還需要定義么?當然,這里其實是一個映射關系,如果不指定,查詢表達式就不知道是附加在那個表的操作,當然定義的時候,注意表名和字段名,代碼和數據的必須保持一致。定義好之后,就能創建數據表,一旦創建了,再次運行創建的代碼,數據庫是不會創建的。

  sqlalchemy內部組件調用順序為:使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 進行數據庫操作。Engine使用Schema Type創建一個特定的結構對象,之后通過SQL Expression Language將該對象轉換成SQL語句,然后通過 ConnectionPooling 連接數據庫,再然后通過 Dialect 執行SQL,並獲取結果。

TIPS:使用類的方式和使用metadata方式創建表時候區別在於metadata可以不指定主鍵,而是用class方式必須要求有主鍵。

demo1:

from sqlalchemy import create_engine,Table,Column,Integer,String,ForeignKey,MetaData

engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     encoding="utf-8",
                     echo=True,
                      max_overflow=5
                     )
#?charset是字符集編碼,echo=True打印輸出信息和執行的sql語句默認Flase,max_overflow=5允許溢出連接池連接數量
meta=MetaData()#生成源類
#定義表結構
user=Table('user',meta,
           Column('id',Integer,nullable=Table,autoincrement=True,primary_key=True),
           Column('name',String(20),nullable=True),
           Column('age',Integer,nullable=True)
           )

host=Table('host',meta,
            Column('ip',String(20),nullable=True),
            Column('hostname',String(20),nullable=True),

)
meta.create_all(engine)#創建表,如果存在則忽視

demo2:

使用orm基類創建

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import create_engine,Table,Column,Integer,String,ForeignKey,MetaData,Date
from sqlalchemy.ext.declarative import declarative_base

engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     encoding="utf-8",
                     echo=True,
                      max_overflow=5
                     )
#?charset是字符集編碼,echo=True打印輸出信息和執行的sql語句默認Flase,max_overflow=5允許溢出連接池連接數量
base=declarative_base()#生成ORM基類
#定義表結構
class User(base):
    __tablename__='book' #表明
    id = Column(Integer, primary_key=True)
    name=Column(String(32))
    date=Column(Date)

base.metadata.create_all(engine)#創建表,如果存在則忽視

orm增刪改查

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import create_engine,Table,Column,Integer,String,ForeignKey
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     max_overflow=5,
                     echo=True)
#數據庫連接信息為,連接類型://用戶名:密碼@數據庫地址:端口/數據庫名字?編碼
#max_overflow創建連接,允許溢出5個連接,echo=True,輸出相應的sql信息到控制台,方便調試。

base=declarative_base()#生成orm基類

class user(base):  #映射表
    __tablename__='user'
    id=Column(Integer,autoincrement=True,primary_key=True)
    name=Column(String(20))
    age=Column(Integer)

sessoion_class=sessionmaker(bind=engine)#創建與數據庫的會話類,這里的sessoion_class是類
Session=sessoion_class()#生成會話實例
user1=user(name='wd',age=22)#生成user對象
Session.add(user1)   #添加user1,可以使用add_all,參數為列表或者tuple
Session.commit()     #提交
#Session.rollback()  #回滾
Session.close()      #關閉會話

data=Session.query(user).filter(user.age==33).delete()
Session.commit()        #提交
Session.close()      #關閉會話

#data=Session.query(user).filter(user.age>20).update({"name":'jarry'})#update語法
data=Session.query(user).filter(user.age==22).first()#面向對象語法
data.name='coco'#如果data中數據多條需要使用for循環設置
Session.commit()        #提交
Session.close()      #關閉會話

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import create_engine,Table,Column,Integer,String,ForeignKey
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     max_overflow=5,
                     echo=True)
#數據庫連接信息為,連接類型://用戶名:密碼@數據庫地址:端口/數據庫名字?編碼
#max_overflow創建連接,允許溢出5個連接,echo=True,輸出相應的sql信息到控制台,方便調試。

base=declarative_base()#生成orm基類

class user(base):  #映射表
    __tablename__='user'
    id=Column(Integer,autoincrement=True,primary_key=True)
    name=Column(String(20))
    age=Column(Integer)

    def __repr__(self):   #定義
        return "(%s,%s,%s)" % (self.id,self.name,self.age)

sessoion_class=sessionmaker(bind=engine)#創建與數據庫的會話類,這里的sessoion_class是類
Session=sessoion_class()#生成會話實例



#data=Session.query(user).get(2)  #get語法獲取primrykey中的關鍵字,在這里主鍵為id,獲取id為2的數據
#data=Session.query(user).filter(user.age>22,user.name=='mack').first()
#filter語法兩個等於號,filter_by語法一個等於號,可以有多個filter,如果多個數據返回列表,first代表獲取第一個,為all()獲取所有
data=Session.query(user).filter(user.age>20,user.name.in_(['mack','wd'])).all()#in語法
print(data[0]) #打印第一個結果
Session.commit()        #提交,如果回滾的話,數據將不存在了
Session.close()      #關閉會話
Common Filter Operators

Here’s a rundown of some of the most common operators used in filter():

equals:

     query.filter(User.name == 'ed')
not equals:

     query.filter(User.name != 'ed')
LIKE:

query.filter(User.name.like('%ed%'))

IN:

NOT IN:
query.filter(~User.name.in_(['ed', 'wendy', 'jack']))

IS NULL:

IS NOT NULL:

AND:
2.1. ObjectRelationalTutorial 17

query.filter(User.name.in_(['ed', 'wendy', 'jack']))
# works with query objects too:
query.filter(User.name.in_( session.query(User.name).filter(User.name.like('%ed%'))

))

query.filter(User.name == None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))
SQLAlchemy Documentation, Release 1.1.0b1

# use and_()

from sqlalchemy import and_
query.filter(and_(User.name == 'ed', User.fullname == 'Ed Jones'))

# or send multiple expressions to .filter()
query.filter(User.name == 'ed', User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')
Note: Makesureyouuseand_()andnotthePythonandoperator! • OR:

Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH:

query.filter(User.name.match('wendy'))
Note: match() uses a database-specific MATCH or CONTAINS f 

常用查詢語法
常用查詢

其他操作

##獲取所有數據
data=Session.query(user).all()#獲取user表所有數據
for i in data:
    print(i)


##統計
#count=Session.query(user).count()#獲取所有的條數
count=Session.query(user).filter(user.name.like("ja%")).count()#獲取某些條數
print(count)


##分組
from sqlalchemy import func#需要導入func函數
res=Session.query(func.count(user.name),user.name).group_by(user.name).all()
print(res)

外間關聯

TIPS:設置外檢的另一種方式 ForeignKeyConstraint(['other_id'], ['othertable.other_id'])

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import create_engine,Table,Column,Integer,String,ForeignKey
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     encoding="utf-8",
                     echo=True,
                      max_overflow=5
                     )
#?charset是連接數據庫的字符集編碼(和數據庫的編碼一樣),echo=True打印輸出信息和執行的sql語句默認Flase,max_overflow=5允許溢出連接池連接數量


Base=declarative_base()

class user(Base):
    __tablename__='user'
    id=Column(Integer,primary_key=True,autoincrement=True)
    name=Column(String(20))
    age=Column(Integer)
    def __repr__(self):
        return "<id:%s,name:%s,age:%s>"%(self.id,self.name,self.age)

class host(Base):
    __tablename__='host'
    user_id=Column(Integer,ForeignKey('user.id'))#user_id關聯user表中的id
    hostname=Column(String(20))
    ip=Column(String(20),primary_key=True)
    host_user=relationship('user',backref='user_host')
    #通過host_user查詢host表中關聯的user信息,通過user_host,在user表查詢關聯的host,與生成的表結構無關,只是為了方便查詢
    def __repr__(self):
        return "<user_id:%s,hostname:%s,ip:%s>"%(self.user_id,self.hostname,self.ip)

Base.metadata.create_all(engine)
Session_class=sessionmaker(bind=engine)
Session=Session_class()
host1=Session.query(host).first()
print(host1.host_user)
print(host1)
user1=Session.query(user).first()
print(user1.user_host)

多外鍵關聯一個表中的一個字段

應用場景:當我們購物時候,你會發現有一個收發票地址,和一個收貨地址。關系如下:默認情況下,發票地址和收獲地址是一致的,但是也有可能我想買東西送給別人,而發票要自己留着,那收貨的地址和寄送發票的地址可以不同。即:同一個人的兩個收獲地址可以不同,多個收獲地址關聯同一個人。

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import Integer, ForeignKey, String, Column
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Base = declarative_base()


class Customer(Base):
    __tablename__ = 'customer'
    id = Column(Integer, primary_key=True)
    name = Column(String)

    billing_address_id = Column(Integer, ForeignKey("address.id"))
    shipping_address_id = Column(Integer, ForeignKey("address.id"))

    billing_address = relationship("Address", foreign_keys=[billing_address_id])
    shipping_address = relationship("Address", foreign_keys=[shipping_address_id])
    #同時關聯同一個字段,使用relationship需要指定foreign_keys,為了讓sqlalchemy清楚關聯的外鍵

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    street = Column(String)
    city = Column(String)
    state = Column(String)

多對多外鍵關聯

很多時候,我們會使用多對多外鍵關聯,例如:書和作者,學生和課程,即:書可以有多個作者,而每個作者可以寫多本書,orm提供了更簡單方式操作多對多關系,在進行刪除操作的時候,orm會自動刪除相關聯的數據。

表結構創建

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import Column,Table,String,Integer,ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import relationship

engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     encoding="utf-8",
                     echo=True,
                      max_overflow=5
                     )

Base=declarative_base()
stu_cour=Table('stu_cour',Base.metadata,
               Column('stu_id',Integer,ForeignKey('student.id')),
               Column('cour_id',Integer,ForeignKey('course.id'))
               )

class student(Base):
    __tablename__='student'
    id=Column(Integer,autoincrement=True,primary_key=True)
    stu_name=Column(String(32))
    stu_age=Column(String(32))
    courses=relationship('course',secondary=stu_cour,backref='students')
    #course是關聯的第一張表,stu_cour是關聯的第二張表,當然,也可以在第三張關聯表中使用兩個relationship關聯student表和course表
    def __repr__(self):
        return '<%s>'%self.stu_name


class course(Base):
    __tablename__='course'
    id=Column(Integer,autoincrement=True,primary_key=True)
    cour_name=Column(String(32))
    def __repr__(self):
        return '<%s>'%self.cour_name
Base.metadata.create_all(engine)

創建表結構
建表

插入數據

#!/usr/bin/env python3
#_*_ coding:utf-8 _*_
#Author:wd
from sqlalchemy import Column,Table,String,Integer,ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker

engine=create_engine("mysql+pymysql://stu:1234qwer@10.0.0.241:3307/student?charset=gbk",
                     encoding="utf-8",
                     echo=True,
                      max_overflow=5
                     )

Base=declarative_base()
stu_cour=Table('stu_cour',Base.metadata,
               Column('stu_id',Integer,ForeignKey('student.id')),
               Column('cour_id',Integer,ForeignKey('course.id'))
               )

class student(Base):
    __tablename__='student'
    id=Column(Integer,autoincrement=True,primary_key=True)
    stu_name=Column(String(32))
    stu_age=Column(String(32))
    courses=relationship('course',secondary=stu_cour,backref='students')
    #course是關聯的第一張表,stu_cour是關聯的第二張表,當然,也可以在第三張關聯表中使用兩個relationship關聯student表和course表
    def __repr__(self):
        return '<%s>'%self.stu_name


class course(Base):
    __tablename__='course'
    id=Column(Integer,autoincrement=True,primary_key=True)
    cour_name=Column(String(32))
    def __repr__(self):
        return '<%s>'%self.cour_name

stu1=student(stu_name='wd',stu_age='22')
stu2=student(stu_name='jack',stu_age=33)
stu3=student(stu_name='rose',stu_age=18)
c1=course(cour_name='linux')
c2=course(cour_name='python')
c3=course(cour_name='go')
stu1.courses=[c1,c2]  #添加學生課程關聯
stu2.courses=[c1]
stu3.courses=[c1,c2,c3]
session_class=sessionmaker(bind=engine)
session=session_class()
session.add_all([stu1,stu2,stu3,c1,c2,c3])
session.commit()

數據插入
插入數據

查詢

session_class=sessionmaker(bind=engine)
session=session_class()
stu_obj=session.query(student).filter(student.stu_name=='wd').first()
print(stu_obj.courses)#查詢wd學生所報名的課程
cour_obj=session.query(course).filter(course.cour_name=='python').first()
print(cour_obj.students)#查詢報名python課程所對應的課程
session.commit()
查詢

刪除

session_class=sessionmaker(bind=engine)
session=session_class()
cour_obj=session.query(course).filter(course.cour_name=='python').first()
session.delete(cour_obj)#刪除python課程
session.commit()
刪除

三、flask-sqlalchemy

flask中使用sqlalchemy時候,個人比較習慣安裝django的項目來進行構建,以下示例以登陸驗證進行說明,項目結構:

taskmanager
├── app01
│   ├── __init__.py       #初始化文件
│   ├── models.py        #數據模型
│   └── views
│       └── account.py   # 視圖函數
├── create_table.py    # 建表
├── run.py             # 啟動服務器
├── settings.py        #配置文件
├── static             #靜態資源
└── templates          #模版
    └── login.html

安裝:

pip3 install flask-sqlalchemy

數據庫配置

SQLALCHEMY_DATABASE_URI    #用於連接的數據庫 URI 。例如:sqlite:////tmp/test.dbmysql://username:password@server/db
SQLALCHEMY_BINDS     #一個映射 binds 到連接 URI 的字典。更多 binds 的信息見用 Binds 操作多個數據庫。
SQLALCHEMY_ECHO    #如果設置為Ture, SQLAlchemy 會記錄所有 發給 stderr 的語句,這對調試有用。(打印sql語句)
SQLALCHEMY_RECORD_QUERIES    #可以用於顯式地禁用或啟用查詢記錄。查詢記錄 在調試或測試模式自動啟用。更多信息見get_debug_queries()。
SQLALCHEMY_NATIVE_UNICODE        #可以用於顯式禁用原生 unicode 支持。當使用 不合適的指定無編碼的數據庫默認值時,這對於 一些數據庫適配器是必須的(比如 Ubuntu 上 某些版本的 PostgreSQL )。
SQLALCHEMY_POOL_SIZE    #數據庫連接池的大小。默認是引擎默認值(通常 是 5 )
SQLALCHEMY_POOL_TIMEOUT    #設定連接池的連接超時時間。默認是 10 。
SQLALCHEMY_POOL_RECYCLE    #多少秒后自動回收連接。這對 MySQL 是必要的, 它默認移除閑置多於 8 小時的連接。注意如果 使用了 MySQL , Flask-SQLALchemy 自動設定 這個值為 2 小時。

新建立settings.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd
import redis
class BaseConfig(object):
    #session配置
    SESSION_TYPE = 'redis'  # session類型為redis
    SESSION_KEY_PREFIX = 'session:'  # 保存到session中的值的前綴
    SESSION_PERMANENT = True  # 如果設置為False,則關閉瀏覽器session就失效。
    SESSION_USE_SIGNER = False  # 是否對發送到瀏覽器上 session:cookie值進行加密
    SESSION_REDIS= redis.Redis(host='10.1.210.33', port='6379')
    #數據庫配置
    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:1234qwer@10.1.210.33:3306/devops?charset=utf8"
    SQLALCHEMY_POOL_SIZE = 10    #數據庫連接池的大小。默認值 5
    SQLALCHEMY_POOL_TIMEOUT = 30  # 指定數據庫連接池的超時時間。默認是 10
    SQLALCHEMY_POOL_RECYCLE = -1
    SQLALCHEMY_MAX_OVERFLOW = 3  # 控制在連接池達到最大值后可以創建的連接數。當這些額外的連接回收到連接池后將會被斷開和拋棄
    SQLALCHEMY_TRACK_MODIFICATIONS = False  # 追蹤對象的修改並且發送信號


class ProductionConfig(BaseConfig):
    """生產配置文件"""
    pass


class DevelopmentConfig(BaseConfig):
    """開發配置文件"""
    pass


class TestingConfig(BaseConfig):
    """
    測試配置文件
    """
    pass

在app目錄下建立models.py

from . import db


class UserProfile(db.Model):
    """
    用戶
    """
    __tablename__ = 'userprofile'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, nullable=False)
    password = db.Column(db.String(64), unique=True, nullable=False)
    email = db.Column(db.String(128), unique=True, nullable=False)

    def __repr__(self):
        return '<user %s>' % self.username

創建視圖函數初始化app,App目錄新建__init__.py 
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .models import *
from .views import account


db = SQLAlchemy()  #實例化

def init_app():
    app = Flask(__name__, template_folder='../templates', static_folder='../static', static_url_path='/static')
    app.config.from_object('settings.DevelopmentConfig')

    # 將db注冊到app中
    db.init_app(app)

    # 注冊藍圖
    app.register_blueprint(account.account)

    return app

在app目錄創建目錄views單個py文件也可以

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd

from flask import Blueprint
from flask import request
from flask import render_template
from .. import db
from .. import models

account = Blueprint('account', __name__)


@account.route('/login',methods=['GET','POST'])
def login():
    if request.method=="GET":
        return render_template("login.html")
    else:
        user_obj=db.session.query(models.UserProfile).filter(models.UserProfile.username==request.form["username"],
                                                             models.UserProfile.password==request.form["password"]).first()
        db.session.close()
        if user_obj:
            return '登陸成功'
        else:
            return render_template("login.html",errors="用戶名或密碼錯誤!")

templates/login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div><h1>用戶登陸</h1></div>
<div>
    <form method="post">
        <input type="text" name="username" placeholder="用戶名">
        <input type="password" name="password" placeholder="密碼">
        <input type="submit">
        {{ errors }}
    </form>
</div>
</body>
</html>
login.html

創建啟動腳本run.py 
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd

from app01 import init_app

app = init_app()  # 創建app

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

離線創建數據庫表腳本create_table.py

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Author:wd

from app01 import init_app
from app01 import db

app = init_app()  #創建app

with app.app_context():  # 執行腳本創建數據庫表
    db.create_all()
create_table.py

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM