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

OTree同页动态选择问题:前置答案未存致次级选项错误求助

OTree动态选项问题解决办法

问题描述

我在OTree中实现功能:同一页面先展示第一个问题gossip_receiver,用户完成选择后显示第二个问题gossip_target。第二个问题的选项需依据第一个问题的答案动态生成,但目前仅能实现第二个问题的显示,因第一个问题的答案未提交存储,导致第二个问题的选项不符合预期。以下是已完成的代码:

Python代码

class Player(BasePlayer):
   
    gossip_receiver = models.IntegerField(
        label='To whom do you want to send the information?'
    )
    gossip_target = models.IntegerField(
        label='About whom do you want to send the information?',
    )

def gossip_receiver_choices(player):
    if player.id_in_group == 1:
        choices = [
            [2, 'Player 2'],
            [3, 'Player 3'],
            [4, 'Player 4']]
    elif player.id_in_group == 2:
        choices = [
            [1, 'Player 1'],
            [3, 'Player 3'],
            [4, 'Player 4']]
    elif player.id_in_group == 3:
        choices = [
            [1, 'Player 1'],
            [2, 'Player 2'],
            [4, 'Player 4']]
    elif player.id_in_group == 4:
        choices = [
            [1, 'Player 1'],
            [2, 'Player 2'],
            [3, 'Player 3']]
    return choices

def gossip_target_choices(player):
    if player.id_in_group == 1 and player.gossip_receiver == 2:
        choices = [
            [3, 'Player 3'],
            [4, 'Player 4']]
    elif player.id_in_group == 1 and player.gossip_receiver == 3:
        choices = [
            [2, 'Player 2'],
            [4, 'Player 4']]
    elif player.id_in_group == 1 and player.gossip_receiver == 4:
        choices = [
            [2, 'Player 2'],
            [3, 'Player 3']]
    elif player.id_in_group == 2 and player.gossip_receiver == 1:
        choices = [
            [3, 'Player 3'],
            [4, 'Player 4']]
    elif player.id_in_group == 2 and player.gossip_receiver == 3:
        choices = [
            [1, 'Player 1'],
            [4, 'Player 4']]
    elif player.id_in_group == 2 and player.gossip_receiver == 4:
        choices = [
            [1, 'Player 1'],
            [3, 'Player 3']]
    elif player.id_in_group == 3 and player.gossip_receiver == 1:
        choices = [
            [2, 'Player 2'],
            [4, 'Player 4']]
    elif player.id_in_group == 3 and player.gossip_receiver == 2:
        choices = [
            [1, 'Player 1'],
            [4, 'Player 4']]
    elif player.id_in_group == 3 and player.gossip_receiver == 4:
        choices = [
            [1, 'Player 1'],
            [2, 'Player 2']]
    elif player.id_in_group == 4 and player.gossip_receiver == 1:
        choices = [
            [2, 'Player 2'],
            [3, 'Player 3']]
    elif player.id_in_group == 4 and player.gossip_receiver == 3:
        choices = [
            [1, 'Player 1'],
            [2, 'Player 2']]
    elif player.id_in_group == 4 and player.gossip_receiver == 2:
        choices = [
            [1, 'Player 1'],
            [3, 'Player 3']]
    else:
        choices = [
            [1, 'Player 1'],
            [2, 'Player 2'],
            [3, 'Player 3'],
            [4, 'Player 4']]
    return choices

HTML代码

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

{% block title %}
    Information Sharing
{% endblock %}
{% block styles %}
<style>
    .otree-timer {
        display: none;
    }
</style>

<style>
    .do-not-show {
        display: none;
    }
</style>

{% endblock %}
{% block content %}

<div class="card bg-light m-3">
<div class="card-body">
<p>
    Which information would you like to share with

    {{ formfield 'gossip_receiver' }}

</p>

    <div id="industry-box" class="do-not-show">
        {{ formfield 'gossip_target' }}
    </div>
</div>
</div>

    {{ next_button }}
{{ endblock }}


