Обязательный слайдер в oTree/ Django
Я хочу использовать oTree как альтернативу для проведения экспериментов. Для этого я ищу возможность включить в формы обязательные вопросы о ползунках, то есть ползунки, которые вам необходимо переместить, прежде чем вы сможете перейти к следующему вопросу. Сначала я попытался изменить шаблон опроса oTrees, чтобы найти решение для будущего использования, но не смог интегрировать в проект общие подходы, такие как fieldtracker.
Вот две измененные (но в настоящее время после ряда неудачных попыток, которые на самом деле не работают) версий файлов models.py и views.py, которые дают подсказку, в каком направлении я хочу идти. Есть ли способ заставить это работать?
# -*- coding: utf-8 -*-
## models.py
# <standard imports>
from __future__ import division
from django.db import models
from django_countries.fields import CountryField
from model_utils import FieldTracker,
from otree import widgets
from otree.constants import BaseConstants
from otree.db import models
from otree.models import BaseSubsession, BaseGroup, BasePlayer
class Constants(BaseConstants):
name_in_url = 'survey'
players_per_group = None
num_rounds = 1
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
def set_payoff(self):
"""Calculate payoff, which is zero for the survey"""
self.payoff = 0
q_country = CountryField(
verbose_name='What is your country of citizenship?')
q_age = IntegerFielder(verbose_name='What is your age?',
min=13, max=125,
initial=25,
widget=widgets.SliderInput())
q_gender = models.CharField(initial=None,
choices=['Male', 'Female'],
verbose_name='What is your gender?',
widget=widgets.RadioSelect())
tracker = FieldTracker()
crt_bat = models.PositiveIntegerField()
crt_widget = models.PositiveIntegerField()
crt_lake = models.PositiveIntegerField()
Вот второй файл:
# -*- coding: utf-8 -*-
##views.py
from __future__ import division
from . import models
from ._builtin import Page, WaitPage
from otree.common import Currency as c, currency_range
from .models import Constants, integerfieldcustom
class Demographics(Page):
form_model = models.Player
form_fields = ['q_country',
'q_age',
'q_gender']
check_age = q_age.tracker.has_changed()
def q_age_error_message(self, ):
if Demographics.check_age == False:
return 'You must move the slider before you can continue'
class CognitiveReflectionTest(Page):
form_model = models.Player
form_fields = ['crt_bat',
'crt_widget',
'crt_lake']
def before_next_page(self):
self.player.set_payoff()
page_sequence = [
Demographics,
CognitiveReflectionTest
]
Заранее спасибо!
3 ответа
Есть два способа сделать это: используя только JS, на стороне клиента и используя Django на стороне сервера.
Простое решение JS: в шаблон добавьте:
{% block scripts %}
<script>
var SliderTouched = false;
var selector = $('[data-slider] input[type="range"]');
selector.change(function() {
SliderTouched = true;
});
$( ".form" ).submit(function( event ) {
if (!SliderTouched){
event.preventDefault();}
});
</script>
{% endblock %}
Так что пока пользователь не сработает change
событие, SliderTOuched
переменная установлена в False
что предотвращает отправку формы. Это компактный способ, но вам придется самостоятельно показывать сообщение об ошибке пользователю.
=================
Более длинное решение на стороне сервера заключается в следующем:
в models.py
определить дополнительное поле:
class Player(BasePlayer):
checkslider = models.IntegerField(blank=True)
в views.py
в дополнение к полю ползунка передайте также это дополнительное поле, которое будет проверять, что ползунок был изменен:
class MyPage(Page):
form_model = models.Player
form_fields = ['q_age', 'checkslider']
def checkslider_error_message(self, value):
if not value:
return 'Please make your decision using slider'
В шаблоне вставьте это скрытое дополнительное поле в HTML:
<input type="hidden" name="checkslider" value="" id="id_checkslider"/>
и установите это поле в текущее значение слайдера, как только слайдер будет изменен:
{% block scripts %}
<script>
var selector = $('[data-slider] input[type="range"]');
selector.change(function() {
$('#id_checkslider').val(selector.val());
});
</script>
{% endblock %}
По умолчанию Django предполагает, что требуется ввод данных.
Я думаю, это означает, что если вы просто удалите начальное значение, оно будет самоутверждаться.
Кроме того, вы вызвали что-то с именем "IntegerFielder()". Вы имели в виду models.IntegerField() или есть импорт, который мы не видим?
Я предлагаю небольшую модификацию ответа Филиппа.
Приведенный выше код по-прежнему вызывает сообщение об ошибке, если участник касается ползунка, но возвращает ползунок в исходное положение по умолчанию.
Чтобы исправить это, я использовал следующий скрипт:
{% block scripts %}
<script>
$('input[name=q_age]').on('input', function(){
$('#id_checkslider').val(1);
});
</script>
{% endblock %}
Код меняется checkslider
от None
в 1
при касании ползунка, даже если участник устанавливает ползунок в исходное положение по умолчанию.