Сохранение данных JSON в БД в Zotonic
Я пытаюсь написать небольшое приложение, которое извлекает файл 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 docs в Списках свойств для получения дополнительной информации о списках свойств.
=== Зависимости были обновлены, так что если вы собираете из исходного кода, шаг ниже, больше не нужен ===
Часть JSON немного сложнее. В состав Zotonic входят mochijson2 и, как вторичная зависимость, также указывается, что есть. Последняя версия 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 оболочке. (вы должны делать это после каждого изменения в вашем коде).
Теперь проверьте в оболочке Zotonic, есть ли jiffy:decode/2, доступный, набравjiffy: <tab>
, он покажет список доступных функций и их арность.
Чтобы получить файл JSON из Интернета, выполните:
{ok, {{_, 200, _}, _, Body}} = httpc:request(get, {"url-to-JSON-here", []}, [], [])
Который даст переменную Body с содержимым. См. Документацию Erlang на http-клиенте для получения дополнительной информации об этом вызове.
Затем преобразуйте содержимое тела в термины Эрланга с помощью:
JsonData = jiffy:decode(Body, [return_maps]).
Что вам нужно делать дальше, во многом зависит от структуры вашего ресурса JSON. Имейте в виду, что теперь все в двоичных строках в кодировке UTF-8! Если вы печатаете JsonData на экран (просто введитеJsonData.
в вашей оболочке Zotonic/Erlang) вы увидите много#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.
Смотрите документацию по строке:to_list/1, чтобы узнать, почему кортежи нужны при приведении. Призыв к m_sitename:insert_items(PropList,z:c(sitename))
вызывает z_db:insert/3 в models/m_sitename.erl
но завернутый в улов:
insert_items(PropList,Context) ->
(catch z_db:insert(?table, PropList, Context)).
Хорошо, довольно длинный пост, но это должно помочь вам начать работу, если вы ищете этот ответ.
Выше было сделано с Zotonic 0.13.2 на Erlang/OTP 18.
Репост (кроме части JSON) моего поста в группе разработчиков Zotonic.