目錄:
- ORM介紹
- sqlalchemy安裝
- sqlalchemy基本使用
- 多外鍵關聯
- 多對多關系
- 表結構設計作業
1. ORM介紹
orm英文全稱object relational mapping,就是對象映射關系程序,簡單來說我們類似python這種面向對象的程序來說一切皆對象,但是我們使用的數據庫卻都是關系型的,
為了保證一致的使用習慣,通過orm將編程語言的對象模型和數據庫的關系模型建立映射關系,這樣我們在使用編程語言對數據庫進行操作的時候可以直接使用編程語言的對象模型進行操作就可以了,
而不用直接使用sql語言。
orm的優點:
- 隱藏了數據訪問細節,“封閉”的通用數據庫交互,ORM的核心。他使得我們的通用數據庫交互變得簡單易行,並且完全不用考慮該死的SQL語句。快速開發,由此而來。
- ORM使我們構造固化數據結構變得簡單易行。
缺點:
- 無可避免的,自動化意味着映射和關聯管理,代價是犧牲性能(早期,這是所有不喜歡ORM人的共同點)。
- 現在的各種ORM框架都在嘗試使用各種方法來減輕這塊(LazyLoad,Cache),效果還是很顯著的。
2. sqlalchemy安裝
在Python中,最有名的ORM框架是SQLAlchemy。用戶包括openstack\Dropbox等知名公司或應用,主要用戶列表http://www.sqlalchemy.org/organizations.html#openstack
Dialect用於和數據API進行交流,根據配置文件的不同調用不同的數據庫API,從而實現對數據庫的操作,如:
MySQL-Python
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
MySQL-Connector
mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
更多詳見:http://docs.sqlalchemy.org/en/latest/dialects/index.html
安裝sqlalchemy
pip install SQLAlchemy<br><br>pip install pymysql #由於mysqldb依然不支持py3,所以這里我們用pymysql與sqlalchemy交互
3.sqlalchemy基本使用
sql原生語句創建表:
CREATE TABLE user ( id INTEGER NOT NULL AUTO_INCREMENT, name VARCHAR(32), password VARCHAR(64), PRIMARY KEY (id) )
orm,實現上面同樣的功能,代碼如下:
第1種創建表結構的方法:
table_structure.py
#Author:Yun
import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String engine = create_engine("mysql+pymysql://root:yun258762@localhost/test_db", encoding='utf-8', echo=True) Base = declarative_base() # 生成orm基類
class User(Base): __tablename__ = 'user' # 表名
id = Column(Integer, primary_key=True) name = Column(String(32)) password = Column(String(64)) Base.metadata.create_all(engine) # 創建表結構
懶惰連接
當create_engine()第一次返回時,引擎實際上還沒有嘗試連接到數據庫; 只有在第一次要求它對數據庫執行任務時才會發生這種情況。
第2種創建表結構的方法:
#Author:Yun
from sqlalchemy import Table, MetaData, Column, Integer, String, ForeignKey from sqlalchemy.orm import mapper from sqlalchemy import create_engine metadata = MetaData()#生成metadata類 #創建user表,繼承metadata類
#Engine使用Schama Type創建一個特定的結構對象 user = Table('new_user', metadata, Column('id', Integer, primary_key=True), Column('name', String(50)), Column('fullname', String(50)), Column('password', String(12)) ) class User(object): def __init__(self, name, fullname, password): self.name = name self.fullname = fullname self.password = password #表元數據是使用Table構造單獨創建的,然后通過mapper()函數與User類相關聯
mapper(User, user) # 通過ConnectionPooling 連接數據庫
engine = create_engine("mysql+pymysql://root:yun258762@localhost/test_db?charset=utf8", max_overflow=5, echo=True) # 通過Dialect執行SQL
metadata.create_all(engine) #創建表結構
事實上,我們用第1種方式創建的表就是基於第2種方式的再封裝。
表已經創建好了開始數據操作:
增加數據:

#Author:Yun import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor user_obj = table_structure.User(name="王昭君", password="123456") # 生成你要創建的數據對象 user_obj2 = table_structure.User(name="韓信", password="123456") # 生成你要創建的數據對象 #print(user_obj.name, user_obj.id) # 此時還沒創建對象呢,不信你打印一下id發現還是None #print(user_obj.name, user_obj.id) # 此時也依然還沒創建 Session.add(user_obj) # 把要創建的數據對象添加到這個session里, 一會統一創建 Session.add(user_obj2) # 把要創建的數據對象添加到這個session里, 一會統一創建 Session.commit() # 現此才統一提交,創建數據
效果:

mysql> select * from user; +----+-------------+----------+ | id | name | password | +----+-------------+----------+ | 1 | 王昭君 | 123456 | | 2 | 韓信 | 123456 | +----+-------------+----------+ 2 rows in set (0.00 sec)
查找數據:

#Author:Yun import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor #查找所有數據 data1 = Session.query(table_structure.User).filter_by().all() #查找第一個數據 data2 = Session.query(table_structure.User).filter_by().first() #查找id=1的數據 data3= Session.query(table_structure.User).filter_by(id=1).all() #查找id=2的數據 data4 = Session.query(table_structure.User).filter(table_structure.User.id ==2).all() #查找id>1的所有數據 data5 = Session.query(table_structure.User).filter(table_structure.User.id>1).all() #多條件查詢,id>0&id<2的數據 data6 = Session.query(table_structure.User).filter(table_structure.User.id>0).filter(table_structure.User.id<2).all() print('data1:',data1) print('data2:',data2) print('data3:',data3) print('data4:',data4) print('data5:',data5) print('data6:',data6)
效果:

data1: [<1 name:王昭君>, <2 name:韓信>] data2: <1 name:王昭君> data3: [<1 name:王昭君>] data4: [<2 name:韓信>] data5: [<2 name:韓信>] data6: [<1 name:王昭君>]
修改數據:

#Author:Yun #修改數據 import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor data = Session.query(table_structure.User).filter_by(name="王昭君").first()#修改數據 data.name = "妲己"#將王昭君修改為妲己 data.password = '654321' Session.commit()
效果:

mysql> select * from user; +----+-------------+----------+ | id | name | password | +----+-------------+----------+ | 1 | 妲己 | 654321 | | 2 | 韓信 | 123456 | +----+-------------+----------+ 2 rows in set (0.00 sec)
回滾:

#Author:Yun #回滾,類似於事物 import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor user_obj = table_structure.User(name="老夫子", password="123456") # 生成你要創建的數據對象 user_obj2 = table_structure.User(name="雅典娜", password="123456") # 生成你要創建的數據對象 Session.add(user_obj) # 把要創建的數據對象添加到這個session里, 一會統一創建 Session.add(user_obj2) # 把要創建的數據對象添加到這個session里, 一會統一創建 print(Session.query(table_structure.User.name).filter(table_structure.User.name.in_(['老夫子','雅典娜'])).all()) print('--------------after rollback---------------:') Session.rollback() print(Session.query(table_structure.User.name).filter(table_structure.User.name.in_(['老夫子','雅典娜'])).all()) Session.commit()#現在才統一提交修改數據

[('老夫子',), ('雅典娜',)] --------------after rollback---------------: []

#Author:Yun #刪除數據 import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor data = Session.query(table_structure.User).filter_by(name="妲己").first()#獲取數據 Session.delete(data)#刪除數據 Session.commit()

mysql> select * from user; +----+-------------+----------+ | id | name | password | +----+-------------+----------+ | 2 | 韓信 | 123456 | +----+-------------+----------+ 1 rows in set (0.00 sec)

#Author:Yun import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor print(Session.query(table_structure.User).filter(table_structure.User.name.in_(['韓信'])).count())#mysql 默認大小寫是一樣的,如 'hello'=='HELLO' Session.commit()#現在才統一提交修改數據

1

#Author:Yun #統計 import table_structure#導入表結構模塊 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_structure.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() # 生成session實例#cursor from sqlalchemy import func print(Session.query(table_structure.User.name,func.count(table_structure.User.name)).group_by(table_structure.User.name).all()) Session.commit()#現在才統一提交修改數據
效果:

