SQLALchemy学习笔记(三)

Tips:在命令行操作这个命令过程中,如果出现以下的错误,不要紧张,可能是session已经失效,可以重新创建一个会话。
错误信息:

Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2980, in count
    return self.from_self(col).scalar()
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2749, in scalar
    ret = self.one()
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2718, in one
    ret = list(self)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2761, in __iter__
    return self._execute_and_instances(context)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2776, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 914, in execute
    return meth(self, multiparams, params)
  File "C:\Python27\lib\site-packages\sqlalchemy\sql\elements.py", line 323, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1010, in _execute_clauseelement
    compiled_sql, distilled_params
  File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1146, in _execute_context
    context)
  File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1341, in _handle_dbapi_exception
    exc_info
  File "C:\Python27\lib\site-packages\sqlalchemy\util\compat.py", line 202, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1139, in _execute_context
    context)
  File "C:\Python27\lib\site-packages\sqlalchemy\engine\default.py", line 450, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.InternalError: (psycopg2.InternalError) 
 [SQL: 'SELECT count(*) AS count_1 \nFROM (SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password \nFROM users \nWHERE users.name LIKE %(name_1)s) AS anon_1'] [parameters: {'name_1': '%e%'}]

查询

我们使用query()方法在会话中创建一个Query对象。

>>> for instance in session.query(User).order_by(User.id):
...     print instance.name, instance.fullname
...
2017-03-09 09:02:10,990 INFO sqlalchemy.engine.base.Engine 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
2017-03-09 09:02:10,990 INFO sqlalchemy.engine.base.Engine {}
ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
>>>

Query还可以接受表中的列名作为参数,这样查询返回的是tuples:

>>> for name, fullname in session.query(User.name, User.fullname):
...     print name, fullname
...
2017-03-09 09:04:20,615 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name, users.fullname AS users_fullname
FROM users
2017-03-09 09:04:20,615 INFO sqlalchemy.engine.base.Engine {}
ed Ed Jones
wendy Wendy Williams
mary Mary Contrary
>>>

还可以返回table中的记录行所映射的的类的对象

>>> for row in session.query(User, User.name).all():
...     print row.User, row.name
...
2017-03-09 09:07:41,134 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
2017-03-09 09:07:41,137 INFO sqlalchemy.engine.base.Engine {}
 ed
 wendy
 mary

使用all()方法返回的是一个列表,保存所有的查询结果。
您可以使用label()构造控制单个列表达式的名称,该构造可以从任何ColumnElement派生的对象获得,也可以使用任何映射到一个(例如User.name)的类属性

>>> for row in session.query(User.name.label('name_label')).all():
...     print row.name_label
...
2017-03-09 09:11:57,808 INFO sqlalchemy.engine.base.Engine SELECT users.name AS name_label
FROM users
2017-03-09 09:11:57,809 INFO sqlalchemy.engine.base.Engine {}
ed
wendy
mary

从上面的运行结果可以看出,我们给查询的列名定义了别名,使用别名就像使用原来的列名效果是一样的。
在数据库查询中,例如SELECT users(name, fullname) FROM users这样的SQL语句,users(name, fullname)就是我们想查询后显示的记录的列名。所以在学习SQLAlchemy查询语句的时候可以和SQL查询语句做个比较,这样理解起来更加容易。

基本的查询操作还包含限制和偏移量查询,最常用的就是使用Python的切片操作。

>>> for u in session.query(User).order_by(User.id)[1:3]:
...     print u
...
2017-03-09 09:25:35,711 INFO sqlalchemy.engine.base.Engine 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
 LIMIT %(param_1)s OFFSET %(param_2)s
2017-03-09 09:25:35,713 INFO sqlalchemy.engine.base.Engine {'param_1': 2, 'param_2': 1}


>>>

在查询到的结果进行过滤,得到我们想要的结果使用filter_by()

>>> for name in session.query(User.name).filter_by(fullname='Ed Jones'):
...     print name
...
2017-03-09 09:28:35,479 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name
FROM users
WHERE users.fullname = %(fullname_1)s
2017-03-09 09:28:35,479 INFO sqlalchemy.engine.base.Engine {'fullname_1': 'Ed Jones'}
(u'ed',)
>>>

或者使用filter().

