Flask筆記:數據庫ORM操做MySQL+pymysql/mysql-python+SQLAlchemy/Flask-SQLAlchemy

  Python中使用sqlalchemy插件能夠實現ORM(Object Relationship Mapping,模型關係映射)框架,而Flask中的flask-sqlalchemy其實就是在sqlalchemy外進行了一層封裝,使得在flask中使用起來更加方便,固然sqlalchemy的原有的方法也均可以使用。也就是說sqlalchemy在普通的Python程序中也可使用,而flask-sqlalchemy是爲flask「定製」的。python

  我這裏使用的是MySQL數據庫,Python3中對應的驅動插件爲pymysql(Python2中對應的驅動插件爲mysql-python),因此這裏的數據庫操做須要安裝MySQL、pymysql(或mysql-python)和flask-sqlalchemy。mysql

 

1、MySQL數據庫安裝(其實MySQL的東西網上一大推)sql

 安裝版:exe安裝版按照步驟一步步來就好了,能夠只安裝MySQL服務便可,若是電腦上沒有對應版本VS C++也會提示你安裝的。數據庫

 免安裝版/解壓版:根據如下步驟進行便可:flask

  • 下載:將下載的zip包解壓到任意目錄。
  • 配置環境變量:將解壓後的文件夾中的bin目錄路徑放入環境變量的path中。
  • 配置文件:在解壓的文件夾下(也就是bin的上一級目錄)新建一個ini配置文件如「my-default.ini」(若是有就須要修改其中的內容),在配置文件中寫入(修改或追加)以下內容:

  [mysqld]
  basedir=D:\MySQL Server 8.0
  datadir=D:\MySQL Server 8.0\datasession

  注:解壓後能夠本身更改文件夾名稱,好比我這兒是「MySQL Server 8.0」,配置這兩個信息後,其中的「data」文件夾不要本身建立(默認是沒有這個文件夾的,可是會在下一步自動建立),不然MySQL服務會啓動失敗。app

  • 安裝MySQL服務:在DOS窗口cd到bin目錄,依次執行命令「mysqld -install」和「mysqld --initialize-insecure」(第二個命令會自動建立data文件夾,裏面包含數據庫的一些基本信息,而且該命令執行成功後是不會返回任何信息的)。
  • 啓動(中止)MySQL服務:執行命令「net start mysql」啓動MySQL服務(中止服務是「net stop mysql」)。
  • 設置密碼:重開DOS窗口(保證是管理者權限),執行命令「mysqladmin -u root -p password」,回車,顯示「Enter password」,不用輸入任何東西直接回車(未設置密碼時默認沒有密碼),顯示「New password」,輸入本身的密碼便可,回車,顯示「Confirm new password」,再次輸入密碼便可,回車,設置密碼成功。
  • 登陸:輸入命令「mysql -u root -p」,回車,而後輸入設置的密碼便可。

  安裝和使用過程當中遇到的問題和解決方案:框架

  1. 使用命令「mysql -u root -p」輸入密碼後提示「access denied for user root @localhost(using password:YES)」,進不去數據庫:刪除所有文件夾從新安裝「MySQL安裝(解壓版)」,從新設置一遍密碼就OK了(關鍵是網上找了許多辦法也無論用,只能這招了)。
  2. 運行Flask鏈接數據庫時提示「Client does not support authentication protocol requested by server; consider upgrading MySQL client」:進入數據庫後依次執行命名「USE mysql;」、「ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';」和「FLUSH PRIVILEGES;」,其中除了「'123456'」是我的設置的密碼外,其餘的字母和字符都copy就好了(第二條命令執行失敗的話,從新進入數據庫或者從新安裝數據庫再試一下吧,我第一次也沒成功,後來重設密碼後又成功了)。

 

2、數據庫驅動插件安裝(pymysql/mysql-python)ide

  1. pymysql安裝:Python3鏈接數據庫的驅動插件爲pymysql,直接用pip3 install pymysql便可。
  2. mysql-python安裝:Python2鏈接MySQL數據庫的驅動爲MySQL-python,也可以使用pip install MySQL-python,這裏若是安裝失敗,可直接下載安裝包進行安裝:https://pypi.org/project/MySQL-python/#files。

 

