You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过JavaScript修改Django模板中的for循环内容实现比赛列表切换

Hey there! Let's break down your problem and fix it step by step.

First, a key thing to understand: Django template tags (like {% regroup %} or {% for %}) run on the server when the page is rendered. By the time the page reaches your browser, those tags have already been converted into plain HTML. That means you can't write Django code inside JavaScript—JS runs client-side, and it has no access to the server-side template engine.

Instead, the right approach is to use AJAX to fetch new match data from your backend when the user clicks a button, then re-render the match list using JavaScript. Here's how to implement this:

1. Create an API endpoint to fetch match data

First, add a new view in views.py that returns match data as JSON (instead of rendering a template). This will let your JavaScript request data for any date range:

from django.http import JsonResponse
from datetime import datetime

def get_matches_api(request):
    # Get start/end dates from the request query parameters
    start_str = request.GET.get('start')
    end_str = request.GET.get('end')
    
    # Validate date format
    try:
        start = datetime.strptime(start_str, "%Y-%m-%d").date()
        end = datetime.strptime(end_str, "%Y-%m-%d").date()
    except ValueError:
        return JsonResponse({'error': 'Invalid date format. Use YYYY-MM-DD.'}, status=400)
    
    # Fetch matches using your existing function
    matches = request_games(start, end)
    
    # Serialize matches to JSON (convert dates to strings since datetime objects aren't JSON-friendly)
    serialized_matches = []
    for match in matches:
        serialized = match.copy()
        serialized['date'] = serialized['date'].strftime("%Y-%m-%d")
        serialized['time'] = serialized['time'].strftime("%H:%M")
        serialized_matches.append(serialized)
    
    return JsonResponse({
        'matches': serialized_matches,
        'start': start_str,
        'end': end_str
    })

Add a URL route for this API in urls.py:

path('api/matches/', views.get_matches_api, name='matches_api'),

2. Update your template to handle client-side rendering

Modify home.html to add pagination buttons, and add JavaScript to handle button clicks, fetch data, and re-render the match list:

