PageView.select(fn.Count(fn.Distinct(PageView.url))).scalar()
传入as_tuple=True来返回多个标量值:
>>> Employee.select(
... fn.Min(Employee.salary), fn.Max(Employee.salary)
... ).scalar(as_tuple=True)
(30000, 50000)
SQL函数,子查询,和原始查询表达式
猜想你想要查询所有名字以a开始的用户,有若干方法,但是其中一个就是使用类似LOWER和SUBSTR的SQL函数。
若用SQL函数,需要fn对象去构造查询:
# Select the user's id, username and the first letter of their username, lower-cased
query = User.select(User, fn.Lower(fn.Substr(User.username, 1, 1)).alias('first_letter'))
# Alternatively we could select only users whose username begins with 'a'
a_users = User.select().where(fn.Lower(fn.Substr(User.username, 1, 1)) == 'a')
>>> for user in a_users:
... print user.username
若经常用到任意sql语句。可以使用特殊SQL类,一种情况是用别名时:
# We'll query the user table and annotate it with a count of tweets for
# the given user
query = User.select(User, fn.Count(Tweet.id).alias('ct')).join(Tweet).group_by(User)
# Now we will order by the count, which was aliased to "ct"
query = query.order_by(SQL('ct'))
用peewee执行修饰过的SQL语句有两种方式:
Database.execute_sql()用于任意类型查询
RawQuery用执行返回模型实例的SELECT查询
db = SqliteDatabase(':memory:')
class Person(Model):
name = CharField()
class Meta:
database = db
# let's pretend we want to do an "upsert", something that SQLite can
# do, but peewee cannot.
for name in ('charlie', 'mickey', 'huey'):
db.execute_sql('REPLACE INTO person (name) VALUES (?)', (name,))
# now let's iterate over the people using our own query.
for person in Person.raw('select * from person'):
print person.name # .raw() will return model instances.
安全和SQL注入
默认peewee将参数化查询,所以所有用户传入的参数将被转义。唯一的例外就是写原始SQL查询或者传递
包含未被信任的数据的SQL对象时。为避免此事发生,确保用户定义的数据作为查询参数而不是实际的SQL查询:
# Bad!
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s' % (user_data,))
# Good. `user_data` will be treated as a parameter to the query.
query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data)
# Bad!
query = MyModel.select().where(SQL('Some SQL expression %s' % user_data))
# Good. `user_data` will be treated as a parameter.
query = MyModel.select().where(SQL('Some SQL expression %s', user_data))
MySQL和Postgresql使用'%s'可以来参数化。SQLite,用'?'。确保使用你的数据库适合的字符集。你也能
通过Database.interpolation来查找这个参数。
peewee提供基础的SQL窗口函数功能,可以通过调用fn.over()来创建传递到你的分区和排序参数中。
# Get the list of employees and the average salary for their dept.
query = (Employee
.select(
Employee.name,
Employee.department,
Employee.salary,
fn.Avg(Employee.salary).over(
partition_by=[Employee.department]))
.order_by(Employee.name))
# Rank employees by salary.
query = (Employee
.select(
Employee.name,
Employee.salary,
fn.rank().over(
order_by=[Employee.salary]))
得到原始的元组/字典
有时你想要简单的列举行数据元组,而不需要创建结果对象。可以用SelectQuery().tuples()或RawQuery.tuples():
stats = Stat.select(Stat.url, fn.Count(Stat.url)).group_by(Stat.url).tuples()
# iterate over a list of 2-tuples containing the url and count
for stat_url, stat_count in stats:
print stat_url, stat_count
简单的,用SelectQuery.dicts()或RawQuery.dicts()可以返回行数据作为字典
stats = Stat.select(Stat.url, fn.Count(Stat.url).alias('ct')).group_by(Stat.url).dicts()
# iterate over a list of 2-tuples containing the url and count
for stat in stats:
print stat['url'], stat['ct']
PostgresqlDatabase 支持在Update,INSERT,DELETE查询里用RETURNING子句。
用RETURNING子句可以将查询语句影响的行数对应的模型对象返回。
例如,你用UpdateQuery去将注册过期的用户冻结。冻结后,你需要向每个用户发送邮件然他们知道情况。而不是用两次
查询,SELECT和UPDATE,通过带有RETURNING 的UPDATE的查询来一次完成。
query = (User
.update(is_active=False)
.where(User.registration_expired == True)
.returning(User))
# Send an email to every user that was deactivated.
for deactivate_user in query.execute():
send_deactivation_email(deactivated_user)
RETURNING也可用在InsertQUery和DeleteQuery上。当用在INSERT时,新增的行返回。用在DELETE时,删除的行
RETURNING子句唯一限制是,它仅包含查询FROM子句中列的表列。用模型类可以返回所有的列字段。
查询操作符
peewee支持一下类型的比较符:
ComparisonMeaning
x equals y
x is less than y
x is less than or equal to y
x is greater than y
x is greater than or equal to y
x is not equal to y
x IN y, where y is a list or query
x IS y, where y is None/NULL
x LIKE y where y may conta
# Find the user whose username is "charlie".
User.select().where(User.username == 'charlie')
# Find the users whose username is in [charlie, huey, mickey]
User.select().where(User.username << ['charlie', 'huey', 'mickey'])
Employee.select().where(Employee.salary.between(50000, 60000))
Employee.select().where(Employee.name.startswith('C'))
Blog.select().where(Blog.title.contains(search_string))
演示如何连接表达式。比较可以很复杂:
# Find any users who are active administrations.
User.select().where(
(User.is_admin == True) &
(User.is_active == True))
# Find any users who are either administrators or super-users.
User.select().where(
(User.is_admin == True) |
(User.is_superuser == True))
# Find any Tweets by users who are not admins (NOT IN).
admins = User.select().where(User.is_admin == True)
non_admin_tweets = Tweet.select().where(
~(Tweet.user << admins))
# Find any users who are not my friends (strangers).
friends = User.select().where(
User.username << ['charlie', 'huey', 'mickey'])
strangers = User.select().where(~(User.id << friends))
虽然你在试图用python 的in,and,or 和not操作符,但是它们不起作用。in 表达式总是返回boolean 值。
同样的,and,or和not将把参数当作boolean值,不能被重载。
所以注意:
用<<代替in
用&代替and
用!代替or
用~代替not
不要忘记你的比较表达式用逻辑运算符连接时使用括号包装。
SQLite中的LIKE 和ILIKE
因为SSQLite的LIKE操作符默认是大小写不敏感,peewee用SQLite GLOB操作来实现大小写敏感的查询。
glob操作符用*代替%作为通配符。如果你使用SQLite想要大小写敏感的字符匹配,记住用*来作为通配符。
三种值化的逻辑
SQL处理NULL的方法不同,可有下列表达式中的操作:
IS NULL
IS NOT NULL
NOT IN
虽然你可以使用IS NULL 和IN前加上否定运算符~来实现,但是最好显式的用IS NOT NULL和NOT IN
来使得语义更明确。
最简单用IS NULL 和IN就是使用操作符重载:
# Get all User objects whose last login is NULL.
User.select().where(User.last_login >> None)
# Get users whose username is in the given list.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(User.username << usernames)
如果不喜欢操作符重载,可以调用字段方法:
# Get all User objects whose last login is NULL.
User.select().where(User.last_login.is_null(True))
# Get users whose username is in the given list.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(User.username.in_(usernames))
取反上面的查询,你可以使用一元运算符~,最好使用IS NOT和NOT IN来消除歧义。
# Get all User objects whose last login is *NOT* NULL.
User.select().where(User.last_login.is_null(False))
# Using unary negation instead.
User.select().where(~(User.last_login >> None))
# Get users whose username is *NOT* in the given list.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(User.username.not_in(usernames))
# Using unary negation instead.
usernames = ['charlie', 'huey', 'mickey']
User.select().where(~(User.username << usernames))
自定义的操作符
因为懒得操作符重载,作者减去了一些操作比如,module。如果你你在上表中没发现你想要的操作符,ni
可以很容易的加上你自己定义的
在SQLite中增加module支持
from peewee import *
from peewee import Expression # the building block for expressions
OP['MOD'] = 'mod'
def mod(lhs, rhs):
return Expression(lhs, OP.MOD, rhs)
SqliteDatabase.register_ops({OP.MOD: '%'})
现在使用自定义的操作构建丰富的查询:
# Users with even ids.
User.select().where(mod(User.id, 2) == 0)
Peewee 设计成提供简单的,印象深刻的,pythonic 来构造SQL查询。这部分给出一些表达式的用法。
主要两种对象类型来创建表达式:
Field 实例
SQL聚合和fn函数
我们假如简单User模型,拥有username和其他事情:
class User(Model):
username = CharField()
is_admin = BooleanField()
is_active = BooleanField()
last_login = DateTimeField()
login_count = IntegerField()
failed_logins = IntegerField()
用查询表达式的比较:
# username is equal to 'charlie'
User.username == 'charlie'
# user has logged in less than 5 times
User.login_count < 5
比较可以用and 和 or的位符号来连接。操作符可以被python控制优先级,同时可以嵌套任意深度:
# User is both and admin and has logged in today
(User.is_admin == True) & (User.last_login >= today)
# User's username is either charlie or charles
(User.username == 'charlie') | (User.username == 'charles')
也可以用函数来比较:
# user's username starts with a 'g' or a 'G':
fn.Lower(fn.Substr(User.username, 1, 1)) == 'g'
我们可以做些有意思的事情,表达式可以与其他表达式比较。表达式之间也支持算术操作符:
# users who entered the incorrect more than half the time and have logged
# in at least 10 times
(User.failed_logins > (User.login_count * .5)) & (User.login_count > 10)
表达式也支持原子性更新:
# when a user logs in we want to increment their login count:
User.update(login_count=User.login_count + 1).where(User.id == user_id)
表达式可以用在查询的任意部分,所以自己去尝试一下!
外键通过特殊的字段类ForeignKeyField来创建。么个外键也在相关联的模型间通过特殊的related_name
创建了向后引用。
再一次来看User和Tweet模型,注意Tweet有个ForeignKeyField指向User。这个外键可以允许你通过它接触到相关联
的user实例。我们称为外键穿越:
>>> tweet.user.username
'charlie'
除非User模型显式的选择,当得到Tweet记录时,得到User数据需要额外的查询。关于如何避免额外查询,
可以看N+1查询问题部分讲解。
反过来,我们可以查询给定User实例的相关联的tweets:
>>> for tweet in user.tweets:
... print tweet.message
http://www.youtube.com/watch?v=xdhLQCYQ-nQ
这种情况下,tweets属性只是拥有预生成指向给定User实例的WHERE子句的SelectQuery而已:
>>> user.tweets
<class 'twx.Tweet'> SELECT t1."id", t1."user_id", t1."message", ...
用join方法去关联其他的表。当在源模型和关联的模型之间有外键时,你可以不用指名别的参数:
my_tweets = Tweet.select().join(User).where(User.username == 'charlie')
默认peewee使用INNER连接,也可以用LEFT OUTER或FULL 连接:
users = (User
.select(User, fn.Count(Tweet.id).alias('num_tweets'))
.join(Tweet, JOIN.LEFT_OUTER)
.group_by(User)
.order_by(fn.Count(Tweet.id).desc()))
for user in users:
print user.username, 'has created', user.num_tweets, 'tweet(s).'
模型多个外键
模型有多个外键时,最好的实践是显示指名你要关联的字段。
再看例子中的模型,Relationship模型用于表示一个用户关注另一个用户。模型定义如下:
class Relationship(BaseModel):
from_user = ForeignKeyField(User, related_name='relationships')
to_user = ForeignKeyField(User, related_name='related_to')
class Meta:
indexes = (
# Specify a unique multi-column index on from/to-user.
(('from_user', 'to_user'), True),
.select()
.join(Relationship, on=Relationship.to_user)
.where(Relationship.from_user == charlie))
另外,我们谁正在关注我,on用from_user列,过滤条件用to_user:
(User
.select()
.join(Relationship, on=Relationship.from_user)
.where(Relationship.to_user == charlie))
任意表字段间关联
如果你想要关联的表之间没有外键,你也可以手动指名关联谓词。
下面例子中,在User和ActiveLog之间没有显式的外键,但是ActiveLog.object_id和Usr.id之间存在隐式的关联。
不是用在特定的字段上关联,我们用表达式去关联:
user_log = (User
.select(User, ActivityLog)
.join(
ActivityLog,
on=(User.id == ActivityLog.object_id).alias('log'))
.where(
(ActivityLog.activity_type == 'user_activity') &
(User.username == 'charlie')))
for user in user_log:
print user.username, user.log.description
#### Print something like ####
charlie logged in
charlie posted a tweet
charlie retweeted
charlie posted a tweet
charlie logged out
通过在关联条件上指名别名,你可以控制peewee将关联的实例赋值到此属性。上面例子中,我们用下面作为关联:
(User.id == ActivityLog.object_id).alias('log')
然后循环列出查询结果时,我们可以直接接触ActivityLog而不用再次查询:
for user in user_log:
print user.username, user.log.description
在多表之间关联
当调用join方法时,peewee使用上一个关联的表当作原表:
User.select().join(Tweet).join(Comment)
这个查询返回一个从User到Tweet的关联,然后从Tweet到Comment的关联。
如果你喜欢将同一个表关联两次,用switch方法:
# Join the Artist table on both `Ablum` and `Genre`.
Artist.select().join(Album).switch(Artist).join(Genre)
实现多对多映射
Peewee没提供一个字段去解决像django 那样的多对多关系映射-这是因为事实上这个字段是隐藏在一张中间表中。
用peewee去实现多对多映射,你必须亲自实现中间表,并且用它作媒介来查询:
class Student(Model):
name = CharField()
class Course(Model):
name = CharField()
class StudentCourse(Model):
student = ForeignKeyField(Student)
course = ForeignKeyField(Course)
去查询选修数学的所有学生:
query = (Student
.select()
.join(StudentCourse)
.join(Course)
.where(Course.name == 'math'))
for student in query:
print student.name
去查询某学生选修的所有课程:
courses = (Course
.select()
.join(StudentCourse)
.join(Student)
.where(Student.name == 'da vinci'))
for course in courses:
print course.name
有效的去列举多对多关系,比如:列举所有学生和他们的选修课程,我们通过StudentCourse来查询,并且预计算Student和
Course:
query = (StudentCourse
.select(StudentCourse, Student, Course)
.join(Course)
.switch(StudentCourse)
.join(Student)
.order_by(Student.name))
下面代码打印学生们和他们的选修课:
last = None
for student_course in query:
student = student_course.student
if student != last:
last = student
print 'Student: %s' % student.name
print ' - %s' % student_course.course.name
因为我们在select子句中包含了Student和Course的所有字段,所以这些外键穿越是免费的,我们在一次查询中完成了整个列举。
ManyToManyField
ManyToManyField提供了在字段中表示多对多映射。你最好使用标准的peewee API去处理大多数情况,除非,如果你的模型是非常简单
并且你的查询不是太复杂,你可以使用ManyToManyField去得到快速的查询提升体验。详细使用说明请看FIelds扩展部分。
用ManyToManyField的studen和course模型:
from peewee import *
from playhouse.fields import ManyToManyField
db = SqliteDatabase('school.db')
class BaseModel(Model):
class Meta:
database = db
class Student(BaseModel):
name = CharField()
class Course(BaseModel):
name = CharField()
students = ManyToManyField(Student, related_name='courses')
StudentCourse = Course.students.get_through_model()
db.create_tables([
Student,
Course,
StudentCourse])
# Get all classes that "huey" is enrolled in:
huey = Student.get(Student.name == 'Huey')
for course in huey.courses.order_by(Course.name):
print course.name
# Get all students in "English 101":
engl_101 = Course.get(Course.name == 'English 101')
for student in engl_101.students:
print student.name
# When adding objects to a many-to-many relationship, we can pass
# in either a single model instance, a list of models, or even a
# query of models:
huey.courses.add(Course.select().where(Course.name.contains('English')))
engl_101.students.add(Student.get(Student.name == 'Mickey'))
engl_101.students.add([
Student.get(Student.name == 'Charlie'),
Student.get(Student.name == 'Zaizee')])
# The same rules apply for removing items from a many-to-many:
huey.courses.remove(Course.select().where(Course.name.startswith('CS')))
engl_101.students.remove(huey)
# Calling .clear() will remove all associated objects:
cs_150.students.clear()
详细使用:
ManyToManyField.add()
ManyToManyField.remove()
ManyToManyField.clear()
ManyToManyField.get_through_model()
peewee为构造包含自己关联自己的查询提供了几种方法。
用模型别名
关联一个模型两次,需要在查询时创建一个代表第二个表实例的别名。以下面这个模型说明:
class Category(Model):
name = CharField()
parent = ForeignKeyField('self', related_name='children')
如果我们查询父种类为电子类的所有类别。一种用自关联来实现:
Parent = Category.alias()
query = (Category
.select()
.join(Parent, on=(Category.parent == Parent.id))
.where(Parent.name == 'Electronics'))
当用ModelAlias来关联时,必须用on关键字来标明关联条件。上例我们使用它的父类别来关联类别。
另一种较少见的情况是使用子查询。下面是另一种用子查询实现得到父类别是电子的所有类别的查询:
oin_query = Category.select().where(Category.name == 'Electronics')
# Subqueries used as JOINs need to have an alias.
join_query = join_query.alias('jq')
query = (Category
.select()
.join(join_query, on=(Category.parent == join_query.c.id)))
将会生成下面SQL语句:
SELECT t1."id", t1."name", t1."parent_id"
FROM "category" AS t1
INNER JOIN (
SELECT t3."id"
FROM "category" AS t3
WHERE (t3."name" = ?)
) AS jq ON (t1."parent_id" = "jq"."id"
用.c魔法方法去根据上下文生成合适的SQL表达式从而通过子查询接触到id值:
Category.parent == join_query.c.id
# Becomes: (t1."parent_id" = "jq"."id")
下面讨论一下用peewee的些许提高性能的方法。
避免N+1查询
N+1查询指的是当应用提交一次查询获取结果,然后在取得结果数据集的每一行时,应用至少再次查询一次(也可以看做是嵌套循环)。
大多数情况下,n 查询可以通过使用SQL join或子查询来避免。数据库本身可能做了嵌套循环,但是它比在你的应用代码本身里做这些n查询更高效,后者通常会导致与数据库再次潜在通讯,没有利用数据库本身关联和执行子查询时会进行切片等优化工作。
Peewee提供了几种API去减轻N+1查询的行为。再看看贯串我们这篇文档的模型,User和Tweet,这部分我们重点讲一下一些N+1场景,说明peewee怎么帮助我们避免N+1查询。
在一些场景里,N+1查询不会明显表现为显著地或可测量的性能瓶颈点。它也由你要查询的数据,使用的数据库本身,以及执行查询获取结果的潜在因素。优化前后可以测试性能,确保和你预测的变化相同。
列出最近的tweets
tweets时间轴显示最近用户的tweets。除了tweet的内容,还要显示tweet作者的用户名。N+1场景描述为:
获取最近的10条tweets
每个tweet,查询作者信息(10次查询)
通过用join选择两个表,peewee使得在一次查询里完成任务:
query = (Tweet
.select(Tweet, User) # Note that we are selecting both models.
.join(User) # Use an INNER join because every tweet has an author.
.order_by(Tweet.id.desc()) # Get the most recent tweets.
.limit(10))
for tweet in query:
print tweet.user.username, '-', tweet.message
没有用join时,得到tweet.user.username会触发一次查询去解析外键tweet.user从而得到相关联的user。
由于我们在User上关联并选择,peewee自动为我们解析外键。
列出所有用户和他们的tweets
你想要显示若干用户和他们所有的tweets的页面。N+1场景为:
取得些许用户。
每个用户取到他们的tweets。
虽然和上个例子相似,但是重要区别是:我们选择tweets时,每个tweet只有一个关联的用户,所以可以直接赋值到外键,
反过来不对,因为一个用户可有任意数量tweets或者没有。
Peewee提供两两种途径去避免O(n)查询:
1.首先取到用户,然后取到关联这些用户的所有tweets。一旦peewee取到tweets,将它们与合适的用户匹配。
这种方法通常很快,但是会在所选择的每个表上执行一次查询。
2.在一个查询里得到用户和tweets。用户数据将复制,所以peewee将在列举结果集时减少重复和聚合tweets。
这种方法导致有许多数据需要传输,并且要有许多python逻辑去减少行重复。
每种方案根据查询数据的大小和结构都会可能比另一种更好。
使用prefetch
peewee使用子查询可以预获取数据。这种方法需要prefetch特殊API使用。Pre-fetch,像其名字本身,
用子查询去急切加载给定用户的相应的tweets。意味着我们用O(k)查询K张表而不是O(n)查询n行纪录。
下面演示我们如何得到若干用户和他们最近一周的tweets:
week_ago = datetime.date.today() - datetime.timedelta(days=7)
users = User.select()
tweets = (Tweet
.select()
.where(
(Tweet.is_published == True) &
(Tweet.created_date >= week_ago)))
# This will perform two queries.
users_with_tweets = prefetch(users, tweets)
for user in users_with_tweets:
print user.username
for tweet in user.tweets_prefetch:
print ' ', tweet.message
注意User 查询和Tweet查询都没有JOIN子句,当我们使用prefetch时不必指名join
prefetch可以用于任意数量的表。可以查看API文档看其他例子。
用prefetch时应考虑的事情:
预查询的模型必须存在外键
通常它比aggregate_rows方法更高效
因为数据没有重复的所以传输的数据更少
因为不用减重复所以python逻辑更少
当你想要在最外的查询里使用LIMIT没问题,但是可能正确的实现限制子查询的返回结果大小有些困难。
使用aggregate_rows
aggregeate_rows一次在内存中减少重复,选择所有的数据。它和prefetch都可以完成任意复杂的查询。
使用这个特性需要当创建查询时用到特殊的标志aggregate_rows。它告诉peewee减少那些根据JOIN的结构可能会重复的行。
因为在减少重复聚合数据时有许多计算,所以可能使用aggregate_rows可能在一些查询中会比用prefetch性能低,即使面对的是
O(n)简单的 查询时,所以你不确定使用哪种方法时测试检查你的代码。
query = (User
.select(User, Tweet) # As in the previous example, we select both tables.
.join(Tweet, JOIN.LEFT_OUTER)
.order_by(User.username) # We need to specify an ordering here.
.aggregate_rows()) # Tell peewee to de-dupe and aggregate results.
for user in query:
print user.username
for tweet in user.tweets:
print ' ', tweet.message
query = (User
.select(User, Tweet) # As in the previous example, we select both tables.
.join(Tweet, JOIN.LEFT_OUTER)
.order_by(User.username) # We need to specify an ordering here.
.aggregate_rows()) # Tell peewee to de-dupe and aggregate results.
for user in query:
print user.username
for tweet in user.tweets:
print ' ', tweet.message
一般情况下,user.tweets返回SelectQuery,迭代它将触发额外的查询。使用aggregate_rows,user.tweets将是一个Python list,没有
额外的查询发生。
我们用LEFT OUTER关联确保没有tweets的用户也在结果数据中。
下面我们获取若干用户以及他们过去一周发表的tweets。因为我们过滤tweets时用户可以没有任何tweets,所以我们需要WHERE
子句允许有NULL ID的tweet。
week_ago = datetime.date.today() - datetime.timedelta(days=7)
query = (User
.select(User, Tweet)
.join(Tweet, JOIN.LEFT_OUTER)
.where(
(Tweet.id >> None) | (
(Tweet.is_published == True) &
(Tweet.created_date >= week_ago)))
.order_by(User.username, Tweet.created_date.desc())
.aggregate_rows())
for user in query:
print user.username
for tweet in user.tweets:
print ' ', tweet.message
使用aggregate_rows需考虑的事情:
必须指定每个关联表的排序,以便行数据可以正确的聚合,有点像itertools.groupby
不要和LIMIT和OFFSET谓词混用,或者get(应用LIMIT 1 SQL 子句)。因为聚合结果集可能由于行的重复问题导致
多个记录返回,限制返回数量可能导致错误。假设你有三个用户,每个有10个tweets,如果你在查询后加上
LIMIT 5,你只返回了第一个用户和他的前5个tweets
通常python去减少数据重复是的这种方法性能比prefetch低,有时甚至比简单的O(n)查询性能还低!有疑问时。
因为每张表的每一列都包含在游标返回的行元组内,所以这种方法比prefetch需要更多带宽。
迭代大量行
默认在迭代SelectQuery时peewee缓存结果。这样使得多次迭代和索引切片容易,但是当你计划迭代大数量的行时可能有问题。
当迭代查询结果时为了减少peewee使用的内存,可用iterator方法。这个方法让你用更少的内存去迭代大量的结果集,不用缓存返回的每个实例。
# Let's assume we've got 10 million stat objects to dump to a csv file.
stats = Stat.select()
# Our imaginary serializer class
serializer = CSVSerializer()
# Loop over all the stats and serialize.
for stat in stats.iterator():
serializer.serialize_object(stat)
可以使用native方法来为简单的查询进一步提升速度。这个方法加速peewee从原始的游标数据构造为peewee模型实例。
可看native文档了解其详细优化信息。
你也可以通过使用dicts和tuples来得到性能提升。
当迭代大量包含多表的列的行时,peewee为每个返回行重建模型图,这种操作在复杂的图上时很费时。为提速模型创建,你可以:
调用native方法,它不会构建图,直接将行的属性分配到模型实例上
使用dicts或tuples
加速批量插入操作
请看批量插入部分去了解为批量插入操作提速的细节