3、SQLAlchemy使用函數

  1.安裝:pip install sqlalchemy

  2.鏈接數據庫:設置數據庫鏈接字符串,並使用create_engine函數建立一個engine對象,並使用engine對象的connect方法鏈接數據庫,注意鏈接數據庫後須要使用close方法關閉鏈接。

from sqlalchemy import create_engine

# 數據庫鏈接信息
HOSTNAME = '127.0.0.1'  # 本機地址
PORT = '3306'  # MySQL默認端口
DATABASE = 'sqlalchemy_demo'  # 數據庫名
USERNAME = 'root'  # 安裝數據庫時設置的用戶名和密碼
PASSWORD = '123456'

# 數據庫鏈接字符串
# dialect+driver://username:password@host:port/database
DB_URI = 'mysql+pymysql://{username}:{password}@{host}:{port}/{database}'.format(
    username=USERNAME, password=PASSWORD, host=HOSTNAME, port=PORT, database=DATABASE
)

# 建立一個engine對象
engine = create_engine(DB_URI)
# 鏈接數據庫
conn = engine.connect()
# 執行SQL語句,並返回執行結果
sel_res = conn.execute('select * from User')
# 獲取並打印一條查詢結果
print(sel_res.fetchone())
# 關閉鏈接
conn.close()

  

  3.定義模型類:使用declarative_base函數生成一個模型類的基類,以便模型類映射到數據庫中,且必須使用__tablename__指定表名(一個類對應數據庫中一張表,不一樣的類實例對應表中不一樣的記錄)。

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

# 數據庫鏈接信息
HOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'sqlalchemy_demo'
USERNAME = 'root'
PASSWORD = '123456'

# 數據庫鏈接字符串
# dialect+driver://username:password@host:port/database
DB_URI = 'mysql+pymysql://{username}:{password}@{host}:{port}/{database}'.format(
    username=USERNAME, password=PASSWORD, host=HOSTNAME, port=PORT, database=DATABASE
)

# 建立一個engine對象
engine = create_engine(DB_URI)
# 建立全部模型類的基類
Base = declarative_base(engine)

# 注意如下定義全是在類中直接進行的,沒有在__init__等方法中進行
class Person(Base):

    # 使用__table__name指定表名
    __tablename__ = 'person'

    # 定義屬性字段id爲整型、主鍵、自動增加
    id = Column(Integer, primary_key=True, autoincrement=True)
    # 定義屬性字段name爲最長50個字符的字符串類型,且非空
    name = Column(String(50), nullable=False)
    # 定義屬性字段age爲整型
    age = Column(Integer)

# 刪除表,不用特意指定表,在加載Python文件時會自動檢查定義了哪些表,並刪除這些表
Base.metadata.drop_all()

# 將全部模型映射到數據庫中,而且相同的模型類只在第一次執行時會生效,即若是第一次執行後,
# 又對類進行了修改,從新運行程序執行metadata.create_all()後並不會把更新的內容映射到數據庫中,前提是數據庫中的表沒有被刪除
Base.metadata.create_all()

  

  4.經常使用數據類型:數據類型對應的類直接從模塊sqlalchemy中導入便可。

    • Integer: 整型。
    • Float(32位)/Double(64位): 浮點類型,可能存在精度丟失的問題,這時候可使用DECIMAL定點類型。
    • Boolean: True/False,對應數據庫裏的1/0。
    • DECIMAL: 定點類型,須要指定數字的總位數(小數點前的位數+小數點後的位數)和小數位數。
    • Enum: 枚舉類型,須要指定全部的枚舉值,且給這類型的字段賦值的時候就只能使用這幾個指定的枚舉值。
    • Date: 日期類型(年月日),即datetime.date()。
    • DateTime: 日期時間類型(年月日 時分秒),即datetime.datetime()。
    • Time: 時間類型(時分秒),即datetime.time()。
    • String: 字符串類型,須要指定最大字符個數。
    • Text: 文本類型。

  

  5.Column經常使用參數:Column的第一個參數name用來指定數據中的屬性名,若是不指定則默認爲該屬性變量名爲屬性名。

    • name:數據庫中的屬性名,默認使用模型對應屬性的變量名。
    • type_:屬性的數據類型(加下劃線是由於Python已經有了關鍵字type)。
    • default: 默認值,即沒有給該屬性賦值時採用默認值。
    • nullable: 是否可爲空,默認爲True。
    • primary_key: 是否爲主鍵,默認False。
    • unique: 是否惟一,默認爲False。
    • autoincrement: 是否自動增加,默認False。
    • onupdate: 在某條記錄更新的時候執行的函數(不管更新的內容是否是該屬性字段都會執行)。

  

  6.增刪改查操做:使用sessionmaker建立session,並在session中進行增刪改查操做,注意若是涉及到修改數據庫,必定要commit後才能在數據庫生效。

    • 增長數據:可使用session的add(obj)方法(單條數據)和add_all([obj1, obj2, ...])(多條數據),最後使用commit方法提交修改。
    • 刪除數據:可使用session的delete(obj)刪除數據,最後使用commit方法提交修改。
    • 修改數據:直接對查詢出的實例屬性值進行從新賦值便可。
    • 查詢數據:可使用session的query方法獲得query對象,再對query對象進行filter等過濾便可得出想要的查詢結果,詳細使用見示例代碼。

