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

如何用Python将已注册足球队展示为淘汰赛图表?

足球队淘汰赛图表可视化实现

我现有一段Django代码,能够从数据库读取注册球队信息并生成填充好的Excel文件,现在需要将这些球队以淘汰赛对阵图表的形式可视化展示。

原代码功能回顾

原代码负责加载Excel模板、填充随机排序的球队信息并导出Excel:

from django.shortcuts import render
from .models import Team, TieSheet
from openpyxl.utils import get_column_letter
from openpyxl.styles import Font
from openpyxl import Workbook
from django.http import HttpResponse
import pandas as pd
from openpyxl.styles import Font, PatternFill, Alignment, borders
from django.http import HttpResponse
from openpyxl import load_workbook
from django.core.files import File
from django.core.files.base import ContentFile

def tie_sheet(request):
    # Load the Excel template
    workbook = load_workbook(filename='static/excel/Tie-sheet.xlsx')
    
    # Get the active worksheet
    worksheet = workbook.active
    
    # Create merged cell for POLL1 and POLL2 columns and set heading
    worksheet.merge_cells(start_row=1, start_column=2, end_row=1, end_column=3)
    worksheet.cell(row=1, column=2, value='POLL1')
    worksheet.cell(row=1, column=4, value='POLL2')
    
    # Get the teams from the database
    teams = Team.objects.all().order_by('?')
    
    # Fill the data into the worksheet
    row_num = 2
    count = 1
    col = 2
    for team in teams:
        cell = worksheet.cell(row=row_num, column=col, value=team.title)
        cell.border = borders.Border(left=borders.Side(style='thin'), right=borders.Side(style='thin'), top=borders.Side(style='thin'), bottom=borders.Side(style='thin'))
        
        if col == 2:
            col = 4
            row_num = row_num
        elif col == 4:
            col = 2
            row_num += 2
        count = count+2
    
    # Add styling
    for row in worksheet.iter_rows(min_row=1, max_row=1):
        for cell in row:
            cell.font = Font(size=12, bold=True)
            cell.alignment = Alignment(horizontal='center')
    
    for col in worksheet.iter_cols(min_col=2, max_col=4):
        for cell in col:
            cell.font = Font(size=12, bold=True)
            cell.alignment = Alignment(horizontal='center')
    
    # Export Excel
    response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    response['Content-Disposition'] = 'attachment; filename=teams.xlsx'
    workbook.save(response)
    
    return response

实现淘汰赛可视化的方案

1. 后端调整:生成结构化的淘汰赛对阵数据

修改视图函数,保留Excel导出功能的同时,生成适合前端渲染的淘汰赛对阵结构(自动将球队数补至最近的2的幂次,不足时填充“轮空”):

from django.shortcuts import render
from .models import Team
from openpyxl.styles import Font, PatternFill, Alignment, borders
from openpyxl import load_workbook
from django.http import HttpResponse
import math

def generate_knockout_bracket(teams):
    # 调整球队数量为最近的2的幂次
    team_count = len(teams)
    next_power = 2 ** math.ceil(math.log2(team_count))
    # 补轮空
    while len(teams) < next_power:
        teams.append({"title": "轮空"})
    
    bracket = []
    current_round = teams.copy()
    round_num = 1
    
    while len(current_round) > 1:
        next_round = []
        matches = []
        # 两两配对生成对阵
        for i in range(0, len(current_round), 2):
            match = {
                "team1": current_round[i],
                "team2": current_round[i+1],
                "winner": None  # 可扩展为后续填写胜者
            }
            matches.append(match)
            next_round.append({"winner": None, "match": match})
        bracket.append({
            "round_name": f"第{round_num}轮",
            "matches": matches
        })
        current_round = next_round
        round_num += 1
    
    # 添加决赛轮
    bracket.append({
        "round_name": "决赛",
        "matches": [{"team1": current_round[0]["match"]["winner"], "team2": current_round[1]["match"]["winner"], "winner": None}]
    })
    return bracket

