The new no-self format¶
As of Jan 2021, there is a new optional format for oTree apps.
pages.py with a single
The new format unifies oTree’s syntax.
For example, before, you needed to write either
self.player.payoff, depending on what part of the code you were in.
Now, you can always write
In fact, the
self keyword has been eliminated entirely
If you use oTree Studio, your code has already been upgraded to the no-self format.
If you use a text editor, you can keep using the existing format, or use the new one if you wish. They both have access to the same features. The models.py format will continue to be fully supported and get access to the newest features.
About the new format¶
- “self” is totally gone from your app’s code.
- Whenever you want to refer to the player, you write player. Same for group and subsession.
- Each method in oTree is changed to a function.
- There is no more models.py and pages.py. The whole game fits into one file (__init__.py).
- Everything else stays the same. All functions and features do the same thing as before.
Here is an example of an __init__.py in the “no self” format (with the dictator game):
class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): kept = models.CurrencyField( min=0, max=Constants.endowment, label="I will keep", ) # FUNCTIONS def set_payoffs(group): player1 = group.get_player_by_id(1) player2 = group.get_player_by_id(2) player1.payoff = group.kept player2.payoff = Constants.endowment - group.kept # PAGES class Introduction(Page): pass class Offer(Page): form_model = 'group' form_fields = ['kept'] def is_displayed(player): return player.id_in_group == 1 class ResultsWaitPage(WaitPage): after_all_players_arrive = 'set_payoffs' class Results(Page): @staticmethod def vars_for_template(player): group = player.group return dict(payoff=player.payoff, offer=Constants.endowment - group.kept)
So, what has changed?
- As you see, set_payoffs has changed from a group method to a regular function that takes “group” as its argument. This should be clearer to most people.
- is_displayed and vars_for_template are no longer page methods that take an argument ‘self’, but direct functions of the player. Now you can directly write ‘player’ without needing ‘self.’ in front of it. (If you are using a text editor like PyCharm, you should add @staticmethod before vars_for_template and is_displayed to indicate that they are not regular methods.)
- There is no longer any distinction between page methods and model methods. The is_displayed and vars_for_template can freely be moved up into the “FUNCTIONS” section, and reused between pages, or put inside a page class if they only pertain to that class.
- The app folder is simplified from this:
dictator/ __init__.py Decide.html Results.html
Also, the “import” section at the top is simplified.
# models.py from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) # pages.py from otree.api import Currency as c, currency_range from ._builtin import Page, WaitPage from .models import Constants
# __init__.py from otree.api import *
You can see the sample games in the new format here: here.
How does this affect you?¶
This no-self format is only available with oTree Lite. oTree Lite supports both formats. Within the same project, you can have some apps that use the models.py format, and some that use the no-self format.
There is a command “otree remove_self” that can automatically convert the models.py format to the no-self format. This is for people who are curious what their app would look like in the no-self format. Later, I will describe this command and how to use it.
Q: Do I need to change my existing apps? A: No, you can keep them as is. The “no-self” format is optional.
Q: Will I have to re-learn oTree for this new format? A: No, you don’t really need to relearn anything. Every function, from creating_session, to before_next_page, etc, does the same thing as before. And there are no changes to other parts of oTree like templates or settings.py.
Q: Why didn’t you implement it this way originally? A: The first reason is that oTree got its structure from Django. But now that I made oTree Lite which is not based on Django, I have more freedom to design the app structure the way I see fit. The second reason is that this is quite a tailored design. It was necessary to wait and see how oTree evolved and how people use oTree before I could come up with the most appropriate design.
How to use it¶
First, ensure that you are using oTree Lite:
pip3 install -U otree
Then do one of the following:
- Convert your existing apps using
otree remove_self, as described in this page.
- Create a new project.
There are now 2 branches of the documentation. These docs you are reading now are based on the no-self format (see the note at the top of the page).
Try it out and send me any feedback!
The “otree remove_self” command¶
If you prefer the no-self format, or are curious what your app would look like in this format, follow these steps. First, then install oTree Lite:
pip3 install -U otree
Note this command pretty aggressively converts all your model methods to functions,
If you have a lot of custom methods,
you should check that your method calls still work.
before_next_pagenow takes a second arg
- You can optionally add a type hint to your function signatures. For example,
def xyz(player: Player). If you use PyCharm or VS Code, that will mean you get better autocompletion.
- If your apps are big and you want to split the code between several files like
pages.py, that should work fine. Just put this in your
from .models import * from .pages import *