python對Mysql操作和使用ORM框架(SQLAlchemy)


python對mysql的操作

Mysql 常見操作

數據庫操作

  • 創建數據庫

    create database fuzjtest

  • 刪除數據庫

    drop database fuzjtest

  • 查詢數據庫

    show databases

  • 切換數據庫

    use databas 123123 ###用戶授權

  • 創建用戶

    create user '用戶名'@'IP地址' identified by '密碼';

  • 刪除用戶

    drop user '用戶名'@'IP地址';

  • 修改用戶

    rename user '用戶名'@'IP地址'; to '新用戶名'@'IP地址';;

  • 修改密碼

    set password for '用戶名'@'IP地址' = Password('新密碼')

  • 查看權限

     show grants for '用戶'@'IP地址'

  • 授權

    grant 權限 on 數據庫.表 to '用戶'@'IP地址'

  • 取消權限
    revoke 權限 on 數據庫.表 from '用戶'@'IP地址'

    PS:用戶權限相關數據保存在mysql數據庫的user表中,所以也可以直接對其進行操作(不建議)
    授權數據庫

all privileges  除grant外的所有權限
select          僅查權限
select,insert   查和插入權限
...
usage                   無訪問權限
alter                   使用alter table
alter routine           使用alter procedure和drop procedure
create                  使用create table
create routine          使用create procedure
create temporary tables 使用create temporary tables
create user             使用create user、drop user、rename user和revoke  all privileges
create view             使用create view
delete                  使用delete
drop                    使用drop table
execute                 使用call和存儲過程
file                    使用select into outfile 和 load data infile
grant option            使用grant 和 revoke
index                   使用index
insert                  使用insert
lock tables             使用lock table
process                 使用show full processlist
select                  使用select
show databases          使用show databases
show view               使用show view
update                  使用update
reload                  使用flush
shutdown                使用mysqladmin shutdown(關閉MySQL)
super                   􏱂􏰈使用change master、kill、logs、purge、master和set global。還允許mysqladmin􏵗􏵘􏲊􏲋調試登陸
replication client      服務器位置的訪問
replication slave       由復制從屬使用
相關權限
對於目標數據庫以及內部其他:
  數據庫名.*           數據庫中的所有
  數據庫名.表          指定數據庫中的某張表
  數據庫名.存儲過程     指定數據庫中的存儲過程
  *.*                所有數據庫
對數據庫授權
用戶名@IP地址         用戶只能在改IP下才能訪問
用戶名@192.168.1.%   用戶只能在改IP段下才能訪問(通配符%表示任意)
用戶名@%             用戶可以再任意IP下訪問(默認IP地址為%)
對用戶和IP

 

實例

grant all privileges on db1.tb1 TO '用戶名'@'IP'

grant select on db1.* TO '用戶名'@'IP'

grant select,insert on *.* TO '用戶名'@'IP'

revoke select on db1.tb1 from '用戶名'@'IP'

 

表操作

  • 創建表

    • 語法
    create table 表名(
        列名  類型  是否可以為空,
        列名  類型  是否可以為空
    )

     

    • 參數
1.是否可空,null表示空,非字符串
          not null    - 不可空
          null        - 可空

2.默認值,創建列時可以指定默認值,當插入數據時如果未主動設置,則自動添加默認值
          create table tb1(
              nid int not null defalut 2,
              num int not null
          )
