拥抱SQLAlchemy 之一 一见钟情

自打接触了python,就被深深的吸引,首先是它频繁出现在我工作使用的著名GIS软件中,一次为了给一个GIS软件写点脚本,就尝试了一下,这一尝试不要紧,心从此就被他栓住了,再开发什么东西首先就考虑用python去做。

为什么会被python吸引的,我思考后得出这个结论:

1、语言简练,亲切,容易学习

2、开源的库众多,只要你能想到就能得到

 

得知有一个很NB的ORM框架SQLAlchemy,所以打算学习一下,从今天起就开始做学习笔记,希望能坚持下去,本笔记简要记录一些自己学习中的心得和要点,尽量多写一些代码范例供以后查看,学习前就说这么多了,下面就开始吧!

image

 

SQLAlchemy拥有多个独立的部分,可以单独或协同工作,下面是主要API组件:

上图中最重要的两个接口是Object Relational Mapper(ORM)和SQL Expression Language

SQL Expression Language可以独立于ORM使用,但ORM需要使用SQL Expression Language构建。

 

要使用,必然要先安装,假定已经安装了python2.5并配置了路径,并下载了setuptools工具,然后可以使用命令:

easy_install SQLalchemy

该命令从Python Cheese Shop下载SQLalchemy并安装到系统。

 

SQLalchemy需要特定数据库的DB-API,根据你要操作的数据库,可以选择不同的DB-API库,比如:oracle需要安装cx_oracle。

 

为了检查是否安装正确可以输入以下命令来检查已安装的sqlalchemy版本:

>>>import sqlalchemy

>>>sqlalchemy.__version__

0.5.5

 

注意:练习均以内存SQLite数据库为对象

 

>>> from sqlalchemy import create_engine
>>> engine=create_engine('sqlite:///:memory:',echo=True)

用create_engine建立一个数据库链接,eacho标志为True则会利用python标准logging模块输入产生的SQL过程。

 

 

>>> from sqlalchemy import Table,Column,Integer,String,MetaData,ForeignKey
>>> metadata=MetaData()
>>> users_table=Table('users',metadata,
          Column('id',Integer,primary_key=True),
          Column('name',String),
          Column('fullname',String),
          Column('password',String)
          )

创建表的模式

 

>>> metadata.create_all(engine)
2009-08-26 16:06:02,812 INFO sqlalchemy.engine.base.Engine.0x...bbb0 PRAGMA table_info("users")
2009-08-26 16:06:02,828 INFO sqlalchemy.engine.base.Engine.0x...bbb0 ()
2009-08-26 16:06:02,842 INFO sqlalchemy.engine.base.Engine.0x...bbb0
CREATE TABLE users (
    id INTEGER NOT NULL,
    name VARCHAR,
    fullname VARCHAR,
    password VARCHAR,
    PRIMARY KEY (id)
)

2009-08-26 16:06:02,858 INFO sqlalchemy.engine.base.Engine.0x...bbb0 ()
2009-08-26 16:06:02,875 INFO sqlalchemy.engine.base.Engine.0x...bbb0 COMMIT

使用MetaDATA的create_all方法,并传入engine实例,在数据库中创建表。

 

class User(object):
    def __init__(self,name,fullname,password):
        self.name=name
        self.fullname=fullname
        self.password=password

    def __repr__(self):
        return "<User('%s','%s','%s')>" % (self.name,self.fullname,self.password)

Table对象定义了数据库的表信息,但没有定义行为,我可以创建一个类来定义行为,这个类是object的子类,类中定义了__init__和__repr__两个方法,这两个方法是可选的,SQLAlchemy从不直接调用__init__()。

 

>>> mapper(User,users_table)
<Mapper at 0x13b4250; User>

现在通过mapper创建了一个mapper对象将User和users_table进行映射关联。

 

>>> ed_user=User('ed','Ed Jones','edspassword')
>>> ed_user.name
'ed'
>>> ed_user.password
'edspassword'
>>> str(ed_user.id)
'None'
上面代码测试了User类,在该类的__init__()方法中没有定义id属性,但id列已经存在于users_table对象,通常情况下mapper会为Table中的所有列创建类属性。

 

前面的代码分别建立了Table,Class和Mapper,也可以在一次声明中创建它们.

from sqlalchemy import Table,Column,Integer,String,MetaData,ForeignKey
from sqlalchemy.ext.declarative import declarative_base

