У попередніх лекціях ми розібралися з основами шаблонів 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 %}
Незабаром ми розглянемо наслідування шаблонів — потужний інструмент для створення складних та підтримуваних інтерфейсів!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