3.自增,如果為某列設置自增列,插入數據時無需設置此列,默認將自增(表中只能有一個自增列)
          create table tb1(
              nid int not null auto_increment primary key,
              num int null
          )
          或
          create table tb1(
              nid int not null auto_increment,
              num int null,
              index(nid)
          )
          注意:1、對於自增列,必須是索引(含主鍵)。
               2、對於自增可以設置步長和起始值
                   show session variables like 'auto_inc%';
                   set session auto_increment_increment=2;
                   set session auto_increment_offset=10;

                   shwo global  variables like 'auto_inc%';
                   set global auto_increment_increment=2;
                   set global auto_increment_offset=10;

 4.主鍵,一種特殊的唯一索引,不允許有空值,如果主鍵使用單個列,則它的值必須唯一,如果是多列,則其組合必須唯一。
          create table tb1(
              nid int not null auto_increment primary key,
              num int null
          )
          或
          create table tb1(
              nid int not null,
              num int not null,
              primary key(nid,num)
          )

 5.外鍵,一個特殊的索引,只能是指定內容
          creat table color(
              nid int not null primary key,
              name char(16) not null
          )

          create table fruit(
              nid int not null primary key,
              smt char(32) null ,
              color_id int not null,
              constraint fk_cc foreign key (color_id) references color(nid)
          )

 

  • 刪除表

   drop table 表名

  • 清空表

    delete from 表名
    truncate table 表名

  • 修改表

    • 添加列:

      alter table 表名 add 列名 類型

    • 刪除列:

      alter table 表名 drop column 列名

    • 修改列:
      alter table 表名 modify column 列名 類型; -- 類型
      alter table 表名 change 原列名 新列名 類型; -- 列名,類型

    • 添加主鍵:

    • 刪除主鍵:
      alter table 表名 drop primary key;
      alter table 表名 modify 列名 int, drop primary key;

    • 添加外鍵:

 

      alter table 從表 add constraint 外鍵名稱(形如:FK_從表_主表) foreign key 從表(外鍵字段) references 主表(主鍵字段);

 

    • 刪除外鍵:

 

      alter table 表名 drop foreign key 外鍵名稱

 

    • 修改默認值:

 

      ALTER TABLE testalter_tbl ALTER i SET DEFAULT 1000;

 

    • 刪除默認值:

 

      ALTER TABLE testalter_tbl ALTER i DROP DEFAULT;

 

基本操作

insert into 表 (列名,列名...) values (值,值,值...)
insert into 表 (列名,列名...) values (值,值,值...),(值,值,值...)
insert into 表 (列名,列名...) select (列名,列名...) from

 

delete from 表
delete from 表 where id=1 and name='fuzj'

 

 update 表 set name = 'fuzj' where id>1

 

select * from 表
select * from 表 where id > 1
select nid,name,gender as gg from 表 where id > 1

 

  • 高級操作

    • 條件

      select * from 表 where id > 1 and name != 'alex' and num = 12;
      
      select * from 表 where id between 5 and 16;
      
      select * from 表 where id in (11,22,33)
      select * from 表 where id not in (11,22,33)
      select * from 表 where id in (select nid from 表)

       

    • 通配符

      select * from 表 where name like 'ale%' - ale開頭的所有(多個字符串)
      select * from 表 where name like 'ale_' - ale開頭的所有(一個字符)

       

    • 限制

      select * from 表 limit 5; - 前5行
      select * from 表 limit 4,5; - 從第4行開始的5行
      select * from 表 limit 5 offset 4 - 從第4行開始的5行

       

    • 排序

      select * from 表 order by 列 asc - 根據 “列” 從小到大排列
      select * from 表 order by 列 desc - 根據 “列” 從大到小排列
      select * from 表 order by 列1 desc,列2 asc - 根據 “列1” 從大到小排列,如果相同則按列2從小到大排序

       

    • 分組

      select num from 表 group by num
      select num,nid from 表 group by num,nid
      select num,nid from 表 where nid > 10 group by num,nid order nid desc
      select num,nid,count(*),sum(score),max(score),min(score) from 表 group by num,nid
      
      select num from 表 group by num having max(id) > 10
      
      特別的:group by 必須在where之后,order by之前

       

    • 連表

      無對應關系則不顯示
      select A.num, A.name, B.name
      from A,B
      Where A.nid = B.nid
      
      無對應關系則不顯示
      select A.num, A.name, B.name
      from A inner join B
      on A.nid = B.nid
      
      A表所有顯示,如果B中無對應關系,則值為null
      select A.num, A.name, B.name
      from A left join B
      on A.nid = B.nid
      
      B表所有顯示,如果B中無對應關系,則值為null
      select A.num, A.name, B.name
      from A right join B
      on A.nid = B.nid

       

    • 組合

      組合,自動處理重合
      select nickname
      from A
      union
      select name
      from B
      
      組合,不處理重合
      select nickname
      from A
      union all
      select name
      from B

       

      python操作Mysql

      python3中第三方模塊pymysql,提供python對mysql的操作
      pip3 install pymysql

  • 執行sql語句

import pymysql

# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='fuzj', passwd='123123', db='fuzj')

# 創建游標
cursor = conn.cursor()