First, adjust your HTML structure to move pagination buttons outside the match container (so we don't delete them when re-rendering):

{% extends "sporty/layout.html" %}
{% load static %}
{% block body %}
<div class="pagination">
    <button id="prev-week">上一页</button>
    <button id="next-week">下一页</button>
</div>
<div class="box">
    {{start|date:"Y-m-d"}},{{end|date:"Y-m-d"}}
    {% regroup matches by date as date_list %}
    {% for date in date_list %}
    <div class="the_date">
        {{date.grouper}}
    </div>
    {% for match in date.list %}
    <div class="match_container">
        <div class="status">
            {% if match.status_code == 1%}
                {{match.minute}}'
            {% elif match.status_code == 11%}
                HT
            {% elif match.status_code == 3 %}
                Finished
            {% endif %}
        </div>
        <div class="match">
            <div class="home">
                {{match.home_name}}
                <img src="{{match.home_logo}}">
            </div>
            <div class="score">
                {% if match.status_code == 0 %}
                    {{match.time}}
                {% elif match.status_code == 17 %}
                    TBD
                {% elif match.status_code == 1%}
                    {{match.home_score}} : {{match.away_score}}
                {% elif match.status_code == 11%}
                    {{match.home_score}} : {{match.away_score}}
                {% elif match.status_code == 3 %}
                    {{match.home_score}} : {{match.away_score}}
                {% endif %}
            </div>
            <div class="away">
                <img src="{{match.away_logo}}">
                {{match.away_name}}
            </div>
        </div>
    </div>
    {% endfor %}
    {% endfor %}
</div>
{% endblock %}

{% block script %}
<script>
// Track current week's start/end dates (initialized from Django template)
let currentStart = "{{ start|date:'Y-m-d' }}";
let currentEnd = "{{ end|date:'Y-m-d' }}";

// Helper function to add/subtract days from a date string (YYYY-MM-DD)
function adjustDate(dateStr, days) {
    const date = new Date(dateStr);
    date.setDate(date.getDate() + days);
    return date.toISOString().split('T')[0];
}

// Function to render matches from JSON data
function renderMatches(data) {
    const box = document.querySelector('.box');
    // Clear existing content
    box.innerHTML = '';

    // Add date range display
    const dateRange = document.createElement('div');
    dateRange.textContent = `${data.start}, ${data.end}`;
    box.appendChild(dateRange);

    // Group matches by date (mimics Django's regroup tag)
    const groupedMatches = {};
    data.matches.forEach(match => {
        if (!groupedMatches[match.date]) {
            groupedMatches[match.date] = [];
        }
        groupedMatches[match.date].push(match);
    });

    // Render each date group
    Object.keys(groupedMatches).sort().forEach(date => {
        // Date header
        const dateDiv = document.createElement('div');
        dateDiv.className = 'the_date';
        dateDiv.textContent = date;
        box.appendChild(dateDiv);

        // Render each match in the group
        groupedMatches[date].forEach(match => {
            // Match container
            const matchContainer = document.createElement('div');
            matchContainer.className = 'match_container';

            // Status section
            const statusDiv = document.createElement('div');
            statusDiv.className = 'status';
            let statusText = '';
            switch(match.status_code) {
                case 1: statusText = `${match.minute}'`; break;
                case 11: statusText = 'HT'; break;
                case 3: statusText = 'Finished'; break;
                default: statusText = '';
            }
            statusDiv.textContent = statusText;
            matchContainer.appendChild(statusDiv);

            // Match details
            const matchDiv = document.createElement('div');
            matchDiv.className = 'match';

            // Home team
            const homeDiv = document.createElement('div');
            homeDiv.className = 'home';
            homeDiv.innerHTML = `${match.home_name} <img src="${match.home_logo}">`;
            matchDiv.appendChild(homeDiv);

            // Score section
            const scoreDiv = document.createElement('div');
            scoreDiv.className = 'score';
            let scoreText = '';
            switch(match.status_code) {
                case 0: scoreText = match.time; break;
                case 17: scoreText = 'TBD'; break;
                case 1: case 11: case 3: 
                    scoreText = `${match.home_score} : ${match.away_score}`; 
                    break;
                default: scoreText = '';
            }
            scoreDiv.textContent = scoreText;
            matchDiv.appendChild(scoreDiv);

            // Away team
            const awayDiv = document.createElement('div');
            awayDiv.className = 'away';
            awayDiv.innerHTML = `<img src="${match.away_logo}"> ${match.away_name}`;
            matchDiv.appendChild(awayDiv);

            matchContainer.appendChild(matchDiv);
            box.appendChild(matchContainer);
        });
    });
}

// Next week button click handler
document.getElementById('next-week').addEventListener('click', () => {
    const newStart = adjustDate(currentStart, 7);
    const newEnd = adjustDate(currentEnd, 7);
    
    fetch(`/api/matches/?start=${newStart}&end=${newEnd}`)
        .then(res => res.json())
        .then(data => {
            if (data.error) {
                alert(data.error);
                return;
            }
            currentStart = data.start;
            currentEnd = data.end;
            renderMatches(data);
        })
        .catch(err => console.error('Failed to fetch matches:', err));
});

// Previous week button click handler
document.getElementById('prev-week').addEventListener('click', () => {
    const newStart = adjustDate(currentStart, -7);
    const newEnd = adjustDate(currentEnd, -7);
    
    fetch(`/api/matches/?start=${newStart}&end=${newEnd}`)
        .then(res => res.json())
        .then(data => {
            if (data.error) {
                alert(data.error);
                return;
            }
            currentStart = data.start;
            currentEnd = data.end;
            renderMatches(data);
        })
        .catch(err => console.error('Failed to fetch matches:', err));
});
</script>
{% endblock %}

3. Key Notes

  • Why no Django code in JS? Django templates are rendered server-side. Once the page loads in the browser, all {% ... %} tags are gone—they've been replaced with HTML. JS can't interact with Django's template engine at that point.
  • Date consistency: Make sure you use the same date format (YYYY-MM-DD) everywhere to avoid parsing errors.
  • Error handling: The API includes basic validation, and the JS handles errors (like invalid dates) with alerts. You can expand this to show more user-friendly messages if needed.
  • Sorting: The request_games function already sorts matches by start time, and we sort the date groups in JS to keep the order consistent with your original template.

内容的提问来源于stack exchange,提问作者omaramgadd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 07:42:41