如何用尽可能少的查询获得多个相关模型
How to get multiple related models with as few queries possible
我遇到了一些困难 linking/obtaining 来自 3 个模型的数据都相互关联。我想我要么设计不正确,要么我不完全理解如何使用父字段和多对多字段。
有 3 个模型(问题末尾显示完整模型):
ClubManager
输入俱乐部名称的地方
ClubTimetables
时间表,与ClubManager
相关
DaysOfTheWeek
周一至周日,由 ClubTimetables
使用
为此,我正在尝试从所有这些信息中检索信息:俱乐部的名称、它的时间表以及每个时间表所关联的星期几。但是,我似乎无法以一种不经常访问数据库的方式来做到这一点。
我尝试的第一件事是获取 ClubManager
和 select_related
它的时间表,但无法弄清楚如何预选它所连接的 DaysOfTheWeek
。
我现在尝试的方法是获取 ClubTimetable
并在它链接的两个模型上使用 select_related
和 prefetch_related
,就像这样
timetable = ClubTimetables.objects.select_related('attached_to').prefetch_related('weekday')
for t in timetable:
print(t.attached_to.name)
这导致这样的查询
connection.queries
[{'sql': 'SELECT "club_clubtimetables"."id", "club_clubtimetables"."sort_order", "club_clubtimetables"."address_line_1", "club_clubtimetables"."address_line_2", "club_clubtimetables"."address_city", "club_clubtimetables"."address_county", "club_clubtimetables"."address_country", "club_clubtimetables"."map_coord_lat", "club_clubtimetables"."map_coord_lon", "club_clubtimetables"."attached_to_id", "club_clubtimetables"."start_time", "club_clubtimetables"."end_time", "club_clubmanager"."id", "club_clubmanager"."name" FROM "club_clubtimetables" INNER JOIN "club_clubmanager" ON ("club_clubtimetables"."attached_to_id" = "club_clubmanager"."id") ORDER BY "club_clubtimetables"."sort_order" ASC',
'time': '0.020'},
{'sql': 'SELECT ("club_clubtimetables_weekday"."clubtimetables_id") AS "_prefetch_related_val_clubtimetables_id", "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" IN (1, 2)',
'time': '0.002'}]
哪个看起来正确(?)
但是,每次尝试获取相关的工作日都会访问数据库。
for t in timetable:
for training in t.weekday.values():
print(training['weekday'])
# Tuesday
# Sunday
In [14]: connection.queries
Out[14]:
[{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 1',
'time': '0.001'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 2',
'time': '0.000'}]
# ran again...
for t in timetable:
for training in t.weekday.values():
print(training['weekday'])
# Tuesday
# Sunday
In [16]: connection.queries
Out[16]:
[{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 1',
'time': '0.001'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 2',
'time': '0.000'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 1',
'time': '0.001'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 2',
'time': '0.001'}]
可能是因为我用的是.values()
?但我不知道如何获得这些值。
我的模型目前看起来像这样
# ClubManager
@register_snippet
class ClubManager(ClusterableModel):
name = models.CharField('Club name',
max_length=255,
help_text='The name of the club.')
panels = [
FieldPanel('name'),
InlinePanel('club_timetable', heading='Timetable Information')
]
# ClubTimetables
class ClubTimetables(Orderable, AddressBase):
attached_to = ParentalKey(
'club.ClubManager', related_name='club_timetable')
weekday = models.ManyToManyField(DaysOfTheWeek)
start_time = models.TimeField()
end_time = models.TimeField()
panels = [ ... ] + AddressBase.panels
# DaysOfTheWeek
class DaysOfTheWeek(models.Model):
weekday = models.CharField(max_length=9)
def __str__(self):
return self.weekday
如何在不重复数据库调用的情况下一次从这些模型中获取所有相关数据?
或者,如果我错误地设计了这些模型,可以通过什么方式构建它们以按预期方式使用它?
您可以通过用双下划线分隔每个步骤来遍历 select_related
或 prefetch_related
子句中的多个级别:
managers = ClubManager.objects.prefetch_related('club_timetable__weekday')
循环遍历它们应该可以在初始查询之外没有其他查询的情况下工作。
for manager in managers:
for timetable in manager.club_timetable.all():
for weekday in timetable.weekday.all():
print(weekday)
我遇到了一些困难 linking/obtaining 来自 3 个模型的数据都相互关联。我想我要么设计不正确,要么我不完全理解如何使用父字段和多对多字段。
有 3 个模型(问题末尾显示完整模型):
ClubManager
输入俱乐部名称的地方ClubTimetables
时间表,与ClubManager
相关
DaysOfTheWeek
周一至周日,由ClubTimetables
使用
为此,我正在尝试从所有这些信息中检索信息:俱乐部的名称、它的时间表以及每个时间表所关联的星期几。但是,我似乎无法以一种不经常访问数据库的方式来做到这一点。
我尝试的第一件事是获取 ClubManager
和 select_related
它的时间表,但无法弄清楚如何预选它所连接的 DaysOfTheWeek
。
我现在尝试的方法是获取 ClubTimetable
并在它链接的两个模型上使用 select_related
和 prefetch_related
,就像这样
timetable = ClubTimetables.objects.select_related('attached_to').prefetch_related('weekday')
for t in timetable:
print(t.attached_to.name)
这导致这样的查询
connection.queries
[{'sql': 'SELECT "club_clubtimetables"."id", "club_clubtimetables"."sort_order", "club_clubtimetables"."address_line_1", "club_clubtimetables"."address_line_2", "club_clubtimetables"."address_city", "club_clubtimetables"."address_county", "club_clubtimetables"."address_country", "club_clubtimetables"."map_coord_lat", "club_clubtimetables"."map_coord_lon", "club_clubtimetables"."attached_to_id", "club_clubtimetables"."start_time", "club_clubtimetables"."end_time", "club_clubmanager"."id", "club_clubmanager"."name" FROM "club_clubtimetables" INNER JOIN "club_clubmanager" ON ("club_clubtimetables"."attached_to_id" = "club_clubmanager"."id") ORDER BY "club_clubtimetables"."sort_order" ASC',
'time': '0.020'},
{'sql': 'SELECT ("club_clubtimetables_weekday"."clubtimetables_id") AS "_prefetch_related_val_clubtimetables_id", "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" IN (1, 2)',
'time': '0.002'}]
哪个看起来正确(?)
但是,每次尝试获取相关的工作日都会访问数据库。
for t in timetable:
for training in t.weekday.values():
print(training['weekday'])
# Tuesday
# Sunday
In [14]: connection.queries
Out[14]:
[{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 1',
'time': '0.001'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 2',
'time': '0.000'}]
# ran again...
for t in timetable:
for training in t.weekday.values():
print(training['weekday'])
# Tuesday
# Sunday
In [16]: connection.queries
Out[16]:
[{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 1',
'time': '0.001'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 2',
'time': '0.000'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 1',
'time': '0.001'},
{'sql': 'SELECT "core_daysoftheweek"."id", "core_daysoftheweek"."weekday" FROM "core_daysoftheweek" INNER JOIN "club_clubtimetables_weekday" ON ("core_daysoftheweek"."id" = "club_clubtimetables_weekday"."daysoftheweek_id") WHERE "club_clubtimetables_weekday"."clubtimetables_id" = 2',
'time': '0.001'}]
可能是因为我用的是.values()
?但我不知道如何获得这些值。
我的模型目前看起来像这样
# ClubManager
@register_snippet
class ClubManager(ClusterableModel):
name = models.CharField('Club name',
max_length=255,
help_text='The name of the club.')
panels = [
FieldPanel('name'),
InlinePanel('club_timetable', heading='Timetable Information')
]
# ClubTimetables
class ClubTimetables(Orderable, AddressBase):
attached_to = ParentalKey(
'club.ClubManager', related_name='club_timetable')
weekday = models.ManyToManyField(DaysOfTheWeek)
start_time = models.TimeField()
end_time = models.TimeField()
panels = [ ... ] + AddressBase.panels
# DaysOfTheWeek
class DaysOfTheWeek(models.Model):
weekday = models.CharField(max_length=9)
def __str__(self):
return self.weekday
如何在不重复数据库调用的情况下一次从这些模型中获取所有相关数据?
或者,如果我错误地设计了这些模型,可以通过什么方式构建它们以按预期方式使用它?
您可以通过用双下划线分隔每个步骤来遍历 select_related
或 prefetch_related
子句中的多个级别:
managers = ClubManager.objects.prefetch_related('club_timetable__weekday')
循环遍历它们应该可以在初始查询之外没有其他查询的情况下工作。
for manager in managers:
for timetable in manager.club_timetable.all():
for weekday in timetable.weekday.all():
print(weekday)