# Models¶

models.py is where you define your app’s data models:

• Subsession
• Group
• Player

A player is part of a group, which is part of a subsession. See Conceptual overview.

The main purpose of models.py is to define the columns of your database tables. Let’s say you want your experiment to generate data that looks like this:

name age is_student
John 30 False
Alice 22 True
Bob 35 False
...

Here is how to define the above table structure:

class Player(BasePlayer):
name = models.StringField()
age = models.IntegerField()
is_student = models.BooleanField()


## Defining a column¶

### Field types¶

Here are the main field types:

• BooleanField (for true/false and yes/no values)
• CurrencyField for currency amounts; see Money & Payoffs.
• IntegerField
• FloatField (for real numbers)
• StringField (for text strings)
• LongStringField (for long text strings; its form widget is a multi-line textarea)

StringField and LongStringField are new (added January 2018). See oTree 2.0 for more information.

### Initial/default value¶

Your field’s initial value will be None, unless you set initial=:

class Player(BasePlayer):
some_number = models.IntegerField(initial=0)


### min, max, choices¶

For info on how to set a field’s min, max, or choices, see Simple form field validation.

## Built-in fields and methods¶

Since your models inherit from oTree’s base classes (BaseSubsession, BaseGroup, and BasePlayer), the tables already have certain pre-defined fields and methods. For example, the Player table has columns called payoff and id_in_group, as well as methods like in_all_rounds() and get_others_in_group().

These built-in fields and methods are listed below.

### Subsession¶

#### session¶

The session this subsession belongs to. See What is “self”?.

#### round_number¶

Gives the current round number. Only relevant if the app has multiple rounds (set in Constants.num_rounds). See Rounds.

#### creating_session()¶

Unlike most other built-in subsession methods, this method is one you must define yourself. Any code you put here is executed when the session is created:

creating_session allows you to initialize the subsession, by setting initial values on fields on players, groups, participants, or the subsession. For example:

class Subsession(BaseSubsession):

def creating_session(self):
for p in self.get_players():
p.payoff = c(10)


Note that self here is a subsession object, because we are inside the Subsession class. So, you cannot do self.player, because there is more than 1 player in the subsession. Instead, use self.get_players() to get all of them.

If your app has multiple rounds, creating_session gets run multiple times consecutively:

class Constants(BaseConstants):
name_in_url = 'print_statements'
players_per_group = None
num_rounds = 5

class Subsession(BaseSubsession):
def creating_session(self):
print('in creating_session', self.round_number)


Will output:

in creating_session 1
in creating_session 2
in creating_session 3
in creating_session 4
in creating_session 5


Note

This method does NOT run at the beginning of each round. For that, you should use a wait page with after_all_players_arrive().

#### before_session_starts¶

before_session_starts has been renamed to creating_session(). However, new versions of oTree still execute before_session_starts, for backwards compatibility.

#### group_randomly()¶

See Group matching.

#### group_like_round()¶

See Group matching.

#### get_group_matrix()¶

See Group matching.

#### set_group_matrix()¶

See Group matching.

#### get_groups()¶

Returns a list of all the groups in the subsession.

#### get_players()¶

Returns a list of all the players in the subsession.

### Group¶

#### session/subsession¶

The session/subsession this group belongs to. See What is “self”?.

See Groups.

See Groups.

See Groups.

#### set_players(players_list)¶

See Group matching.

### Player¶

#### id_in_group¶

Automatically assigned integer starting from 1. In multiplayer games, indicates whether this is player 1, player 2, etc.

#### payoff¶

The player’s payoff in this round. See payoffs.

#### session/subsession/group/participant¶

The session/subsession/group/participant this player belongs to. See What is “self”?.

See Groups.

See Groups.

#### role()¶

Unlike most other built-in player methods, this is one you define yourself.

This function should return a label with the player’s role, usually depending on id_in_group.

For example:

def role(self):
if self.id_in_group == 1:
if self.id_in_group == 2:
return 'seller'


Then you can use get_player_by_role('seller') to get player 2. See Groups.