>>> for name in session.query(User.name).filter(User.fullname=='Ed Jones'):
...     print name
...
2017-03-09 09:29:40,678 INFO sqlalchemy.engine.base.Engine SELECT users.name AS users_name
FROM users
WHERE users.fullname = %(fullname_1)s
2017-03-09 09:29:40,680 INFO sqlalchemy.engine.base.Engine {'fullname_1': 'Ed Jones'}
(u'ed',)
>>>

Query对象是完全生成的,意味着大多数方法调用返回一个新的Query对象,可以在其上添加更多的条件,我们可以调用两次filter()方法。

>>> for user in session.query(User).filter(User.name=='ed').filter(User.fullname=='Ed Jones'):
...     print user
...
2017-03-09 09:32:03,328 INFO sqlalchemy.engine.base.Engine 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 = %(name_1)s AND users.fullname = %(fullname_1)s
2017-03-09 09:32:03,328 INFO sqlalchemy.engine.base.Engine {'fullname_1': 'Ed Jones', 'name_1': 'ed'}

>>>
对比filter()filter_by()方法的区别。
  • filter() 的参数是一个boolean类型,所以它的参数不是一个表达式。
  • filter_by()的参数是一个表达式,而不是boolean值。

常用的Filter操作

  • equals:直接可以使用连登号表示==
>>> session.query(User).filter(User.name=='ed').all()
2017-03-09 09:41:05,974 INFO sqlalchemy.engine.base.Engine 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 = %(name_1)s
2017-03-09 09:41:05,976 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed'}
[]
  • Not equal: 可以使用不等号!表示
>>> session.query(User).filter(User.name!='ed').all()
2017-03-09 09:43:05,983 INFO sqlalchemy.engine.base.Engine 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 != %(name_1)s
2017-03-09 09:43:05,983 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed'}
[, ]
>>>
  • LIKE:%e%这样就可以匹配所有含有e字母的单词。
>>> session.query(User).filter(User.name.like('%we%')).all()
2017-03-09 09:44:48,046 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s
2017-03-09 09:44:48,048 INFO sqlalchemy.engine.base.Engine {'name_1': '%we%'}
[]
>>>
Note

ColumnOperators.like()是区分大小写的,如果想使用不区分大小写的就使用ilike

  • ILIKe:首字母I的含义我认为就是Ignore忽略的意思。
>>> session.query(User).filter(User.name.ilike('%We%')).all()
2017-03-09 09:47:38,999 INFO sqlalchemy.engine.base.Engine 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 ILIKE %(name_1)s
2017-03-09 09:47:39,000 INFO sqlalchemy.engine.base.Engine {'name_1': '%We%'}
[]
>>>
  • IN
>>> session.query(User).filter(User.name.in_(['ed', 'wendy'])).all()
2017-03-09 09:48:39,142 INFO sqlalchemy.engine.base.Engine 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 IN (%(name_1)s, %(name_2)s)
2017-03-09 09:48:39,144 INFO sqlalchemy.engine.base.Engine {'name_2': 'wendy', 'name_1': 'ed'}
[, ]
>>>
  • NOT IN和In相反的就是不在给定的列表中,但是在实现时有所不同,注意那个符号
>>> session.query(User).filter(~User.name.in_(['ed', 'wendy'])).all()
2017-03-09 09:49:41,615 INFO sqlalchemy.engine.base.Engine 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 NOT IN (%(name_1)s, %(name_2)s)
2017-03-09 09:49:41,617 INFO sqlalchemy.engine.base.Engine {'name_2': 'wendy', 'name_1': 'ed'}
[]
>>>
  • IS NULL
>>> session.query(User).filter(User.name==None)
或者使用下面的方法,两种方法是等效的
query.filter(User.name.isnot(None))
  • AND
# 使用 and_()
>>> from sqlalchemy import and_
>>> 
session.query(User).filter(and_(User.name=='ed', User.fullname=='Ed Jones'))

# 直接传给filter一个元组
>>> session.query(User).filter(User.name=='ed', User.fullname=='Ed Jones')

# 或者使用多个过滤器组成的链来实现
>>> session.query(User).filter(User.name=='ed').filter(User.fullname=='Ed Jones')

>>>
  • OR
>>> print session.query(User).filter(or_(User.name=='ed', User.name=='wendy'))
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 = :name_1 OR users.name = :name_2
>>>
Note

这里使用的or_()不是python中的or

