4.flask數據庫

1.安裝MySQL

直接去下載便可,若是是windows建能夠下載msi,一路next便可。我已經安裝過了,這裏就再也不演示了。

最後使用Navicat鏈接測試一下,我這裏是沒有問題的

 

2.SQLAlchemy的介紹和基本使用

sqlalchemy是一款orm框架

注意:SQLAlchemy自己是沒法處理數據庫的,必須依賴於第三方插件,比方說pymysql,cx_Oracle等等

SQLAlchemy等因而一種更高層的封裝,裏面封裝了許多dialect(至關因而字典),定義了一些組合,比方說: 能夠用pymysql處理mysql,也能夠用cx_Oracle處理Oracle,關鍵是程序員使用什麼 而後會在dialect裏面進行匹配,同時也將咱們高層定義的類轉化成sql語句,而後交給對應的DBapi去執行。

除此以外,SQLAlchemy還維護一個數據庫鏈接池,數據庫的連接和斷開是很是耗時的 SQLAlchemy維護了一個數據庫鏈接池,那麼就能夠拿起來直接用

首先要安裝sqlalchemy和pymysql,直接pip install  便可

我在mysql新建了一個名爲satori的數據庫

# 鏈接數據庫須要建立引擎
from sqlalchemy import create_engine

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"

# 鏈接方式:數據庫+驅動://用戶名:密碼@ip:端口/數據庫
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")

# 測試鏈接是否成功
conn = engine.connect()
# 咱們直接可使用conn去獲取數據
data = conn.execute("show databases")
print(data.fetchall()) # [('information_schema',), ('mysql',), ('performance_schema',), ('sakila',), ('satori',), ('sys',), ('world',)]

顯然是能夠獲取成功的

 

3.orm介紹

orm: object relation mapping,就是能夠把咱們寫的類轉化成表。將類裏面的元素映射到數據庫表裏面

4.定義orm模型並映射到數據庫中

from sqlalchemy import create_engine

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")

# 須要如下步驟
'''
1.建立一個orm模型,這個orm模型必須繼承SQLAlchemy給咱們提供好的基類
2.在orm中建立一些屬性,來跟表中的字段進行一一映射,這些屬性必須是SQLAlchemy給咱們提供好的數據類型
3.將建立好的orm模型映射到數據庫中
'''
# 1.建立orm,繼承基類
from sqlalchemy.ext.declarative import declarative_base
# 這個declarative_base只是一個函數,咱們須要傳入engine,而後返回基類
Base = declarative_base(engine)

class Girls(Base):
    # 首先是表名,建立表要指定名字,如何指定呢?
    __tablename__ = "girls"  # __tablename__就是咱們的表名,咱們指定爲girls
    # 2.建立屬性,來跟表中的字段一一映射
    # 首先咱們須要有列,還要有字段的屬性。注意:我這裏是爲了演示,工做中要寫在開頭。
    from sqlalchemy import Column, Integer, String
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))  # String也是一個類,裏面能夠傳入數字表示最多能夠存多少字節的字符串
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))


# 3.建立完畢,那麼接下來就要映射到數據庫中
Base.metadata.create_all()  # 表示將Base的全部子類的模型映射到數據庫中,一旦映射,即便改變模型以後再次執行,也不會再次映射。

 

 此時裏面尚未表,咱們執行一下看看

 

 咱們剛纔建立的表已經在裏面了

 

5.SQLAlchemy對數據的增刪改查操做

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

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

class Girls(Base):
    __tablename__ = "girls"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))


# 實例化一個對象,是用來增刪改查數據的
girls =Girls()

# 會獲得一個Session類
Session = sessionmaker(bind=engine)
# 而後實例化獲得一個Session對象,這個session使用來提交操做的
session = Session()

  

增:

girls.name = "satori"
girls.age = 16
girls.gender = "f"
girls.anime = "東方地靈殿"

# 這兩步表示將girls作的操做所有提交上去執行
session.add(girls)
session.commit()

# 也能夠批量操做
session.add_all([Girls(name="koishi", age=15, gender="f", anime="東方地靈殿"),
                 Girls(name="mashiro", age=16, gender="f", anime="櫻花莊的寵物女孩"),
                 Girls(name="matsuri", age=400, gender="f", anime="sola"),
                 Girls(name="kurisu", age=20, gender="f", anime="命運石之門")])
session.commit()

  

 

 

查:

# session.query獲得的是一個query對象,調用filter_by依舊是一個query對象
# 調用first方法獲取第一條查詢記錄,若是不存在則爲None
girls = session.query(Girls).filter_by(gender="f").first()
print(girls.name, girls.age, girls.anime)  # satori 16 東方地靈殿

  

改:

# 改和查比較相似
# 仍是先獲取相應的記錄
girls = session.query(Girls).filter_by(gender="f").first()
# 直接修改便可
girls.name = "古明地覺"
# 一旦涉及到數據的變動,那麼就必需要進行提交,查不涉及因此不須要
# 如何提交呢?咱們只是改,因此不須要add,直接使用commit便可
session.commit()

  

刪:

# 篩選出要刪除的字段
girl = session.query(Girls).filter_by(name="古明地覺").first()
# 添加用add,刪除用delete
session.delete(girl)
# 別忘了提交
session.commit()

  

 

6.SQLAlchemy屬性經常使用數據類型

SQLAlchemy經常使用數據類型:
  Integer:整形,映射到數據庫是int類型
  Float:浮點型,映射到數據庫是float類型,佔32位。
  Boolean:布爾類型,傳遞True or False,映射到數據庫是tinyint類型
  DECIMAL:頂點類型,通常用來解決浮點數精度丟失的問題
  Enum:枚舉類型,指定某個字段只能是枚舉中指定的幾個值,不能爲其餘值,
  Date:傳遞datetime.date()進去,映射到數據庫中是Date類型
  DateTime:傳遞datetime.datetime()進去,映射到數據庫中也是datatime類型
  String:字符類型,映射到數據庫中varchar類型
  Text:文本類型,能夠存儲很是長的文本
  LONGTEXT:長文本類型(只有mysql纔有這種類型),能夠存儲比Text更長的文本

 

7.Column經常使用參數

  default:默認值,若是沒有傳值,則自動爲咱們指定的默認值
  nullable:是否可空,False的話,表示不能夠爲空,那麼若是不傳值則報錯
  primary_key:是否爲主鍵
  unique:是否惟一,若是爲True,表示惟一,那麼傳入的值存在的話則報錯
  autoincrement:是否自動增加
  name:該屬性在數據庫中的字段映射,若是不寫,那麼默認爲賦值的變量名。

 

8.query查詢

1.模型,指定查找這個模型中全部的對象python

2.模型中的屬性,能夠指定查找模型的某幾個屬性mysql

3.聚合函數git

  func.count:統計行的數量程序員

  func.avg:求平均值sql

  func.max:求最大值數據庫

  func.min:求最小值flask

  func.sum:求總和windows

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

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)
# 以上這些都是必須的