增刪改查簡單示例(數據庫鏈接代碼和模型類代碼見前面的代碼示例):

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

engine = create_engine(DB_URI)

# 別忘了實例化後再一次調用(加括號),會調用類的__call__生成session
session = sessionmaker(engine)()


# 增長數據
def add_data():
    p = Person(name='Jason', age=19)
    # 添加一條數據到內存中
    session.add(p)
    # 添加多條數據到內存中
    p1 = Person(name='Mickle', age=20)
    p2 = Person(name='Marya', age=21)
    session.add_all([p1, p2])
    # 提交到數據庫中
    session.commit()


# 刪除數據
def del_data():
    p = session.query(Person).first()
    # 直接刪除提交便可
    session.delete(p)
    session.commit()


# 修改數據
def update_data():
    p = session.query(Person).first()
    # 獲取對象後,直接修改提交便可
    p.name = 'JiuyunnZhao'
    session.commit()


# 查詢數據
def search_data():
    # 查找模型類對應表的的全部數據(可用for遍歷)
    # persons = session.query(Person).all()
    # 查詢知足某個條件的全部數據(可for遍歷),查詢條件只有參數和一個等號(這種方式比較簡單,由於只針對一個類)
    # persons = session.query(Person).filter_by(name='Mickle').all()
    # 查詢知足某個條件的全部數據(可for遍歷),查詢條件類名.參數和雙等號(filter中的條件能夠更加複雜,這裏只是簡單舉例)
    # persons = session.query(Person).filter(Person.name == 'Mickle').all()
    # for p in persons:
    #     print(p)
    # 根據主鍵查詢某一條數據,沒有對應的主鍵則返回None
    # p = session.query(Person).get(100)
    # 獲取某張表的第一條數據
    p = session.query(Person).first()
    p = session.query(Person).filter(Person.name == 'Mickle').first()
    p = session.query(Person).filter_by(name='Mickle').first()
    print(p)