返回列表或者标量

  • all()返回一个列表
>>> query = session.query(User).filter(User.name.like('%ed')).order_by(User.id)
>>> query.all()
2017-03-09 10:06:01,701 INFO sqlalchemy.engine.base.Engine select version()
2017-03-09 10:06:01,701 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,703 INFO sqlalchemy.engine.base.Engine select current_schema()
2017-03-09 10:06:01,704 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,706 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2017-03-09 10:06:01,707 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,707 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2017-03-09 10:06:01,707 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,709 INFO sqlalchemy.engine.base.Engine show standard_conforming_strings
2017-03-09 10:06:01,710 INFO sqlalchemy.engine.base.Engine {}
2017-03-09 10:06:01,710 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2017-03-09 10:06:01,711 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s ORDER BY users.id
2017-03-09 10:06:01,713 INFO sqlalchemy.engine.base.Engine {'name_1': '%ed'}
[]
  • first()限制返回第一个结果作为标
>>> query.first()
2017-03-09 10:07:46,233 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s ORDER BY users.id
 LIMIT %(param_1)s
2017-03-09 10:07:46,234 INFO sqlalchemy.engine.base.Engine {'name_1': '%ed', 'param_1': 1}

>>>
  • one()完全获取所有行,如果结果中不存在一个对象标识或复合行,则会引发错误。
>>> query = session.query(User).filter(User.name.like('%e%')).order_by(User.id)
>>> user = query.one()
2017-03-09 10:10:34,095 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s ORDER BY users.id
2017-03-09 10:10:34,095 INFO sqlalchemy.engine.base.Engine {'name_1': '%e%'}
Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2727, in one
    "Multiple rows were found for one()")
sqlalchemy.orm.exc.MultipleResultsFound: Multiple rows were found for one()
>>>

我们使用like查找到多行记录,但是使用one出现错误,意思是有多行记录被one找到。
下面我们修改一下,让查找结果为空

>>> query = session.query(User).filter(User.name.like('%haha%')).order_by(User.id)
>>> query.one()
2017-03-09 10:12:14,464 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s ORDER BY users.id
2017-03-09 10:12:14,464 INFO sqlalchemy.engine.base.Engine {'name_1': '%haha%'}
Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2724, in one
    raise orm_exc.NoResultFound("No row was found for one()")
sqlalchemy.orm.exc.NoResultFound: No row was found for one()
>>>

one()方法对于希望处理“没有找到项目”与“找到多个项目”的系统有所不同;例如RESTful Web服务,当没有找到结果时,它可能想要引发“404未找到”,但在找到多个结果时引发应用程序错误。

  • one_or_none()有点和one()很相似,但是它在没有查询到结果的时候不会返回错误信息,而是返回None,但是当查找到多个结果时,它会抛出异常。
>>> query = session.query(User).filter(User.name.like('%haha%')).order_by(User.id)
>>> query.one_or_none()
2017-03-09 10:15:23,055 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s ORDER BY users.id
2017-03-09 10:15:23,056 INFO sqlalchemy.engine.base.Engine {'name_1': '%haha%'}

上面没有查到结果,也没有返回错误。

>>> query = session.query(User).filter(User.name.like('%e%')).order_by(User.id)
>>> query.one_or_none()
2017-03-09 10:16:31,351 INFO sqlalchemy.engine.base.Engine 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 %(name_1)s ORDER BY users.id
2017-03-09 10:16:31,351 INFO sqlalchemy.engine.base.Engine {'name_1': '%e%'}
Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\query.py", line 2698, in one_or_none
    "Multiple rows were found for one_or_none()")
sqlalchemy.orm.exc.MultipleResultsFound: Multiple rows were found for one_or_none()

这个查询到多个记录,使用one_or_none()返回了错误。

  • scalar()调用one()方法,并且在成功时返回该行的在表中的位置
>>> query = session.query(User.id).filter(User.name=='ed').order_by(User.id)
>>> query.scalar()
2017-03-09 10:24:43,888 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id
FROM users
WHERE users.name = %(name_1)s ORDER BY users.id
2017-03-09 10:24:43,888 INFO sqlalchemy.engine.base.Engine {'name_1': 'ed'}
1

使用文本SQL

文字字符串可以灵活使用Query,通过指定text()结构的使用,文本sql能被很多方法接受,比如filter() order_by()