#conn.set_charset('utf-8')
# 執行SQL,並返回收影響行數
#effect_row = cursor.execute("create table user (id int not NULL auto_increment primary key  ,name char(16) not null) ")    #創建一個user表
#print(effect_row)
# 執行SQL,並返回受影響行數,使用占位符 實現動態傳參
cursor.execute('SET CHARACTER SET utf8;')
effect_row = cursor.execute("insert into user (name) values (%s) ", ('323'))
effect_row = cursor.executemany("insert into user (name) values (%s) ", [('123',),('456',),('789',),('0',),('1',),('2',),('3',)])

#print(effect_row)
# 執行多個SQL,並返回受影響行數,列表中每個元素都相當於一個條件
effect_row = cursor.executemany("update user set name = %s WHERE  id = %s", [("fuzj",1),("jeck",2)])
print(effect_row)

 

  • 獲取新創建數據自增ID
#使用游標的lastrowid方法獲取
new_id = cursor.lastrowid

 

  • 獲取查詢數據
import pymysql

# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='fuzj', passwd='123123', db='fuzj')

# 創建游標
cursor = conn.cursor()


cursor.execute("select * from user")

# 獲取第一行數據
row_1 = cursor.fetchone()
print(row_1)
# 獲取前n行數據
row_2 = cursor.fetchmany(3)
print(row_2)
# 獲取所有數據
row_3 = cursor.fetchall()
print(row_3)
conn.commit()
cursor.close()
conn.close()import pymysql

# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='fuzj', passwd='123123', db='fuzj')

# 創建游標
cursor = conn.cursor()


cursor.execute("select * from user")

# 獲取第一行數據
row_1 = cursor.fetchone()
print(row_1)
# 獲取前n行數據
row_2 = cursor.fetchmany(3)
print(row_2)
# 獲取所有數據,返回元組形式
row_3 = cursor.fetchall()
print(row_3)
conn.commit()
cursor.close()
conn.close()

 

輸出:

(1, 'fuzj')
((2, 'jeck'), (3, '323'), (4, '123'))
((5, '456'), (6, '789'), (7, '0'), (8, '1'), (9, '2'), (10, '3'), (11, '323'), (12, '123'), (13, '456'), (14, '789'), (15, '0'), (16, '1'), (17, '2'), (18, '3'), (19, '323'), (20, '123'), (21, '456'), (22, '789'), (23, '0'), (24, '1'), (25, '2'), (26, '3'))

 

注:在fetch數據時按照順序進行,可以使用cursor.scroll(num,mode)來移動游標位置,如:

cursor.scroll(1,mode='relative') # 相對當前位置移動
cursor.scroll(2,mode='absolute') # 相對絕對位置移動

  • fetch數據類型
import pymysql

# 創建連接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='fuzj', passwd='123123', db='fuzj')

# 創建游標
#cursor = conn.cursor()
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

cursor.execute("select * from user")

row_1 = cursor.fetchone()
print(row_1)
# 獲取前n行數據
row_2 = cursor.fetchmany(3)
print(row_2)
# 獲取所有數據
row_3 = cursor.fetchall()
print(row_3)
conn.commit()
cursor.close()
conn.close()

 

輸出結果:

{'id': 1, 'name': 'fuzj'}
[{'id': 2, 'name': 'jeck'}, {'id': 3, 'name': '323'}, {'id': 4, 'name': '123'}]
[{'id': 5, 'name': '456'}, {'id': 6, 'name': '789'}, {'id': 7, 'name': '0'}, {'id': 8, 'name': '1'}, {'id': 9, 'name': '2'}, {'id': 10, 'name': '3'}, {'id': 11, 'name': '323'}, {'id': 12, 'name': '123'}, {'id': 13, 'name': '456'}, {'id': 14, 'name': '789'}, {'id': 15, 'name': '0'}, {'id': 16, 'name': '1'}, {'id': 17, 'name': '2'}, {'id': 18, 'name': '3'}, {'id': 19, 'name': '323'}, {'id': 20, 'name': '123'}, {'id': 21, 'name': '456'}, {'id': 22, 'name': '789'}, {'id': 23, 'name': '0'}, {'id': 24, 'name': '1'}, {'id': 25, 'name': '2'}, {'id': 26, 'name': '3'}]

 

ORM框架

SQLAlchemy是Python編程語言下的一款ORM框架,該框架建立在數據庫API之上,使用關系對象映射進行數據庫操作,簡言之便是:將對象轉換成SQL,然后使用數據API執行SQL並獲取執行結果。

SQLAlchemy本身無法操作數據庫,其必須以來pymsql等第三方插件,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

 

  • 底層處理

