SQLAlchemy

關于字符集

修改 /etc/my.cnf文件添加如下內(nèi)容:

[mysqld]
collation-server = utf8mb4_unicode_ci
init-connect='SET NAMES utf8mb4'
character-set-server = utf8mb4

[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4

之后重啟 mysql

查看字符

show variables like 'collation_%';

show variables like 'character_set_%';

查看數(shù)據(jù)庫 db_1803 的字符集、字符序

MariaDB [(none)]> use db_1803
Database changed
MariaDB [db_1803]> SELECT @@character_set_database, @@collation_database;
+--------------------------+----------------------+
| @@character_set_database | @@collation_database |
+--------------------------+----------------------+
| utf8                     | utf8_general_ci      |
+--------------------------+----------------------+

其他的一些設置方法:

修改數(shù)據(jù)庫的字符集

 use mydb     
 alter database mydb_name character set utf-8;

創(chuàng)建數(shù)據(jù)庫指定數(shù)據(jù)庫的字符集

create database mydb_name character set utf-8; 

一、介紹

SQLAlchemy 是 Python 中一個通過 ORM 操作數(shù)據(jù)庫的框架。

SQLAlchemy對象關系映射器提供了一種方法,用于將用戶定義的Python類與數(shù)據(jù)庫表相關聯(lián),并將這些類(對象)的實例與其對應表中的行相關聯(lián)。它包括一個透明地同步對象及其相關行之間狀態(tài)的所有變化的系統(tǒng),稱為工作單元,以及根據(jù)用戶定義的類及其定義的彼此之間的關系表達數(shù)據(jù)庫查詢的系統(tǒng)。

官方解釋:
它提供了一整套眾所周知的企業(yè)級持久性模式,旨在實現(xiàn)高效,高性能的數(shù)據(jù)庫訪問,并采用簡單的Pythonic域語言。

可以讓我們使用類和對象的方式操作數(shù)據(jù)庫,從而從繁瑣的 sql 語句中解脫出來。

ORM 就是 Object Relational Mapper 的簡寫,就是關系對象映射器的意思。

架構圖

千鋒教育云計算

Schema / Types 定義了類到表之間的映射框架(規(guī)則)

SQL Expression Language 封裝好的 SQL 語句

Engine 操作者

Connection Pooling 連接池

Dialect 根據(jù)用戶的配置,調(diào)用不同的數(shù)據(jù)庫 API(Oracle, postgresql, Mysql) 并執(zhí)行對應的 SQL語句

二、安裝

shell> pip3 install sqlalchemy

SQLAlchemy本身無法操作數(shù)據(jù)庫,其必須使用 pymsql 等第三方插件,從而實現(xiàn)對數(shù)據(jù)庫的操作,如:

pymysql 使用如下方式連接:

mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]

更多參考:https://docs.sqlalchemy.org/en/latest/dialects/index.html

使用 EngineConnectionPoolingDialect 對數(shù)據(jù)庫進行操作的流程:

Engine (SQLAlchemy 引擎) 使用 ConnectionPooling 連接數(shù)據(jù)庫,之后再通過 Dialect 執(zhí)行 SQL 語句。

三、連接數(shù)據(jù)庫

下面一連接 Mysql 為例來演示連接語法的具體實現(xiàn)

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from sqlalchemy import create_engine
 
engine = create_engine(
  "mysql+pymysql://root:123@172.16.153.160:3306/dbname?charset=utf8mb4", 
  echo=True, 
  max_overflow=5)

echo 標志是設置SQLAlchemy日志記錄的快捷方式。 啟用它后,我們將看到所有生成的SQL。

Max_overflow 指定了連接池的最大連接數(shù)。

create_engine() 的返回值是一個實例引擎,它代表了一個數(shù)據(jù)庫的核心接口。

此時的連接是惰性的,當create_engine()第一次返回的引擎,其實并沒有試圖連接到數(shù)據(jù)庫之中; 只有在第一次要求它對數(shù)據(jù)庫執(zhí)行任務時才會發(fā)生這種情況。比如使用了以下的任一方法:

engine.execute()

四、執(zhí)行原生 sql 語句

ret = engine.execute("select * from t1;")
# print(dir(engine))
# print(ret.fetchone())
print(ret.fetchall())

五、ORM 的基本操作

ORM 流程

