转载--SqlAlchemy ORM 学习
轉(zhuǎn)載原文地址:http://blog.csdn.net/yueguanghaidao/article/details/7485345,http://blog.csdn.net/yueguanghaidao/article/details/7485967
?
--------------------------第一部分--------------------------
一直覺得orm很高深,今天時間比較充分打算學(xué)習(xí)下
安裝說一點,我的系統(tǒng)是win7 64位家庭普通版
用easy_install安裝sqlalchemy出現(xiàn)問題,于是下載最新版0.7.6的zip壓縮包,用python setup.py install出現(xiàn)錯誤:
WARNING: The C extension could not be compiled, speedups are not enabled. Plain-Python build succeeded.自己未解決,向廣大朋友們請教了
?
沒辦法又下了0.6.8版的zip包,這次安裝成功了。
開始我的sqlalchemy之旅
本文主要是翻譯這篇文章(還有自己的操作和思考,我會竟可能做到有圖有真相,呵呵):點擊打開鏈接:如果有錯請指正
http://www.blog.pythonlibrary.org/2010/02/03/another-step-by-step-sqlalchemy-tutorial-part-1-of-2/創(chuàng)建我們第一個腳本:
? ? ? ? 我們將利用SqlAlchemy創(chuàng)建我們第一個例子,創(chuàng)建一個簡單的表,存儲用戶的姓名,年齡和密碼
from sqlalchemy import create_engine from sqlalchemy import MetaData, Column, Table, ForeignKey from sqlalchemy import Integer, String engine = create_engine('sqlite:///tutorial.db',echo=True) metadata = MetaData(bind=engine) users_table = Table('users', metadata,Column('id', Integer, primary_key=True),Column('name', String(40)),Column('age', Integer),Column('password', String),) addresses_table = Table('addresses', metadata,Column('id', Integer, primary_key=True),Column('user_id', None, ForeignKey('users.id')),Column('email_address', String, nullable=False)) # create tables in database metadata.create_all()深入看下代碼:
首先需要導(dǎo)入一些包,然后我們創(chuàng)建一個數(shù)據(jù)庫連接engine對象。在這里我們使用sqlite數(shù)據(jù)庫,不需要提供認(rèn)證信息。
?
你肯定注意到了,我們將echo設(shè)置為True,目的是SqlAlchemy將會把執(zhí)行sql命令的過程輸出到標(biāo)準(zhǔn)輸出。這主要是便于調(diào)試,但如果是用于生產(chǎn)環(huán)境中應(yīng)該設(shè)置為False。
然后我們創(chuàng)建一個MetaData對象,這個對象包含了數(shù)據(jù)庫的所有元數(shù)據(jù)。它由表的描述信息和其它的數(shù)據(jù)庫schema-level 的對象組成。我們可以在創(chuàng)建MetaData對象時就把它綁定到我們的數(shù)據(jù)庫,也可以在上面代碼的最后即?create_all 時綁定。在第二部分有示例(點擊打開鏈接)
下面部分就是創(chuàng)建表的過程。這是由SqlAlchemy的表對象和列對象完成的。我們可以使用各種字段類型,如String,Integer等等。在這里,我們創(chuàng)建一個名為users的表,然后傳給了metadata對象。然后我們創(chuàng)建了4個字段,id設(shè)置為主鍵,當(dāng)我們向這個表中增加用戶呢時,SqlAlchemy?會自動將id加一(自增)。name字段設(shè)置為String類型,40個字符長度。age字段是簡單的Integer類型,password字段也被設(shè)置為String類型,而且我們沒有設(shè)置長度。addresses_table與users表的主要不同就是我們設(shè)置了外鍵屬性用來聯(lián)系兩張表。
最后一個片段實際上創(chuàng)建了數(shù)據(jù)庫和表。每當(dāng)你調(diào)用create_all()時,在創(chuàng)建表之前會檢查表是否存在。也就是說,你可以創(chuàng)建額外的表,但當(dāng)你調(diào)用create_all時SqlAlchemy
僅僅會創(chuàng)建新的表。
Tips:
SqlAlchemy 也提供了裝載已經(jīng)創(chuàng)建的表的方法:
someTable = Table("users", metadata, autoload=True, schema="schemaName")插入數(shù)據(jù):
有幾種不同的方式插入數(shù)據(jù)和查詢數(shù)據(jù)。我們先考慮低級的方式,然后再考慮這個系列的其它部分,我們將進(jìn)入稍微抽象的Sessions和Declarative樣式。
讓我們一起看看插入數(shù)據(jù)的幾種不同方法吧。
# create an Insert object ins = users_table.insert() # add values to the Insert object new_user = ins.values(name="Joe", age=20, password="pass")# create a database connection conn = engine.connect() # add user to database by executing SQL conn.execute(new_user)上面的代碼展示了如何利用連接對象插入數(shù)據(jù)的方式。首先,你需要調(diào)用表的insert()方法創(chuàng)建一個Insert對象,然后你就可以用Insert的values()方法增加一行你所需要的數(shù)據(jù)
。然后我們創(chuàng)建了Connection對象,最后對Insert對象調(diào)用Connection對象的execute方法。這聽起來有點復(fù)雜,其實很簡單。
?檢驗一下吧:我是用sqlite.exe打開tutorial.db
我們發(fā)現(xiàn),插入成功了,而且id字段自動設(shè)為1,符合我們定義的剛才對primary_key的解釋。
?
下面的片段展示了一些沒有用Connection對象的插入數(shù)據(jù)方法:
# a connectionless way to Insert a user ins = users_table.insert() result = engine.execute(ins, name="Shinji", age=15, password="nihongo")# another connectionless Insert result = users_table.insert().execute(name="Martha", age=45, password="dingbat")我們檢驗一下成功了沒有?
有沒有發(fā)現(xiàn)id的確是自增的?
在上面的兩個列子中,你都需要調(diào)用表對象的insert方法。在插入方法的最后,我們看一下如何插入多行數(shù)據(jù)。
conn.execute(users_table.insert(), [{"name": "Ted", "age":10, "password":"dink"},{"name": "Asahina", "age":25, "password":"nippon"},{"name": "Evan", "age":40, "password":"macaca"} ])這已經(jīng)相當(dāng)明白了,要點就是你需要先前創(chuàng)建的Connection對象,并傳遞兩個參數(shù)。
效果如下:
?
現(xiàn)在,我們來看看查詢操作。
SqlAlchemy為查詢操作提供了一組方法。這里,我們將關(guān)注簡單的方法。
最常見的例子就是做一個全查詢,我們開始吧
from sqlalchemy.sql import select s = select([users_table]) result = s.execute() for row in result:print row結(jié)果如下:
?
首先我們導(dǎo)入select方法,然后我們把表作為一個元素的列表傳入,最后我們調(diào)用select對象的execute方法,并把結(jié)果賦給了result變量,最后就是迭代了。
如果你需要所有的結(jié)果存儲在一個由元組組成的列表而不是行對象,你可以這么做。
# get all the results in a list of tuples conn = engine.connect() res = conn.execute(s) rows = res.fetchall()結(jié)果如下:
?
如果你只需要第一條記錄返回,你可以用fetchone()代替fetchall():
res = conn.execute(s) row = res.fetchone()現(xiàn)在假設(shè)我們需要更小粒度的結(jié)果,下面,我們僅僅想返回name和age,而不要密碼。
s = select([users_table.c.name, users_table.c.age]) result = conn.execute(s) for row in result:print row結(jié)果如下:
?
很好,這是相當(dāng)?shù)暮唵巍N覀兯枰龅闹皇窃趕elect語句中明確列名。那個'c'基本意思是:column.
如果你有多個表,也許select語句應(yīng)該有點像這樣
select([tableOne, tableTwo])當(dāng)然這很可能返回重復(fù)的結(jié)果,因此你為了解決這個問題,你將這么做:
s = select([tableOne, tableTwo], tableOne.c.id==tableTwo.c.user_id)SqlAlchemy文檔中,稱第一種結(jié)果集為笛卡爾積。上訴第二種語句消除了那些煩惱。怎么消除的?在這種查詢的方式中使用了where子句是一種解決辦法。在下面部分,我將用sessions展示一種與where查詢子句不同的方法。
下面是一些示列,注釋中有解釋
from sqlalchemy.sql import and_ # The following is the equivalent to # SELECT * FROM users WHERE id > 3 s = select([users_table], users_table.c.id > 3) # You can use the "and_" module to AND multiple fields together s = select(and_(users_table.c.name=="Martha", users_table.c.age < 25))?
?
--------------------------第二部分--------------------------
在Step-by-Step SqlAlchemy Tutorial的第一部分(點擊打開鏈接),我們調(diào)用SqlAlchemy的SQL表達(dá)式與數(shù)據(jù)庫交互。在我們開始更高級和更加抽象的方法之前,我們需要學(xué)習(xí)更加抽象的方式做
事情。這就像許多數(shù)學(xué)課程一樣,比如微積分,在你知道捷徑之前,你需要很長時間去學(xué)習(xí)發(fā)現(xiàn)一些微積分的標(biāo)準(zhǔn)偏差。
在第二部分,我們將用簡單的方法去應(yīng)用SqlAlchemy。它被稱為‘關(guān)系對象’方法,SqlAlchemy的官方文檔實際上也從這里開始。
適應(yīng)數(shù)據(jù)映射:
這為什么叫數(shù)據(jù)映射呢?因為我們將把數(shù)據(jù)庫的數(shù)據(jù)映射到Python的類。讓我們開始吧!
from sqlalchemy import create_engine from sqlalchemy import Column, MetaData, Table from sqlalchemy import Integer, String, ForeignKey from sqlalchemy.orm import mapper, sessionmaker#################################################### class User(object):""""""#----------------------------------------------------------------------def __init__(self, name, fullname, password):"""Constructor"""self.name = nameself.fullname = fullnameself.password = passworddef __repr__(self):return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)# create a connection to a sqlite database # turn echo on to see the auto-generated SQL engine = create_engine("sqlite:///tutorial.db", echo=True)# this is used to keep track of tables and their attributes metadata = MetaData() users_table = Table('users', metadata,Column('user_id', Integer, primary_key=True),Column('name', String),Column('fullname', String),Column('password', String)) email_table = Table('email', metadata,Column('email_id', Integer, primary_key=True),Column('email_address', String),Column('user_id', Integer, ForeignKey('users.user_id')))# create the table and tell it to create it in the # database engine that is passed metadata.create_all(engine)# create a mapping between the users_table and the User class mapper(User, users_table)我們注意到,與我們先前例子相比第一個不同就是User類。我們把最初的例子(見第一部分)稍微改變了一下,即現(xiàn)在的參數(shù),name,full name和password。其余的部分都一樣,僅僅最后多了mapper語句。這種方便的方法把User類映射到了user_table這個表。這看起來沒什么大不了,但我們向數(shù)據(jù)庫增加用戶會變得更加簡單明了。
然而在我們開始之前,我們需要討論聲明構(gòu)型樣式。雖然這個樣式給了我們更加粒度的控制我們的表,映射和類,但大部分我們不需要那么的復(fù)雜。聲明樣式使得配置變得相當(dāng)簡單。我所知道的第一個SqlAlchemy附加的聲明樣式叫做Elixir.SqlAlchemy內(nèi)嵌的聲明樣式?jīng)]有Elixir那么強大,但是它很方便,因為你不需要安裝額外的東西。那就讓我們看看聲明是怎樣的與眾不同吧。
from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import backref, mapper, relation, sessionmakerBase = declarative_base()######################################################################## class User(Base):""""""__tablename__ = "users"id = Column(Integer, primary_key=True)name = Column(String)fullname = Column(String)password = Column(String)#----------------------------------------------------------------------def __init__(self, name, fullname, password):"""Constructor"""self.name = nameself.fullname = fullnameself.password = passworddef __repr__(self):return "<User('%s','%s', '%s')>" % (self.name, self.fullname, self.password)######################################################################## class Address(Base):"""Address ClassCreate some class properties before initilization"""__tablename__ = "addresses"id = Column(Integer, primary_key=True)email_address = Column(String, nullable=False)user_id = Column(Integer, ForeignKey('users.id'))# creates a bidirectional relationship# from Address to User it's Many-to-One# from User to Address it's One-to-Manyuser = relation(User, backref=backref('addresses', order_by=id))#----------------------------------------------------------------------def __init__(self, email_address):"""Constructor"""self.email_address = email_addressdef __repr__(self):return "<Address('%s')>" % self.email_address# create a connection to a sqlite database # turn echo on to see the auto-generated SQL engine = create_engine("sqlite:///tutorial.db", echo=True)# get a handle on the table object users_table = User.__table__ # get a handle on the metadata metadata = Base.metadata metadata.create_all(engine)如上所示,幾乎所有的都在類中創(chuàng)建了。我們創(chuàng)建了類屬性(就像類的全局變量)用來標(biāo)識表的列。然后創(chuàng)建了初始化函數(shù)__init__,和上例差不多。當(dāng)然,類繼承的是declarative_base?,而不是基本的object。如果需要一個表的對象,我們需要調(diào)用魔力方法User.__table__。為了獲得元數(shù)據(jù),我們需要調(diào)用基類的Base.metadata.現(xiàn)在我們所關(guān)心的都解決了。
這里需要提一點的是,還記得在Step-by-Step SqlAlchemy Tutorial (part 1 of 2)中我在解釋metadata?=?MetaData(bind=engine)時說過,“也可以在上面代碼的最后即?create_all 時綁定",這一次是不是在create_all時綁定的。
下面我們看看如何向表中插入數(shù)據(jù)。
?
?
Class is now in Session
利用對象關(guān)系方法之美在于可以用一小段簡短的代碼與數(shù)據(jù)庫交互。讓我們看看如何創(chuàng)建一行。
mike_user = User("mike", "Mike Driscoll", "password") print "User name: %s, fullname: %s, password: %s" % (mike_user.name,mike_user.fullname,mike_user.password)如上所示,我們用User類創(chuàng)建一個用戶,我么可以使用.訪問屬性。你甚至還可以用來更新數(shù)據(jù)。例如,如果你需要改變用戶對象,你可以這么做:
# this is how you would change the name field mike_user.fullname = "Mike Dryskull"當(dāng)然上面所示的并不會向表中插入一行數(shù)據(jù)。為此我們需要一個Session對象才能完成插入數(shù)據(jù)。下面我們就開始Session的基本使用吧!
from sqlalchemy.orm import sessionmakerSession = sessionmaker(bind=engine) session = Session()mike_user = User("mike", "Mike Driscoll", "password") session.add(mike_user)我們在這里停一下,解釋上面到底發(fā)生了什么?
首先我們需要導(dǎo)入sessionmaker,然后把它綁定到engine.然后我們創(chuàng)建一個session實例,然后我們實例化一個user對象,并把它傳給session的add方法。
到此,沒有任何的SQL代碼被運行,這個事物也是被掛起的。
這時我們查看一下數(shù)據(jù)庫:
我們發(fā)現(xiàn)沒有數(shù)據(jù),為了保留這一行數(shù)據(jù),我們需要調(diào)用session.commit()方法提交事物。
session.commit()我們再查看一下:
?
如果你需要增加多個用戶,你可以這么做:
session.add_all([User('Mary', 'Mary Wonka', 'foobar'),User('Sue', 'Sue Lawhead', 'xxg527'),User('Fay', 'Fay Ray', 'blah')])如果在你提交事物后碰巧改變了其中一個用戶的屬性,你可以使用session.dirty去校驗?zāi)莻€是被修改過的。如果你僅僅想知道有哪些行處在掛起中,你可以調(diào)用session.new.最后我們可以使用session.rollback()去回滾一個事物。
下面讓我們看一下一些查詢示例:
# do a Select all all_users = session.query(User).all()# Select just one user by the name of "mike" our_user = session.query(User).filter_by(name='mike').first() print our_user# select users that match "Mary" or "Fay" users = session.query(User).filter(User.name.in_(['Mary', 'Fay'])).all() print users# select all and print out all the results sorted by id for instance in session.query(User).order_by(User.id):print instance.name, instance.fullname下面我們將討論joins話題
Join in the Fun
joins的SQL表達(dá)式語法這里就不說了,當(dāng)然我們將利用對象關(guān)系方法來示例。如果你回頭看看我們創(chuàng)建表的示例,你將注意到我們已經(jīng)用外鍵對象來使用join了。聲明格式像下面所示:
user_id = Column(Integer, ForeignKey('users.id'))# creates a bidirectional relationship # from Address to User it's Many-to-One # from User to Address it's One-to-Many user = relation(User, backref=backref('addresses', order_by=id))我們通過創(chuàng)建一個新用戶來看看這是怎么工作的。
prof = User("Prof", "Prof. Xavier", "fudge") prof.addresses由于外鍵和backref命令,user對象有個address屬性。如果你運行上面的代碼,你會發(fā)現(xiàn)prof.address是一個空值。讓我們增加一些address吧!
prof.addresses = [Address(email_address='profx@dc.com'),Address(email_address='xavier@yahoo.com')]看,這是多么的簡單啊?同樣也很簡單從中獲取數(shù)據(jù)。例如,如果你想取出第一個地址,你可以這樣訪問:prof.addresses[0]
假如現(xiàn)在你需要修改地址,這是易如反掌。
現(xiàn)在,讓我們來做一些join查詢:
for u, a in session.query(User, Address).filter(User.id==Address.user_id).filter(Address.email_address=='xavier@yahoo.com').all():print u, a這是一個相當(dāng)長的查詢!我發(fā)現(xiàn)這對我來說比較困難,所以我經(jīng)常這么做:
sql = session.query(User, Address) sql = sql.filter(User.id==Address.user_id) sql = sql.filter(Address.email_address=='xavier@yahoo.com')for u, a in sql.all():print u, a如果你喜歡一行式的方式,第一個示例并沒有什么不妥,兩種方法產(chǎn)生相同的結(jié)果集。
?
我很幸運的發(fā)現(xiàn)更高版本的調(diào)試更加簡單。最后,我還可以用一下真正的join:
from sqlalchemy.orm import join session.query(User).select_from(join(User, Address)).filter(Address.email_address=='xavier@yahoo.com').all()這和上面兩個示例做的是一樣的事,但更加的明確。
?
轉(zhuǎn)載于:https://www.cnblogs.com/mzpy1119/p/5394224.html
總結(jié)
以上是生活随笔為你收集整理的转载--SqlAlchemy ORM 学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS GCD, 同步,异步,串行队列,
- 下一篇: php错误以及常用笔记