#1
by
AndreL
Stack Exchange seems to absolutely hate patterned variable names or loops over var names, but I need to have loops, within either Page or Player classes, over logically-named sets of variables. In Stata or R, this is extremely easy to do. How can I simplify this highly redundant code from Python for oTree 5, without using ExtraModel? ========================= class C.Constants(BaseConstants) N_STEPS= 5 COST= 120 GLOBAL_MAX = 800 class Player(BasePlayer): max_price= models.FloatField() cost= models.FloatField() step1= models.IntegerField() step2= models.IntegerField() step3= models.IntegerField() step4= models.IntegerField() step5= models.IntegerField() bid_step1= models.IntegerField() bid_step2= models.IntegerField() bid_step3= models.IntegerField() bid_step4= models.IntegerField() bid_step5= models.IntegerField() def creating_session(subsession: Subsession) player.cost= C.COST class Boundary(Page): form_model = 'player' form_fields = ['max_price'] def before_next_page(player: Player, timeout_happened): player.step1= ((player.max_price -player.cost) / C.N_STEPS * 1) player.step2= ((player.max_price -player.cost) / C.N_STEPS * 2) player.step3= ((player.max_price -player.cost) / C.N_STEPS * 3) player.step4= ((player.max_price -player.cost) / C.N_STEPS * 4) player.step5= ((player.max_price -player.cost) / C.N_STEPS * 5) class ElicitBids(Page): form_model = 'player' form_fields = ['offer_step1', 'offer_step2', 'offer_step3', 'offer_step4, 'offer_step5'] --------------- Then on Elicitation.html {{ if player.step1 <= C.GLOBAL_MAX }} {{ formfield offer_step1 }} {{ endif }} {{ if player.step2 <= C.GLOBAL_MAX }} {{ formfield offer_step2 }} {{ endif }} {{ if player.step3 <= C.GLOBAL_MAX }} {{ formfield offer_step3 }} {{ endif }} {{ if player.step4 <= C.GLOBAL_MAX }} {{ formfield offer_step4 }} {{ endif }} {{ if player.step5 <= C.GLOBAL_MAX }} {{ formfield offer_step5 }} {{ endif }} ====================================================
#2
by
gr0ssmann
I think within Python something like this should work: for i in range(20): setattr(Player, f"myvar{i}", models.IntegerField()) I believe that within the template, you would have to generate the code beforehand. I don't think oTree's template language permits reflection (https://en.wikipedia.org/wiki/Reflective_programming).
#3
by
Chris_oTree
See getattr_setattr in otree-snippets. As for the form in the template, you should put the dynamic logic in get_form_fields then just do {{ formfields }}.
#4
by
BonnEconLab
See also https://www.otreehub.com/forum/823/.