[('韓信', 1)]

#Author:Yun #連表操作 import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String,DATE,ForeignKey from sqlalchemy.orm import sessionmaker,relationship engine = create_engine("mysql+pymysql://root:yun258762@localhost/test_db", encoding='utf-8') Base = declarative_base() # 生成orm基類 class Student(Base): __tablename__ = 'student2' # 表名 id = Column(Integer, primary_key=True) name = Column(String(32),nullable=False) register_date = Column(DATE,nullable=False) def __repr__(self): return "<%s name:%s>" % (self.id,self.name) class stu_record(Base): __tablename__ = 'study_record' id = Column(Integer,primary_key=True) day = Column(Integer,nullable=False) status = Column(String(32),nullable=False) stu_id = Column(Integer,ForeignKey('student2.id')) # stu_obj = query(id=1) # student = query(Student).filter(Student.id == stu_obj.stu_id).first() #只是在內存中建立關聯關系 #允許在stu_record表中用字段self.student.name查詢Student表中的數據 #stu_obj2 = Session.query(Student).filter(Student.name == 'luban1').first() #允許在Student表中用字段stu_obj2.my_study_record查詢stu_record表中的數據 student = relationship('Student',backref='my_study_record') def __repr__(self): return "<%s day:%s status:%s>" % (self.student.name,self.day,self.status) Base.metadata.create_all(engine) # 創建表結構
為表添加數據:

#Author:Yun import table_struct#導入剛剛創建的表結構 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_struct.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class() #生成session實例#cursor s1 = table_struct.Student(name='學員A',register_date='2015-11-29') s2 = table_struct.Student(name='學員B',register_date='2016-11-29') s3 = table_struct.Student(name='學員C',register_date='2017-11-29') s4 = table_struct.Student(name='學員D',register_date='2018-11-29') stu_obj1 = table_struct.stu_record(day=1,status='Yes',stu_id=5) stu_obj2 = table_struct.stu_record(day=2,status='Yes',stu_id=5) stu_obj3 = table_struct.stu_record(day=2,status='Yes',stu_id=6) stu_obj4 = table_struct.stu_record(day=3,status='No',stu_id=6) Session.add_all([s1,s2,s3,s4,stu_obj1,stu_obj2,stu_obj3,stu_obj4]) Session.commit()#現在才統一提交修改數據

#Author:Yun #連表 import table_struct#導入剛剛創建的表結構 from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=table_struct.engine) # 創建與數據庫的會話session class ,注意,這里返回給session的是個class,不是實例 Session = Session_class()#生成session實例#cursor #跨表查詢 stu_obj = Session.query(table_struct.Student).filter(table_struct.Student.name=='學員A').first() print(stu_obj.my_study_record) Session.commit()#現在才統一提交修改數據

[<學員A day:1 status:Yes>, <學員A day:2 status:Yes>]
以下是filter()中使用的一些最常見運算符的概述
-
equals:
query.filter(User.name == 'ed')
-
not equals:
query.filter(User.name != 'ed')
-
LIKE:
query.filter(User.name.like('%ed%'))
-
IN:
-
NOT IN:
query.filter(~User.name.in_(['ed', 'wendy', 'jack'])) -
IS NULL:
-
IS NOT NULL:
-
AND:
2.1. ObjectRelationalTutorial 17
query.filter(User.name.in_(['ed', 'wendy', 'jack']))
# works with query objects too:
query.filter(User.name.in_( session.query(User.name).filter(User.name.like('%ed%'))
))
query.filter(User.name == None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.is_(None))
query.filter(User.name != None)
# alternatively, if pep8/linters are a concern
query.filter(User.name.isnot(None))
SQLAlchemy Documentation, Release 1.1.0b1
# use and_()
from sqlalchemy import and_
query.filter(and_(User.name == 'ed', User.fullname == 'Ed Jones'))
# or send multiple expressions to .filter()
query.filter(User.name == 'ed', User.fullname == 'Ed Jones')
# or chain multiple filter()/filter_by() calls
query.filter(User.name == 'ed').filter(User.fullname == 'Ed Jones')
Note: Makesureyouuseand_()andnotthePythonandoperator! • OR:
Note: Makesureyouuseor_()andnotthePythonoroperator! • MATCH:
query.filter(User.name.match('wendy'))
Note: match() uses a database-specific MATCH or CONTAINS f
4.多外鍵關聯