使用 Engine/ConnectionPooling/Dialect 進行數據庫操作,Engine使用ConnectionPooling連接數據庫,然后再通過Dialect執行SQL語句。

from sqlalchemy import create_engine

#創建引擎
engine = create_engine("mysql+pymysql://fuzj:123123@127.0.0.1:3306/fuzj", max_overflow=5)
#執行sql語句
engine.execute("INSERT INTO user (name) VALUES ('dadadadad')")

result = engine.execute('select * from user')
res = result.fetchall()
print(res)

 

  • ORM功能使用
    使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 所有組件對數據進行操作。根據類創建對象,對象轉換成SQL,執行SQL。

    • 創建表
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
    from sqlalchemy.orm import sessionmaker, relationship
    from sqlalchemy import create_engine
    
    engine = create_engine("mysql+pymysql://fuzj:123123@127.0.0.1:3306/123", max_overflow=5)
    
    Base = declarative_base()
    
    # 創建單表
    class Users(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(32))
    extra = Column(String(16))
    
    __table_args__ = (
    UniqueConstraint('id', 'name', name='uix_id_name'),
       Index('ix_id_name', 'name', 'extra'),
    )
    
    # 一對多
    class Favor(Base):
    __tablename__ = 'favor'
    nid = Column(Integer, primary_key=True)
    caption = Column(String(50), default='red', unique=True)
    
    class Person(Base):
    __tablename__ = 'person'
    nid = Column(Integer, primary_key=True)
    name = Column(String(32), index=True, nullable=True)
    favor_id = Column(Integer, ForeignKey("favor.nid"))
    
    # 多對多
    class ServerToGroup(Base):
    __tablename__ = 'servertogroup'
    nid = Column(Integer, primary_key=True, autoincrement=True)
    server_id = Column(Integer, ForeignKey('server.id'))
    group_id = Column(Integer, ForeignKey('group.id'))
    
    class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer, primary_key=True)
    name = Column(String(64), unique=True, nullable=False)
    
    class Server(Base):
    __tablename__ = 'server'
    
    id = Column(Integer, primary_key=True, autoincrement=True)
    hostname = Column(String(64), unique=True, nullable=False)
    port = Column(Integer, default=22)
    
    Base.metadata.create_all(engine)  #創建表
    # Base.metadata.drop_all(engine)   #刪除表 

     

    obj = Users(name="alex0", extra='sb')
    session.add(obj)
    session.add_all([
        Users(name="alex1", extra='sb'),
        Users(name="alex2", extra='sb'),
    ])
    session.commit()

     

    session.query(Users).filter(Users.id > 2).delete()
    session.commit()

     

    session.query(Users).filter(Users.id > 2).update({"name" : "099"})
    session.query(Users).filter(Users.id > 2).update({Users.name: Users.name + "099"}, synchronize_session=False)
    session.query(Users).filter(Users.id > 2).update({"num": Users.num + 1}, synchronize_session="evaluate")
    session.commit()

     

    ret = session.query(Users).all()
    ret = session.query(Users.name, Users.extra).all()
    ret = session.query(Users).filter_by(name='alex').all()
    ret = session.query(Users).filter_by(name='alex').first()

     

    • 其它
# 條件
ret = session.query(Users).filter_by(name='alex').all()
ret = session.query(Users).filter(Users.id > 1, Users.name == 'eric').all()
ret = session.query(Users).filter(Users.id.between(1, 3), Users.name == 'eric').all()
ret = session.query(Users).filter(Users.id.in_([1,3,4])).all()
ret = session.query(Users).filter(~Users.id.in_([1,3,4])).all()
ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(name='eric'))).all()
from sqlalchemy import and_, or_
ret = session.query(Users).filter(and_(Users.id > 3, Users.name == 'eric')).all()
ret = session.query(Users).filter(or_(Users.id < 2, Users.name == 'eric')).all()
ret = session.query(Users).filter(
    or_(
        Users.id < 2,
        and_(Users.name == 'eric', Users.id > 3),
        Users.extra != ""
    )).all()


# 通配符
ret = session.query(Users).filter(Users.name.like('e%')).all()
ret = session.query(Users).filter(~Users.name.like('e%')).all()

# 限制
ret = session.query(Users)[1:2]

# 排序
ret = session.query(Users).order_by(Users.name.desc()).all()
ret = session.query(Users).order_by(Users.name.desc(), Users.id.asc()).all()