class Girls(Base):
    __tablename__ = "girls"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))

    # 之後就不會打印一個query對象了,而是會打印返回值
    def __repr__(self):
        return f"{self.name}--{self.age}--{self.anime}"


Session = sessionmaker(bind=engine)
session = Session()

# 這裏指定了Girls,也就是模型,那麼會查找全部的模型對象
for girl in session.query(Girls).all():
    print(girl)
    '''
    koishi--15--東方地靈殿
    mashiro--16--櫻花莊的寵物女孩
    matsuri--400--sola
    kurisu--20--命運石之門
    '''

# 但若是我不想要全部屬性,而是要部分屬性怎麼辦呢?好比我只想要name和age,就能夠這樣
# 會獲取一個有元組組成的字典
print(session.query(Girls.name, Girls.age).all())  # [('koishi', 15), ('mashiro', 16), ('matsuri', 400), ('kurisu', 20)]


# 聚合函數
# 獲得query對象雖然只有一條數據,但仍是要加上first
print(session.query(func.count(Girls.name)).first())  # (4,)
print(session.query(func.avg(Girls.age)).first())  # (Decimal('112.7500'),)
print(session.query(func.max(Girls.age)).first())  # (400,)
print(session.query(func.min(Girls.age)).first())  # (15,)
print(session.query(func.sum(Girls.age)).first())  # (Decimal('451'),)

  

9.filter方法經常使用過濾條件

 

爲了方便演示,我這裏有多建了幾條數據

 

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

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class Girls(Base):
    __tablename__ = "girls"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(30))
    age = Column(Integer)
    gender = Column(String(1))
    anime = Column(String(50))

    # 之後就不會打印一個query對象了,而是會打印返回值
    def __repr__(self):
        return f"{self.name}--{self.age}--{self.anime}"


Session = sessionmaker(bind=engine)
session = Session()

# 關於過濾,有兩種方法,一種是filter,另外一種是filter_by
# filter須要使用類名.屬性的方式,filter_by只須要屬性便可,這就意味着後者使用簡單,但前者能夠寫出更復雜的查詢

# 下面來看看都有哪些查詢方法
# 一、==,因爲可能有多條,咱們只選擇一條。
print(session.query(Girls).filter(Girls.age == 16).first())  # 古明地覺--16--東方地靈殿

# 二、!=
print(session.query(Girls).filter(Girls.age != 16).first())  # 四方茉莉--400--sola

# 三、like,%和sql裏的同樣。除此以外還要ilike,不區分大小寫
print(session.query(Girls).filter(Girls.anime.like("%久%")).all())  # [雨宮優子--16--悠久之翼, 宮村宮子--15--悠久之翼]

# 四、in_,至於爲何多了多了一個_,這個bs4相似,爲了不和python裏的關鍵字衝突。爲何不是_in,若是放在前面那表明不想被人訪問
print(session.query(Girls).filter(Girls.age.in_([16, 400])).all())  # [古明地覺--16--東方地靈殿, 椎名真白--16--櫻花莊的寵物女孩, 四方茉莉--400--sola, 春日野穹--16--緣之空, 雨宮優子--16--悠久之翼]

# 五、notin_,等價於~Girls.age.in_()
print(session.query(Girls).filter(Girls.age.notin_([16, 20, 400])).all())  # [立華奏--18--angelbeats, 古河渚--19--Clannad, 阪上智代--18--Clannad, 古明地戀--15--東方地靈殿, 宮村宮子--15--悠久之翼]

# 六、isnull
print(session.query(Girls).filter(Girls.age is None).first())  # None

# 七、isnotnull
print(session.query(Girls).filter(Girls.age is not None).first())  # 古明地覺--16--東方地靈殿

# 八、and
print(session.query(Girls).filter(and_(Girls.age == 16, Girls.anime == "櫻花莊的寵物女孩")).first())  # 椎名真白--16--櫻花莊的寵物女孩

# 九、or
print(session.query(Girls).filter(or_(Girls.age == 15, Girls.anime == "悠久之翼")).all())  # [古明地戀--15--東方地靈殿, 雨宮優子--16--悠久之翼, 宮村宮子--15--悠久之翼]

 

10.外鍵及其四種約束

在mysql中,外鍵可使表之間的關係更加緊密。而SQLAlchemy中也一樣支持外鍵,經過ForforeignKey來實現,而且能夠指定表的外鍵約束

外鍵約束有如下幾種:

1.RESTRICT:父表數據被刪除,會阻止刪除

2.NO ACTION:在mysql中,等同於RESTRICT

3.CASCADE:級聯刪除

4.SET NULL:父表數據被刪除,zishuju字表數據會跟着刪除,也就是設置爲NULL

既然如此的話,咱們是否是要有兩張表啊,我這裏新建立兩張表

 

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

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


# 我如今新建兩張表
# 表People--表language ==》父表--子表
class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    # 這個pid指的是People裏面的id,因此要和People裏的id保持一致
    pid = Column(Integer, ForeignKey("People.id"))  # 引用的表.引用的字段


Base.metadata.create_all()

  

  

  

咱們也能夠看看錶的建立語法

那麼下面添加記錄

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

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    # 還記得約束的種類嗎
    # 1.RESTRICT:父表刪除數據,會阻止。
    '''
    由於在子表中,引用了父表的數據,若是父表的數據刪除了,那麼字表就會懵逼,不知道該找誰了。
    '''

    # 2.NO ACTION
    '''
    和RESTRICT做用相似
    '''

    # 3.CASCADE
    '''
    級聯刪除:咱們Language表的pid關聯了People的id。若是People中id=1的記錄被刪除了,那麼Language中關聯id=1的記錄也會被刪除
    就是咱們關聯了,個人pid關聯了你的id,若是你刪除了一條記錄,那麼根據你刪除記錄的id,我也會相應的刪除一條,要死一塊死。
    '''

    # 4.SET NULL
    '''
    設置爲空:和CASCADE相似,就是個人pid關聯你的id,若是id沒了,那麼pid會被設置爲空,不會像CASCADE那樣,把相應pid所在整條記錄都給刪了
    '''
    # 那麼如何設置呢?在ForeignKey中有一個onedelete參數,能夠接收上面四種中的一種
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))


# 因爲咱們以前已經建立了,因此再次建立已不會修改,因此只能先刪除,而後再次建立
Base.metadata.drop_all()
Base.metadata.create_all()


# 而後添加幾條記錄吧
Session = sessionmaker(bind=engine)
session = Session()

session.add_all([People(name="Guido van Rossum", age=62),
                 People(name="Dennis Ritchie", age=77),
                 People(name="James Gosling", age=63),
                 Language(name="Python", birthday=1991, type="解釋型", pid=1),
                 Language(name="C", birthday=1972, type="編譯型", pid=2),
                 Language(name="Java", birthday=1995, type="解釋型", pid=3)])

