高级特性¶
这些高级特性大多数在oTree Studio中不被支持。
附加模型¶
当单个玩家就需要存储几十上百个数据点时,附加模型非常有用。例如存在一系列出价,刺激与反馈时。这常与 实时页面 一起使用。
这里 有很多例子。
一个附加模型应当链接到另一个模型上:
class Bid(ExtraModel):
player = models.Link(Player)
amount = models.CurrencyField()
每当用户出价时,将其存到数据库中:
Bid.create(player=player, amount=500)
之后,你就可以提取出玩家出价的列表:
bids = Bid.filter(player=player)
一个附加模型可以有多个链接:
class Offer(ExtraModel):
sender = models.Link(Player)
receiver = models.Link(Player)
group = models.Link(Group)
amount = models.CurrencyField()
accepted = models.BooleanField()
此时你就可以以多种方式查询:
this_group_offers = Offer.filter(group=group)
offers_i_accepted = Offer.filter(receiver=player, accepted=True)
对于更加复杂的过滤与排序,你应当使用列表操作:
offers_over_500 = [o for o in Offer.filter(group=group) if o.amount > 500]
参考心理学实验的例子,如斯特鲁普测试,它们展示了如何从CSV表格中的每一行中生成附加模型的数据。
To export your ExtraModel data to CSV/Excel, use 自定义数据导出.
Reading CSV files¶
注解
This feature is in beta (new in oTree 5.8.2)
To read a CSV file (which can be produced by Excel or any other spreadsheet app),
you can use read_csv()
. For example, if you have a CSV file like this:
name,price,is_organic
Apple,0.99,TRUE
Mango,3.79,FALSE
read_csv()
will output a list of dicts, like:
[dict(name='Apple', price=0.99, is_organic=True),
dict(name='Mango', price=3.79, is_organic=False)]
You call the function like this:
rows = read_csv('my_app/my_data.csv', Product)
The second argument is a class that specifies the datatype of each column:
class Product(ExtraModel):
name = models.StringField()
price = models.FloatField()
is_organic = models.BooleanField()
(Without this info, it would be ambiguous whether TRUE
is supposed to be a bool,
or the string 'TRUE'
, etc.)
read_csv()
does not actually create any instances of that class.
If you want that, you must use .create()
additionally:
rows = read_csv('my_app/my_data.csv', Product)
for row in rows:
Product.create(
name=row['name'],
price=row['price'],
is_organic=row['is_organic'],
# any other args:
player=player,
)
The model can be an ExtraModel
, Player
, Group
, or Subsession
.
It’s fine if it also contains other fields; they will be ignored by read_csv()
.
模板¶
template_name¶
如果模板需要有一个与你的页面类不同的名字(例如你在不同的页面中共享同一模板),设置 template_name
即可。例如:
class Page1(Page):
template_name = 'app_name/MyPage.html'
CSS/JS 与基模板¶
为将相同的JS/CSS包含在一个应用的所有页面中,可将其放入一个 static file 中,也可将其放入一个可包含的模板。
静态文件¶
下面说明了如何在页面中包含图片(或任何静态文件如.css, .js,等等)。
在你的oTree项目的根文件夹中,有一个 _static/
文件夹。将文件放在这里,例如 puppy.jpg
.然后,在你的模板中,你可以通过 {{ static 'puppy.jpg' }}
获取文件的URL。
要显示图片,使用 <img>
标签,如:
<img src="{{ static 'puppy.jpg' }}"/>
上面我们将文件保存在 _static/puppy.jpg
,但是实际上更好的做法是创建一个子文件夹,起名为你应用的名字,并存为 _static/your_app_name/puppy.jpg
,这使得文件组织有条理,并避免了名字冲突。
此时你的HTML代码变为:
<img src="{{ static 'your_app_name/puppy.jpg }}"/>
(如果你愿意的话,你也可以将静态文件放入你的应用文件夹,在子文件夹 static/your_app_name
中。)
如果静态文件在你更改了之后仍不改变,这是因为你的浏览器缓存了这个文件。重载整个页面即可(通常快捷键是 Ctrl+F5)
如果你需要视频或者高解析度的图片,更好的做法是将其存储在线上别的地方并使用URL进行引用。因为大文件使得上传 .otreezip文件非常缓慢。
等待页面¶
自定义等待页面模板¶
You can make a custom wait page template.
For example, save this to your_app_name/MyWaitPage.html
:
{{ extends 'otree/WaitPage.html' }}
{{ block title }}{{ title_text }}{{ endblock }}
{{ block content }}
{{ body_text }}
<p>
My custom content here.
</p>
{{ endblock }}
然后在等待页面中使用这一模板:
class MyWaitPage(WaitPage):
template_name = 'your_app_name/MyWaitPage.html'
此时你可以如寻常一样使用 vars_for_template
。实际上, body_text
与 title_text
属性就是设置 vars_for_template
的简略方式;下面两段代码是等价的:
class MyWaitPage(WaitPage):
body_text = "foo"
class MyWaitPage(WaitPage):
@staticmethod
def vars_for_template(player):
return dict(body_text="foo")
如果你想全局应用你的自定义等待页面模板,将其保存在 _templates/global/WaitPage.html
.oTree将自动地在所有地方使用它替换内置的等待页面。
货币¶
为将”points”自定义为别的名字如”tokens” 或 “credits”, 设置 POINTS_CUSTOM_NAME
,例如 POINTS_CUSTOM_NAME = 'tokens'
.
你可以改变真实世界货币的小数位数,通过设置 REAL_WORLD_CURRENCY_DECIMAL_PLACES
。如果额外的小数位数显示出来但总是0,那么你应当重置数据库。