Base=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):
        self.name=name
        self.fullname=fullname
        self.password=password

    def __repr__(self):
        return "<User('%s','%s','%s')>" % (self.name,self.fullname,self.password)

users_table=User.__table__  #用来获得Table
metadata=Base.metadata  #获得MetaDATA

 

 

创建Session并绑定一个数据库链接

from sqlalchemy.orm import sessionmaker

Session=sessionmaker(bind=engine)

 

如果没有数据库链接,可以这样创建session

Session=sessionmaker()

 

当后来由数据库链接后可以这样绑定

Session.configure(bind=engine)

 

我理解session就是一个事务,它在提交和关闭前,维护着一个数据库链接。

 

>>> ed_user = User(’ed’, ’Ed Jones’, ’edspassword’)
>>> session.add(ed_user)

通过session的add方法添加一个对象

 

>>> our_user = session.query(User).filter_by(name=’ed’).first()
BEGIN
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
[’ed’, ’Ed Jones’, ’edspassword’]
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS user_password
FROM users
WHERE users.name = ?
LIMIT 1 OFFSET 0
[’ed’]

>>> our_user
<User(’ed’,’Ed Jones’, ’edspassword’)>

使用session来查询刚才创建的记录

 

>>>ed_user is our_user

True

实际上Session返回的our_user和ed_user是同一个对象,session对于同一个主关键字只有一个对象与之对应,如果想创建第二个,将会发生错误。

 

>>> session.add_all([
... User(’wendy’, ’Wendy Williams’, ’foobar’),
... User(’mary’, ’Mary Contrary’, ’xxg527’),
... User(’fred’, ’Fred Flinstone’, ’blah’)])

使用session的add_all方法添加多个记录

 

>>> ed_user.password = ’f8s7ccs’

当Ed这个用户发现他的密码不安全时,可以这样修改密码  :)

 

>>> session.dirty
IdentitySet([<User(’ed’,’Ed Jones’, ’f8s7ccs’)>])

由于修改了ed的密码,所以该记录成为了脏数据,可以这样查看脏数据,session对象很聪明,它什么都知道  :)

 

>>> session.new
IdentitySet([<User(’wendy’,’Wendy Williams’, ’foobar’)>,
<User(’mary’,’Mary Contrary’, ’xxg527’)>,
<User(’fred’,’Fred Flinstone’, ’blah’)>])

session还知道刚才添加的3个新数据

 

>>> session.commit()
UPDATE users SET password=? WHERE users.id = ?
[’f8s7ccs’, 1]
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
[’wendy’, ’Wendy Williams’, ’foobar’]
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
[’mary’, ’Mary Contrary’, ’xxg527’]
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
[’fred’, ’Fred Flinstone’, ’blah’]
COMMIT

现在是时候提交事务并将改变应用到数据库当中了,就这样,很简单吧 :)

提交后,session将连接释放回连接池,下次需要操作需要新建一个事务了。

 

>>> ed_user.id
BEGIN
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.
FROM users
WHERE users.id = ?
[1]1

现在查看Ed用户的id就不是之前的NONE了,它有了自己的值。

 

>>> ed_user.name = ’Edwardo’

>>> fake_user = User(’fakeuser’, ’Invalid’, ’12345’)
>>> session.add(fake_user)

为了继续做实验,我们先修改一个用户的name,再添加一个fake用户

 