>>> for user in session.query(User).filter(text("id<2")).order_by(text("id")).all():
...     print usr.name
...
2017-03-09 10:30:36,473 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE id<2 ORDER BY id
2017-03-09 10:30:36,476 INFO sqlalchemy.engine.base.Engine {}
ed
>>>

在没有使用文本SQL时,我们的filter()和order_by()中都要指定类名User

绑定参数可以使用基于字符串的SQL指定,使用冒号。要指定值,使用params()方法。这个方法可以类比在java中操作数据库 时,定义PreparedStatement对象一样,可以使用?来做占位符,在后面来讲这个问号多代表的值赋值给问号。

>>> session.query(User).filter(text("id < :value and name = :name")).params(value=2, name='ed').order_by(User.id).one()
2017-03-09 10:37:25,960 INFO sqlalchemy.engine.base.Engine SELECT users.id AS users_id, users.name AS users_name, users.fullname AS users_fullname, users.password AS users_password
FROM users
WHERE id < %(value)s and name = %(name)s ORDER BY users.id
2017-03-09 10:37:25,960 INFO sqlalchemy.engine.base.Engine {'name': 'ed', 'value': 2}

>>>

上面的查询语句中,我们使用:变量名起占位符的作用,然后在params中使用关键字参数形式将占位符所代表的的值赋给它。

要使用完全基于字符串的语句,可以将表示完整语句的text()结构传递给from_statement()。如果没有其他说明符,字符串SQL中的列将基于名称与模型列匹配,如下所示,其中我们仅使用星号来表示加载所有列。

>>> session.query(User).from_statement(text("SELECT * FROM users WHERE name=:name")).params(name='ed').all()
2017-03-09 10:41:43,762 INFO sqlalchemy.engine.base.Engine SELECT * FROM users WHERE name=%(name)s
2017-03-09 10:41:43,762 INFO sqlalchemy.engine.base.Engine {'name': 'ed'}
[]
>>>

这样就可以将完整的SQL语句通过text()传给from_statement

上面的只是使用*号模糊匹配了所有列,但是我们想查询某些具体列怎么办?
可是使用下面的方法

>>>stmt = text("SELECT name, fullname FROM users 
WHERE name=:name")
>>>stmt = stmt.columns(User.name, User.fullname)
>>> session.query(User).from_statement(stmt).params(name="ed").all()

计数Counting

Query包含了一个方便的计数count方法:

>>> print session.query(User).filter(User.name.like("%e%")).count()
2017-03-09 10:55:40,842 INFO sqlalchemy.engine.base.Engine SELECT count(*) AS count_1
FROM (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 %(name_1)s) AS anon_1
2017-03-09 10:55:40,842 INFO sqlalchemy.engine.base.Engine {'name_1': '%e%'}
2
>>>

count()方法用于确定SQL语句将返回多少行,类比我们的SQL语句SELECT COUNT(*) FROM users
我们只是想知道返回多少行结果,可以使用func.count()产生和SQL
语句中的count()一样的效果。

>>> print session.query(func.count(User.name), User.name).group_by(User.name).all()
2017-03-09 10:58:27,927 INFO sqlalchemy.engine.base.Engine SELECT count(users.name) AS count_1, users.name AS users_name
FROM users GROUP BY users.name
2017-03-09 10:58:27,936 INFO sqlalchemy.engine.base.Engine {}
[(1L, u'wendy'), (1L, u'ed'), (1L, u'mary')]

实现简单的SELECT  count(*) from table我们可以这样:

>>> session.query(func.count("*")).select_from(User).scalar()
2017-03-09 11:04:57,135 INFO sqlalchemy.engine.base.Engine SELECT count(%(param_1)s) AS count_1
FROM users
2017-03-09 11:04:57,137 INFO sqlalchemy.engine.base.Engine {'param_1': '*'}
3L

如果直接使用User主键表示计数,可以删除select_from()的用法:

>>> session.query(func.count(User.id)).scalar()
2017-03-09 11:06:22,874 INFO sqlalchemy.engine.base.Engine SELECT count(users.id) AS count_1
FROM users
2017-03-09 11:06:22,874 INFO sqlalchemy.engine.base.Engine {}
3L
之后是建立关系Relationship待续。。

你可能感兴趣的:(SQLALchemy学习笔记(三))