session.commit()

咱們下面刪除數據,直接在Navicat裏面演示

剩下的便再也不演示了,都是比較相似的。另外,若是再ForeignKey中不指定onedelete,那麼默認就是RESTRICT

 

 

11.ORM層外鍵和一對多關係

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    # 若是我想經過Language表查詢People表的數據呢?
    # 能夠經過relationship進行關聯
    people = relationship("People")


Base.metadata.drop_all()
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
session = Session()
session.add_all([People(name="Guido van Rossum", age=62),
                 People(name="Dennis Ritchie", age=77),
                 People(name="James Gosling", age=63),
                 Language(name="Python", birthday=1991, type="解釋型", pid=1),
                 Language(name="C", birthday=1972, type="編譯型", pid=2),
                 Language(name="Java", birthday=1995, type="解釋型", pid=3)])

session.commit()

people = session.query(Language).all()
for i in people:
    # Language的people已經和People這個表關聯了,我經過language依舊能夠訪問
    print(i.people.name, i.people.age)
    '''
    Guido van Rossum 62
    Dennis Ritchie 77
    James Gosling 63
    '''

  通常狀況下,一們語言對應一個做者,可是一個做者能夠有多個語言。

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)
    language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    people = relationship("People")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"


Session = sessionmaker(bind=engine)
session = Session()

# 經過People裏的language,查找Language表的記錄
# 實際上這裏的language仍是全部People對象的集合
language = session.query(People).all()
# 遍歷People對象
for i in language:
    # People的language已經和Language這個表關聯了,我經過language依舊能夠訪問
    # 因爲People和Language是一對多的關係,因此一個People記錄可能對應多個Language記錄,因此獲得的都是一個列表
    # 經過i.language,直接會去查找Language表,從而打印出結果
    print(i.language)
    '''
    [Python--1991--解釋型]
    [C--1972--編譯型, unix--1973--操做系統]
    [Java--1995--解釋型]
    '''

# 這裏獲得是每個People對象
for i in language:
    # 這裏遍歷每個People對象下的language,經過這裏的language查找Language表的記錄。
    # 而這裏的兩個language是不同的,因此我這裏起名字失誤了,不該該都叫language的,容易引發歧義,まぁいいや
    for j in i.language:
        print(i.name, j.name, j.birthday, j.type)
        '''
        Guido van Rossum Python 1991 解釋型
        Dennis Ritchie C 1972 編譯型
        Dennis Ritchie unix 1973 操做系統
        James Gosling Java 1995 解釋型
        '''

  在relationship中還有一個反向引用

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)

    # 咱們把這一行註釋掉
    # language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    # 咱們在People表中把relationship給註釋掉了,可是在Language表的relationship中添加了一個backref="language",反向引用
    # 意思是說,咱們即使在People中不指定,也依舊能夠經過People對象.language的方式來訪問到Language表中的全部屬性
    # 由於前面指定了People表示已經和People表關聯了
    # 固然這裏也能夠不叫language,比方說我改爲中文試試
    '''
    people = relationship("People", backref="language")
    '''
    people = relationship("People", backref="古明地盆")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"


Session = sessionmaker(bind=engine)
session = Session()

language = session.query(People).all()
# 遍歷People對象
for i in language:
    print(i.古明地盆)
    '''
    [Python--1991--解釋型]
    [C--1972--編譯型, unix--1973--操做系統]
    [Java--1995--解釋型]
    '''
    # 能夠看到即使用中文依舊能夠訪問,可是工做中千萬別這麼幹,不然被技術leader看到了,要麼把你罵一頓,要麼把你給開除了
    

  

12.一對一關係實現

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)

    # 咱們把這一行註釋掉
    # language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    people = relationship("People", backref="language")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"


Session = sessionmaker(bind=engine)
session = Session()

people = People(id=6, name="KenThompson", age=75)
language1 = Language(name="B", birthday=1968, type="編譯型", pid=6)
language2 = Language(name="Go", birthday=2009, type="編譯型", pid=6)

# 因爲People和Language是關聯的,而且經過language能夠訪問到Language表的屬性
# 那麼能夠經過people.language.append將Language對象添加進去
people.language.append(language1)
people.language.append(language2)

# 而後只須要提交People對象people,就會自動將Language對象language一、language2也提交了
session.add(people)
session.commit()

  

 

 可是若是我想反向添加怎麼辦呢?

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)

    # 咱們把這一行註釋掉
    # language = relationship("Language")

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"


class Language(Base):
    __tablename__ = "Language"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)
    birthday = Column(Integer, nullable=False)
    type = Column(String(20), nullable=False)
    pid = Column(Integer, ForeignKey("People.id", ondelete="RESTRICT"))

    people = relationship("People", backref="language")

    def __repr__(self):
        return f"{self.name}--{self.birthday}--{self.type}"


Session = sessionmaker(bind=engine)
session = Session()

people = People(id=7, name="松本行弘", age=53)
language = Language(name="ruby", birthday=1995, type="解釋型", pid=7)

# 反向添加,剛纔的是正向添加的。
# 正向:people.language.append,反向:language.people=people
# 正向裏的language是backref,反向裏的people是people = relationship("People", backref="language")的左值
language.people = people

# 而後只須要提交People對象people,就會自動將Language對象language一、language2也提交了
session.add(people)
session.commit()

 

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)

    # 表示獲得的不是一個列表,由於咱們是一對一,只有一個元素
    extend = relationship("People_extend", uselist=False)

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"


class People_extend(Base):
    __tablename__ = "People_extend"
    id = Column(Integer, primary_key=True, autoincrement=True)
    country = Column(String(20))
    uid = Column(Integer, ForeignKey("People.id"))

    # 而且這裏不須要backref了,加了會報錯
    people = relationship("People")

'''
people = People()
extend = People_extend()
而後經過people.extend = extend實現便可
'''

  

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)

    # 表示獲得的不是一個列表,由於咱們是一對一,只有一個元素
    # extend = relationship("People_extend", uselist=False)

    def __repr__(self):
        return f"{self.name}--{self.age}--{self.languages}"


class People_extend(Base):
    __tablename__ = "People_extend"
    id = Column(Integer, primary_key=True, autoincrement=True)
    country = Column(String(20))
    uid = Column(Integer, ForeignKey("People.id"))
    
    # 或者把上面的註釋掉,導入backref模塊
    people = relationship("People", backref=backref("extend", uselist=False))

'''
用法和以前的同樣
people = People()
extend = People_extend()
而後經過people.extend = extend實現便可

再使用People訪問的時候,直接經過people.extend.來訪問便可
'''

  

12.多對多關係實現

好比說博客園的文章,一篇文章可能對應多個標籤。同理一個標籤也會有有多個文章。這就是一個多對多的關係

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