# 測試代碼
if __name__ == '__main__':
    add_data()
    # del_data()
    # update_data()
    # search_data()

   

  7.聚合函數:從sqlalchemy中導入func,func中有許多聚合函數可使用。其實func中沒有「真正」的聚合函數,都是MySQL中的函數,也就是或MySQL中的聚合函數均可以在func中找到和使用。

    • func.count(col_name):統計某個字段的行數。
    • func.avg(col_name):統計某個字段的平均值。
    • func.max: 統計某個字段的最大值。
    • func.min: 統計某個字段的最小值。
    • func.sum: 統計某個字段的和。
    • 示例代碼:session.query(func.avg(Person.age)).first(),結果是元組,其實也只有一條數據。

   

  8.filter中的條件過濾(注意不是filter_by方法):

    • ==(equal):如session.query(Person).filter(Person.name == 'Mickle').first()。
    • !=(not equal):如session.query(Person).filter(Person.name != 'Mickle').all()。
    • like/ilike:後者不區分大小寫,且兩個方法查詢條件中的通配符與SQL一致,如session.query(Person).filter(Person.name.like('Mi%')).all()。
    • in_(in):之因此有下劃線,Python自己有in的關鍵字了,如session.query(Person).filter(Person.name.in_(['Mickle', 'Marya'])).all()。
    • notin_(not in):如session.query(Person).filter(Person.name.notin_(['Mickle', 'Marya'])).all()。
    • ~(取反):如session.query(Person).filter(~Person.name.in_(['Mickle', 'Marya'])).all(),這裏的效果就至關於not in了。
    • ==None/!=None(is null/is not null):如session.query(Person).filter(Person.name == None).all()。
    • and_(and):這個方法不經常使用,使用方法:導入「from sqlalchemy import and_」,如session.query(Person).filter(and_(Person.name.like('M%'), Person.age == 18)).all()。經常使用的是session.query(Person).filter(Person.name.like('M%'), Person.age == 18).all(),多個and條件使用逗號分隔傳參便可。
    • or_(or):導入「from sqlalchemy import or_」,如session.query(Person).filter(or_(Person.name.like('Mi%'), Person.name.like('Ma%'))).all(),多個or條件使用逗號分隔傳參便可。

   

  9.外鍵及外鍵約束:在想要定義爲外鍵的列中傳入參數ForeignKey('tablename.column_name')便可,若是想要定義外鍵約束,在ForeignKey中指定參數ondelete爲相應約束便可,默認約束爲RESTRICT。

    • RESTRICT: 當要刪除的父表中的數據在子表中有外鍵引用時,就會阻止刪除操做,即不可刪除。
    • NO ACTION: 在MySQL中,效果同RESTRICT。
    • CASCADE: 級聯刪除,即刪除父表中的數據時,會同時刪除與之有外鍵關聯的子表的數據。
    • SET NULL: 刪除父表數據時,子表相應的外鍵列的值就會設置爲NULL(此時,外鍵列的nullable屬性就不可設置爲False了,由於這自相矛盾,會報錯的)。
    • 注:在ORM中刪除數據,即便用session.delete(xxx)時會無視MySQL數據庫中的外鍵約束,能夠直接刪除數據庫中的數據。當刪除一條數據時,若是有從表引用了它,即從表中有它的外鍵時,按照MySQL中RESTRICT約束,會刪除失敗,可是直接在ORM的代碼中使用session.delete(xxx)時,就會刪除成功,且刪除的步驟爲,先將該數據對應的從表中的外鍵設置爲NULL,再刪除該條數據。因此爲了不這種與數據庫不一致的行爲(略帶危險性),須要給外鍵加上一個nullable=False的約束,這樣不能設置爲NULL後,就不會去進行後面的刪除步驟了。
from sqlalchemy import create_engine, Column, Integer, String, Text, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

HOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'sqlalchemy_demo'
USERNAME = 'root'
PASSWORD = '123456'

# dialect+driver://username:password@host:port/database
DB_URI = 'mysql+pymysql://{username}:{password}@{host}:{port}/{database}'.format(
    username=USERNAME, password=PASSWORD, host=HOSTNAME, port=PORT, database=DATABASE
)


engine = create_engine(DB_URI)
Base = declarative_base(engine)


# 父表
class User(Base):
    __tablename__ = 'user'
    user_id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

# 子表
class Article(Base):
    __tablename__ = 'article'
    atc_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)

    # 外鍵列,意爲一個用戶能夠有多篇文章,注意此列的類型應該對應父表的主鍵類型一致
