Mandatory slider in oTree/django

China☆狼群 提交于 2019-12-19 10:49:06

问题


I want to use oTree as an alternative for conducting experiments. For this purpose I am looking for a possibility to include mandatory slider questions in forms, i. e. sliders you are required to move before you are able to proceed to the next question. As a start I tried to modify oTrees survey template to achieve a solution for future usage but wasn't able to integrate common approaches like a fieldtracker into the project.

Here are two modified (yet currently after a number of unsuccessful try-outs not really functioning) versions of the models.py and views.py files which give a hint in which direction I want to go. Is there a way to get this to work?

# -*- 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()

Here comes the second file:

# -*- 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
]

Thanks in advance!


回答1:


There are two ways of doing it: by using JS only, on the client's side, and by using Django at the server side.

The simple JS solution: in the template add:

  {% 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 %}

So until the user triggers change event, the SliderTOuched var is set to False which prevents a form to be submitted. It is a compact way, but you have to deal with showing an error message to the user yourself.

=================

The longer server-side solution is the following:

in models.py define an additional field:

    class Player(BasePlayer):
        checkslider = models.IntegerField(blank=True)

in views.py in addition to your slider field pass also this extra field that will check that the slider was changed:

    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'

in template insert this hidden extra field to html:

  <input type="hidden" name="checkslider" value="" id="id_checkslider"/>

and set this field to current slider value as soon as slider is changed:

  {% block scripts %}
    <script>
      var selector = $('[data-slider] input[type="range"]');
      selector.change(function() {
        $('#id_checkslider').val(selector.val());
      });
    </script>
  {% endblock %}



回答2:


By default, Django assumes an input is required.

I think that means if you just remove the initial value, it will self-validate.

Also, you called something named "IntegerFielder()." Did you mean models.IntegerField() or is there an import that we're not seeing?




回答3:


I suggest a slight modification to Philipp's answer.

The code above still triggers the error message if the participant touches the slider, but returns the slider to the default starting position.

To fix this, I used the following script:

{% block scripts %}
    <script>
        $('input[name=q_age]').on('input', function(){
            $('#id_checkslider').val(1);
        });
    </script>
{% endblock %}

The code changes checkslider from None to 1 when the slider is touched, even if the participant sets the slider to the default starting position.



来源:https://stackoverflow.com/questions/39113641/mandatory-slider-in-otree-django

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!