ORM架構與mysql資料庫的無縫對接,orm架構mysql資料庫
ORM
現在咱們聊聊深入操作資料庫了。
大家都知道,使用資料庫的原生語句來寫,一旦資料量了,首先就得瘋狂重複寫代碼,而且還基本不怎麼重用,都是一次寫死,後面需要又得再寫,那麼面對對象編程的核心裏面教你一個面對對象資料庫,這個時候咱們看效果
普通sql資料庫建表一開始這樣:
1 create table table_name(id int not null auto_increment,2 name varchar(32),3 password varchar(32),4 primary key(id)5 )
這是非常簡單的sql的表,如果複雜點,加些什麼關聯的,估計頭都要炸,那麼,orm是什麼鬼?它是一個架構,封裝了資料庫原語,我們可以物件導向來玩弄資料庫了。
import sqlalchemyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import create_enginefrom sqlalchemy import Colum,Integer,Stringfrom sqlalchemy.orm import sessionmakerengine=create_engine('mysql+pymysql://root:123456@localhost/slkdb',encoding ='utf-8',echo=True)'''注意寫法,orm支援很多借口,我這裡是用pymysql,所以是mysql+pymysql://<user>:<password>@<host>/<dbname> #user是使用者名稱,password是密碼,host是資料庫所在ip地址,dbname是要處理的資料表名字,什麼,你的資料表不支援中文?報錯?什麼拉丁的錯?我告訴你,只要在dbname後面加一個?charset=utf8 既可解決中文支援寫法:engine=create_engine('mysql+pymysql://root:123456@localhost/slkdb?charset=utf8',encoding ='utf-8',echo=True)由於我是python3,不支援安裝MySQL-Python這個模組,這個介面對應的建立就不寫了。'''Base = declarative_base()#產生orm基類class User(Base): __tablename__='user'#表名 id = Colum(Integer,primary_key=True) name = Colum(String(32)) password = Colum(String(32))Base.metadata.create_all(engine)#建立表結構session_class=sessionmaker(bind=engine)#建立一個資料庫的會話,注意這裡只是建立了對象session=session_class()#此時才是產生執行個體,相當於cursorUser1=User(name='hehe',password = '456456')#產生要產生的資料對象User2=User(name='zhaff',password = '298789')#產生要產生的資料對象session.add(User1)session.add(User2)session.commit()#此時提交後才最終把資料建立完畢
好了 資料也插入進去了運行結果資訊:
--------------------------------------------
2018-03-06 15:49:45,082 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2018-03-06 15:49:45,082 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8' and `Collation` = 'utf8_bin'
2018-03-06 15:49:45,085 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,086 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2018-03-06 15:49:45,086 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin AS anon_1
2018-03-06 15:49:45,087 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,088 INFO sqlalchemy.engine.base.Engine DESCRIBE `user`
2018-03-06 15:49:45,088 INFO sqlalchemy.engine.base.Engine {}
2018-03-06 15:49:45,091 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, password) VALUES (%(name)s, %(password)s)
2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine {'name': 'hehe', 'password': '456456'}
2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine INSERT INTO user (name, password) VALUES (%(name)s, %(password)s)
2018-03-06 15:49:45,092 INFO sqlalchemy.engine.base.Engine {'name': 'zhaff', 'password': '298789'}
2018-03-06 15:49:45,093 INFO sqlalchemy.engine.base.Engine COMMIT
-------------------------------------------------------------------
'''注意:運行過程中可能會出現 Warning: (1366, "Incorrect string value: '\\xD6\\xD0\\xB9\\xFA\\xB1\\xEA...' for column 'VARIABLE_VALUE' at row 481")
result = self._query(query)
這樣的警告或者其他巴拉巴拉的警告,這個時候忽略它既可,因為他僅僅是警告,並不是error,程式還是正常運行,資料庫表內資料已經建立。技術有限,目前並未解決這個問題,後期有機會我最佳化'''
大家可能看到這會感覺麻蛋怎麼更複雜了的感覺,畢竟那麼多包模組的各種亂入還有各種對象建立看起來很複雜,
好,是時候表演真正的技術了:
增加資料已經說過,就上面的add,下面說查詢
data = session.query(User).filter(User.id>=1).filter(User.id<4).all()#多條件查詢,直接多個filter既可,這裡的符號都是比較,單等號會出錯喲~
print(data)
也可以使用in_([*arg]),表示在arg元組中匹配
data = session.query(User).filter(User.name.in_(['zhaoxin','zhaff'])).all()print(data)
-------------
輸出:[zhaoxin : 29999, zhaoxin : 29999, zhaff : 298789]
修改:
data = session.query(User).filter(User.name=='hehe').all()[0]#多條件查詢,這裡的all[0]也可以用frist()代替
print(data)
data.name = 'sb'
session.commit()#修改需要提交,不然無法修改,一般單個修改較多,多重修改可以用
print(data)
輸出結果:
------------------------
hehe : 456456
sb : 456456
------------------
同理 刪除就是delete了
data = session.query(User).filter(User.name=='sb').all()[0]#多條件查詢,這裡的all[0]也可以用frist()代替
print(data)
data.name = 'sb'
session.commit()
如此便刪除了名字為'sb'的資料。
刪改查都是先query查到後才做的修改,這點不要弄混了。
分組統計
統計大家都知道,count,那麼這裡自然就是
data = session.query(User).filter(User.name=='sb').count()#沒有all,寫了all就相當於一個類了,那麼此時count就變成了類方法,錯誤提示該User類中沒有count這個attribute。
分組統計
from sqlalchemy import func
data = session.query(User.name,func.count(User.name)).group_by(User.name).all()
print(data)
-----------
輸出:[('zhaff', 1), ('zhaoxin', 2)]
group_by的作用就和命令列的group by一樣,詳情移步:http://www.cnblogs.com/Jason504327775/p/8512953.html
連表查詢:
此時如果建立了一個新的類對象。
class Bala(Base):
*****此處省略了
同上一樣
那麼兩個表,我們要關聯他們兩個,比如表內 name這欄,名字一樣的全給我弄出來
result = session.query(User,Bala).filter(User.name == Bala.name).all()#這種是強制指定name聯絡的,不需要有外鍵聯絡
輸出結果以列表套元組格式出現,每個元組中對應的就是User.name == Bala.name對應的那兩條資料的詳細資料。這就不示範了。
有外鍵聯絡的可以使用
result = session.query(User).join(Bala).all()#來實現
下面orm最牛逼的地方就是外部索引鍵關聯的方便性,直接上代碼:
import sqlalchemyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import create_enginefrom sqlalchemy import Column,Integer,String,ForeignKeyfrom sqlalchemy.orm import sessionmaker,relationshipengine=create_engine('mysql+pymysql://root:123456@localhost/slkdb',encoding ='utf-8')Base = declarative_base()#產生orm基類class Student(Base):#重建兩個資料表,之前的都刪了重新開始 __tablename__='student'#表名 id = Column(Integer,primary_key=True) name = Column(String(32),nullable=False) register_date = Column(String(32),nullable=False) def __repr__(self): return 'ID:%s,name: %s'%(self.id,self.name)class Stu_record(Base): __tablename__='stcored' id = Column(Integer,primary_key=True) day = Column(Integer) statue = Column(String(32),nullable=False) stu_id = Column(Integer, ForeignKey('student.id'))#外部索引鍵關聯就在這,注意是table_name名.id而不是類對象名.id student = relationship('Student',backref='my_study_recode')#這個實現了反向查詢,這行代碼相當於執行個體化一個Student的對象執行個體,此時下面的self.student.name可以調用,通過my_study_recode可以調用Stu_record這個對象的屬性,這個函數是從記憶體中調用,python中封裝出來的,並不是資料庫裡面的功能,如此方便很多倍 def __repr__(self): return '%s第%s天的登入狀態%s'%(self.student.name,self.day,self.statue)Base.metadata.create_all(engine)#建立表結構session_class=sessionmaker(bind=engine)session=session_class()s1 = Student(name='zhaoxin',register_date='2011-02-14')s2 = Student(name='jianji',register_date='2012-01-14')s3 = Student(name='ruizi',register_date='2011-05-14')s4 = Student(name='luoli',register_date='2011-06-11')t1 = Stu_record(day=1,statue='YES',stu_id=1)t2 = Stu_record(day=2,statue='NO',stu_id=1)t3 = Stu_record(day=3,statue='YES',stu_id=1)t4 = Stu_record(day=4,statue='NO',stu_id=1)t5 = Stu_record(day=5,statue='YES',stu_id=2)session.add_all([s1,s2,s3,s4,t1,t2,t3,t4,t5])session.commit()s_obj=session.query(Student).filter(Student.name=='zhaoxin').first()#此時返回的是student這個類print(s_obj.my_study_recode)#通過關聯,這個關聯是記憶體裡進行的通過類之間的映射,反查到Stu_record中所有的屬性,並調用Stu_record的__repr__函數。
----------------
[zhaoxin第1天的登入狀態YES, zhaoxin第2天的登入狀態NO, zhaoxin第3天的登入狀態YES, zhaoxin第4天的登入狀態NO]
一對多外部索引鍵關聯也是異曲同工,但是依然要注意幾個細節:
在關聯時,我們都知道了是類似這樣的stu_id = Column(Integer, ForeignKey('student.id')),那麼這樣一對多代表著這樣的代碼不止一行,關聯的鍵不一樣;
那麼有幾條這個,就有幾條類似student = relationship('Student',backref='my_study_recode')的代碼,這個時候要指定
foreign_keys = [***這裡是本對象下關聯外鍵的屬性名稱***],如此一來電腦就能識別到底誰關聯了誰,就不會分不清,如果把這個寫進函數,那麼電腦就會混淆,然後報錯:大概說的就是有多個foreign_keys關聯在這個表裡,foreign_keys這個屬性應該指定,否則會傻傻分不清楚。
好了,下面最後講下最重要的多對多關聯
直接在代碼裡面講解,重新建立新表。(為了示範效果,我並未分開放,作為合格的開發人員,建立和修改查詢等檔案寫在一個代碼裡實為不妥,但是這裡是示範,就寫一起了。)
import sqlalchemyfrom sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy import create_engine,Tablefrom sqlalchemy import Column,Integer,String,ForeignKeyfrom sqlalchemy.orm import sessionmaker,relationshipengine=create_engine('mysql+pymysql://root:123456@localhost/slkdb?charset=utf8')Base = declarative_base()book_m2m_author =Table('book_m2m_author',Base.metadata, Column('book_id',Integer,ForeignKey('book.id')), Column('author_id',Integer,ForeignKey('author.id')) )#為什麼不用class的方式建立這個表呢?因為我不會在裡面插任何資料或者更改,我只需要改來源資料就是那兩個class裡面的,那麼這個由映射都會自動更改class Book(Base): __tablename__='book' id=Column(Integer,primary_key=True) name = Column(String(32)) put_date = Column(String(32)) authors = relationship('Author',secondary=book_m2m_author,backref='books')#串連author表,查詢時通過第三張表book_m2m_author查詢 def __repr__(self): return self.nameclass Author(Base): __tablename__='author' id = Column(Integer, primary_key=True) name = name = Column(String(32)) def __repr__(self): return self.nameBase.metadata.create_all(engine)session_class=sessionmaker(bind=engine)session=session_class()b1=Book(name='聖墟+琴帝',put_date='2016-6-12')b2=Book(name='長生界+我欲封天',put_date='2013-2-19')b3=Book(name='神墓+求魔',put_date='2009-10-11')b4=Book(name='仙逆+一念永恒',put_date='2011-6-12')b5=Book(name='鬥羅大陸+遮天',put_date='2010-8-2')b6=Book(name='三合一',put_date='2018-3-7')a1=Author(name='辰東')a2=Author(name='耳根')a3=Author(name='唐家三少')b1.authors=[a1,a3]#第三個表通過這個方式關聯起author和book這兩個表格,以下同。就是Book類中authors裡的secondary這個屬性的實現。b2.authors=[a1,a2]b3.authors=[a1,a2]b4.authors=[a2,a3]b5.authors=[a3,a1]b6.authors=[a1,a2,a3]session.add_all([b1,b2,b3,b4,b5,b6,a1,a2,a3])session.commit()author_obj = session.query(orm_m2m.Author).filter(orm_m2m.Author.name=='辰東').first()print(author_obj.books)#通過book實現到Book裡面調用匹配辰東屬性的反查效果book_obj = session.query(orm_m2m.Book).filter(orm_m2m.Book.name=='三合一').first()print(book_obj,book_obj.authors)#如此就實現作者查書 和書查作者的關聯
---------------------------------
輸出:
[聖墟+琴帝, 長生界+我欲封天, 神墓+求魔, 鬥羅大陸+遮天, 三合一]
三合一 [辰東, 耳根, 唐家三少]
好了,到這裡orm具體也講完了,實際生活中,多表格關聯會很多,如果您看的一臉懵逼,那對不起,可能我自己水平還不高,無法講解入微,人生苦短,我用python。