我正在尝试编写一个小型应用程序来检索 JSON 文件(它包含一个项目列表,它们都有一些属性),将其内容保存到数据库,然后稍后显示其中的一些。我已经启动并运行了 Zotonic,生成一些 HTML 没有问题。
ATM 我一直在试图弄清楚如何定义自定义资源以及如何从数据库中的 JSON 获取数据。当数据在那里时,我应该没问题,文档似乎涵盖了该部分。
我编写了一些独立的 erlang 脚本来获取数据,我注意到 Zotonic 有一个用于解码 JSON 的库,所以这部分应该没问题。关于在哪里放置哪些代码或在哪里进一步查看的任何提示?
1 回答
z_db 模块允许使用以下方法创建自定义表:
z_db:create_table(Table, Cols, Context).
Table 变量是您的表名,可以是一个原子或包含单个原子的列表。
Cols 是由记录定义的列定义列表。目前记录定义(你可以在 include/zotonic.hrl 中找到)是:
-record(column_def, {name, type, length, is_nullable=true, default, primary_key}).
有关记录的更多信息,请参阅有关记录的Erlang 文档
我放在 users/sites/[sitename]/models/m_[sitename].erl 中的示例代码:
init(Context) ->
case z_db:table_exists(?table,Context) of
false ->
z_db:create_table(tablename,
[
#column_def{name=id, type="serial"},
#column_def{name=gid, type="integer", is_nullable=false},
#column_def{name=magnitude, type="real"},
#column_def{name=depth, type="real"},
#column_def{name=location, type="character varying"},
#column_def{name=time, type="integer"},
#column_def{name=date, type="integer"}
], Context);
true -> ok
end,
ok.
注意您指定的记录的哪些选项。我得到的大多数错误是例如在整数字段上指定长度。
models/m_sitename:init/1
不会在站点启动时被调用。确实被调用了,所以我在sitename:init/1
那里调用 init 函数以确保表存在。例子:
init(Context) ->
m_sitename:init(Context).
它由 Zotonic 使用站点的 Context 变量自动调用。您也可以使用 手动获取此变量z:c(sitename).
。因此,如果您m_sitename:init(Context).
从其他地方调用,您会这样做:
m_sitename:init(z:c(sitename)).
接下来,可以通过以下方式插入数据库:
z_db:insert(Table, PropList, Context).
其中 Table 又是一个原子或包含表示表名的单个原子的列表。上下文与上述相同。
PropList 是一个属性列表,它是一个包含由两个元素组成的元组的列表,其中第一个是原子,第二个是其关联的值/属性。例子:
PropList = [
{row, Value},
{anotherrow, AnotherValue}
].
Table = tablename.
Context = z:c(sitename).
z_db:insert(Table, PropList, Context).
有关属性列表的更多信息,请参阅属性列表上的Erlang 文档。
=== 依赖项已更新,因此如果您从源代码构建,则不再需要直接在下面的步骤 ===
JSON 部分有点棘手。Zotonic 包含 mochijson2 和jiffy作为次要依赖项。最新版本的 jiffy 包含 jiffy:decode/2 ,它允许您将映射指定为返回类型。比标准{struct, {struct, <<"">>}}
怪物更具可读性。要更新到最新版本,请编辑其中的deps/twerl/rebar.config
行
{jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.8.3"}}},
至
{jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.14.3"}}},
现在z:m().
在Zotonic shell中运行。(您必须在每次更改代码后执行此操作)。
现在检查 Zotonic shell 是否有可用的 jiffy:decode/2 jiffy: <tab>
,它会显示可用函数的列表及其数量。
要从 Internet 检索 JSON 文件,请运行:
{ok, {{_, 200, _}, _, Body}} = httpc:request(get, {"url-to-JSON-here", []}, [], [])
这将产生带有内容的变量 Body。有关此调用的更多信息,请参阅http 客户端上的 Erlang 文档。
接下来将 Body 的内容转换为 Erlang 术语:
JsonData = jiffy:decode(Body, [return_maps]).
接下来要做的很大程度上取决于 JSON 资源的结构。请记住,现在所有内容都是二进制 UTF-8 编码字符串!如果您将 JsonData 打印到屏幕上(只需JsonData.
在 Zotonic/Erlang shell 中输入),您将看到很多#map{<<"key"", <<"Value">>}
这样的内容。
我的数据是嵌套的,所以我必须像这样提取所需的数据:
[{_,ItemList}|_] = ListData.
这给了我一个地图列表,为了将它们作为单独的项目处理,我使用了以下函数:
get_maps([]) ->
done;
get_maps([First|Rest]) ->
Map = maps:get(<<"properties">>, First),
case is_map(Map) of
true ->
map_to_proplist(Map),
get_maps(Rest);
false -> done
end,
done;
get_maps(_) ->
done.
您可能还记得,该z_db:insert/3
函数需要一个属性列表来填充行,因此调用的目的map_to_proplist/1
是什么。这个函数的外观完全取决于您的数据的外观,但作为一个例子,这对我有用:
map_to_proplist(Map) ->
case is_map(Map) of
true ->
{Value1,_} = string:to_integer(binary_to_list(maps:get(<<"key1">>, Map))),
{Value2,_} = string:to_float(binary_to_list(maps:get(<<"key2">>, Map))),
{Value3,_} = string:to_float(binary_to_list(maps:get(<<"key3">>, Map))),
Value4 = binary_to_list(maps:get(<<"key4">>, Map)),
{Value5,_} = string:to_integer(binary_to_list(maps:get(<<"key5">>, Map))),
{Value6,_} = string:to_integer(binary_to_list(maps:get(<<"key6">>, Map))),
PropList = [{rowname1, Value1}, {rowname2, Value2}, {rowname3, Value3}, {rowname4, Value4}, {rowname5, Value5}, {rowname6, Value6}],
m_sitename:insert_items(PropList,z:c(sitename)),
ok;
false ->
ok
end.
请参阅有关 string:to_list/1 的文档,了解为什么在转换时需要元组。调用m_sitename:insert_items(PropList,z:c(sitename))
z_db:insert/3 inmodels/m_sitename.erl
但包含在一个 catch 中:
insert_items(PropList,Context) ->
(catch z_db:insert(?table, PropList, Context)).
好的,很长的帖子,但是如果您正在寻找这个答案,这应该可以帮助您启动并运行。
以上是在 Erlang/OTP 18 上使用 Zotonic 0.13.2 完成的。我在 Zotonic Developers group 中的
帖子的转贴(JSON 部分除外)。