2016-01-12 12 views
6

ho i prossimi modelliRender relazione molti a molti JSON a Phoenix quadro

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 

è per una relazione molti a molti. Il mio controller per mostrare è

def index(conn, _params) do 
    users = Repo.all(User) 
     |> Repo.preload(:roles) 

    render(conn, "index.json", users: users) 
end 

Nella vista ho

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 
} 

Quando ho inviato la richiesta GET ho ottenuto questo errore

unable to encode value: {nil, "roles"} 

So che potrebbe essere perché user.roles necessità di essere formattato in qualche modo per decodificare il JSON, ma non ho alcun indizio su questo. Ho provato nel modulo

def render("user.json", %{user: user}) do 
    %{id: user.id, 
    name: user.name, 
    roles: render_many(roles, App.UserView, "roles.json") 
} 

Ma non funziona.

Qual è il modo migliore per rendere in vista molti o molti rapporti?

risposta

17

L'utilizzo di render_many/4 è corretto.

Se si desidera definire la "role.json" rendere la funzione nello stesso modulo che si può fare:

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 

noti che passiamo as: :role alla funzione render_many. Questo perché la parte assegnata (la parte %{role: role}) viene dedotta dal nome della vista. In questo caso è il UserView quindi sarebbe %{user: user} per impostazione predefinita.

Se si definisce un modulo RoleView allora si può solo spostare la funzione def render("role.json") al nuovo RoleView e chiamare render_many senza l'opzione as:

... 
roles: render_many(user.roles, MyApp.RoleView, "role.json") 
... 

Un'altra opzione che può essere preferibile per voi è quello di ricavare un protocollo nel tuo modello:

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 

Personalmente ritengo che questo modello accoppi il tuo modello, quindi preferisco utilizzare la prima opzione.

+0

Funziona! Grazie! Ho ottenuto un ruolo come "ruoli": [ { "nome": "Administrador", "id": 2 } ], – Gidrek

+0

Ottima risposta e utile. Una cosa però: non dovrebbero i: ruoli: render_many (user.roles, __MODULE__, "roles.json", come:: role) effettivamente: ruoli: render_many (user.roles, __MODULE__, "role.json ", come:: ruolo) per abbinare correttamente con la funzione di rendering definita prendendo" role.json "come argomento? – Nik

+0

@Nik Ottimo posto. Ho aggiornato la risposta. Solo per quello che sai, puoi usare backtick (\ 'Come questo \') in modo che '__MODULE__' non venga visualizzato come __MODULE__. – Gazler

Problemi correlati