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

如何在OTree中延迟展示结果,将各应用结果页存为静态HTML?

实现oTree多应用结果统一展示的简单方案

嘿,我完全懂你的需求——不想搞一堆participant变量来存每个结果,而是直接把每个应用的结果页面“固化”成带真实数据的静态HTML,最后统一展示,这个思路真的很省心!作为老派开发者,咱就搞简单直接的方案,不用复杂的Bakery模块(那玩意儿更适合整站静态化,不太适配oTree这种动态会话场景),下面给你两个实用方向:

方案一:直接把渲染好的HTML存在Participant Vars里(最省事)

这个方案完全不用碰文件读写,直接把每个应用的结果页面渲染成字符串存在participant的变量里,最后统一拿出来展示,简单到爆:

步骤1:在每个应用的结果页面添加渲染逻辑

打开每个应用的pages.py,找到最后那个结果页面(比如ResultsPage),添加before_next_page方法:

from django.template.loader import render_to_string
from otree.api import Page

class ResultsPage(Page):
    def before_next_page(self):
        # 自动提取当前应用的名称(比如从模块名里拆出来)
        app_name = self.__class__.__module__.split('.')[0]
        # 用Django的模板渲染器把页面转成带真实值的HTML字符串
        rendered_html = render_to_string(
            f"{app_name}/results_page.html",  # 你的结果页面模板路径
            context=self.get_context_data()   # 这里包含了模板里所有需要的变量:player、group、participant等
        )
        # 把渲染好的HTML存到participant vars里,用应用名当键区分
        self.participant.vars[f"app_results_{app_name}"] = rendered_html

这里的render_to_string会自动把模板里的{{player.payoff}}、{{group.total}}这些变量替换成实际数值,生成完整的HTML字符串。

步骤2:创建最终结果应用展示所有内容

新建一个叫FinalResults的应用(或者随便起个名字),在它的pages.py里写:

from otree.api import Page
from django.utils.safestring import mark_safe

class FinalResultsPage(Page):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 从participant vars里把所有应用的结果HTML捞出来
        results_htmls = []
        for key, html_str in self.participant.vars.items():
            if key.startswith('app_results_'):
                # 标记这是安全的HTML,避免Django把标签转义成文本
                results_htmls.append(mark_safe(html_str))
        context['results_htmls'] = results_htmls
        return context

然后在这个应用的模板FinalResultsPage.html里循环展示所有结果:

{% extends "global/Page.html" %}
{% load otree static %}

{% block title %}所有实验结果汇总{% endblock %}

{% block content %}
    <h2>各应用结果详情</h2>
    {% for html in results_htmls %}
        <div class="app-result-container">
            {{ html }}
        </div>
        <hr style="margin: 2rem 0; border-color: #eee;">
    {% endfor %}
{% endblock %}

方案二:把渲染好的HTML存成静态文件(适合需要备份的场景)

如果你需要把每个参与者的结果页面存成独立文件留底,就用这个方案:

步骤1:每个应用结果页面保存HTML文件

还是在每个应用的ResultsPage里加逻辑:

from django.template.loader import render_to_string
import os
from otree.api import Page

class ResultsPage(Page):
    def before_next_page(self):
        app_name = self.__class__.__module__.split('.')[0]
        rendered_html = render_to_string(
            f"{app_name}/results_page.html",
            context=self.get_context_data()
        )
        # 定义保存路径(比如项目根目录下的static/app_results文件夹)
        save_dir = os.path.join(os.getcwd(), 'static', 'app_results')
        # 确保文件夹存在,不存在就创建
        os.makedirs(save_dir, exist_ok=True)
        # 生成唯一文件名,避免不同参与者/会话的文件互相覆盖
        file_name = f"result_{self.participant.unique_id}_{self.session.code}_{app_name}.html"
        file_path = os.path.join(save_dir, file_name)
        # 写入文件
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(rendered_html)
        # 把文件名存到participant vars里,方便最后找到这个文件
        self.participant.vars[f"result_file_{app_name}"] = file_name

步骤2:最终结果应用读取并展示文件

在FinalResults的pages.py里:

from otree.api import Page
import os
from django.utils.safestring import mark_safe

class FinalResultsPage(Page):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        results_htmls = []
        save_dir = os.path.join(os.getcwd(), 'static', 'app_results')
        # 遍历participant vars里的文件名,读取每个文件的内容
        for key, file_name in self.participant.vars.items():
            if key.startswith('result_file_'):
                file_path = os.path.join(save_dir, file_name)
                with open(file_path, 'r', encoding='utf-8') as f:
                    html_content = f.read()
                results_htmls.append(mark_safe(html_content))
        context['results_htmls'] = results_htmls
        return context

模板和方案一的一样就行。

一些注意事项

  • 关于模板里的静态资源:如果你的results_page.html里引用了CSS、图片之类的静态资源,渲染后的HTML路径还是有效的,不用额外调整。
  • 文件清理(方案二):如果用文件存储,时间久了会积累很多文件,可以写个简单的脚本定期清理,或者在会话结束时自动删除(比如在session_config.py里加finish方法)。
  • 权限问题(方案二):部署到服务器时,要确保oTree运行的用户有static/app_results文件夹的读写权限。

这两个方案都不用深入Django,用的都是基础的模板渲染和变量/文件操作,完全符合你的需求~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:25:56