# 如今咱們的兩張表建好了,既然要實現多對多,須要藉助第三張表。
# SQLAlchemy已經爲咱們提供好了一個table,讓咱們去使用

Article_Tag = Table("Article_Tage", Base.metadata,
                    Column("article_id", Integer, ForeignKey("Article.id"), primary_key=True),
                    Column("tag_id", Integer, ForeignKey("Tag.id"), primary_key=True)
                    )


class Article(Base):
    __tablename__ = "Article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    tags = relationship("Tag", backref="articles", secondary=Article_Tag)

class Tag(Base):
    __tablename__ = "Tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

'''
總結一下:
一、先把兩個須要多對多的模型創建出來
二、使用Table定義一箇中間表,中間表就是包含兩個模型的外鍵字段就能夠了,而且讓它們兩個作一個複合主鍵
三、在兩個須要作多對多的模型中隨便選擇一個模型,定義一個relationship屬性,來綁定三者之間的關係,在使用relationship的時候,須要傳入一個secondary="中間表"
'''

Base.metadata.create_all()

   

看一下表的建立過程

添加數據

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


# 如今咱們的兩張表建好了,既然要實現多對多,須要藉助第三張表。
# SQLAlchemy已經爲咱們提供好了一個table,讓咱們去使用

Article_Tag = Table("Article_Tag", Base.metadata,
                    Column("article_id", Integer, ForeignKey("Article.id"), primary_key=True),
                    Column("tag_id", Integer, ForeignKey("Tag.id"), primary_key=True)
                    )


class Article(Base):
    __tablename__ = "Article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)

    tags = relationship("Tag", backref="articles", secondary=Article_Tag)


class Tag(Base):
    __tablename__ = "Tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

'''
總結一下:
一、先把兩個須要多對多的模型創建出來
二、使用table定義一箇中間表,中間表就是包含兩個模型的外鍵字段就能夠了,而且讓它們兩個作一個複合主鍵
三、在兩個須要作多對多的模型中隨便選擇一個模型,定義一個relationship屬性,來綁定三者之間的關係,在使用relationship的時候,須要傳入一個secondary="中間表"
'''

Base.metadata.create_all()


article1 = Article(title="article1")
article2 = Article(title="article2")

tag1 = Tag(name="tag1")
tag2 = Tag(name="tag2")

# 每一篇文章,添加兩個標籤
article1.tags.append(tag1)
article1.tags.append(tag2)
article2.tags.append(tag1)
article2.tags.append(tag2)

Session = sessionmaker(bind=engine)
session = Session()

# 只需添加article便可,tag會被自動添加進去
session.add_all([article1, article2])
session.commit()

  

獲取數據

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


# 如今咱們的兩張表建好了,既然要實現多對多,須要藉助第三張表。
# SQLAlchemy已經爲咱們提供好了一個table,讓咱們去使用

Article_Tag = Table("Article_Tag", Base.metadata,
                    Column("article_id", Integer, ForeignKey("Article.id"), primary_key=True),
                    Column("tag_id", Integer, ForeignKey("Tag.id"), primary_key=True)
                    )


