Двухуровневое меню: делать ли необработанный SQL-запрос?
Django 3.2.6
class Menu(NameUniqueMixin,
ArchivedMixin,
models.Model):
TYPE_CHOICES = [
(MenuTypes.TOP.value, MenuTypes.TOP.value),
]
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
class MenuLevelOne(NameUniqueMixin,
ArchivedMixin,
models.Model):
menu = models.ForeignKey(Menu,
on_delete=models.PROTECT,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss", )
html = models.TextField(default="",
blank=False,
null=False)
rank = models.PositiveIntegerField(default=0,
null=False,
unique=True,
db_index=True, )
class MenuLevelTwo(NameUniqueMixin,
ArchivedMixin,
models.Model):
level_one = models.ForeignKey(MenuLevelOne,
on_delete=models.PROTECT,
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss", )
html = models.TextField(default="",
blank=False,
null=False)
rank = models.PositiveIntegerField(default=0,
null=False,
db_index=True, )
Я хотел бы сделать древовидное меню. Например, так:
os
\windows
\linux
hardware
\motherboards
\sound cards
Здесь мы видим двухуровневое меню.
Я не могу представить, какой лучший способ выбрать данные из базы данных.
Разумеется, я также собираюсь использовать шаблон. Я собираюсь сделать несколько запросов к базе данных, затем использовать цикл. И кэшировать все это в шаблоне.
Другими словами: Я планирую организовать здесь ужасно неэффективный кусок кода и скрыть его с помощью cache.
Или я также думаю о пользовательском SQL-запросе, который мне совсем не нравится
Подскажите, пожалуйста, какой способ выбора данных из базы данных в этом случае является самым лучшим? И как может выглядеть черновой вариант шаблона для этого меню?
Вы можете использовать prefetch_related
для получения всех Menu
объектов, а также получить все связанные подменю, сопоставленные с каждым меню над ним.
Дополнительно, вы можете использовать Prefetch
с кверисетом, чтобы указать порядок объектов, в этом случае я предполагаю, что он будет основан на rank
, так:
menu = Menu.objects.all().prefetch_related(
Prefetch('app_label_menulevelone_related', queryset=MenuLevelOne.objects.order_by('rank'),
Pefetch('app_label_menulevelone_related__app_label_menuleveltwo_related', queryset=MenuLevelTwo.objects.order_by('rank'),
)
# Would work the same in the templates
for m in menu:
print(m.type)
for one in m.app_label_menulevelone_related.all():
print(one.html)
for two in one.app_label_menuleveltwo_related.all():
print(two.html)
Всего будет сделано 3 запроса, один для Menu
, один для MenuLevelOne
и один для MenuLevelTwo
. Просто измените app_label_*
на фактическое имя используемой вами связи.