В предыдущих лекциях мы разобрались с основами шаблонов Django и простой передачей данных через контекст. А сегодня пойдём дальше и научимся работать со сложными структурами данных: объектами с методами, вложенными словарями и списками. Ещё разберёмся с типичными ошибками и научимся их исправлять.
Работа со сложными структурами данных
В реальных проектах редко приходится передавать в шаблон просто строку или число. Чаще всего это объекты моделей, сложные словари или вложенные списки. Давайте разберём, как Django справляется с такими данными.
Например, у нас есть объект продукта с методами и связанными данными:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
self.reviews = []
def get_price_with_discount(self):
return self.price * 0.9
def add_review(self, text, rating): # Метод с аргументами
self.reviews.append({"text": text, "rating": rating})
def catalog_view(request):
product = Product("Телефон", 1000)
product.add_review("Отличный!", 5)
return render(request, "catalog.html", {"product": product})
В шаблоне можно использовать атрибуты и методы без аргументов:
<h2>{{ product.name }}</h2>
<p>Цена: {{ product.price }}</p>
<p>Со скидкой: {{ product.get_price_with_discount }}</p>
<!-- Но этот вызов НЕ сработает, так как метод требует аргументов -->
{{ product.add_review }}
Django из соображений безопасности позволяет вызывать только методы объектов, которые не требуют аргументов.
Вложенные структуры и циклы
Часто в шаблон нужно передать данные с несколькими уровнями вложенности. Например, каталог товаров с категориями и подкатегориями. Django отлично справляется с такими структурами:
def catalog_view(request):
catalog = {
'electronics': {
'name': 'Электроника',
'categories': [
{
'name': 'Смартфоны',
'products': [
{'name': 'iPhone', 'price': 999},
{'name': 'Samsung', 'price': 899}
]
},
{
'name': 'Ноутбуки',
'products': [
{'name': 'MacBook', 'price': 1299},
{'name': 'Dell XPS', 'price': 1199}
]
}
]
}
}
return render(request, 'catalog.html', {'catalog': catalog})
В шаблоне мы можем использовать вложенные циклы и проверки:
<h1>{{ catalog.electronics.name }}</h1>
{% for category in catalog.electronics.categories %}
<h2>{{ category.name }}</h2>
<ul>
{% for product in category.products %}
<li>
{{ product.name }} — ${{ product.price }}
{% if product.price > 1000 %}
<span>Премиум товар</span>
{% endif %}
</li>
{% empty %}
<li>В этой категории пока нет товаров</li>
{% endfor %}
</ul>
{% endfor %}
Обратите внимание на тег {% empty %} — он показывает альтернативный контент, если список пустой. Это удобнее, чем проверять длину списка через if.
Ловушки и как их избегать
При работе со сложными данными в шаблонах часто встречаются подводные камни. Давайте разберём основные проблемы и способы их решения:
- Безопасный доступ к вложенным данным:
def view(request): data = { 'user': { 'profile': None # Допустим, профиль не создан } } return render(request, 'template.html', {'data': data})
<!-- Рискованный вариант -->
{{ data.user.profile.name }} <!-- Вызовет ошибку! -->
<!-- Безопасный вариант -->
{% if data.user.profile %}
{{ data.user.profile.name }}
{% else %}
<p>Профиль не заполнен</p>
{% endif %}
Работа с методами объектов:
class Article: def get_comments_count(self): return len(self.comments) def add_comment(self, text): # Метод с аргументом self.comments.append(text)
<!-- Сработает -->
<p>Комментариев: {{ article.get_comments_count }}</p>
<!-- НЕ сработает, так как метод требует аргумент -->
<p>{{ article.add_comment }}</p>
- Отладка сложных структур:
def view(request): complex_data = get_complex_data() print("Debug data:", complex_data) # Поможет при отладке return render(request, 'template.html', {'data': complex_data})
Практические примеры
Давайте разберём реальный пример — панель администратора с информацией о курсах и студентах:
def admin_dashboard(request):
courses_data = {
'python': {
'name': 'Python Pro',
'groups': [
{
'name': 'PY-1',
'students': [
{
'name': 'Анна',
'progress': 95,
'has_homework': True,
'completed_tasks': [
{'title': 'Django Models', 'score': 100},
{'title': 'API Views', 'score': 90}
]
},
{
'name': 'Борис',
'progress': 80,
'has_homework': False,
'completed_tasks': []
}
]
}
]
}
}
return render(request, 'dashboard.html', {'courses': courses_data})
А вот как мы можем это отобразить в шаблоне, используя все изученные техники:
{% for course_id, course in courses.items %}
<h2>{{ course.name }}</h2>
{% for group in course.groups %}
<div class="group-card">
<h3>Группа {{ group.name }}</h3>
{% for student in group.students %}
<div class="student {% if student.progress < 85 %}needs-attention{% endif %}">
<h4>{{ student.name }}</h4>
<!-- Безопасный доступ к данным -->
<p>Прогресс: {{ student.progress }}%</p>
<!-- Вложенные циклы с empty -->
{% for task in student.completed_tasks %}
<div>✓ {{ task.title }} ({{ task.score }}%)</div>
{% empty %}
<div>Нет выполненных заданий</div>
{% endfor %}
<!-- Сложные условия -->
{% if student.has_homework and student.progress < 90 %}
<div class="warning">Требуется внимание ментора!</div>
{% endif %}
</div>
{% empty %}
<p>В группе пока нет студентов</p>
{% endfor %}
</div>
{% endfor %}
{% endfor %}
Продвинутые приёмы отладки
Когда работаешь со сложными данными, отладка становится важной частью процесса. Вот несколько полезных техник:
Debug-режим Django
# settings.py
DEBUG = True
# views.py
def complex_view(request):
data = get_complex_data()
context = {'data': data}
if settings.DEBUG:
print("Context data:", context) # Увидим в консоли при разработке
return render(request, 'template.html', context)
Отладочные теги в шаблоне
<!-- Временно добавляем для проверки структуры данных -->
<pre>
{{ complex_data|pprint }}
</pre>
<!-- Или проверяем конкретный участок -->
{% if debug %}
<div class="debug-info">
Текущий объект: {{ current_object|pprint }}
</div>
{% endif %}
Специальные комментарии
{# Этот комментарий не попадёт в HTML #}
{% comment %}
Тут можно писать много строк
И проверять разные варианты
Временно отключая код
{% endcomment %}
Вскоре мы рассмотрим наследование шаблонов — мощный инструмент для создания сложных и поддерживаемых интерфейсов!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
{{ complex_data|pprint }} # как будо лишняя "p"