# 分組
from sqlalchemy.sql import func

ret = session.query(Users).group_by(Users.extra).all()
ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name).all()

ret = session.query(
    func.max(Users.id),
    func.sum(Users.id),
    func.min(Users.id)).group_by(Users.name).having(func.min(Users.id) >2).all()

# 連表

ret = session.query(Users, Favor).filter(Users.id == Favor.nid).all()

ret = session.query(Person).join(Favor).all()

ret = session.query(Person).join(Favor, isouter=True).all()


# 組合
q1 = session.query(Users.name).filter(Users.id > 2)
q2 = session.query(Favor.caption).filter(Favor.nid < 2)
ret = q1.union(q2).all()

q1 = session.query(Users.name).filter(Users.id > 2)
q2 = session.query(Favor.caption).filter(Favor.nid < 2)
ret = q1.union_all(q2).all()

 

  • ORM解決中文編碼問題 sqlalchemy 默認使用latin-1進行編碼。所以當出現中文時就會報如下錯誤:
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 39-41: ordinal not in range(256)

 


解決方法:

在連接數據庫的時候直接指定字符編碼:

#engine = create_engine("mysql+pymysql://fuzj:123.com@127.0.0.1:3306/fuzj?charset=utf8", max_overflow=5,encoding='utf-8')

 

參考:http://firefish.blog.51cto.com/298258/112794

  • ORM 指定查詢返回數據格式 默認使用query查詢返回的結果為一個對象
res = session.query(User).all()
print(res)
#使用for循環遍歷列表才能取出name
for i in res:
    print(i.name)

輸出結果:
[<__main__.User object at 0x10385c438>, <__main__.User object at 0x10385c4a8>, <__main__.User object at 0x10385c550>, <__main__.User object at 0x10385c5f8>, <__main__.User object at 0x10385c6a0>]
fuzj
jie
張三
李四
王五

 

使用__repr__定義返回的數據

class User(Base):
    __tablename__ = 'user'
    nid = Column(Integer,primary_key=True,autoincrement=True)
    name = Column(String(10),nullable=False)
    role = Column(Integer,ForeignKey('role.rid'))
    group = relationship("Role",backref='uuu')    #Role為類名

    def __repr__(self):
        output = "(%s,%s,%s)" %(self.nid,self.name,self.role)
        return output
        
res = session.query(User).all()
print(res)

