#django #django-models #django-views #django-templates #django-queryset
#django #django-модели #django-представления #django-шаблоны #django-набор запросов
Вопрос:
У меня возникли проблемы с рендерингом связанных объектов в шаблоне. У меня есть модель страницы, которая имеет отношение ForeignKey к самой себе для определения отношений родитель-потомок.
class Page(BaseModel):
title = models.CharField(
max_length=280,
blank=False,
null=False,
)
description = models.CharField(
max_length=280,
blank=False,
null=False,
)
slug = models.SlugField(
blank=False,
null=False,
)
is_home = models.BooleanField(
default=False,
)
is_parent = models.BooleanField(
default=False,
)
parent = models.ForeignKey(
'self',
on_delete=models.PROTECT,
default='Home',
blank=True,
null=True,
related_name='children',
)
content = RichTextField(
blank=False,
null=False,
)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('page_detail', args=[str(self.slug)])
Мой views.py отфильтровывает страницы, не являющиеся родительскими:
class PageListView(ListView):
queryset = Page.objects.filter(is_parent=True, is_home=False)
template_name = 'pages/page_list.html'
context_object_name = 'page'
Но когда дело доходит до рендеринга «дочерних» объектов в моем шаблоне, я застрял. Я понял, что мне нужен цикл внутри цикла (первый для родительских страниц, второй для дочерних страниц на родительской странице), но не могу заставить второй цикл работать. Это была моя последняя попытка, которая привела к ошибке «Объект ‘Страница’ не повторяется».
{% extends '_base.html' %}
{% block content %}
<div class="container">
{% for page in page %}
<p>{{ page.title }}</p>
{% for children in page %}
<p>{{ page.title }}</p>
{% endfor %}
{% endfor %}
</div>
{% endblock content %}
Ответ №1:
Вы можете выполнить итерацию по менеджеру, созданному с помощью related_name:
{% block content %}
<div class="container">
{% for page in page %}
<p>{{ page.title }}</p>
{% for child in page.children.all %}
<p>{{ child.title }}</p>
{% endfor %}
{% endfor %}
</div>
{% endblock content %}
Вы можете повысить эффективность с помощью .prefetch_related(…)
предложения [Django-doc]:
class PageListView(ListView):
queryset = Page.objects.filter(
is_parent=True, is_home=False
).prefetch_related('children')
template_name = 'pages/page_list.html'
context_object_name = 'page'
Кроме того, я бы посоветовал не создавать дополнительное поле is_parent
, поскольку это форма дублирования данных. Оказывается, что синхронизировать поля даже в одной базе данных может быть сложнее, чем кажется на первый взгляд. Вы можете проверить, является ли объект родительским с помощью:
class PageListView(ListView):
queryset = Page.objects.filter(
children__isnull=False, is_home=False
).distinct().prefetch_related('children')
template_name = 'pages/page_list.html'
context_object_name = 'page'
Комментарии:
1. Спасибо @Willem-van-onsem — проблема решена! Также воспользуюсь вашим советом, чтобы убедиться, что мои запросы тоже эффективны.