>>> session.query(User).filter(User.name.in_([’Edwardo’, ’fakeuser’])).all()
UPDATE users SET name=? WHERE users.id = ?
[’Edwardo’, 1]
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
[’fakeuser’, ’Invalid’, ’12345’]
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, user_password
FROM users
WHERE users.name IN (?, ?)
[’Edwardo’, ’fakeuser’][<User(’Edwardo’,’Ed Jones’, ’f8s7ccs’)>, <User(’fakeuser’,’Invalid’,’12345’>

我们执行一个查询操作会导致前面的的事务被提交。

 

>>> session.rollback()
ROLLBACK>>> ed_user.name
BEGIN
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users_password
FROM users
WHERE users.id = ?
[1]u’ed’
>>> fake_user in session
False

我们回滚一下,ed_user的名字又变为ed,而fake_user被撤销了。

 

>>> session.query(User).filter(User.name.in_([’ed’, ’fakeuser’])).all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.p
FROM users
WHERE users.name IN (?, ?)
[’ed’, ’fakeuser’][<User(’ed’,’Ed Jones’, ’f8s7ccs’)>]

不信的话,再执行以下SELECT查询看看数据库是什么样的。

 

>>> for instance in session.query(User).order_by(User.id):
... print instance.name, instance.fullname
SELECT users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname, users.password AS users_password
FROM users ORDER BY users.id
[]ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
fred Fred Flinstone

使用session的query方法对数据库执行一下排序查询,并把它们打印出来

 

>>> for name, fullname in session.query(User.name, User.fullname):
... print name, fullname
SELECT users.name AS users_name, users.fullname AS users_fullname
FROM users
[]ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
fred Fred Flinstone

session的query方法可以接受多种参数,当然也可以这样查询字段

 

>>> for row in session.query(User, User.name).all():
... print row.User, row.name
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users_password
FROM users
[]<User(’ed’,’Ed Jones’, ’f8s7ccs’)> ed
<User(’wendy’,’Wendy Williams’, ’foobar’)> wendy
<User(’mary’,’Mary Contrary’, ’xxg527’)> mary
<User(’fred’,’Fred Flinstone’, ’blah’)> fred

用query方法返回全部记录和name字段。

 

>>> from sqlalchemy.orm import aliased
>>> user_alias = aliased(User, name=’user_alias’)
>>> for row in session.query(user_alias, user_alias.name.label(’name_label’)).all():
... print row.user_alias, row.name_label
SELECT users_1.id AS users_1_id, users_1.name AS users_1_name, users_1.fullname AS users_1_fullname
FROM users AS users_1
[]
<User(’ed’,’Ed Jones’, ’f8s7ccs’)> ed
<User(’wendy’,’Wendy Williams’, ’foobar’)> wendy
<User(’mary’,’Mary Contrary’, ’xxg527’)> mary
<User(’fred’,’Fred Flinstone’, ’blah’)> fred

在query方法中使用aliased()和label(),可以创建别名和字段标签


 

>>> for u in session.query(User).order_by(User.id)[1:3]:
... print u
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, user.password AS user_password
FROM users ORDER BY users.id
LIMIT 2 OFFSET 1
[]<User(’wendy’,’Wendy Williams’, ’foobar’)>
<User(’mary’,’Mary Contrary’, ’xxg527’)>

这个厉害了,竟然可以对查询的结果做切片操作

 

>>> for name, in session.query(User.name).filter_by(fullname=’Ed Jones’):
... print name
SELECT users.name AS users_name FROM users
WHERE users.fullname = ?
[’Ed Jones’]ed

根据字段值条件来查询记录

 

>>> for name, in session.query(User.name).filter(User.fullname==’Ed Jones’):
... print name
SELECT users.name AS users_name FROM users
WHERE users.fullname = ?
[’Ed Jones’]ed

之前的查询还可以这样写,注意红色部分

 

>>> for user in session.query(User).filter(User.name==’ed’).filter(User.fullname==’Ed Jones’)
... print user
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, use
FROM users
WHERE users.name = ? AND users.fullname = ?
[’ed’, ’Ed Jones’]<User(’ed’,’Ed Jones’, ’f8s7ccs’)>

如果又要查name字段又要查fullname字段,其实就是AND关系啦,两次调用filter就是AND关系啦。

 

下面蓝色的部分就是常用的过滤操作,很常用的哦

• equals:
query.filter(User.name == ’ed’)   #等于
• not equals:
query.filter(User.name != ’ed’)  #不等于
• LIKE:
query.filter(User.name.like(’%ed%’))  #LIKE
• IN:
query.filter(User.name.in_([’ed’, ’wendy’, ’jack’]))  #in操作
• IS NULL:
filter(User.name == None)  #IS NULL
• AND:
from sqlalchemy import and_
filter(and_(User.name == ’ed’, User.fullname == ’Ed Jones’))  #AND操作

filter(User.name == ’ed’).filter(User.fullname == ’Ed Jones’)  #另一种AND操作方式为多次调用filter()或filter_by()
• OR:

from sqlalchemy import or_
filter(or_(User.name == ’ed’, User.name == ’wendy’))  #OR操作
• match:
query.filter(User.name.match(’wendy’))  #匹配,这个目前我不太清楚,不知道和LIKE有啥功能上的区别

 

>>> query = session.query(User).filter(User.name.like(’%ed’)).order_by(User.id)
>>> query.all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE users.name LIKE ? ORDER BY users.id
[’%ed’][<User(’ed’,’Ed Jones’, ’f8s7ccs’)>, <User(’fred’,’Fred Flinstone’, ’blah’)>]

使用query.all()获得一个记录列表。

 

>>>query.first()

只返回第一条记录

 

>>> from sqlalchemy.orm.exc import MultipleResultsFound
>>> try:
... user = query.one()
... except MultipleResultsFound, e:
... print e

[’%ed’]Multiple rows were found for one()

这个one()我就不太理解了,你要知道请留言告诉我,我从精神上感谢你,但是one()是会引发异常的,小心使用!

 

>>> from sqlalchemy.orm.exc import NoResultFound
>>> try:
... user = query.filter(User.id == 99).one()
... except NoResultFound, e:
... print e

[’%ed’, 99]No row was found for one()

这个和上面的一样,我不知道,嘿嘿,你知道一定要告诉我哦~

 

>>> for user in session.query(User).filter("id<224").order_by("id").all():
... print user.name

在filter()和orader_by()中可以直接使用SQL字符串。

 

>>> session.query(User).filter("id<:value and name=:name").\
... params(value=224, name=’fred’).order_by(User.id).one()

可以使用params()来传递参数

 

>>> session.query(User).from_statement("SELECT * FROM users where name=:name").params(name=’ed’).all()

这个就对于熟悉SQL的用户很方便,不过你要保证SQL语句中的字段名是被映射过的。

 

现在我们该引入第2个表了,每个用户都可能有多个email地址与用户名关联,这是一个典型的1对多的关系,下面先创建这个包含email地址的addresses表

>>> from sqlalchemy import ForeignKey
>>> from sqlalchemy.orm import relation, backref
>>> class Address(Base):
... __tablename__ = ’addresses’
... id = Column(Integer, primary_key=True)
... email_address = Column(String, nullable=False)
... user_id = Column(Integer, ForeignKey(’users.id’))
...
... user = relation(User, backref=backref(’addresses’, order_by=id))
...
... def __init__(self, email_address):

... self.email_address = email_address
...
... def __repr__(self):
... return "<Address(’%s’)>" % self.email_address

代码的缩进格式已经完蛋了,索性不管他了,我想缩进不会难倒任何python程序员的,这段代码大部分已经在前面学习了,黑体部分是现在要学习的。

其中上面的代码是在数据库级别在两个表之间建立关系,ForeignKey增加一个user_id来关联users.id属性,建立从Address到User的多对1关系,增加一个user属性,同样给User类增加一个addresses集合,backref建立从user从User到Address是1对多的关系。

 

class User(Base):
# ....
addresses = relation(Address, order_by=Address.id, backref="user")

relation为User类定义一个关系。

 

>>> metadata.create_all(engine)
PRAGMA table_info("users")
()
PRAGMA table_info("addresses")
()
CREATE TABLE addresses (
id INTEGER NOT NULL,
email_address VARCHAR NOT NULL,
user_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
)
()
COMMIT

再次create_all(),已经存在的表将被忽略。

 

>>> jack = User(’jack’, ’Jack Bean’, ’gjffdd’)
>>> jack.addresses
[]

现在只有一个有数据的User和没有数据的Addresses,查询后就是这个样子的 :(

 

>>> jack.addresses = [Address(email_address=’jack@google.com’), Address(email_address=’j25@yahoo.com’)

可以像这样为用户添加一个或多个邮箱。

 

>>> jack.addresses[1]
<Address(’j25@yahoo.com’)>

这样就可以返回一个与用户关联的邮箱地址


>>> jack.addresses[1].user
<User(’jack’,’Jack Bean’, ’gjffdd’)>

通过邮箱地址还可以返回这个用户呢!赞!

 

>>> session.add(jack)
>>> session.commit()
INSERT INTO users (name, fullname, password) VALUES (?, ?, ?)
[’jack’, ’Jack Bean’, ’gjffdd’]
INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
[’jack@google.com’, 5]
INSERT INTO addresses (email_address, user_id) VALUES (?, ?)
[’j25@yahoo.com’, 5]
COMMIT

先把提交jack到数据库中,这将会导致插入1个用户记录和两个相关的邮箱记录。

 

>>> jack = session.query(User).filter_by(name=’jack’).one()

现在查询一下数据库里的jack记录

 

>>> jack.addresses

再通过查询出的jack来查询相关联的邮箱记录

 

>>> from sqlalchemy.orm import eagerload
>>> jack = session.query(User).options(eagerload(’addresses’)).filter_by(name=’jack’).one()

>>> jack
<User(’jack’,’Jack Bean’, ’gjffdd’)>
>>> jack.addresses
[<Address(’jack@google.com’)>, <Address(’j25@yahoo.com’)>]

这段代码其实和之前的两段代码干的一个事,不过之前的代码的user和address是在取值时才从数据库取数据,而这段代码是一次取回数据的。

>>> for u, a in session.query(User, Address).filter(User.id==Address.user_id).\
... filter(Address.email_address==’jack@google.com’).all():
... print u, a

这个好理解,就是执行一个多表查询。

 

 

>>> from sqlalchemy.orm import join
>>> session.query(User).select_from(join(User, Address)).\
... filter(Address.email_address==’jack@google.com’).all()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, user.password=user_password
FROM users JOIN addresses ON users.id = addresses.user_id
WHERE addresses.email_address = ?
[’jack@google.com’][<User(’jack’,’Jack Bean’, ’gjffdd’)>]

使用join()来执行JOIN查询。

 

join()知道如何join表User和Address,因为这两表之间只有一个外键,如果他们咩有外键或有多个外键,则需要这样设置join()

join(User, Address, User.id==Address.user_id) # 描述关系

join(User, Address, User.addresses) # 从左到右指定relation

join(User, Address, ’addresses’) # 同上,这是字符串表示方法

 

>>> session.query(User).join(User.addresses).\
... filter(Address.email_address==’jack@google.com’).all()

[’jack@google.com’][<User(’jack’,’Jack Bean’, ’gjffdd’)>]

这是join()一种更简单的写法。

 

session.query(User).join((Address, User.addresses))

join()的其他写法

 

session.query(Foo).join(Foo.bars, Bar.bats, (Bat, ’widgets’))

这是多个JOIN的表示方法,SQL类似这样:foo JOIN bars ON <onclause> JOIN bats ON<onclause> JOIN widgets ON <onclause>

 

>>> from sqlalchemy.orm import aliased
>>> adalias1 = aliased(Address)
>>> adalias2 = aliased(Address)
>>> for username, email1, email2 in \
... session.query(User.name, adalias1.email_address, adalias2.email_address).\
... join((adalias1, User.addresses), (adalias2, User.addresses)).\

... filter(adalias1.email_address==’jack@google.com’).\
... filter(adalias2.email_address==’j25@yahoo.com’):
... print username, email1, email2
SELECT users.name AS users_name, addresses_1.email_address AS addresses_1_email_address,
addresses_2.email_address AS addresses_2_email_address
FROM users JOIN addresses AS addresses_1 ON users.id = addresses_1.user_id
JOIN addresses AS addresses_2 ON users.id = addresses_2.user_id
WHERE addresses_1.email_address = ? AND addresses_2.email_address = ?
[’jack@google.com’, ’j25@yahoo.com’]jack jack@google.com j25@yahoo.com

使用别名同时来获取多个不同的值

 

 

SELECT users.*, adr_count.address_count FROM users LEFT OUTER JOIN
(SELECT user_id, count(*) AS address_count FROM addresses GROUP BY user_id) AS adr_count
ON users.id=adr_count.user_id

上面的子查询SQL可以写作下面的代码

>>> from sqlalchemy.sql import func
>>> stmt = session.query(Address.user_id, func.count(’*’).label(’address_count’)).group_by(Address.user_id)

 

 

>>> for u, count in session.query(User, stmt.c.address_count).\
... outerjoin((stmt, User.id==stmt.c.user_id)).order_by(User.id):
... print u, count
SELECT users.id AS users_id, users.name AS users_name,
users.fullname AS users_fullname, users.password AS users_password,
anon_1.address_count AS anon_1_address_count
FROM users LEFT OUTER JOIN (SELECT addresses.user_id AS user_id, count(?) AS address_count
FROM addresses GROUP BY addresses.user_id) AS anon_1 ON users.id = anon_1.user_id
ORDER BY users.id
[’*’]<User(’ed’,’Ed Jones’, ’f8s7ccs’)> None
<User(’wendy’,’Wendy Williams’, ’foobar’)> None
<User(’mary’,’Mary Contrary’, ’xxg527’)> None
<User(’fred’,’Fred Flinstone’, ’blah’)> None
<User(’jack’,’Jack Bean’, ’gjffdd’)> 2
左外连接

 

>>> stmt = session.query(Address).filter(Address.email_address != ’j25@yahoo.com’).subquery()

>>> adalias = aliased(Address, stmt)

>>> for user, address in session.query(User, adalias).join((adalias, User.addresses)):
... print user, address
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname,
users.password AS users_password, anon_1.id AS anon_1_id,
anon_1.email_address AS anon_1_email_address, anon_1.user_id AS anon_1_user_id
FROM users JOIN (SELECT addresses.id AS id, addresses.email_address AS email_address, addresses.user_id
FROM addresses
WHERE addresses.email_address != ?) AS anon_1 ON users.id = anon_1.user_id
[’j25@yahoo.com’]<User(’jack’,’Jack Bean’, ’gjffdd’)> <Address(’jack@google.com’)>

在子查询中使用别名

 

>>> from sqlalchemy.sql import exists
>>> stmt = exists().where(Address.user_id==User.id)
>>> for name, in session.query(User.name).filter(stmt):
... print name
SELECT users.name AS users_name
FROM users
WHERE EXISTS (SELECT *
FROM addresses
WHERE addresses.user_id = users.id)
[]jack

EXISTS关键字的使用

 

>>> for name, in session.query(User.name).filter(User.addresses.any()):
... print name
SELECT users.name AS users_name
FROM users
WHERE EXISTS (SELECT 1
FROM addresses
WHERE users.id = addresses.user_id)
[]jack

any()的使用

 

>>> for name, in session.query(User.name).\
... filter(User.addresses.any(Address.email_address.like(’%google%’))):

... print name
SELECT users.name AS users_name
FROM users
WHERE EXISTS (SELECT 1
FROM addresses
WHERE users.id = addresses.user_id AND addresses.email_address LIKE ?)
[’%google%’]jack

any()的使用

 

>>> session.query(Address).filter(~Address.user.has(User.name==’jack’)).all()
SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address,
addresses.user_id AS addresses_user_id
FROM addresses
WHERE NOT (EXISTS (SELECT 1
FROM users
WHERE users.id = addresses.user_id AND users.name = ?))
[’jack’][]

has()用法

 

常用的关系操作,如下蓝色部分:

• equals (用于多对一):
query.filter(Address.user == someuser)
• not equals (用于多对一):
query.filter(Address.user != someuser)
• IS NULL (用于多对一):
query.filter(Address.user == None)
• contains (用于一对多和多对多集合):
query.filter(User.addresses.contains(someaddress))
• any (用于一对多和多对多集合):
query.filter(User.addresses.any(Address.email_address == ’bar’))
# 提供关键字参数:
query.filter(User.addresses.any(email_address=’bar’))
• has (用于多对一 ):
query.filter(Address.user.has(name=’ed’))
• with_parent (用于任何关系):
session.query(Address).with_parent(someuser, ’addresses’)

 

>>> session.delete(jack)
>>> session.query(User).filter_by(name=’jack’).count()
UPDATE addresses SET user_id=? WHERE addresses.id = ?
[None, 1]
UPDATE addresses SET user_id=? WHERE addresses.id = ?
[None, 2]
DELETE FROM users WHERE users.id = ?
[5]
SELECT count(1) AS count_1
FROM users
WHERE users.name = ?
[’jack’]0

删除jack,并统计删除后的user表记录数

 

>>> session.query(Address).filter(
... Address.email_address.in_([’jack@google.com’, ’j25@yahoo.com’])
... ).count()
SELECT count(1) AS count_1
FROM addresses
WHERE addresses.email_address IN (?, ?)
[’jack@google.com’, ’j25@yahoo.com’]2

删除jack后再查询一下Address表,看看jack关联的邮箱记录是否存在,哦,他们依然存在,可恶!他们的user_id仅仅是被赋值为NULL

 

>>> session.close() # 回滚并关闭事务
>>> from sqlalchemy.orm import clear_mappers
>>> clear_mappers() #清除映射

为了能在删除用户的时候删除关联的邮箱记录,需要关闭session然后清除映射,并重新配置

>>> mapper(User, users_table, properties={
... ’addresses’:relation(Address, backref=’user’, cascade="all, delete, delete-orphan")

... })
<Mapper at 0x...; User>
>>> addresses_table = Address.__table__
>>> mapper(Address, addresses_table)
<Mapper at 0x...; Address>

下面重新配置映射,关键是cascade的值,然后重新映射Address和addresses_table

 

# load Jack by primary key
>>> jack = session.query(User).get(5)
BEGIN
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password
FROM users
WHERE users.id = ?
[5]# remove one Address (lazy load fires off)
>>> del jack.addresses[1]
SELECT addresses.id AS addresses_id, addresses.email_address AS addresses_email_address, addresses.user
FROM addresses
WHERE ? = addresses.user_id
[5]# only one address remains
>>> session.query(Address).filter(
... Address.email_address.in_([’jack@google.com’, ’j25@yahoo.com’])
... ).count()
DELETE FROM addresses WHERE addresses.id = ?
[2]
SELECT count(1) AS count_1
FROM addresses
WHERE addresses.email_address IN (?, ?)
[’jack@google.com’, ’j25@yahoo.com’]1

通过重新配置并映射,现在删除jack的addresses集合里的信箱地址时,数据库里也会被删除掉。

 

>>> session.delete(jack)
>>> session.query(User).filter_by(name=’jack’).count()
DELETE FROM addresses WHERE addresses.id = ?
[1]
DELETE FROM users WHERE users.id = ?
[5]
SELECT count(1) AS count_1
FROM users
WHERE users.name = ?
[’jack’]0

删除jack这个用户,剩余的相关邮箱记录也被删除

 

>>> session.query(Address).filter(
... Address.email_address.in_([’jack@google.com’, ’j25@yahoo.com’])
... ).count()
SELECT count(1) AS count_1
FROM addresses
WHERE addresses.email_address IN (?, ?)
[’jack@google.com’, ’j25@yahoo.com’]0

再查询一下数据库,看看jack的信箱记录是否还存在?

 

如果要写一个BLOG程序,就会用到多对多的关系,比如:日志和关键字之间的关系

>>> from sqlalchemy import Text
>>> # 关系表
>>> post_keywords = Table(’post_keywords’, metadata,
... Column(’post_id’, Integer, ForeignKey(’posts.id’)),
... Column(’keyword_id’, Integer, ForeignKey(’keywords.id’))
... )

>>> class BlogPost(Base):   #日志类
... __tablename__ = ’posts’
...
... id = Column(Integer, primary_key=True)
... user_id = Column(Integer, ForeignKey(’users.id’))
... headline = Column(String(255), nullable=False)
... body = Column(Text)
...
... # many to many BlogPost<->Keyword
... keywords = relation(’Keyword’, secondary=post_keywords, backref=’posts’)
...
... def __init__(self, headline, body, author):
... self.author = author
... self.headline = headline
... self.body = body
...
... def __repr__(self):
... return "BlogPost(%r, %r, %r)" % (self.headline, self.body, self.author)

>>> class Keyword(Base):   #关键字类
... __tablename__ = ’keywords’
...
... id = Column(Integer, primary_key=True)
... keyword = Column(String(50), nullable=False, unique=True)
...
... def __init__(self, keyword):
... self.keyword = keyword

 

>>> from sqlalchemy.orm import backref
>>> # "dynamic" loading relation to User
>>> BlogPost.author = relation(User, backref=backref(’posts’, lazy=’dynamic’))  #动态设置关系

>>> metadata.create_all(engine)  #创建表
PRAGMA table_info("users")
()
PRAGMA table_info("addresses")
()
PRAGMA table_info("posts")
()
PRAGMA table_info("keywords")
()
PRAGMA table_info("post_keywords")
()
CREATE TABLE posts (
id INTEGER NOT NULL,
user_id INTEGER,
headline VARCHAR(255) NOT NULL,
body TEXT,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
)
()
COMMIT
CREATE TABLE keywords (
id INTEGER NOT NULL,
keyword VARCHAR(50) NOT NULL,
PRIMARY KEY (id),
UNIQUE (keyword)
)
()

COMMIT
CREATE TABLE post_keywords (
post_id INTEGER,
keyword_id INTEGER,
FOREIGN KEY(post_id) REFERENCES posts (id),
FOREIGN KEY(keyword_id) REFERENCES keywords (id)
)
()
COMMIT

#下面先写一个日志

>>> wendy = session.query(User).filter_by(name=’wendy’).one()
SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password FROM users
WHERE users.name = ?
LIMIT 2 OFFSET 0
[’wendy’]>>> post = BlogPost("Wendy’s Blog Post", "This is a test", wendy)
>>> session.add(post)

#给日志添加两个关键字

>>> post.keywords.append(Keyword(’wendy’))
>>> post.keywords.append(Keyword(’firstpost’))

#查找一个包含firstpost的日志

>>> session.query(BlogPost).filter(BlogPost.keywords.any(keyword=’firstpost’)).all()
INSERT INTO keywords (keyword) VALUES (?)
[’wendy’]
INSERT INTO keywords (keyword) VALUES (?)
[’firstpost’]
INSERT INTO posts (user_id, headline, body) VALUES (?, ?, ?)
[2, "Wendy’s Blog Post", ’This is a test’]
INSERT INTO post_keywords (post_id, keyword_id) VALUES (?, ?)
[[1, 2], [1, 1]]
SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.bod
FROM posts
WHERE EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword =
[’firstpost’][BlogPost("Wendy’s Blog Post", ’This is a test’, <User(’wendy’,’Wendy Williams’, ’foobar’)>

#查找作者为wendy的日志

>>> session.query(BlogPost).filter(BlogPost.author==wendy).\
... filter(BlogPost.keywords.any(keyword=’firstpost’)).all()
SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.bod
FROM posts
WHERE ? = posts.user_id AND (EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword =
[2, ’firstpost’][BlogPost("Wendy’s Blog Post", ’This is a test’, <User(’wendy’,’Wendy Williams’, ’foobar

#用wendy来查找

>>> wendy.posts.filter(BlogPost.keywords.any(keyword=’firstpost’)).all()
SELECT posts.id AS posts_id, posts.user_id AS posts_user_id, posts.headline AS posts_headline, posts.bod
FROM posts
WHERE ? = posts.user_id AND (EXISTS (SELECT 1
FROM post_keywords, keywords
WHERE posts.id = post_keywords.post_id AND keywords.id = post_keywords.keyword_id AND keywords.keyword =
[2, ’firstpost’][BlogPost("Wendy’s Blog Post", ’This is a test’, <User(’wendy’,’Wendy Williams’, ’foobar

 

 

这篇日志好长呀,写了3个小时,一些代码自己也动手测试了,今天的收获就是对SQLAlchemy的概貌有了一个了解,可以创建表(类)、映射、session,以及使用session做一些query。

今天只学习了38页,还有250页的资料再等着我,希望我能坚持下去!!!


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SQLAlchemy中,一对一关系可以通过ForeignKey和relationship来定义。假设我们有两个表,一个是User,一个是Profile,每个用户都有一个唯一的个人资料。我们可以按照以下步骤进行操作: 1. 定义表结构 ```python from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) profile = relationship("Profile", uselist=False, back_populates="user") class Profile(Base): __tablename__ = 'profiles' id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey('users.id')) email = Column(String) user = relationship("User", back_populates="profile") ``` 在这里,我们使用ForeignKey将Profile表与User表关联起来,并使用relationship定义了表之间的关系。在User表中,我们使用back_populates参数来指定Profile表与之关联的属性名,并将uselist参数设置为False,表示每个用户只有一个个人资料。 2. 创建Session并查询数据 ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine('sqlite:///example.db') Session = sessionmaker(bind=engine) session = Session() # 创建用户和个人资料对象并保存到数据库中 user = User(name='Alice') profile = Profile(email='[email protected]', user=user) session.add(user) session.commit() # 查询用户及其关联的个人资料信息 query = session.query(User).options( relationship(User.profile, uselist=False)).filter_by(name='Alice') # 打印查询结果 for user in query.all(): print(f"User: {user.name}, Email: {user.profile.email}") ``` 在这个例子中,我们首先创建了一个用户对象和一个个人资料对象,并通过relationship建立了它们之间的一对一关系。然后我们将它们保存到数据库中。接着,我们使用query.options()函数来指定我们要查询的属性,其中uselist参数设置为False,表示我们只需要查询每个用户的一个个人资料。最后,我们通过for循环遍历查询结果并打印出来。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值