#Author:Yun import sqlalchemy from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String,DATE,ForeignKey from sqlalchemy.orm import relationship engine = create_engine("mysql+pymysql://root:yun258762@192.168.176.139/test_db", encoding='utf-8') Base = declarative_base() # 生成orm基類 class Customer(Base): __tablename__ = 'customer' # 表名 id = Column(Integer, primary_key=True) name = Column(String(32),nullable=False) builling_address_id = Column(Integer,ForeignKey('address.id')) shipping_address_id = Column(Integer,ForeignKey('address.id')) builling_address = relationship("Address",foreign_keys=[builling_address_id]) shipping_address = relationship("Address",foreign_keys=[shipping_address_id]) def __repr__(self): return "<%s name:%s>" % (self.id,self.name) class Address(Base): __tablename__ = 'address' id = Column(Integer,primary_key=True) street = Column(String(64),nullable=False) city = Column(String(64),nullable=False) state = Column(String(64),nullable=False) def __repr__(self): return self.street Base.metadata.create_all(engine) # 創建表結構

#Author:Yun #同一張表里關聯另一個表里的兩個地址 import orm_fk from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_fk.engine)#創建於數據 session = Session_class()#生成session實例 #cursor add1 = orm_fk.Address(street='中路',city='艾歐尼亞',state='召喚師峽谷' ) add2 = orm_fk.Address(street='上路',city='艾歐尼亞',state='召喚師峽谷' ) add3 = orm_fk.Address(street='下路',city='艾歐尼亞',state='召喚師峽谷' ) session.add_all([add1,add2,add3]) c1 = orm_fk.Customer(name='魯班',builling_address=add1,shipping_address=add1) c2 = orm_fk.Customer(name='卡莎',builling_address=add1,shipping_address=add2) c3 = orm_fk.Customer(name='妖姬',builling_address=add2,shipping_address=add3) #為表添加數據 #session.add_all([c1,c2,c3]) obj = session.query(orm_fk.Customer).filter(orm_fk.Customer.name=='妖姬').first() print(obj.name,obj.builling_address,obj.shipping_address) session.commit()

妖姬 上路 下路
5.多對多關系
現在來設計一個能描述“圖書”與“作者”的關系的表結構,需求是
- 一本書可以有好幾個作者一起出版
- 一個作者可以寫好幾本書

一本書----》多個作者;一個作者-----》多本書