class Article(Base):
    __tablename__ = "Article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    '''
    Article對象.tags獲取Tag裏面的屬性
    Tag對象.articles(backref)獲取Article裏面的屬性
    '''
    tags = relationship("Tag", backref="articles", secondary=Article_Tag)

    def __repr__(self):
        return f"{self.title}"


class Tag(Base):
    __tablename__ = "Tag"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)

    def __repr__(self):
        return f"{self.name}"
'''
總結一下:
一、先把兩個須要多對多的模型創建出來
二、使用table定義一箇中間表,中間表就是包含兩個模型的外鍵字段就能夠了,而且讓它們兩個作一個複合主鍵
三、在兩個須要作多對多的模型中隨便選擇一個模型,定義一個relationship屬性,來綁定三者之間的關係,在使用relationship的時候,須要傳入一個secondary="中間表"
'''

Base.metadata.create_all()


# article1 = Article(title="article1")
# article2 = Article(title="article2")
#
# tag1 = Tag(name="tag1")
# tag2 = Tag(name="tag2")
#
# # 每一篇文章,添加兩個標籤
# article1.tags.append(tag1)
# article1.tags.append(tag2)
# article2.tags.append(tag1)
# article2.tags.append(tag2)
#
# Session = sessionmaker(bind=engine)
# session = Session()
#
# # 只需添加article便可,tag會被自動添加進去
# session.add_all([article1, article2])
# session.commit()

Session = sessionmaker(bind=engine)
session = Session()
tag = session.query(Tag).first()
print(tag.articles)  # [article1, article2]

article = session.query(Article).first()
print(article.tags)  # [tag1, tag2]

# 能夠看到,相應的數據所有獲取出來了

  

13.ORM層面刪除數據注意事項

 咱們知道一旦關聯,那麼刪除父表裏面的數據是沒法刪除的,只能先刪除字表的數據,而後才能刪除關聯的父表數據。若是在orm層面的話,能夠直接刪除父表數據,由於這裏等同於兩步。先將字表中關聯的字段設置爲NULL,而後刪除父表中的數據,等同於這兩步。爲了方便演示,我這裏將全部表所有刪除

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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)
    # uid是爲了創建外鍵,咱們要和use表的id進行關聯,因此類型也要和user表的id保持一致
    uid = Column(Integer, ForeignKey("user.id"))
    # 這個是爲了咱們可以經過一張表訪問到另一張表
    # 之後User對象即可以經過articles來訪問Articles表的屬性了
    author = relationship("User", backref="articles")


Base.metadata.create_all()

# 建立幾條記錄
user = User(username="guido van rossum")
article = Article(title="Python之父談論python的將來")

article.author = user
# 而後使用session添加article便可,會自動添加user
session.add(article)
session.commit()

此時咱們在數據庫層面刪除一下數據

咱們試試在orm層面刪除數據

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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)
    # uid是爲了創建外鍵,咱們要和use表的id進行關聯,因此類型也要和user表的id保持一致
    uid = Column(Integer, ForeignKey("user.id"))
    # 這個是爲了咱們可以經過一張表訪問到另一張表
    # 之後User對象即可以經過articles來訪問Articles表的屬性了
    author = relationship("User", backref="articles")


Base.metadata.create_all()

# # 建立幾條記錄
# user = User(username="guido van rossum")
# article = Article(title="Python之父談論python的將來")
#
# article.author = user
# # 而後使用session添加article便可,會自動添加user
# session.add(article)
# session.commit()

# 由於只有一條數據,因此直接first
user = session.query(User).first()
session.delete(user)
session.commit()

  

刪除父表的數據,這個過程至關於先將article中的uid設置爲NULL,而後刪除父表的數據

可是這樣也有危險,若是不熟悉SQLAlchemy的話,會形成不可避免的後果,怎麼辦呢?直接將uid設置爲不可爲空便可,加上nullable=False

 

14.relationship中的cascade屬性

ORM層面的cascade:

首先咱們知道若是若是數據庫的外鍵設置爲RESTRICT,那麼在orm層面,若是刪除了父表的數據,字表的數據將會被設置爲NULL,若是想避免這一點,那麼只須要將nullable設置爲False便可

可是在SQLAlchemy中,咱們只須要將一個數據添加到session中,提交以後,與其關聯的數據也被自動地添加到數據庫當中了,這是怎麼辦到的呢?實際上是經過relationship的時候,其關鍵字參數cascade設置了這些屬性:

1.save-update:默認選項,在添加一條數據的時候,會自動把與其相關聯的數據也添加到數據庫當中。這種行爲就是save-update所影響的

2.delete:表示刪除某一個模型的數據時,是否也刪掉使用relationship與其相關聯的數據。

3.delete-orphan:表示當對一個orm對象解除了父表中的關聯對象的時候,本身便會被刪掉。固然父表中的數據被刪除了,本身也會被刪除。這個選項只能用在一對多上,不能用在多對多以及多對一上。而且還須要在子模型的relationship中,添加一個single_parent=True的選項

4.merge:默認選項,當使用session.merge選項合併一個對象的時候,會將使用了relationship相關聯的對象也進行merge操做

5.expunge:移除操做的時候,會將相關聯的對象也進行移除。這個操做只會從session中刪除,並不會從數據庫當中移除

6.all:以上五種狀況的總和

下面我來逐一演示

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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)

    uid = Column(Integer, ForeignKey("user.id"))

    # 能夠看到其餘都是不變的,只是這裏咱們顯式的指定了cascade
    # 這裏設置爲"",這樣就不會有save-update屬性了。
    author = relationship("User", backref="articles", cascade="")


Base.metadata.drop_all()
Base.metadata.create_all()

# 建立幾條記錄
user = User(username="guido van rossum")
article = Article(title="Python之父談論python的將來")

article.author = user
# 而後使用session添加article便可,此時就不會添加user了
session.add(article)
session.commit()

  

  

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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)

    uid = Column(Integer, ForeignKey("user.id"))

    # 能夠看到其餘都是不變的,只是這裏咱們顯式的指定了cascade
    # 咱們也能夠手動指定save-update,固然這樣和不指定cascade是同樣,由於默認就是save-update
    author = relationship("User", backref="articles", cascade="save-update")


Base.metadata.drop_all()
Base.metadata.create_all()

# 建立幾條記錄
user = User(username="guido van rossum")
article = Article(title="Python之父談論python的將來")

article.author = user
# 而後使用session添加article便可,會添加user
session.add(article)
session.commit()

  

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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)

    uid = Column(Integer, ForeignKey("user.id"))

    # 能夠看到其餘都是不變的,只是這裏咱們顯式的指定了cascade
    # 咱們試一下delete,先說一下,能夠設置多個屬性,屬性之間使用英文的逗號分隔
    # 這樣指定以後,若是當我刪除了Article表的數據,那麼與之相關聯的User表中的數據也會被刪除
    author = relationship("User", backref="articles", cascade="save-update,delete")


Base.metadata.drop_all()
Base.metadata.create_all()

# 建立幾條記錄
user = User(username="guido van rossum")
article = Article(title="Python之父談論python的將來")

article.author = user

# 若是不加delete,那麼我刪除Article表的數據,User表的數據依舊會堅挺在那裏
# 可是我如今加上了delete,那麼當我刪除Article表的數據,User表的數據也會被刪除
session.add(article)
session.commit()

  

 

15.三種排序方式 

1.order_by

正序:session.query(table).order_by("table.column").all() ,或者 session.query(table).order_by("column").all()

倒序:session.query(table).order_by("table.column.desc").all()  或者 session.query(table).order_by("-column").all()

 

2.在模型中定義

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

    # 就以User爲例,在SQLAlchemy中有一個屬性叫作__mapper_args__
    # 是一個字典,咱們能夠指定一個叫作order_by的key,value則爲一個字段
    __mapper_args__ = {"order_by": age}
    # 倒序的話,__mapper_args__ = {"order_by": age.desc()}
    # 之後再查找的時候直接session.query(table).all()便可,自動按照咱們指定的排序

  

3.使用backref

 

16.limit、offset以及切片操做 

limit:session.query(table).limit(10).all(),從開頭開始取十條數據

offset:session.query(table).offset(10).limit(10).all(),從第十條開始取10條數據

slice:session.query(table).slice(1, 8).all(),從第一條數據開始取到第八條數據。或者session.query(table)[1:8]這樣更簡單,連all()都不用了

 

17.數據查詢懶加載技術 

 在一對多或者多對多的時候,若是想要獲取多的這一部分數據的時候,每每經過一個屬性就能夠所有獲取了。好比有一個做者,若是想要獲取這個做者的全部文章,那麼經過user.articles就能夠所有獲取了。但有時候咱們不想獲取全部的數據,好比只獲取這個做者今天發表的文章,那麼這個時候就能夠給relationship傳遞一個lazy=「dynamic」,之後經過user.articles獲取到的就不是一個列表,而是一個AppendQuery對象了。之後就能夠對這個對象再進行過濾和排序工做。

 

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    uid = Column(Integer, ForeignKey("user.id"))

    author = relationship("User", backref=backref("articles"))


Base.metadata.drop_all()
Base.metadata.create_all()

user = User(username="guido", age=63)
for i in range(10):
    article = Article(title=f"title{i}")
    article.author = user
    session.add(article)

session.commit()

user = session.query(User).first()
pprint(user.articles)
'''
[<__main__.Article object at 0x000001B73F8A5208>,
 <__main__.Article object at 0x000001B73F8A5278>,
 <__main__.Article object at 0x000001B73F8A52E8>,
 <__main__.Article object at 0x000001B73F8A5358>,
 <__main__.Article object at 0x000001B73F8A53C8>,
 <__main__.Article object at 0x000001B73F8A5438>,
 <__main__.Article object at 0x000001B73F8A54A8>,
 <__main__.Article object at 0x000001B73F8A5550>,
 <__main__.Article object at 0x000001B73F8A55F8>,
 <__main__.Article object at 0x000001B73F804400>]