輸出:
[(1,fuzj,1), (2,jie,2), (3,張三,2), (4,李四,1), (5,王五,3)]

 

  • ORM 一對多具體使用

    mysql表中一對多指的是表A中的數據和表B中的數據存在對應的映射關系,表A中的數據在表B中對應存在多個對應關系,如表A存放用戶的角色 DBA,SA,表B中存放用戶,表B通過外鍵關聯之表A中,多個用戶可以屬於同一個角色

    • 設計兩張表,user表和role表,

      user 表中存放用戶,role表中存放用戶角色,role表中角色對應user表中多個用戶,user表中一個用戶只對應role表中一個角色,中間通過外鍵約束

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String,ForeignKey
    from sqlalchemy.orm import sessionmaker,relationship
    from sqlalchemy import create_engine
    
    engine = create_engine("mysql+pymysql://fuzj:123.com@127.0.0.1:3306/fuzj?charset=utf8", max_overflow=5,encoding='utf-8') 
    Base = declarative_base()
    
    class Role(Base):
        __tablename__ = 'role'
        rid = Column(Integer, primary_key=True, autoincrement=True)    #主鍵,自增
        role_name = Column(String(10))
    
        def __repr__(self):
            output = "(%s,%s)" %(self.rid,self.role_name)
            return output
    
    class User(Base):
        __tablename__ = 'user'
        nid = Column(Integer,primary_key=True,autoincrement=True)
        name = Column(String(10),nullable=False)
        role = Column(Integer,ForeignKey('role.rid'))  #外鍵關聯
    
        def __repr__(self):
            output = "(%s,%s,%s)" %(self.nid,self.name,self.role)
            return output
    Base.metadata.create_all(engine)
    
    Session = sessionmaker(bind=engine)
    session = Session()
    
    #添加角色數據
    session.add(Role(role_name='dba'))
    session.add(Role(role_name='sa'))
    session.add(Role(role_name='net'))
    
    #添加用戶數據
    session.add_all([
        User(name='fuzj',role='1'),
        User(name='jie',role='2'),
        User(name='張三',role='2'),
        User(name='李四',role='1'),
        User(name='王五',role='3'),
    ])
    session.commit()
    session.close()

     

    • 普通連表查詢
    res = session.query(User,Role).join(Role).all()    #查詢所有用戶,及對應的role id
    res1 = session.query(User.name,Role.role_name).join(Role).all()  #查詢所有用戶和角色,
    res2 = session.query(User.name,Role.role_name).join(Role,isouter=True).filter(Role.role_name=='sa').all() #查詢所有DBA的用戶
    print(res)
    print(res1)
    print(res2)
    
    輸出結果:
    [((1,fuzj,1), (1,dba)), ((2,jie,2), (2,sa)), ((3,張三,2), (2,sa)), ((4,李四,1), (1,dba)), ((5,王五,3), (3,net))]
    [('fuzj', 'dba'), ('jie', 'sa'), ('張三', 'sa'), ('李四', 'dba'), ('王五', 'net')]
    [('jie', 'sa'), ('張三', 'sa')]

     

    • 使用relationship 添加影射關系進行查詢

      • 首先在User表中添加relationship影射關系
      class User(Base):
          __tablename__ = 'user'
          nid = Column(Integer,primary_key=True,autoincrement=True)
          name = Column(String(10),nullable=False)
          role = Column(Integer,ForeignKey('role.rid'))
          group = relationship("Role",backref='uuu')    #Role為類名

       

      • 查詢
      #正向查詢
      print('正向查詢')
      res = session.query(User).all()  #查詢所有的用戶和角色
      for i in res:
          print(i.name,i.group.role_name)    #此時的i.group 就是role表對應的關系
      res = session.query(User).filter(User.name=='fuzj').first()  #查詢fuzj用戶和角色
      print(res.name,res.group.role_name)
      
      print('反向查找')
      #反向查找
      res = session.query(Role).filter(Role.role_name =='dba').first()   #查找dba組下的所有用戶
      print(res.uuu)   #此時 print的結果為[(1,fuzj,1), (4,李四,1)]
      for i in res.uuu:
          print(i.name,res.role_name)
      
      輸出結果:
      正向查詢
      fuzj dba
      jie sa
      張三 sa
      李四 dba
      王五 net
      fuzj dba
      反向查找
      [(1,fuzj,1), (4,李四,1)]
      fuzj dba
      李四 dba

       

    • 說明

      relationship 在user表中創建了新的字段,這個字段只用來存放user表中和role表中的對應關系,在數據庫中並不實際存在
      正向查找: 先從user表中查到符合name的用戶之后,此時結果中已經存在和role表中的對應關系,group對象即role表,所以直接使用obj.group.role_name就可以取出對應的角色
      反向查找:relationship參數中backref='uuu',會在role表中的每個字段中加入uuu,而uuu對應的就是本字段在user表中對應的所有用戶,所以,obj.uuu.name會取出來用戶名
      所謂正向和反向查找是對於relationship關系映射所在的表而說,如果通過該表(user表)去查找對應的關系表(role表),就是正向查找,反正通過對應的關系表(role表)去查找該表(user表)即為反向查找。而relationship往往會和ForeignKey共存在一個表中。

  • ORM 多對多具體使用

    Mysql多對多關系指的是兩張表A和B本沒有任何關系,而是通過第三張表C建立關系,通過關系表C,使得表A在表B中存在多個關聯數據,表B在表A中同樣存在多個關聯數據

    • 創建三張表 host表 hostuser表 host_to_hostuser表

    host表中存放主機,hostuser表中存放主機的用戶, host_to_hostuser表中存放主機用戶對應的主機,hostuser表中用戶對應host表中多個主機,host表中主機對應hostuser表中多個用戶,中間關系通過host_to_hostuser表進行關聯。host_to_hostuser和host表、user表進行外鍵約束

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import Column, Integer, String,ForeignKey
    from sqlalchemy.orm import sessionmaker,relationship
    from sqlalchemy import create_engine
    class Host(Base):
        __tablename__ = 'host'
        nid = Column(Integer, primary_key=True,autoincrement=True)
        hostname = Column(String(32))
        port = Column(String(32))
        ip = Column(String(32))
    
    class HostUser(Base):
        __tablename__ = 'host_user'
        nid = Column(Integer, primary_key=True,autoincrement=True)
        username = Column(String(32))
    
    class HostToHostUser(Base):
        __tablename__ = 'host_to_host_user'
        nid = Column(Integer, primary_key=True,autoincrement=True)
    
        host_id = Column(Integer,ForeignKey('host.nid'))
        host_user_id = Column(Integer,ForeignKey('host_user.nid'))
    
    Base.metadata.create_all(engine)
    
    Session = sessionmaker(bind=engine)
    session = Session()
    
    #添加數據
    session.add_all([
        Host(hostname='c1',port='22',ip='1.1.1.1'),
        Host(hostname='c2',port='22',ip='1.1.1.2'),
        Host(hostname='c3',port='22',ip='1.1.1.3'),
        Host(hostname='c4',port='22',ip='1.1.1.4'),
        Host(hostname='c5',port='22',ip='1.1.1.5'),
    ])
    
    session.add_all([
        HostUser(username='root'),
        HostUser(username='db'),
        HostUser(username='nb'),
        HostUser(username='sb'),
    ])
    
    session.add_all([
        HostToHostUser(host_id=1,host_user_id=1),
        HostToHostUser(host_id=1,host_user_id=2),
        HostToHostUser(host_id=1,host_user_id=3),
        HostToHostUser(host_id=2,host_user_id=2),
        HostToHostUser(host_id=2,host_user_id=4),
        HostToHostUser(host_id=2,host_user_id=3),
    ])
    
    session.commit()
    session.close()

     

    • 普通多次查詢
    host_id = session.query(Host.nid).filter(Host.hostname=='c2').first()   #查找hostbane對應的hostid,返回結果為元組(2,)
    user_id_list = session.query(HostToHostUser.host_user_id).filter(HostToHostUser.host_id==host_id[0]).all()  #查詢hostid對應的所有userid
    user_id_list = zip(*user_id_list)   #user_id_list 初始值為[(2,), (4,), (3,)],使用zip轉換為[2,4,3]對象
    #print(list(user_id_list))    #結果為[(2, 4, 3)]
    user_list = session.query(HostUser.username).filter(HostUser.nid.in_(list(user_id_list)[0])).all()  #查詢符合條件的用戶
    print(user_list)
    
    #或者:
    user_id_list = session.query(HostToHostUser.host_user_id).join(Host).filter(Host.hostname=='c2').all()
    user_id_list = zip(*user_id_list)
    user_list = session.query(HostUser.username).filter(HostUser.nid.in_(list(user_id_list)[0])).all()
    print(user_list)

     

    
    
    輸出結果:
    [('db',), ('nb',), ('sb',)]

     

    • 使用relationship映射關系查詢

      • 首先在關系表Host_to_hostuser中加入relationship關系映射
      class HostToHostUser(Base):
          __tablename__ = 'host_to_host_user'
          nid = Column(Integer, primary_key=True,autoincrement=True)
      
          host_id = Column(Integer,ForeignKey('host.nid'))
          host_user_id = Column(Integer,ForeignKey('host_user.nid'))
          host = relationship('Host',backref='h') #對應host表
          host_user = relationship('HostUser',backref='u') #對應host_user表

       

      • 查詢
      #查找一個服務器上有哪些用戶
      res = session.query(Host).filter(Host.hostname=='c2').first()  #返回的是符合條件的服務器對象
      res2 = res.h    #通過relationship反向查找 Host_to_Hostuser中的對應關系
      for i in res2:   #i為host_to_hostuser表和host表中c2主機有對應關系的條目
          print(i.host_user.username)        #正向查找, 通過relationship ,找到host_to_hostuser中對應的hostuser 即i.host_user
      
      #查找此用戶有哪些服務器
      res = session.query(HostUser).filter(HostUser.username=='sb').first()
      for i in res.u:
          print(i.host.hostname)

       

    • 擴展查詢

    不查詢關系表,直接在hostuser表中指定關系表,然后獲取host表

在host表中使用 relationship的secondary指定關系表。

class Host(Base):
    __tablename__ = 'host'
    nid = Column(Integer, primary_key=True,autoincrement=True)
    hostname = Column(String(32))
    port = Column(String(32))
    ip = Column(String(32))
    host_user = relationship('HostUser',secondary=lambda :HostToHostUser.__table__,backref='h')

注意使用lambda是為了使表的順序不在閑置

查詢:

host_obj = session.query(Host).filter(Host.hostname=='c1').first()
for i in host_obj.host_user:
    print(i.username)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM