Here are things for Django developers to know about oTree.

otree command

The otree command is a customized version of Django’s

For example, otree runserver is basically equivalent to python runserver.

In addition to the built-in Django management commands like runserver and startapp, oTree defines a few extra ones like resetdb, create_session, and runprodserver.

For the list of available commands, enter otree help. For information about a specific command, enter otree help [command], e.g. otree help test.

Migrations and “resetdb”

If you are using oTree, you generally shouldn’t use makemigrations and migrate. We are not fully compatible with migrations yet. Instead, run otree resetdb, which will reset and sync the database.

Project folder

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 otree runprodserver, which internally starts the Daphne server, several channels workers, and a task queue. More info here.


  • If you are using oTree in a typical way (with and, You don’t need to explicitly call .save() on your models; oTree will do it automatically.
  • null=True and default=None are not necessary in your model field declarations; in oTree fields are null by default.
  • initial is an alias for default in a model field’s kwargs.
  • On CharFields, max_length is not required.

Adding custom views & URLs

You can create URLs and views that are independent of oTree, using Django’s URL dispatcher and views.

First, define the view function in one of your project modules. It can be a function-based view or class-based view.

from django.http import HttpResponse

def my_view(request):
    return HttpResponse('This is a custom view')

Create a file in your project root. In this file, put:

from django.conf.urls import url
from otree.urls import urlpatterns

urlpatterns.append(url(r'^my_view/$', 'my_module.my_view'))

In your, set ROOT_URLCONF to point to the that you just created:


If you need to access oTree’s models, you will have to handle querying and saving objects yourself.

Real-time and WebSockets

oTree uses Django channels for real-time (WebSocket) functionality.

If you are comfortable with more advanced programming, you can add your own real-time interactions such as chat between players.

First, create a module in one of your apps. For each WebSocket, you should create a connect consumer and disconnect consumer. Here we will use a trivial example, taken from the Channels “getting started” documentation:

# In
from channels import Group

# Connected to websocket.connect
def ws_add(message):
    message.reply_channel.send({"accept": True})

# Connected to websocket.disconnect
def ws_disconnect(message):

See otree.channels.consumers for examples of more complex consumers. If you need to access oTree’s models in your consumers, you will have to handle querying and saving objects yourself.

Next, create a module (either in your project root or in an app) and append your routes to oTree’s built-in routes:

from channels.routing import route
from myapp.consumers import ws_add, ws_disconnect
from otree.channels.routing import channel_routing

channel_routing += [
    route("websocket.connect", ws_add, path=r"^/chat"),
    route("websocket.disconnect", ws_disconnect, path=r"^/chat"),

In, set CHANNEL_ROUTING = 'routing.channel_routing' (this is the dotted path to your channel_routing variable in

Chat box

I have created a beta oTree chat based on Django channels here.