'''

  以上獲取了所有數據,若是隻獲取一部分呢?

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_
from sqlalchemy import Column, Integer, String, ForeignKey, Table
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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


class Article(Base):
    __tablename__ = "article"
    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(50), nullable=False)
    uid = Column(Integer, ForeignKey("user.id"))

    # 若是轉化成一個AppendQuery對象,獲取部分數據呢?只須要加上一個lazy="dynamic"便可
    # 注意這裏必定要寫在backref裏面,咱們的目的是爲了經過user取article,把article轉成一個AppendQuery對象,因此要寫在backref裏面
    # 若是寫在了外面,那麼就至關於經過article去找user,把user變成一個AppendQuery對象了
    # 因此咱們要導入backref,表示反向引用。backref="articles",是爲了經過user取得article,因此明明定義在article裏面,卻反過來被user使用
    # 同理backref=backref("articles")和backref="articles"是同樣的,可是之因此要加上裏面的這個backref,是爲了給user提供更好的屬性,好比這裏的懶加載
    # 是爲了讓user找article的時候進行懶加載,若是沒有這個backref,那麼這個lazy屬性咱們可能要定義在user裏面了,這樣很麻煩
    # 可是經過導入backref,咱們能夠直接在article裏面定義,反過來讓user去使用,一旦定義在了外面,不反向了就變成article的了
    # 等於經過article找user進行懶加載
    author = relationship("User", backref=backref("articles", lazy="dynamic"))

    def __repr__(self):
        return f"{self.title}"


Base.metadata.drop_all()
Base.metadata.create_all()

user = User(username="guido", age=63)
for i in range(10):
    article = Article(title=f"title{i}")
    article.author = user
    session.add(article)

session.commit()

user = session.query(User).first()
# 此時再也不是InstrumentList,而是一個AppendQuery對象
print(type(user.articles))  # <class 'sqlalchemy.orm.dynamic.AppenderQuery'>
# 查看一下源碼發現,AppenderQuery這個類繼承在Query這個類,也就是說Query可以使用的,它都能使用

print(user.articles.filter(Article.id > 5).all())  # [title5, title6, title7, title8, title9]

# 也能夠動態添加數據
article = Article(title="100")
user.articles.append(article)
session.commit()  # 不用add,直接commit

print(user.articles.filter(Article.id > 5).all())  # [title5, title6, title7, title8, title9, 100]


'''
lazy有如下參數:
1.select:默認選項,以user.articles爲例,若是沒有訪問user.articles屬性,那麼SQLAlchemy就不會從數據庫中查找文章。一旦訪問,就會查找全部文章,最爲InstrumentList返回
2.dynamic:返回的不是一個InstrumentList,而是一個AppendQuery對象。
主要使用這兩種狀況
'''

  

18.group_by和having字句

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy import Column, Integer, String, ForeignKey, Table, Enum
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True, autoincrement=True)
    username = Column(String(50), nullable=False)
    age = Column(Integer)
    gender = Column(Enum("male", "female","secret"), default="male")


Base.metadata.drop_all()
Base.metadata.create_all()

user1 = User(username="神田空太", age=16, gender="male")
user2 = User(username="椎名真白", age=16, gender="female")
user3 = User(username="四方茉莉", age=400, gender="female")
user4 = User(username="木下秀吉", age=15, gender="secret")
user5 = User(username="牧瀨紅莉棲", age=18, gender="female")

session.add_all([user1, user2, user3, user4, user5])
session.commit()

# group_by:分組,比方說我想查看每一個年齡對應的人數
print(session.query(User.age, func.count(User.id)).group_by(User.age).all())
'''
輸出結果:
[(15, 1), (16, 2), (18, 1), (400, 1)]
'''

# having:在group_by分組的基礎上進行進一步查詢,比方說我想查看年齡大於16的每個年齡段對應的人數
print(session.query(User.age, func.count(User.id)).group_by(User.age).having(User.age > 16).all())
'''
輸出結果:
[(18, 1), (400, 1)]
'''

# 若是想看看底層的語句是什麼寫的,能夠不加上all()進行查看
print(session.query(User.age, func.count(User.id)).group_by(User.age).having(User.age > 16))
'''
SELECT user.age AS user_age, count(user.id) AS count_1 
FROM user GROUP BY user.age 
HAVING user.age > %(age_1)s

