#django #wagtail
#django #трясогузка
Вопрос:
У меня возникли некоторые трудности с привязкой / получением данных из 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
Что я могу сделать, чтобы получить все соответствующие данные из этих моделей за один раз, не повторяя вызовы БД?
Или, если я неправильно спроектировал эти модели, каким образом можно было бы создать их, чтобы использовать их так, как задумано?
Ответ №1:
Вы можете пройти несколько уровней в select_related
prefetch_related
предложении or, разделяя каждый шаг двойным подчеркиванием:
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)
Комментарии:
1. Очень простое и элегантное решение. Я не знал, что
prefetch_related
ClubTimetable
это приведет к тому, что это не будет отношением «многие ко многимClubManager
» или что использование подчеркивания приведет к получению как расписания, так и записей дня недели, я видел цепочку, упомянутую в документах Django, но подумал, что это должно было получить только последнюю часть, например, будние дни. Спасибо за помощь, это именно то, что мне было нужно, и я очень признателен!