JavaRush /Курси /Модуль 3: Django /Передача даних у шаблони через context

Передача даних у шаблони через context

Модуль 3: Django
Рівень 5 , Лекція 2
Відкрита

У попередніх лекціях ми розібралися з основами шаблонів 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.

Пастки та як їх уникнути

При роботі зі складними даними у шаблонах часто зустрічаються підводні камені. Давайте розберемо основні проблеми та способи їх вирішення:

  1. Безпечний доступ до вкладених даних:
    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 %}
  1. Робота з методами об'єктів:

    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>
  1. Відлагодження складних структур:
    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 %}

Незабаром ми розглянемо наслідування шаблонів — потужний інструмент для створення складних та підтримуваних інтерфейсів!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