def tie_sheet(request):
    teams = Team.objects.all().order_by('?')
    # 转换为字典列表便于前端处理
    team_list = [{"id": team.id, "title": team.title} for team in teams]
    # 生成淘汰赛对阵结构
    knockout_bracket = generate_knockout_bracket(team_list)
    
    if request.GET.get('export') == 'excel':
        # 原Excel导出逻辑
        workbook = load_workbook(filename='static/excel/Tie-sheet.xlsx')
        worksheet = workbook.active
        
        worksheet.merge_cells(start_row=1, start_column=2, end_row=1, end_column=3)
        worksheet.cell(row=1, column=2, value='POLL1')
        worksheet.cell(row=1, column=4, value='POLL2')
        
        row_num = 2
        col = 2
        for team in teams:
            cell = worksheet.cell(row=row_num, column=col, value=team.title)
            cell.border = borders.Border(left=borders.Side(style='thin'), right=borders.Side(style='thin'), top=borders.Side(style='thin'), bottom=borders.Side(style='thin'))
            
            if col == 2:
                col = 4
            elif col == 4:
                col = 2
                row_num += 2
        
        for row in worksheet.iter_rows(min_row=1, max_row=1):
            for cell in row:
                cell.font = Font(size=12, bold=True)
                cell.alignment = Alignment(horizontal='center')
        
        for col in worksheet.iter_cols(min_col=2, max_col=4):
            for cell in col:
                cell.font = Font(size=12, bold=True)
                cell.alignment = Alignment(horizontal='center')
        
        response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        response['Content-Disposition'] = 'attachment; filename=teams.xlsx'
        workbook.save(response)
        return response
    
    # 渲染可视化页面
    return render(request, 'knockout_bracket.html', {'bracket': knockout_bracket})

2. 前端模板:绘制淘汰赛图表

创建templates/knockout_bracket.html,用HTML+CSS实现横向排列的淘汰赛对阵图:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>球队淘汰赛对阵图</title>
    <style>
        .bracket-container {
            display: flex;
            justify-content: space-around;
            padding: 20px;
            gap: 40px;
            overflow-x: auto;
            min-height: 600px;
        }
        .round {
            display: flex;
            flex-direction: column;
            gap: 80px;
        }
        .match {
            display: flex;
            flex-direction: column;
            gap: 10px;
            min-width: 200px;
            position: relative;
        }
        .team {
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            text-align: center;
            background: #f8f9fa;
            font-weight: bold;
        }
        .winner {
            background: #d4edda;
            border-color: #c3e6cb;
        }
        .round-title {
            text-align: center;
            font-size: 1.2em;
            font-weight: bold;
            margin-bottom: 10px;
        }
        /* 对阵连接线条 */
        .match::after {
            content: '';
            position: absolute;
            left: 100%;
            top: 25px;
            width: 40px;
            height: 2px;
            background: #ccc;
        }
        .round:not(:last-child) .match::after {
            display: block;
        }
        .round:last-child .match::after {
            display: none;
        }
    </style>
</head>
<body>
    <h1 style="text-align: center;">足球队淘汰赛对阵图</h1>
    <div class="bracket-container">
        {% for round in bracket %}
            <div class="round">
                <div class="round-title">{{ round.round_name }}</div>
                {% for match in round.matches %}
                    <div class="match">
                        <div class="team">{{ match.team1.title }}</div>
                        <div class="team">{{ match.team2.title }}</div>
                    </div>
                {% endfor %}
            </div>
        {% endfor %}
    </div>
    <div style="text-align: center; margin-top: 30px;">
        <a href="?export=excel" style="padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 4px;">导出Excel对阵表</a>
    </div>
</body>
</html>

功能说明

  • 后端自动处理球队数量,确保符合淘汰赛对阵要求(2的幂次)
  • 前端通过Flex布局实现横向的淘汰赛对阵图,轮次清晰区分
  • 保留原Excel导出功能,通过?export=excel参数触发
  • 可扩展胜者标记功能:修改后端winner字段并更新前端.winner样式即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 14:20:35