{{ block scripts }}
<script>
    let industry_box = document.getElementById("industry-box");
    let gossip_receiver_select = document.getElementById("id_gossip_receiver");
    let gossip_target_select = document.getElementById("id_gossip_target");

    gossip_receiver_select.addEventListener("change", function() {
        if (gossip_receiver_select.value != "0") {
            industry_box.classList.remove("do-not-show");
            gossip_target_select.required = true;
        } else {
            industry_box.classList.add("do-not-show");
            gossip_target_select.required = false;
        }
    });
</script>

{{ endblock }}

核心问题

页面加载时gossip_receiver的选择未提交到服务器,后端的gossip_target_choices函数无法获取实时选择值,导致选项无法动态匹配。以下是两种可行解决方案:

方案1:前端直接生成选项(适合简单逻辑)

你的选项逻辑本质是排除当前玩家和选中的gossip_receiver,可以直接用JavaScript在前端动态生成选项,无需依赖后端实时交互。

修改HTML中的脚本部分:

let industry_box = document.getElementById("industry-box");
let gossip_receiver_select = document.getElementById("id_gossip_receiver");
let gossip_target_select = document.getElementById("id_gossip_target");
// 传入当前玩家的id_in_group
const currentPlayerId = {{ player.id_in_group }};

gossip_receiver_select.addEventListener("change", function() {
    const selectedReceiver = parseInt(this.value);
    if (selectedReceiver) {
        industry_box.classList.remove("do-not-show");
        gossip_target_select.required = true;
        // 清空现有选项
        gossip_target_select.innerHTML = "";
        // 生成排除当前玩家和接收者的选项
        for (let i = 1; i <= 4; i++) {
            if (i !== currentPlayerId && i !== selectedReceiver) {
                const option = document.createElement("option");
                option.value = i;
                option.textContent = `Player ${i}`;
                gossip_target_select.appendChild(option);
            }
        }
    } else {
        industry_box.classList.add("do-not-show");
        gossip_target_select.required = false;
        gossip_target_select.innerHTML = "";
    }
});

同时简化后端gossip_target_choices函数,作为提交时的校验:

def gossip_target_choices(player):
    if not player.gossip_receiver:
        return []
    # 排除当前玩家和选中的接收者
    available_players = [p for p in [1,2,3,4] if p != player.id_in_group and p != player.gossip_receiver]
    return [[p, f'Player {p}'] for p in available_players]

方案2:AJAX请求后端获取选项(适合复杂逻辑)

如果选项逻辑更复杂,前端无法直接实现,可通过AJAX将选中的gossip_receiver值传给后端,获取对应的选项列表后更新前端。

  1. 在页面类中添加AJAX处理方法:
from django.http import JsonResponse

class YourGossipPage(Page):
    form_model = 'player'
    form_fields = ['gossip_receiver', 'gossip_target']

    def get_target_choices(self, request):
        receiver = int(request.GET.get('receiver'))
        player = self.player
        # 复用原逻辑,传入receiver参数
        def get_choices(player, receiver):
            if player.id_in_group == 1 and receiver == 2:
                return [[3, 'Player 3'], [4, 'Player 4']]
            elif player.id_in_group == 1 and receiver == 3:
                return [[2, 'Player 2'], [4, 'Player 4']]
            # 其余逻辑省略,按原函数补充
            else:
                return [[p, f'Player {p}'] for p in [1,2,3,4] if p != player.id_in_group and p != receiver]
        
        choices = get_choices(player, receiver)
        return JsonResponse({'choices': choices})
  1. 修改前端脚本,添加AJAX请求:
gossip_receiver_select.addEventListener("change", function() {
    const selectedReceiver = this.value;
    if (selectedReceiver) {
        industry_box.classList.remove("do-not-show");
        gossip_target_select.required = true;
        // 发送AJAX请求获取选项
        fetch(`/${self.__name__.lower()}/get-target-choices?receiver=${selectedReceiver}`)
            .then(response => response.json())
            .then(data => {
                gossip_target_select.innerHTML = "";
                data.choices.forEach(choice => {
                    const option = document.createElement("option");
                    option.value = choice[0];
                    option.textContent = choice[1];
                    gossip_target_select.appendChild(option);
                });
            });
    } else {
        industry_box.classList.add("do-not-show");
        gossip_target_select.required = false;
        gossip_target_select.innerHTML = "";
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:05:56