從user表中選出age,計算count(user.id)做爲count_1,按照user.age分組,而且user.age要大於咱們指定的值
'''

  

19.join實現複雜查詢

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy import Column, Integer, String, ForeignKey, Table, Enum
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


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)
    uid = Column(Integer, ForeignKey("user.id"))

    author = relationship("User", backref=backref("articles", lazy="dynamic"))

    def __repr__(self):
        return f"{self.title}"


Base.metadata.drop_all()
Base.metadata.create_all()

user1 = User(username="guido")
user2 = User(username="ken")

article1 = Article(title="python")
article1.author = user1

article2 = Article(title="B")
article2.author = user2
article3 = Article(title="go")
article3.author = user2

session.add_all([article1, article2, article3])
session.commit()

  

在數據庫層面上進行查詢,查詢每一個做者發表了多少文章,由於id是不重複的,因此對id進行count便可

# 找到全部用戶,按照發表文章的數量進行排序
res = session.query(User.username, func.count(Article.id)).join(Article, User.id == Article.uid).\
    group_by(User.id).order_by(func.count(Article.id))
print(res)
'''
SELECT user.username AS user_username, count(article.id) AS count_1 
FROM user INNER JOIN article ON user.id = article.uid GROUP BY user.id ORDER BY count(article.id)
'''
print(res.all())
'''
[('guido', 1), ('ken', 2)]
'''

  

20.subquery實現複雜查詢

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine, and_, or_, func
from sqlalchemy import Column, Integer, String, ForeignKey, Table, Enum
from sqlalchemy.orm import sessionmaker, relationship, backref
from pprint import pprint

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)

Session = sessionmaker(bind=engine)
session = Session()


class Girl(Base):
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(50), nullable=False)
    anime = Column(String(50), nullable=False)
    age = Column(Integer, nullable=False)

    def __repr__(self):
        return f"{self.name}--{self.anime}--{self.age}"


Base.metadata.create_all()

girl1 = Girl(name="雨宮優子", anime="悠久之翼", age=16)
girl2 = Girl(name="宮村宮子", anime="悠久之翼", age=16)
girl3 = Girl(name="古河渚", anime="clannad", age=19)
girl4 = Girl(name="牧瀨紅莉棲", anime="命運石之門", age=18)

session.add_all([girl1, girl2, girl3, girl4])
session.commit()

  

# 下面要尋找和雨宮優子在同一anime,而且age相同的記錄
girl = session.query(Girl).filter(Girl.name == "雨宮優子").first()
# Girl.anime == girl.anime, Girl.age == girl.age,girl是咱們查找的name="雨宮優子"的記錄,Girl則是整個表
# 查找整個表中的anime等於girl.anime 而且 age等於girl.age的記錄
expect_girls = session.query(Girl).filter(Girl.anime == girl.anime, Girl.age == girl.age, Girl.name != "雨宮優子").all()
print(expect_girls)  # [宮村宮子--悠久之翼--16]

  

# 使用subquery
# 這裏也可使用label取個別名,固然我這裏取得仍是anime
ygyz = session.query(Girl.anime.label("anime"), Girl.age).filter(Girl.name == "雨宮優子").subquery()
# 這裏的ygyz.c的c代指的是column,是一個簡寫
res = session.query(Girl).filter(Girl.anime == ygyz.c.anime, Girl.age == ygyz.c.age)
print(res)
'''
SELECT girls.id AS girls_id, girls.name AS girls_name, girls.anime AS girls_anime, girls.age AS girls_age 
FROM girls, (SELECT girls.anime AS anime, girls.age AS age 
FROM girls 
WHERE girls.name = %(name_1)s) AS anon_1 
WHERE girls.anime = anon_1.anime AND girls.age = anon_1.age
能夠看到除了起了一些名字以外,SQLAlchemy生成的sql語句與咱們以前寫的幾乎相似
'''
print(res.all())
'''
[雨宮優子--悠久之翼--16, 宮村宮子--悠久之翼--16]
'''

  和咱們以前寫的代碼貌似沒啥區別,也要兩步,並且這個貌似代碼量還多一些。可是倒數據庫裏面,咱們如今寫的只須要進行一次查詢,效率更高一些。

 

21.Flask-SQLAlchemy的使用

Flask-SQLAlchemy的一個插件,能夠更加方便咱們去使用,SQLAlchemy是能夠獨立於flask而存在的,這個插件是將SQLAlchemy集成到flask裏面來。咱們以前使用SQLAlchemy的時候,要定義Base,session,各個模型之類,使用這個插件能夠簡化咱們的工做

這個插件首先要安裝,直接pip install flask-sqlalchemy便可api

 

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
db_url = f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}"
# 這個配置不直接與咱們的SQLAlchemy這個類發生關係,而是要添加到app.config裏面,這一步是少不了的
# 至於這裏的key,做者規定就是這麼寫的
app.config["SQLALCHEMY_DATABASE_URI"] = db_url
# 而且還要加上這一段,否則會彈出警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 接收一個app,今後db便具備了app的功能
db = SQLAlchemy(app)


# 創建模型,確定要繼承,那麼繼承誰的,繼承自db.Module,至關於以前的Base。這裏操做簡化了,不須要咱們去建立了
class User(db.Model):
    __tablename__ = "user"
    # 能夠看到,以前須要導入的統統不須要導入了,都在db下面。不過本質上調用的仍是sqlalchemy模塊裏的類。
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)


class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), nullable=False)
    uid = db.Column(db.Integer, db.ForeignKey("user.id"))

    authror = db.relationship("User", backref="articles")
    # 因此咱們發現這和SQLAlchemy框架中的使用方法基本上是一致的,只不過咱們在SQLAlchemy中須要導入的,如今所有能夠經過db來訪問


# 那麼如何映射到數據庫裏面呢?這裏也不須要Base.metadata了,直接使用db便可。
db.drop_all()
db.create_all()

  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
db_url = f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}"
# 這個配置不直接與咱們的SQLAlchemy這個類發生關係,而是要添加到app.config裏面,這一步是少不了的
# 至於這裏的key,做者規定就是這麼寫的
app.config["SQLALCHEMY_DATABASE_URI"] = db_url
# 而且還要加上這一段,否則會彈出警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 接收一個app,今後db便具備了app的功能
db = SQLAlchemy(app)


# 創建模型,確定要繼承,那麼繼承誰的,繼承自db.Module,至關於以前的Base。這裏操做簡化了,不須要咱們去建立了
class User(db.Model):
    __tablename__ = "user"
    # 能夠看到,以前須要導入的統統不須要導入了,都在db下面。不過本質上調用的仍是sqlalchemy模塊裏的類。
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)


class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), nullable=False)
    uid = db.Column(db.Integer, db.ForeignKey("user.id"))

    author = db.relationship("User", backref="articles")
    # 因此咱們發現這和SQLAlchemy框架中的使用方法基本上是一致的,只不過咱們在SQLAlchemy中須要導入的,如今所有能夠經過db來訪問


# # 那麼如何映射到數據庫裏面呢?這裏也不須要Base.metadata了,直接使用db便可。
# db.drop_all()
# db.create_all()

# 下面添加數據
user = User(name="guido")
article = Article(title="python之父談python的將來")
user.articles.append(article)

db.session.add(user)
'''
或者
article.author = user
db.session.add(article)
'''
db.session.commit()
# 跟咱們以前使用SQLAlchemy的流程基本一致

  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
db_url = f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}"
# 這個配置不直接與咱們的SQLAlchemy這個類發生關係,而是要添加到app.config裏面,這一步是少不了的
# 至於這裏的key,做者規定就是這麼寫的
app.config["SQLALCHEMY_DATABASE_URI"] = db_url
# 而且還要加上這一段,否則會彈出警告
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

# 接收一個app,今後db便具備了app的功能
db = SQLAlchemy(app)


# 創建模型,確定要繼承,那麼繼承誰的,繼承自db.Module,至關於以前的Base。這裏操做簡化了,不須要咱們去建立了
class User(db.Model):
    __tablename__ = "user"
    # 能夠看到,以前須要導入的統統不須要導入了,都在db下面。不過本質上調用的仍是sqlalchemy模塊裏的類。
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)

    def __repr__(self):
        return f"{self.name}"


class Article(db.Model):
    __tablename__ = "article"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    title = db.Column(db.String(50), nullable=False)
    uid = db.Column(db.Integer, db.ForeignKey("user.id"))

    author = db.relationship("User", backref="articles")

    def __repr__(self):
        return f"{self.tit }"


# 如何查找數據呢?
# 首先咱們能夠想到db.session.query(User),這毫無疑問是能夠的,可是咱們的模型繼承了db.Model,那麼咱們有更簡單的方法
# 直接使用User.query便可,就等價於db.session.query(User)
users = User.query.all()
print(users)  # [guido]
'''
排序:User.query.order_by(User.id.desc()).all()
過濾:User.query.filter(User.id == 1).all()
修改:User.query.filter(User.id == 1).first().name == "guido van rossum"
刪除:刪除的話仍是要依賴db.session()的,找到user,db.session.delete(user)
'''
# 另外忘記提了,若是沒有指定表名,也就是__tablename__,那麼默認會將模型的名字轉成小寫當成表名。
'''
User-->user
但若是是駝峯命名法,UserModel-->user_model
可是不推薦這樣使用,仍是顯示的指定比較好,也符合python之禪,明言勝於暗喻。
'''

  

22.alembic數據庫遷移工具的使用

 alembic是SQLAlchemy做者所寫的一款用於作ORM與數據庫的遷移和映射的一個框架,相似於git。

首先確定要安裝,pip install alembic

這是個人項目結構,一個文件夾,裏面一個py文件ruby

進入文件夾裏面,輸入alembic init xxxx

 

 

接下來創建模型,這個是獨立於flask的,因此咱們此次仍是使用SQLAlchemy作演示

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

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"
engine = create_engine(f"{dialect}+{driver}://{username}:{password}@{hostname}:{port}/{database}")
Base = declarative_base(engine)


class People(Base):
    __tablename__ = "People"
    id = Column(Integer, primary_key=True, autoincrement=True)
    name = Column(String(20), nullable=False)  # 不可爲空
    age = Column(Integer, nullable=False)

    def __repr__(self):
        return f"{self.name}--{self.age}"

下面修改配置文件,alembic.ini

 接下來生成遷移文件,alembic revision --autogenerate -m "message",這裏的message是咱們註釋信息

 

下面就要更新數據庫了,將剛纔生成的遷移文件映射到數據庫。至於爲何須要遷移文件,那是由於沒法直接映射orm模型,須要先轉化爲遷移文件,而後才能映射到數據庫當中。

alembic upgrade head,將剛剛生成的遷移文件映射到數據庫當中

若是須要修改表的結構,不須要再drop_all,create_all了,若是裏面有大量數據,不可能清空以後從新建立。那麼在修改以後,直接再次生成遷移文件而後映射到數據庫就能夠了

 總結一下,就是五個步驟:

1.定義好本身的模型

2.使用alembic init 倉庫名,建立一個倉庫

3.修改兩個配置文件,在alembic.ini中配置數據庫的鏈接方式,在env中修改target_metada=Base.metadata

4.生成遷移文件

5.將遷移文件映射到數據庫

能夠看出,這和Django很是相似。若是之後修改模型,那麼重複4和5

 

23.alembic經常使用命令和經典錯誤解決辦法

經常使用命令:

  init:建立一個alembic倉庫

  revision:建立一個新的版本文件

  --autogenerate:自動將當前模型的修改,生成遷移腳本

  -m:本次遷移作了哪些修改,用戶能夠指定這個參數,方便回顧

  upgrade:將指定版本的遷移文件映射到數據庫中,會執行版本文件中的upgrade函數。若是有多個遷移腳本沒有被映射到數據庫中,那麼會執行多個遷移腳本

  [head]:表明最新的遷移腳本的版本號

  downgrade:降級,咱們每個遷移文件都有一個版本號,若是想退回之前的版本,直接使用alembic downgrade version_id

  heads:展現head指向的腳本文件

  history:列出全部的遷移版本及其信息

  current:展現當前數據庫的版本號

經典錯誤:

  FAILED:Target database is not up to date。緣由:主要是heads和current不相同。current落後於heads的版本。解決辦法:將current移動到head上,alembic upgrade head

  FAILED:can’t locate revision identified by 「78ds75ds7s」。緣由:數據庫中村的版本號不在遷移腳本文件中。解決辦法:刪除數據庫中alembic_version表的數據,而後從新執行alembic upgrade head

  執行upgrade head 時報某個表已經存在的錯誤。解決辦法:1.刪除version中全部的遷移文件的代碼,修改遷移腳本中建立表的代碼

 

24.Flask-SQLAlchemy下alembic的配置

從新建一個項目

config.py,用於存放配置文件

hostname = "localhost"
port = 3306
database = "satori"
username = "root"
password = "zgghyys123"
dialect = "mysql"
driver = "pymysql"

SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{username}:{password}@{hostname}:{port}/{database}"

  

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config
app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)


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


class User(db.Model):
    __tablename__ = "user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(50), nullable=False)


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

  alembic.ini

# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = alembic

# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s

# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =

# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40

# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false

# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false

# version location specification; this defaults
# to alembic/versions.  When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions

# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8

sqlalchemy.url = mysql+pymysql://root:zgghyys123@localhost:3306/satori


# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console
qualname =

[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine

[logger_alembic]
level = INFO
handlers =
qualname = alembic

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

  

from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
import sys, os
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
import start

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = start.db.metadata

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline():
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url, target_metadata=target_metadata, literal_binds=True)

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online():
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix='sqlalchemy.',
        poolclass=pool.NullPool)

    with connectable.connect() as connection:
        context.configure(
            connection=connection,
            target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()

if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

  生成遷移文件

 

映射到數據庫

 

25.flask-script講解

flask-script的做用是能夠經過命令行的方式來操做flask。例如經過命令來跑一個開發版本的服務器,設置數據庫,定時任務等等。要使用的話,首先要安裝,pip install flask-script

這個多了一個manage.py,能夠類比Django,若是咱們把全部東西都寫在一個py文件裏面,會很是的凌亂。那這個manage.py是幹什麼的呢?可讓咱們經過命令行來工做

start.py

from flask import Flask
app = Flask(__name__)


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


if __name__ == '__main__':
    manager.run()  # 注意這裏是manager.run(),不是app.run()

  

manage.py

from flask_script import Manager
from start import app

manager = Manager(app)


@manager.command
def hello():
    print("你好啊")
    

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

  

 

from flask_script import Manager
from start import app

manager = Manager(app)


@manager.command
def hello():
    return "你好啊"


@manager.option("--name", dest="username")
@manager.option("--age", dest="age")
def foo(username, age):
    # 相似於python裏的optparse,能夠見個人python經常使用模塊,裏面有介紹
    print(f"name={username}, age={age}")


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

  

不傳參,使用command,傳參使用option

 

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import config
app = Flask(__name__)
app.config.from_object(config)
db = SQLAlchemy(app)


class BackendUser(db.Model):
    __tablename__ = "backend_user"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(50), nullable=False)
    email = db.Column(db.String(50), nullable=False)


# alembic很強大,但這裏就不用了,直接使用create_all
db.create_all()


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


if __name__ == '__main__':
    app.run()
from flask_script import Manager
from start import app, BackendUser, db

manager = Manager(app)


@manager.command
def hello():
    return "你好啊"


@manager.option("--name", dest="username")
@manager.option("--email", dest="email")
def add_user(username, email):
    user = BackendUser(username=username, email=email)
    db.session.add(user)
    db.session.commit()


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

  之後就能夠經過終端來添加了

並且咱們還能夠模擬數據庫遷移,映射等等。Django的manage.py不就是這麼作的嗎?python manage.py makemigrations遷移,而後再python manage.py migrate映射。

 

26.flask-migrate

在實際的數據庫開發中,常常會出現數據表修改的行爲。通常咱們不會手動修改數據庫,而是去修改orm模型,而後再把模型映射到數據庫中。這個時候若是能有一個工具專門作這件事情就很是好了,而flask-migrate就是用來幹這個的。flask-migrate是基於alembic的一個封裝,並集成到flask當中,而全部的操做都是alembic作的,它能跟蹤模型的變化,並將模型映射到數據庫中。

from flask_migrate import Migrate, MigrateCommand

manager = Manager(app, db)  # 綁定app和db到flask_migrate

manager.add_command("db", MigrateCommand)  # 添加Migrate的全部子命令到db下

1.初始化一個環境:python manage.py db init

2.自動檢測模型,生成遷移腳本,python manage.py db migrate

3.將遷移腳本映射到數據庫中,python manage.py db upgrade

相關文章
相關標籤/搜索