# 注意這裏是「表名.屬性名」,而不是「類名.屬性變量名」 user_id = Column(Integer, ForeignKey('user.user_id', ondelete='CASCADE'))

   

  10.外鍵關係:經過relationship將兩個存在外鍵關聯的兩張表創建一個關係屬性,並經過這個屬性去進行互相訪問。

    • 一對一:在一張表中定義一個外鍵,另外一張表中定義一個到此表的關係,並指明relationship的參數uselist爲False,表示此表與另外一張表是一對一的關係(默認是True,即一對多關係);也可以使用backref函數進行反向引用,使得外鍵的定義和關係的定義均可以在同一個模型類中。
    • 一對多:經過外鍵進行自動關聯,且外鍵所在一方爲「多」,另外一方爲「一」;定義關係時可使用backref參數,使得外鍵的定義和關係的定義均可以在同一個模型類中;在「一」中定義的表示「多」的關係變量其實繼承自list,因此append等方法也適用於它。
    • 多對多:須要用到Table類生成中間表和relationship的參數secondary指明這個中間表。
    • relationship參數:
      • 第一個參數:用於關聯另外一張表時用的是類名,而不是表名。
      • backref:用於進行反向引用,便可以在backref中定義另外一張表對此表的引用。
      • secondary:用於多對多關係創建時指定中間表。
      • uselist:指定此表與另外一張表是一對一的關係。
      • cascade:指定關係的約束,能夠指定多個約束,每一個值用逗號鏈接便可,且只會影響到定義cascade時所在的類,cascade參數值選項及含義以下:
        • save-update:默認選項,執行commit操做提交一條數據時,會將該數據經過relationship關聯的其餘數據一塊兒更新到數據庫中。
        • merge:也是默認選項,等效於session.merge(xxx),當添加的數據和已有的數據衝突時,會替換掉已有的數據。
        • delete:當刪除一條數據時,會同時刪除與其經過relationship關聯的其餘數據。
        • delete-orphan:配合delete使用,只能使用在一對多的「一」中,表示當主表的數據被刪除時,該數據的從表中的數據也會被刪除。這種效果也能夠在從表的relationship中指定參數single_parent=True來達到,這種狀況下,就算是多對多的關係,只要刪除了主表數據,從表的對應數據也會被刪除,只是反過來就不行了, 但delete-orphan是能夠的,因此它使用的前提條件是一對多的「一」。
        • expunge:移除數據,等效於session.expunge(xxx),與session.add(xxx)做用恰好相反。
        • all:等效於cascade='save-update,merge,refresh-expire,expunge,delete'(注意沒有delete-orphan)。
      • lazy:值爲dynamic時便可實現懶加載,即查詢數據或append追加數據的時候並不會立刻進行數據庫操做,而是返回一個AppenderQuery查詢對象,在最後才進入數據庫進行操做。

一對一關係簡單示例:

# 一對一關係示例1:一張表定義外鍵,另外一張表定義一對一關係,即uselist=False
class User(Base):
    __tablename__ = 'user'
    user_id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    # 添加一個到模型Article的一對一關係,關鍵在於uselist=False,由於默認是True,那就是一對多了
    article = relationship('Article', uselist=False)


class Article(Base):
    __tablename__ = 'article'
    atc_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)

    # 外鍵列「表名.屬性名」,而不是「類名.屬性變量名」
    user_id = Column(Integer, ForeignKey('user.user_id'))

    # 添加一個到模型User的關係,因爲User類中定義了一對一uselist=False,因此這個關係天然也是一對一了,也不用再指定uselist=False
    author = relationship('User')


# 一對一示例2:利用反向引用函數backref創建一對一關係
# backref函數中的參數和relationship中的參數是一致的,relationship中可用的參數它均可以用
from sqlalchemy.orm import sessionmaker, relationship, backref


class User(Base):
    __tablename__ = 'user'
    user_id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)


class Article(Base):
    __tablename__ = 'article'
    atc_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)

    # 外鍵列
    user_id = Column(Integer, ForeignKey('user.user_id'))
    # 這裏backref的使用至關於在User類中創建了一個名爲article的關係,且參數uselist爲False
    author = relationship('User', backref=backref('article', uselist=False))

一對多關係簡單示例

# 一對多關係示例1
class User(Base):
    __tablename__ = 'user'
    user_id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    # 添加一個到模型Article的關係,一對多,表示「多」的的變量articles其實繼承自list,因此append等方法也適用這個屬性
    articles = relationship('Article')


class Article(Base):
    __tablename__ = 'article'
    atc_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)

    # 外鍵列,注意這裏是「表名.屬性名」,而不是「類名.屬性變量名」
    user_id = Column(Integer, ForeignKey('user.user_id'))

    # 添加一個到模型User的關係,多對一
    author = relationship('User')


# 一對多關係示例2
class User(Base):
    __tablename__ = 'user'
    user_id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)


