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值传给后端,获取对应的选项列表后更新前端。
- 在页面类中添加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})
- 修改前端脚本,添加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
相关产品推荐
相关产品推荐

