Двухуровневое меню: делать ли необработанный 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_* на фактическое имя используемой вами связи.

Вернуться на верх