class Article(Base):
    __tablename__ = 'article'
    atc_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    content = Column(Text, nullable=False)

    # 外鍵列,注意這裏是「表名.屬性名」,而不是「類名.屬性變量名」
    user_id = Column(Integer, ForeignKey('user.user_id'))

    # 反向引用,backref參數就至關於自動給User模型添加了一個名爲articles且關聯到User模型的的關係
    author = relationship('User', backref='articles')

# 使用示例,如下代碼將在article表中添加兩條數據
user =User(username='Mickle')
article1 = Article(title='aaa', content='1111')
article2 = Article(title='bbb', content='222')
user.articles.append(article1)
user.articles.append(article2)
session.add(user)
session.commit()

多對多關係簡單示例

# 多對多關係示例:一篇文章能夠有多個標籤,一個標籤也能夠用於多篇文章
from sqlalchemy import create_engine, Column, Integer, String, Text, ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship


HOSTNAME = '127.0.0.1'
PORT = '3306'
DATABASE = 'sqlalchemy_demo'
USERNAME = 'root'
PASSWORD = '123456'

# dialect+driver://username:password@host:port/database
DB_URI = 'mysql+pymysql://{username}:{password}@{host}:{port}/{database}'.format(
    username=USERNAME, password=PASSWORD, host=HOSTNAME, port=PORT, database=DATABASE
)

engine = create_engine(DB_URI)
Base = declarative_base(engine)
session = sessionmaker(engine)()


class Article(Base):
    __tablename__ = 'article'
    artcle_id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    # 與Tag創建關聯關係和反向引用關係,並指明中間表爲article_tag
    # 這個關係能夠定義在關聯的兩張表中的任意一張表中均可以
    tags = relationship('Tag', backref='articles', secondary=article_tag)


class Tag(Base):
    __tablename__ = 'tag'
    tag_id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)


# 經過類Table來生成一箇中間表
article_tag = Table(
    'article_tag',  # 表名
    Base.metadata,  # 繼承的模型類型
    # 定義中間表中的列,聲明此列關聯到表article的列article_id,並指明以此列來做爲複合主鍵的其中一列
    Column('article_id', Integer, ForeignKey('article.article_id'), primary_key=True),
    # 定義中間表中的列,聲明此列關聯到表tag的列tag_id,並指明以此列來做爲複合主鍵的其中一列
    Column('tag_id', Integer, ForeignKey('tag.tag_id'), primary_key=True)
)

# 添加數據到數據庫中
article1 = Article(title='my article 1')
article2 = Article(title='my article 2')
tag1 = Tag(name='my tag 1')
tag2 = Tag(name='my tag 2')
article1.tags.append(tag1)
article1.tags.append(tag2)
article2.tags.append(tag1)
article2.tags.append(tag2)
session.add(article1)
session.add(article2)
session.commit()

 

   11.查詢排序:默認升序,降序通常使用對應屬性的desc()方法便可。

    • session中指定排序:使用Query結果對象的order_by(attr_name)方法便可,降序使用order_by(attr_name.desc())或者使用「-」取反排序也可。
    • __mapper_args__中指定排序:__mapper_args__是一個字典,設置鍵order_by的值爲對應的屬性便可,降序一樣使用屬性的desc()方法。
    • realtionship中指定排序:設置order_by參數的值爲對應的屬性便可,降序一樣使用屬性的desc()方法。

 升序降序簡單示例:

# 如下列子以模型類Article中的字段create_time做爲示例(字符串中的屬性名是數據庫中的屬性名而不是變量名(沒有定義時二者是同樣的))
# 方式一:在session中使用order_by查詢排序
# 升序
articles = session.query(Article).order_by(Article.create_time).all()
articles = session.query(Article).order_by('create_time').all()
# 降序)
articles = session.query(Article).order_by(Article.create_time.desc()).all()
articles = session.query(Article).order_by('-create_time').all()

# 方式2、在模型類中使用__mapper_args__的order_by指定查詢排序
class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, nullable=False, default=datetime.now)

    __mapper_args__ = {
        'order_by': create_time,  # 升序
        # 'order_by': create_time.desc(),  # 降序
    }

# 方式3、使用模型類中relationship的roder_by進行指定查詢排序
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)