這樣就相當於通過book_m2m_author表完成了book表和author表之前的多對多關聯
用orm如何表示呢?#Author:Yun
from sqlalchemy import Table, Column, Integer,String,DATE, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("mysql+pymysql://root:yun258762@localhost/test_db?charset=utf8", ) Base = declarative_base() #通orm自動維護這表,所以不需要用類方法創建,映射
book_m2m_author = Table('book_m2m_author', Base.metadata, Column('book_id',Integer,ForeignKey('books.id')), Column('author_id',Integer,ForeignKey('authors.id')), ) class Book(Base): __tablename__ = 'books' id = Column(Integer,primary_key=True) name = Column(String(64)) pub_date = Column(DATE) authors = relationship('Author',secondary=book_m2m_author,backref='books') def __repr__(self): return "<book_name:%s date:%s>" % (self.name,self.pub_date) class Author(Base): __tablename__ = 'authors' id = Column(Integer, primary_key=True) name = Column(String(32)) def __repr__(self): return "<author:%s>" %self.name Base.metadata.create_all(engine) # 創建表結構
import orm_fk from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_fk.engine)#創建於數據
session = Session_class() b1 = orm_fk.Book(name='消費者行為學',pub_date='2018-11-29') b2 = orm_fk.Book(name='活着',pub_date='2018-11-29') b3 = orm_fk.Book(name='盲井',pub_date='2018-11-29') a1 = orm_fk.Author(name='魯班') a2 = orm_fk.Author(name='妖姬') a3 = orm_fk.Author(name='妲己') a4 = orm_fk.Author(name='提莫') b1.authors = [a1,a3] b2.authors = [a2,a4] b3.authors = [a1,a2,a3] session.add_all([b1,b2,b3,a1,a2,a3]) session.commit()
mysql> select * from authors; +----+--------+ | id | name | +----+--------+ | 1 | 魯班 | | 2 | 妖姬 | | 3 | 提莫 | | 4 | 妲己 | +----+--------+ 4 rows in set (0.00 sec) mysql> select * from books; +----+--------------------+------------+ | id | name | pub_date | +----+--------------------+------------+ | 1 | 消費者行為學 | 2018-11-29 | | 2 | 盲井 | 2018-11-29 | | 3 | 活着 | 2018-11-29 | +----+--------------------+------------+ 3 rows in set (0.00 sec) mysql> select * from book_m2m_author; +---------+-----------+ | book_id | author_id | +---------+-----------+ | 1 | 1 | | 1 | 4 | | 2 | 1 | | 2 | 2 | | 2 | 4 | | 3 | 2 | | 3 | 3 | +---------+-----------+ 7 rows in set (0.00 sec)
import orm_fk from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_fk.engine)#創建於數據
session = Session_class() print('----通過作者表查關聯的書-------') author_obj = session.query(orm_fk.Author).filter(orm_fk.Author.name=='魯班').first() print(author_obj.books) print('----通過書表查看關聯的作者-------') book_obj = session.query(orm_fk.Book).filter(orm_fk.Book.id==1).first() print(book_obj.authors) session.commit()
效果:
----通過作者表查關聯的書------- [<book_name:消費者行為學 date:2018-11-29>, <book_name:盲井 date:2018-11-29>] ----通過書表查看關聯的作者------- [<author:魯班>, <author:妲己>]
多對多刪除
刪除數據時不用管boo_m2m_authors , sqlalchemy會自動幫你把對應的數據刪除
通過書刪除作者
#Author:Yun
import orm_fk from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_fk.engine)#創建於數據
session = Session_class() author_obj = session.query(orm_fk.Author).filter_by(name="魯班").first() book_obj = session.query(orm_fk.Book).filter_by(name="盲井").first() book_obj.authors.remove(author_obj) # 從一本書里刪除一個作者
session.commit()
刪除之前: mysql> select * from book_m2m_author; +---------+-----------+
| book_id | author_id |
+---------+-----------+
| 1 | 1 |
| 1 | 4 |
| 2 | 1 |
| 2 | 2 |
| 2 | 4 |
| 3 | 2 |
| 3 | 3 |
+---------+-----------+
7 rows in set (0.00 sec) 刪除之后: mysql> select * from book_m2m_author; +---------+-----------+
| book_id | author_id |
+---------+-----------+
| 1 | 1 |
| 1 | 4 |
| 2 | 2 |
| 2 | 4 |
| 3 | 2 |
| 3 | 3 |
+---------+-----------+
6 rows in set (0.00 sec)
直接刪除作者
刪除作者時,會把這個作者跟所有書的關聯關系數據也自動刪除
#Author:Yun
import orm_fk from sqlalchemy.orm import sessionmaker Session_class = sessionmaker(bind=orm_fk.engine)#創建於數據
session = Session_class() author_obj =session.query(orm_fk.Author).filter_by(name="魯班").first() print(author_obj.name , author_obj.books) session.delete(author_obj) session.commit()
效果:
mysql> select * from book_m2m_author; +---------+-----------+ | book_id | author_id | +---------+-----------+ | 1 | 1 | | 1 | 4 | | 2 | 2 | | 2 | 4 | | 3 | 2 | | 3 | 3 | +---------+-----------+ 6 rows in set (0.00 sec) mysql> select * from book_m2m_author; +---------+-----------+ | book_id | author_id | +---------+-----------+ | 1 | 4 | | 2 | 2 | | 2 | 4 | | 3 | 2 | | 3 | 3 | +---------+-----------+ 5 rows in set (0.00 sec)