Here are things for Django developers to know about oTree.
otree command is a customized version of Django’s
In addition to the built-in Django management commands like
oTree defines a few extra ones like
Migrations and “resetdb”¶
You generally shouldn’t use
otree resetdb, which will reset and sync the database.
If you need to preserve the database between updates, you can try the strategy
mentioned in Modifying an existing database.
The folder containing your games is a Django project, as explained here.
It comes pre-configured with all the files, settings and dependencies so that it works right away. You should create your apps inside this folder.
oTree doesn’t work with Gunicorn, mod_wsgi, or any other typical WSGI server.
Because it uses Django Channels
for WebSocket support, it should be run with
which internally starts the Daphne server, several channels workers, and a task queue.
More info here.
Auto-save of models¶
In oTree, you don’t need to explicitly call
.save() on your models;
oTree will do it automatically (it uses an idmap cache).
However, this auto-save feature does not apply to custom models or pages that don’t inherit from oTree’s,
or custom WebSocket/AJAX code. In that case, you have to remember to save your database
models yourself as you would in a regular Django project.
You will also need to figure out how to query your models using Django’s ORM and the model’s pk/code, etc.
Misc notes on models¶
default=Noneare not necessary in your model field declarations; in oTree fields are null by default.
initialis an alias for
defaultin a model field’s kwargs.
labelis an alias for
verbose_namein a model field’s kwargs.
Adding custom pages & URLs¶
First, define the view function in one of your project modules.
# In my_app.pages from django.http import HttpResponse def my_view(request): return HttpResponse('This is a custom view')
Create a file
urls.py in your project root.
In this file, put:
# urls.py from django.urls import path from otree.urls import urlpatterns import my_app.pages urlpatterns.append(path('my_view/', my_app.pages.my_view))
In your settings.py, set
ROOT_URLCONF to point to the
urls.py that you just created:
# settings.py ROOT_URLCONF = 'urls'
Real-time and WebSockets¶
This section is for advanced programmers who want to use oTree’s internal and unsupported features.
The information below has changed as of July 2019.
oTree uses Django channels for real-time (WebSocket) functionality. You can add your own real-time interactions such as a continuous-time market.
As of September 2019, we use Django Channels 2.x. (Previously, oTree used Django Channels 0.17.3.)
Django Channels 2.x has many API changes. Any existing oTree apps that depend on the old version of Channels will break when you upgrade.
This article lists the differences in the new version of channels. In particular:
channels.Groupno longer exists. Instead, you use
If your functions are not async, you need to wrap
If you want to send to a group from
pages.py, you use
get_channel_layer(), then do
group_send. Rather than sending JSON to the websocket directly, you invoke a method on your consumer class, by adding
"type": "your_method_name"to the event. See here (don’t be confused by dots in type names, they just get converted to underscores).
The “ChatConsumer” example here is a good simple example showing the new API.
You also need to define websocket routes (which are like URL patterns that decide which consumer to run).
You can put them in a module called
You should make a list of routes called
channel_routing like before).
EXTENSION_APPS = ['your_app'].
to see how oTree queries and saves models inside consumers.
If you are building your app for long-term stability,
beware of importing anything from
otree.channels into your code.
Like anything outside of
otree.api, it may be removed abruptly.