Визуализируйте отношения JSON со многими ко многим в Phoenix Framework
У меня есть следующие модели
defmodule App.User do
use App.Web, :model
alias App.User
schema "users" do
field :name, :string
has_many :roles_users, App.RolesUser
has_many :roles, through: [:roles_users, :role]
timestamps
end
end
defmodule App.Role do
use App.Web, :model
schema "roles" do
has_many :roles_users, App.RolesUser
has_many :users, through: [:roles_users, :user]
field :name, :string
timestamps
end
end
defmodule App.RolesUser do
use App.Web, :model
schema "roles_users" do
belongs_to :role, App.Role
belongs_to :user, App.User
timestamps
end
end
Для многих ко многим отношениям. Мой контроллер, чтобы показать это
def index(conn, _params) do
users = Repo.all(User)
|> Repo.preload(:roles)
render(conn, "index.json", users: users)
end
По мнению у меня есть
def render("index.json", %{users: users}) do
%{users: render_many(users, App.UserView, "user.json")}
end
def render("show.json", %{user: user}) do
%{user: render_one(user, App.UserView, "user.json")}
end
def render("user.json", %{user: user}) do
%{id: user.id,
name: user.name,
roles: user.roles
}
Когда я отправил запрос GET, я получил эту ошибку
unable to encode value: {nil, "roles"}
Я знаю, что это может быть потому, что user.roles
Мне нужно каким-то образом отформатировать, чтобы декодировать JSON, но я понятия не имею об этом. Я попробовал в форме
def render("user.json", %{user: user}) do
%{id: user.id,
name: user.name,
roles: render_many(roles, App.UserView, "roles.json")
}
Но не работает.
Каков наилучший способ отобразить многие отношения?
1 ответ
Использование render_many/4 является правильным.
Если вы хотите определить функцию рендеринга "role.json" в том же модуле, вы можете сделать:
def render("user.json", %{user: user}) do
%{
id: user.id,
name: user.name,
roles: render_many(user.roles, __MODULE__, "role.json", as: :role)
}
end
def render("role.json", %{role: role}) do
%{
id: role.id
...
}
end
Обратите внимание, что мы проходим as: :role
в функцию render_many. Это потому, что присваивает (%{role: role}
) часть выведена из названия вида. В этом случае это UserView
так было бы %{user: user}
по умолчанию.
Если вы определите RoleView
модуль, то вы можете просто переместить def render("role.json")
функция к вашему новому RoleView
и позвонить render_many
без as
опция:
...
roles: render_many(user.roles, MyApp.RoleView, "role.json")
...
Другой вариант, который может быть предпочтительным для вас, - это создать протокол в вашей модели:
defmodule App.Role do
use App.Web, :model
@derive {Poison.Encoder, only: [:id, :name]}
schema "roles" do
has_many :roles_users, App.RolesUser
has_many :users, through: [:roles_users, :user]
field :name, :string
timestamps
end
Лично я чувствую, что это связывает вашу модель с вашей точки зрения, поэтому я предпочитаю использовать первый вариант.