如何通过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_gamesfunction 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