Also, the player’s role will be displayed in the oTree admin interface, in the “results” tab.

### Session¶

#### num_participants¶

The number of participants in the session.

#### vars¶

See session.vars.

### Participant¶

#### vars¶

See participant.vars.

#### id_in_session¶

The participant’s ID in the session. This is the same as the player’s id_in_subsession.

See payoffs.

See payoffs.

### Constants¶

The Constants class is the recommended place to put your app’s parameters and constants that do not vary from player to player.

Here are the required constants:

• name_in_url: the name used to identify your app in the participant’s URL.

For example, if you set it to public_goods, a participant’s URL might look like this:

http://otree-demo.herokuapp.com/p/zuzepona/public_goods/Introduction/1/

• players_per_group (described in Groups)

• num_rounds (described in Rounds)

## Miscellaneous topics¶

You can define your own methods on models. This helps you keep your code organized as it gets more complex. For example, you can define a function to set players’ payoffs:

class Group(BaseGroup):
def set_payoffs(self):
print('in set_payoffs')
# etc ...


Just remember to call this function from somewhere, such as your page:

class MyWaitPage(WaitPage):
def after_all_players_arrive(self):
self.group.set_payoffs()


Because it will not be executed automatically, unlike built-in functions like creating_session(), after_all_players_arrive(), etc.

### Don’t put random values in Constants¶

Any code that is not inside a method is basically global and will only be executed once – when the server starts.

Some people write code mistakenly thinking that it will be re-executed for each new session. For example, someone who wants to generate a random probability that a coin flip will come up “heads” might do this in models.py:

class Constants(BaseConstants):
p = random.random() # wrong
print('p is', p)


As you can see from the the print output, p is only calculated when the server starts:

C:\oTree> otree devserver
p is 0.9627848454010105


That means it will be the same for all participants in all sessions (or may have some other mysterious behavior).

For the same reason, this will not work either:

class Player(BasePlayer):

p = models.FloatField(
# wrong
initial=random.random()
)


The solution is to generate the random variables inside a method, such as creating_session().

### What’s the difference between “Player” and “player”?¶

In your code, you should always use lower-case player, group, and subsession. The only exception is defining the classes in models.py, where you use class Player(BasePlayer) etc.

We use uppercase (e.g. Player) when we are referring to the whole table of players, and lowercase (player) when referring to a particular player, i.e. a row in the table. In Python, Player is a class, and player is an instance of that class.

For example, in a template, to display a player’s payoff, we must use {{ player.payoff }}, not {{ Player.payoff }}.

However, for Constants, we always use uppercase. That’s because Constants is not a database table with instances/rows, because the constants are the same for all players.

### What’s the difference between IntegerField and Integer?¶

An IntegerField is a column in the database table. An integer is one value in that table.

### How to make many fields¶

Let’s say your app has many fields that are almost the same, such as:

class Player(BasePlayer):

f1 = models.IntegerField(
blank=True, initial=0
)
f2 = models.IntegerField(
blank=True, initial=0
)
f3 = models.IntegerField(
blank=True, initial=0
)
f4 = models.IntegerField(
blank=True, initial=0
)
f5 = models.IntegerField(
blank=True, initial=0
)
f6 = models.IntegerField(
blank=True, initial=0
)
f7 = models.IntegerField(
blank=True, initial=0
)
f8 = models.IntegerField(
blank=True, initial=0
)
f9 = models.IntegerField(
blank=True, initial=0
)
f10 = models.IntegerField(
blank=True, initial=0
)

# etc...


This is quite complex; you should look for a way to simplify.

Are the fields all displayed on separate pages? If so, consider converting this to a 10-round game with just one field. See the real effort sample game for an example of how to just have 1 page that gets looped over many rounds, varying the question that gets displayed with each round.

If that’s not possible, then you can reduce the amount of repeated code by defining a function that returns a field (make_field is just an example name; you can call it anything).

def make_field(label):
return models.IntegerField(
choices=[1,2,3,4,5],
label=label,