class Article(Base):
    __tablename__ = 'article'
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    create_time = Column(DateTime, nullable=False, default=datetime.now)

    user_id = Column(Integer, ForeignKey('user.id'))

    author = realtionship('User', backref=backref('articles', order_by=create_time))  # 升序
    # author = realtionship('User', backref=backref('articles', order_by=create_time.desc()))  # 降序

 

  12.query對象經常使用方法:

    • limit:指定每次查詢數據的條數,例只查找前面10篇數據:articles = session.query(Article).limit(10).all()。
    • offset:指定查詢的起始偏移量,例從第5條數據開始,並查詢15條數據:articles = session.query(Article).offset(5).limit(15).all()。
    • slice(start, stop):指定查詢的區間(切片操做),例查詢第3到第18條數據:articles = session.query(Article)[3:18]或者articles = session.query(Article).slice(3, 18).all(),第一種方式最經常使用,第二種不太經常使用。

 

  13.高級查詢:

    • group_by:實現查詢分組,例根據年齡統計人數:session.query(User.age, func.count(User.id)).group_by(User.age).all()。
    • having:對查詢結果進行二次查詢,例根據年齡統計人數並篩選出年齡小於18的分組:session.query(User.age, func.count(User.id)).group_by(User.age).having(User.age < 18).all()。
    • join/outterjoin:即inner join和left join,例統計用戶文章數看,並根據用戶名分組,根據文章數排序:session.query(User.username, func.count(Article.id)).join(Article, User.id == Article.user_id).group_by(User.username).order_by(func.count(Article.id).desc()).all(),這裏若是User和Article之間有惟一的外鍵在關聯的話,join中的鏈接條件User.id == Article.user_id是能夠不用寫的,sqlalchemy會自動使用外鍵進行鏈接。
    • subquery:對查詢結果進行子查詢,例如查找和用戶「Jason」同年齡和同城市的用戶:sub_q = session.query(User.city.label('city'), User.age.label('age')).filter(User.username == 'Jason').subquery(); q_res = session.query(User).filter(User.city == sub_q.c.city, User.age == sub_q.c.age).all()。

 

4、Flask-SQLAlchemy使用

  因爲Flask-SQLAlchemy實際上是SQLAlchemy在外面進行了一層封裝,以便更好的適用於flask,因此Flask-SQLAlchemy和SQLAlchemy在使用上除了最外層封裝的定義外都是同樣的,下面只講一些常須要注意的不一樣之處

  安裝:pip install flask-sqlalchemy

  1. 配置:須要在配置文件中配置數據庫鏈接字符串「SQLALCHEMY_DATABASE_URI」,其中driver爲Python2的mysqldb,Python3的pymysql,具體以安裝的插件爲準。

# 數據庫鏈接固定格式格式字符串
# dialect+driver://username:password@host:port/database
DIALECT = 'mysql'
DRIVER = 'mysqldb'
USERNAME = 'root'
PASSWORD = 123456
HOST = '127.0.0.1'
PORT = '3306'
DATABASE = 'db_demo1'

SQLALCHEMY_DATABASE_URI = '{dialect}+{driver}://{username}:{password}@{host}:{port}/{database}?charset=utf8'.format(
    dialect=DIALECT, driver=DRIVER, username=USERNAME, password=PASSWORD, host=HOST, port=PORT, database=DATABASE
)

  2.使用:Column、relationship等均可以直接在「from flask_sqlalchemy import SQLAlchemy」的SQLAlchemy實例中直接使用

    • 實例化:實例化一個SQLAlchemy對象,須要使用一個Flask對象做爲參數傳入,若是實例化的時候不傳入,能夠在須要的時候使用init_app(flask_obj)方法傳入。
    • class模型:須要繼承db.Model,對應數據庫中的表,一個class實例對應一條記錄。
    • __tablename__:對應數據庫中的表名,若未指定此變量的值,則使用class模型類名的全小寫做爲表名。

簡單示例(配置信息寫在了配置文件config.py中了)

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config

app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)
db.create_all()


class User(db.Model):
    __tablename__ = 'user'  # 設置表名,未設置則以類名全小寫爲表名
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)  # 定義列id,整型,主鍵,自增加
    username = db.Column(db.String(100), nullable=False)  # 定義列username,字符型(最大100個字符),非空
相關文章
相關標籤/搜索