ORM 操作的流程是,一個 Engine 使用 Schema Type 創(chuàng)建一個特定的結構對象,之后通過 SQL Expression Language 將該對象轉(zhuǎn)換成 SQL 語句, 接著使用 ConnectionPling 連接數(shù)據(jù)庫,最后通過 Dialect 執(zhí)行 SQL 語句,并獲取結果。

1. 聲明一個映射類

官方解釋:

使用ORM時,配置過程首先描述我們將要處理的數(shù)據(jù)庫表,然后定義我們自己的類,這些類將映射到這些表。在現(xiàn)代SQLAlchemy中,這兩個任務通常使用稱為Declarative的系統(tǒng)一起執(zhí)行,這允許我們創(chuàng)建包含指令的類,以描述它們將映射到的實際數(shù)據(jù)庫表。

大白話:

創(chuàng)建一個類,一個類對應了一個數(shù)據(jù)庫中的一張表,類的數(shù)據(jù)屬性對應了表中的字段名,這個類稱為映射類。

根據(jù)映射類創(chuàng)建出一個一個的對象,每個對象對應了表中的一條實際的數(shù)據(jù)。

千鋒云計算

使用Declarative系統(tǒng)映射的類是根據(jù)基類定義的,換句話說每個映射類需要繼承這個基類。我們使用declarative_base() 函數(shù)可以創(chuàng)建這個基類,如下所示:

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

現(xiàn)在我們有了一個Base,我們可以根據(jù)它定義任意數(shù)量的映射類。

我們將從一個名為 teacher 的表開始。

示例表結構

teacher

id name age city
1 yangge 18 北京
2 qiangge 19 北京
3 shark 19 北京

Teacher將是我們映射此表的類。

在類中,我們定義了有關我們將要映射的表的詳細信息,主要是表名,以及列的名稱和數(shù)據(jù)類型:

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

Base = declarative_base()

class Teacher(Base):
    __tablename__ = 'teacher'
    id = Column(Integer, primary_key=True)
    name = Column(String(12))
    age = Column(String(2))
    city = Column(String(16))
    
    def __repr__(self):
        tpl = "Teacher(id={}, name={}, age={}, city={})"
        return tpl.format(self.id, self.name,
                          self.age, self.city)

__repr__ 方法定義了一個對象的比較易讀的顯式方式

使用 Declarative 的類至少需要一個__tablename__屬性,并且至少有一個 Column屬于主鍵。

2. 創(chuàng)建表到數(shù)據(jù)庫中

我們可以使用MetaData 為所有數(shù)據(jù)庫中尚不存在的表向數(shù)據(jù)庫發(fā)出CREATE TABLE語句。

