Обязательный слайдер в 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 при касании ползунка, даже если участник устанавливает ползунок в исходное положение по умолчанию.

Другие вопросы по тегам