【发布时间】:2021-10-20 10:14:02
【问题描述】:
我正在尝试避免 http://docs.peewee-orm.com/en/latest/peewee/relationships.html#avoiding-the-n-1-problem 中所述的 N+1 问题,但仍会执行其他查询。
我的模特:
from peewee import (SqliteDatabase, Model, BigAutoField, CharField, ForeignKeyField)
db = SqliteDatabase(':memory:')
class TestModel(Model):
class Meta:
database = db
legacy_table_names = False
class TestUser(TestModel):
id = BigAutoField(primary_key=True)
name = CharField()
class Book(TestModel):
id = BigAutoField(primary_key=True)
name = CharField()
user = ForeignKeyField(TestUser, backref='books')
class Movie(TestModel):
id = BigAutoField(primary_key=True)
name = CharField()
user = ForeignKeyField(TestUser, backref='movies')
class Tape(TestModel):
id = BigAutoField(primary_key=True)
name = CharField()
user = ForeignKeyField(TestUser, backref='tapes')
我的测试:
from peewee import JOIN
from playhouse.shortcuts import model_to_dict
from playhouse.test_utils import count_queries
from test_n_plus_1l import *
def test_should_avoid_n_plus_one_problem():
db.create_tables([TestUser, Book, Movie, Tape])
tu = TestUser.create(name='Test')
Book.create(name='Book1', user_id=tu.id)
Movie.create(name='Movie1', user_id=tu.id)
Tape.create(name='Tape1', user_id=tu.id)
with count_queries() as counter:
tu = TestUser.select(TestUser, Book, Movie, Tape) \
.join_from(TestUser, Book, JOIN.LEFT_OUTER) \
.join_from(TestUser, Movie, JOIN.LEFT_OUTER) \
.join_from(TestUser, Tape, JOIN.LEFT_OUTER) \
.where(TestUser.id == tu.id).get()
model_to_dict(tu, backrefs=True, manytomany=True, max_depth=4)
assert counter.count == 1
运行后我得到断言错误:
E assert 4 == 1
peewees打印执行的sql,所以我清楚的看到joins被执行了,但是为什么peewee会执行额外的查询:
('SELECT "t1"."id", "t1"."name", "t2"."id", "t2"."name", "t2"."user_id", "t3"."id", "t3"."name", "t3"."user_id", "t4"."id", "t4"."name", "t4"."user_id" FROM "test_user" AS "t1" LEFT OUTER JOIN "book" AS "t2" ON ("t2"."user_id" = "t1"."id") LEFT OUTER JOIN "movie" AS "t3" ON ("t3"."user_id" = "t1"."id") LEFT OUTER JOIN "tape" AS "t4" ON ("t4"."user_id" = "t1"."id") WHERE ("t1"."id" = ?) LIMIT ? OFFSET ?', [1, 1, 0])
('SELECT "t1"."id", "t1"."name", "t1"."user_id" FROM "book" AS "t1" WHERE ("t1"."user_id" = ?)', [1])
('SELECT "t1"."id", "t1"."name", "t1"."user_id" FROM "movie" AS "t1" WHERE ("t1"."user_id" = ?)', [1])
('SELECT "t1"."id", "t1"."name", "t1"."user_id" FROM "tape" AS "t1" WHERE ("t1"."user_id" = ?)', [1])
【问题讨论】:
-
问题是每个 TestUser 可能有任意数量的关联书籍、电影或磁带。因此,这是一个您可能受益于使用 prefetch() 帮助程序的实例 - 因为外键的方向。也就是说,您最好进行分析,因为 prefetch() 可能不会比仅执行查询快。