下面,我們調(diào)用該MetaData.create_all()方法,將我們Engine 作為數(shù)據(jù)庫連接源傳遞。我們將看到首先發(fā)出特殊命令以檢查teacher表的存在,然后是實際的語句:CREATE TABLE
`

Base.metadata.create_all(engine)

執(zhí)行成功后可以檢查是否創(chuàng)建成功

終端檢查結果

MariaDB [(none)]> use db_1803
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [db_1803]> show tables;
+-------------------+
| Tables_in_db_1803 |
+-------------------+
| teacher           |
+-------------------+
1 row in set (0.00 sec)

MariaDB [db_1803]>

3. 創(chuàng)建映射類的實例

yg_teacher = Teacher(name='yangge', 
               age='18', 
               city='BeiJing')

此時,實例對象只是在此刻環(huán)境的內(nèi)存中有效,并沒有在表中真正生成數(shù)據(jù)。

要想生成數(shù)據(jù)到表中,需要創(chuàng)建一個和數(shù)據(jù)庫溝通的會話對象,利用這個會話對象對數(shù)據(jù)庫中的表進行操作(增加、更新,刪除、查詢)

4. 創(chuàng)建會話

現(xiàn)在準備開始與數(shù)據(jù)庫交談,需要使用一個引擎的實例來創(chuàng)建一個 Session類的實例。

from sqlalchemy.orm import sessionmaker

# 把當前的引擎綁定給這個會話
Session = sessionmaker(bind=engine) 

# 實例化
session = Session()

5. 通過 Session 實例的方法進行操作表中的數(shù)據(jù)

剛才我們已經(jīng)創(chuàng)建了一條數(shù)據(jù)的實例了,要想把這條數(shù)據(jù)實例真正的增加到數(shù)據(jù)庫中的teacher 表中,需要先把這條數(shù)據(jù)對象添加到會話實例中,再通過會話實例的 commit() 方法提交事務到數(shù)據(jù)庫,此時,數(shù)據(jù)庫的 teacher 表中才會有這條數(shù)據(jù)。

添加單條數(shù)據(jù)到 session 中

session.add(yg_teacher)

此時這個數(shù)據(jù)并沒有被同步的數(shù)據(jù)庫中,而是處于等待的狀態(tài)。

執(zhí)行執(zhí)行了 commit() 方法后,才會真正在數(shù)據(jù)表中創(chuàng)建數(shù)據(jù)。

如果我們查詢數(shù)據(jù)庫,則首先刷新所有待處理信息,然后立即發(fā)出查詢。

our_teacher = session.query(Teacher).filter_by(
    name='yangge').first() 
print(our_teacher)

注意:此時得到的結果是并不是 數(shù)據(jù)庫表中的最終數(shù)據(jù),而是映射類的一個對象

6. 提交

session.commit()

此時,數(shù)據(jù)真正的被寫入到數(shù)據(jù)庫中了

增加多條數(shù)據(jù)

session.add_all([
    Teacher(name='qiangge', age='19',city='北京'),
    Teacher(name='shark', age='19',city='北京'),
])

一起提交

session.commit()

7. 關于回滾

在 commit() 之前,對實例對象的屬性所做的更改,可以進行回滾。回到更改之前。

session.rollback()

示例:

假如目前向表中添加一條新的數(shù)據(jù),同時查詢出一條已有的數(shù)據(jù),并且把已有的數(shù)據(jù)某個字段的值進行更改。

# 新增數(shù)據(jù)
session.add(Teacher(name='shark2', 
                    age='18',
                    city='ZhengZhou'))

# 修改已有數(shù)據(jù)的字段值
shark=session.query(Teacher).filter_by(name='shark').first()
print(shark.age)
shark.age = '28'
shark.name = 'shark3'

# 查詢操作結果
two_user = session.query(Teacher).filter(Teacher.name.in_(['shark2','shark3'])).all()
print(two_user)

#輸出的結果
[Teacher(id=4, name=shark, age=38, city='北京'),
 Teacher(id=23, name=shark2, age=18, city=ZhengZhou)]

此時數(shù)據(jù)的更改只是發(fā)生于會話對象的事務中,并沒有發(fā)生在數(shù)據(jù)庫中。

現(xiàn)在進行回滾操作

session.rollback()

之后再次進行查詢操作會發(fā)現(xiàn)已恢復到修改之前

[Teacher(id=4, name=shark, age=23, city='北京')]

可以看出這里提到的回滾,本質(zhì)上只是把某一條數(shù)據(jù)(也就是映射類的實例)從內(nèi)存中刪除而已。

六、ORM 進階操作

1. 導入模塊

# 創(chuàng)建連接相關
from sqlalchemy import create_engine

# 和 sqlapi 交互,執(zhí)行轉(zhuǎn)換后的 sql 語句,用于創(chuàng)建基類
from sqlalchemy.ext.declarative import declarative_base

# 創(chuàng)建表中的字段(列)
from sqlalchemy import Column

# 表中字段的屬性
from sqlalchemy import Integer, String, ForeignKey
from sqlalchemy import UniqueConstraint, Index
from sqlalchemy.orm import sessionmaker, relationship

2. 連接數(shù)據(jù)庫

# 創(chuàng)建連接對象,并使用 pymsql 引擎
conn_str = "mysql+pymysql://{user}:{pwd}@{}:3306/{db_name}?charset='utf8mb4"
connect_info = conn_str.format(user='root',
                              pwd='123456',
                              db_name='db_1803')

engine = create_engine(connect_info, max_overflow=5)

3. 創(chuàng)建表

# 創(chuàng)建基類
Base = declarative_base()

# 創(chuàng)建單表
class Person(Base):
    __tablename__ = 'person'  # 表名
    id = Column(Integer, primary_key=True)
    # 必須指定長度,在PostgreSQL上不需要
    name = Column(String(32))
    age = Column(Integer)      # 整型

    __table_args__ = (
    # 設置聯(lián)合唯一
    UniqueConstraint('id', 'name', name='uix_id_name'),
    
    # 建立索引   
    Index('uix_id_name', 'name'),
    )


def init_db():
    """創(chuàng)建所有定義的表到數(shù)據(jù)庫中"""
    Base.metadata.create_all(engine)


def drop_db():
    """從數(shù)據(jù)庫中刪除所有定義的表"""
    Base.metadata.drop_all(engine)
    
# 執(zhí)行創(chuàng)建表
#init_db()
    
# 創(chuàng)建會話實例對象
Session = sessionmaker(bind=engine)
session = Session()

4. 查詢數(shù)據(jù)

a. 結果集內(nèi)看到的是對象 、還是數(shù)據(jù)

? query(類名) 返回的就是對象

? query(類名.字段名) 返回的就是含有數(shù)據(jù)的元組對象

# 所有數(shù)據(jù),且結果集中是一個一個的對象
ret = session.query(Teacher).all()
# 結果 [obj1, obj2, obj3]

#  指定字段查詢,返回所有的數(shù)據(jù),是一個列表,列表內(nèi)是一個一個的元組
ret = session.query(Teachers.name, Teachers.arg).all()
# 結果 [('yangge', '18'), ('qiangge', '19'), ('shark', '23')]

b. 迭代查詢結果集

for name, age, in session.query(Teacher.name, Teacher.age):
    print(name, age)
    
# 輸出結果
yangge 18
qiangge 19
shark 23

c. 給列起別名

可以使用 label() 給每個列名起別名

for row in session.query(Teacher.name.label('t_name')).all():
    print(row.t_name)

d. 條件查詢

filter_by() 接收的是關鍵字參數(shù)

filter() 允許使用 python 的比較或關系運算符,實現(xiàn)更靈活的查詢

# filter_by()
ret = session.query(Teacher).filter_by(name='yangge').first()
# 結果 Teacher(id=2, name=yangge, age=18, city=BeiJing)

# filter()
ret = session.query(Teacher).filter(Teacher.age>'20').first()
# 結果 Teacher(id=4, name=shark, age=23, city='北京')
  1. 關系運算符的查詢

以下適用于 filter()

以下查詢都是以這個查詢對象為基礎的過濾

query = session.query(Teacher)
  • 相等
query.filter(Teacher.name == 'shark').all()
  • 不相等
query.filter(Teacher.name != 'shark').all()
  • LIKE

    在某些數(shù)據(jù)庫中,這個可能會不區(qū)分大小寫,也有可能區(qū)分大小寫。

query.filter(Teacher.name.like('%sha%')).all()

  • ILIKE

    確保忽略大小寫, 大部分數(shù)據(jù)庫不支持 ilike

query.filter(Teacher.name.ilike('Sha___')).all()
  • IN
query.filter(Teacher.id.in_([2,3])).all()
  • NOT IN

    使用波浪號~ 表示非

query.filter(~Teacher.id.in_([2,3])).all()
  • BETWEEN

    使用 between 表示范圍

query.filter(Teacher.id.between(1, 3)).all()
  • IS NULL

    數(shù)據(jù)庫中的空字符串不是 NUll , python 中的 None 存到數(shù)據(jù)庫中是 NULL。

query.filter(Teacher.name == None).all()
# 或者
query.filter(Teacher.name.is_(None)).all()
  • IS NOT NULL
query.filter(Teacher.name != None).all()
# 或者
query.filter(Teacher.name.isnot(None)).all()
  • AND
# 使用 and_()
from sqlalchemy import and_
query.filter(and_(Teacher.name == 'shark', 
                  Teacher.city == '北京')).all()

# 或者使用逗號
query.filter(Teacher.name == 'shark', 
             Teacher.city == '北京').all()
  • OR
from sqlalchemy import or_
query.filter(or_(Teacher.name == 'shark', 
                 Teacher.name == 'xiguatian')).all()
  • AND 和 OR 的綜合使用
query.filter(
    or_(
        Teacher.id <= 2,
        and_(Teacher.name == 'shark', Teacher.id > 3)
    )).all()

e. 控制返回的查詢結果集

all() 返回的是所有的結果集,是列表

first() 返回的是所有結果集中的第一個數(shù)據(jù)

ret = session.query(Teacher).all()
# 結果 [obj1,obj2,obj3]

ret = session.query(Teacher).first()
# 結果 obj1
  • one

? 提取結果集中的所有數(shù)據(jù),假如沒有或者數(shù)據(jù)多于一條則會報錯

? 找到后返回的是一個元組

  • one_or_none

    one() 一樣,但是沒找到返回 None

  • scalar

? scalar() 調(diào)用 one() 方法,找不到,返回 None

? 找到后返回的是赤裸裸的數(shù)據(jù)

  • limit
  • 使用 python 的切片控制輸出多少行
session.query(Teacher).all()[0:2]
  • order by 排序

?

# 正序
session.query(Teacher).order_by(Teacher.name).all()

# 倒序
session.query(Teacher).order_by(Teacher.name.desc()).all()

# 先按名字排序,假如有相同的再安裝 id 排序
session.query(Teacher).order_by(Teacher.name,       
                                Teacher.id.desc()).all()
  • count 統(tǒng)計
session.query(Teacher).filter(Teacher.age=='18').count()

f. 嵌套的查詢

# 嵌套,從最內(nèi)層的查詢結果中再查詢想要的數(shù)據(jù)
session.query(Teacher).filter(
    Teacher.id.in_(
        session.query(Teacher.id
                      ).filter_by(
            name='yangge'))).all()

g. 分組統(tǒng)計查詢

from sqlalchemy.sql import func
# 統(tǒng)計表中所有的數(shù)據(jù)
session.query(func.count('*')).select_from(Teacher).first()

# 以年齡分組,并統(tǒng)計每組的數(shù)據(jù)數(shù)量
session.query(func.count(Teacher.age),Teacher.age
                    ).group_by(Teacher.age).all()

# 以年齡為分組,并統(tǒng)計每組的最大/最小 id 號,年齡總和/平均值,
session.query(
    func.max(Teacher.id),
    func.min(Teacher.id),
    func.sum(Teacher.age),
    func.avg(Teacher.age),
    Teacher.id
                    ).group_by(Teacher.age).all()

# 從分組的數(shù)據(jù)中再查找需要的數(shù)據(jù)
session.query(
    func.max(Teacher.id),
    func.min(Teacher.age),
    func.sum(Teacher.age),
    func.avg(Teacher.age),

    Teacher.id
    ).group_by(Teacher.age
               ).having(func.min(Teacher.id) > 2).all()

h. 組合

# 再創(chuàng)建一個表
class Student(Base):
    __tablename__ = 'student'
    id = Column(Integer,primary_key=True)
    name = Column(String(12))
    age = Column(String(2))
    city = Column(String(16))

# 組合  用一條數(shù)據(jù)將兩個表中的要查詢的數(shù)據(jù)組合在一張表里展示出來
q1 = session.query(Teacher.name).filter(Teacher.id > 2)
q2 = session.query(Student.name).filter(Student.id < 2)
## 去重
ret = q1.union(q2).all()
## 不去重 
q1 = session.query(Teacher.name).filter(Teacher.id > 2)
q2 = session.query(Student.name).filter(Student.id < 2)
ret = q1.union_all(q2).all()

七、更新數(shù)據(jù)

session.query(Teacher).filter(Teacher.id == 3).update(
    {"name" : "xiguatian"})

session.query(Teacher).filter(Teacher.id > 3).update(
    {Teacher.name: Teacher.name + "_云計算講師"},
    synchronize_session=False) 
# 不同步,數(shù)據(jù)的更新在 commit 之后

session.query(Teacher).filter(Teacher.id > 2).update(
    {"age": int(Teacher.age + 1)}, 
    synchronize_session="evaluate") 

session.commit()

八、刪除數(shù)據(jù)

session.query(Teacher).filter(Teacher.id > 4).delete()
session.commit()

九、連表操作

INNER JOIN 內(nèi)連接
LEFT JOIN 左連接
RIGHT JOIN 右連接
FULL JOIN 完全連接

  • 內(nèi)連接 顯示的是兩個表的兩列數(shù)據(jù)匹配的相關數(shù)據(jù),并且返回的是組合后的數(shù)據(jù)
  • 外連接 分為左外連接、右外連接和全連接,mysql中不支持全連接,所以以左外連接為例來討論區(qū)別:
    • 左外連接是以左表為主,返回的是只在join關鍵字前面這張表(即左表)中,所有符合 where 子句的數(shù)據(jù),不管是否符合連接條件。即這張表的內(nèi)容都要全部顯示。而后面的那張表只顯示匹配連接條件的數(shù)據(jù)。
    • 右外聯(lián)接則剛好和左外連接相反。

1. 一對多

示例表:

groups

id name full_name cn_name
1 Other 默認組
2 PM Product Manager 產(chǎn)品經(jīng)理
3 RD Research and Development engineer 開發(fā)
4 QA Qualtiy Assurance 測試
5 OP Operator 運維
6 DBA Database Administrator 數(shù)據(jù)庫管理員

users

id name goup_id
1 Yangge 2
2 Tom 2
3 Rose 3
4 Shark 3
5 Xiguatian 5
6 Jack 6
7 new_user 1

創(chuàng)建表的原生語句

CREATE TABLE groups (
    id INTEGER NOT NULL AUTO_INCREMENT, 
    name VARCHAR(5) NOT NULL, 
    full_name VARCHAR(64), 
    cn_name VARCHAR(64), 
    PRIMARY KEY (id), 
    UNIQUE (name)
)

CREATE TABLE users (
    id INTEGER NOT NULL AUTO_INCREMENT, 
    name VARCHAR(32) NOT NULL, 
    group_id INTEGER, 
    PRIMARY KEY (id), 
    FOREIGN KEY(group_id) REFERENCES groups(id)
)
  1. 創(chuàng)建表
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship

engine = create_engine("mysql+pymysql://root:123456@172.16.153.160:3306/db_1802", echo=True, max_overflow=5)
Base = declarative_base()
class Groups(Base):
    __tablename__= 'groups'
    id = Column(Integer, primary_key=True)
    name = Column(String(12), 
                  unique=True,     # 值必須唯一
                  nullable=False)  # 不允許為空
    full_name = Column(String(64), nullable=True)
    cn_name = Column(String(64))

class Users(Base):
    __tablename__= 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(32), 
                  unique=True,
                  index=True,  # 此列建立索引
                  nullable=False)
    group_id = Column(Integer, 
                      ForeignKey('groups.id'),  # 定義外鍵
                      default=1)  # 默認值
    
    # 下面此列與創(chuàng)建表無關,僅用于查詢使用,group 用于正向查詢,user 用于反向查詢
    group = relationship('Groups',  # 字符串類型的映射類名稱。 
                         backref='user')
    
# 創(chuàng)建所有的表結構
Base.metadata.create_all(engine)
  1. 添加數(shù)據(jù)
from sqlalchemy.orm import sessionmaker

# 把當前的引擎綁定給這個會話
Session = sessionmaker(bind=engine) 

# 實例化
session = Session()

session.add_all([
    Groups(name='Other',),
    Groups(name='PM',full_name='Product Manager', cn_name='產(chǎn)品經(jīng)理'),
    Groups(name='RD',full_name='Research and Development engineer', cn_name='開發(fā)'),
    Groups(name='QA',full_name='Product Manager', cn_name='測試'),
    Groups(name='OP',full_name='Product Manager', cn_name='運維'),
    Groups(name='DBA',full_name='Product Manager', cn_name='數(shù)據(jù)庫管理員'),
])

session.commit()

session.add_all([
    Users(name='Yangge', group_id=2),
    Users(name='Tom', group_id=2),
    Users(name='Rose', group_id=3),
    Users(name='shark', group_id=3),
    Users(name='xiguatian', group_id=5),
    Users(name='Jack', group_id=6),
    Users(name='new_user'),
])

session.commit()

補充

print(session.query(Groups).join(Users).all())
  1. 查詢數(shù)據(jù)

問題一: 查詢用戶 Shark 是屬于哪個組 ,打印出組名?

user_obj = session.query(Users).filter_by(name='Shark').scalar()
print(user_obj.group.name)

scalar() 假如存在,必須得到一個對象,不存在則返回 None

問題二: 查詢組 RD 組中的成員都有誰 ?

group_obj = session.query(Groups).filter_by(name='RD').one()
for u_obj in group_obj.user:
    print(u_obj.name)
  1. 更新數(shù)據(jù)

現(xiàn)在把用戶 new_user 加入到 測試組 QA

g_id = session.query(Groups.id).filter(Groups.name=='QA').scalar()
user_obj = session.query(Users).filter(Users.group_id == 1).update(
    {Users.group_id: g_id}
)
session.commit()
  1. 刪除數(shù)據(jù)

刪除用戶 jack , 之后在刪除組 DBA

# 刪用戶
session.query(Users).filter_by(name='jack').delete()
session.commit()
# 刪組
query_obj = session.query(Groups).filter_by(name='DBA')
g_obj = query_obj.scalar()
u_obj = g_obj.user
if not u_obj:
    query_obj.delete()
    session.commit()

十、多對多 (擴展)

表示例

server

id hostname ip
1 web 172.16.153.10
2 elk 172.16.153.10

sys_user

id name password key
1 root
2 shark

host_to_sys_user

id host_id sys_user_id
1 1 1
2 1 2
3 2 1
  1. 創(chuàng)建表
# 多對多
# 一個服務器中可以有個用戶
# 同名的用戶可以出現(xiàn)在多臺服務器中,也就是多臺服務器可能存在相同的用戶名
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy import Column, Integer, String, ForeignKey

# 需要安裝 sqlalchemy-utils 和 passlib
# pip3 install sqlalchemy-utils passlib
from sqlalchemy_utils import IPAddressType, PasswordType

from sqlalchemy.orm import relationship

engine = create_engine("mysql+pymysql://root:123456@172.16.153.160:3306/db_1803",
                       echo=True,
                       max_overflow=5)
Base = declarative_base()

class ServersToSysUsers(Base):
    __tablename__ = 'servers_to_sys_users'
    nid = Column(Integer, primary_key=True, autoincrement=True)
    server_id = Column(Integer, ForeignKey('servers.id'))
    group_id = Column(Integer, ForeignKey('sys_users.id'))
    
    # 方式一
    #servers = relationship("Servers", backref='ser_to_user')
    #sys_users = relationship("SysUsers", backref='ser_to_user')

class Servers(Base):
    __tablename__ = 'servers'

    id = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(64), unique=True, nullable=False)
    ip = Column(IPAddressType)  # 輸入字符串,輸出時是對象
    
    # 方式二
    sys_users = relationship('SysUsers',
                           secondary=ServersToSysUsers.__table__,
                           backref='servers')


class SysUsers(Base):
    __tablename__ = 'sys_users'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True, nullable=False)
    password = Column(PasswordType(
        schemes=[
            'pbkdf2_sha512',
            'md5_crypt'
        ],
        deprecated=['md5_crypt']))
    key = Column(String(128),nullable=True)


Base.metadata.create_all(engine)
  1. 添加數(shù)據(jù)
session.add_all([
    Servers(hostname='web', ip='1.1.1.1'),
    Servers(hostname='elk', ip='1.1.1.2'),
    SysUsers(name='root',password='upsa'),
    SysUsers(name='shark',password='upsa'),
])
session.commit()

session.add_all([
    ServersToSysUsers(server_id=1,sys_users_id=1),
    ServersToSysUsers(server_id=1,sys_users_id=2),
    ServersToSysUsers(server_id=2,sys_users_id=1),
])
session.commit()
  1. 查詢數(shù)據(jù)

問題一: 查詢服務器 web 下的所有用戶名和密碼

# 方式一的查詢
query = session.query(Servers).filter_by(hostname='web')
if query:
    for obj in query.scalar().ser_to_user:
        print(obj.sys_users.name)

# 方式二的查詢
query = session.query(Servers).filter_by(hostname='web')
if query:
    for u_obj in query.scalar().sys_users:
        print(u_obj.name, u_obj.password)

問題二: 查詢用戶 root 存在于哪些服務器上

# 方式一的查詢 (作業(yè)練習)

# 方式二的查詢:
query = session.query(SysUsers).filter_by(name='root')
if query:
    for server_obj in query.scalar().servers:
        print(server_obj.hostname,server_obj.ip)
  1. 更新數(shù)據(jù)(作業(yè)練習)

增加一條服務器信息

主機名: mysql

IP 地址: 1.1.1.100

可以登錄的賬戶: db_admin, select, db_op

同時完善其他表

  1. 刪除數(shù)據(jù)(作業(yè)練習)

刪除服務器主機名為: mysql 的相關信息

十一、 擴展功能

使用 sqlalchemy-utils 會得到更多的功能

pip3 install sqlalchemy-utils

更多字段的數(shù)據(jù)類型

http://sqlalchemy-utils.readthedocs.io/en/latest/data_types.html

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容