關于字符集
修改 /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
使用 Engine
、ConnectionPooling
、Dialect
對數(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='北京')
- 關系運算符的查詢
以下適用于 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)
)
- 創(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)
- 添加數(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())
- 查詢數(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)
- 更新數(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()
- 刪除數(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 |
- 創(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)
- 添加數(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()
- 查詢數(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)
- 更新數(shù)據(jù)(作業(yè)練習)
增加一條服務器信息
主機名: mysql
IP 地址: 1.1.1.100
可以登錄的賬戶: db_admin, select, db_op
同時完善其他表
- 刪除數(shù)據(jù)(作業(yè)練習)
刪除服務器主機名為: mysql
的相關信息
十一、 擴展功能
使用 sqlalchemy-utils
會得到更多的功能
pip3 install sqlalchemy-utils
更多字段的數(shù)據(jù)類型
http://sqlalchemy-utils.readthedocs.io/en/latest/data_types.html