如何在凤凰城中将Ecto选择查询转换为结构?

Tor*_*ein 13 elixir ecto phoenix-framework

我有两个模型,歌曲和投票,歌曲有很多票.我想选择所有歌曲并计算每张歌曲的票数.

使用mix gen任务生成的SongController中的索引操作已修改为:

def index(conn, _params) do
  query = from s in Song, select: %{id: s.id, name: s.name, artist: s.artist} 
  songs = Repo.all(query)
  render(conn, "index.html", songs: songs)
end
Run Code Online (Sandbox Code Playgroud)

在这种情况下songs包含列表列表.但是在原始的生成函数中,songs = Repo.all(Song)它是一个Song结构列表.

这意味着模板中的song_path函数会出现以下错误消息: maps cannot be converted to_param. A struct was expected, got: %{artist: "Stephen", id: 3, name: "Crossfire"}

当然,我真正想做的是以某种方式num_votes在select语句中添加一个字段,然后以某种方式为Song结构创建一个相应的字段?

Gaz*_*ler 16

首先,我们应该为歌曲模式添加一个虚拟字段,以便它可以用于存储num_votes结果:

defmodule Song do
  use Ecto.Schema

  schema "songs" do
    field :num_votes, :integer, virtual: true
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

使用的组合Ecto.Query.select/3,Ecto.Query.join/5Ecto.Query.API.count/1,我们可以计数添加到您正在使用,以从查询中选择地图:

  query = from s in Song,
    left_join: v in assoc(:votes),
    select: %{id: s.id, name: s.name, artist: s.artist, num_votes: count(v.id)} 
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用Kernel.struct将每个项目转换为结构:

  songs =
    query
    |> Repo.all()
    |> Enum.map(fn(song) -> struct(Song, song) end)
Run Code Online (Sandbox Code Playgroud)

这将返回可在视图中使用的歌曲结构列表.

  • 对于一个虚拟字段,我找到了解决方案:`select:%{s | num_votes:count(v.id)}`,但我一直坚持添加更多. (3认同)
  • 有没有办法不枚举查询的选择部分中的所有字段,但说ecto"嘿,选择架构中的所有字段加上这个num_votes:count()列"? (2认同)