我有一个简单的待办事项/作者模型,其中待办事项具有author_id字段。
模型定义如下:
defmodule TodoElixir.User.Author do
use Ecto.Schema
import Ecto.Changeset
schema "authors" do
field :email, :string
field :name, :string
field :password, :string, virtual: true
field :hash, :string
has_many :todos, Main.Todo
timestamps()
end
我在这里得到一个
警告:模式TodoElixir.User.Author中的无效关联
todo
: 关联的架构Main.Todo不存在
和待办事项模型:
defmodule TodoElixir.Main.Todo do
use Ecto.Schema
import Ecto.Changeset
schema "todos" do
field :date, :date
field :description, :string
field :title, :string
belongs_to :author, User.Author
timestamps()
end
我每个人也都有一个迁移:
defmodule TodoElixir.Repo.Migrations.CreateAuthors do
use Ecto.Migration
def change do
create table(:authors) do
add :name, :string
add :email, :string
add :hash, :string
has_many :todos, Main.Todo
timestamps()
end
end
end
defmodule TodoElixir.Repo.Migrations.CreateTodos do
use Ecto.Migration
def change do
create table(:todos) do
add :title, :string
add :description, :string
add :date, :date
add :author_id, references(:authors)
timestamps()
end
end
end
如果我从模块中删除了has_many :todos, Main.Todo
,它将编译并可以查询
http://localhost:4000/api/todos,但未设置作者字段。
我尝试使用预加载和assoc,但是在https://elixirschool.com/en/lessons/ecto/associations/之后,关联应该是自动的...
在todo控制器中,我有:
def index(conn, _params) do
todos = Main.list_todos()
render(conn, "index.json", todos: todos)
end
和list_todos =
def list_todos do
Repo.all(Todo)
end
编辑:
在控制器中,我放置了:
def index(conn, _params) do
todos = Repo.all(Todo) |> Repo.preload(:author)
render(conn, "index.json", todos: todos)
end
我在控制台中看到查询:
[debug]使用TodoElixirWeb.TodoController.index / 2进行处理
参数:%{}管道:[:api] [调试] QUERY OK source =“ todos” db = 6.3ms解码= 1.7ms队列= 0.8ms SELECT t0。“ id”,t0。“ date”, t0。“描述”,t0。“标题”,t0。“ author_id”,t0。“ insertted_at”, t0。“ updated_at”来自“待办事项”,为t0 [] [debug]查询成功 source =“ authors” db = 0.6ms队列= 1.0ms SELECT a0。“ id”,a0。“ email”, a0。“名称”,a0。“哈希”,a0。“ insertted_at”,a0。“ updated_at”,a0。“ id” FROM “作者”为a a0哪里(a0。“ id” = $ 1)
对我来说哪个看起来不错,但是JSON结果:
{"data":[{"date":null,"description":"we need to do this","id":1,"title":"My first todo"}]}
我还应该告诉Elixir在JSON响应中添加关联吗?怎么样?
答案 0 :(得分:1)
您需要明确preload关系:
todos = Main.list_todos()
|> Repo.preload(:todos) # don't forget to alias repo
如果引发错误,则关系未正确引用,否则将进行联接查询,并且您将在todos
中拥有所有关系。
如果您阅读has_many/3文档,则会注意到以下内容:
:foreign_key-设置外键,该外键应映射到 其他模式,默认为当前模式的带下划线的名称 _id后缀
因此,如果您有一个外键名不同,则可以显式使用此参数:
has_many :todos, Main.Todo, foreign_key: :author_id
此外,您不应添加与迁移的关系,在迁移中,您仅定义对表所做的结构和修改,因此请删除:
has_many :todos, Main.Todo
您可以了解有关在迁移here中可以做什么的更多信息。
答案 1 :(得分:1)
根据所需要求
我有一个简单的待办事项/作者模型,其中待办事项具有一个author_id字段,需要将其解析为JSON。
defmodule TodoElixir.Repo.Migrations.CreateAuthorsTodos do
use Ecto.Migration
def change do
# create authors
create table(:authors) do
add :name, :string
add :email, :string
add :hash, :string
timestamps()
end
flush() # this one will execute migration commands above [see Ecto.Migration flush/0][1]
# create todos
create table(:todos) do
add :title, :string
add :description, :string
add :date, :date
add :author_id, references(:authors)
timestamps()
end
end
end
defmodule TodoElixir.User.Author do
use Ecto.Schema
import Ecto.Changeset
schema "authors" do
field :email, :string
field :name, :string
field :password, :string, virtual: true
field :hash, :string
has_many :todos, TodoElixir.Main.Todo
timestamps()
end
end
defmodule TodoElixir.User.Todo do
use Ecto.Schema
import Ecto.Changeset
schema "todos" do
field :date, :date
field :description, :string
field :title, :string
belongs_to :author, TodoElixir.User.Author # -> this will be used upon preload in your controller
timestamps()
end
end
alias TodoElixir.User.{Author, Todo} # -> your tables
alias TodoElixir.Repo # -> call your repo
def index(conn, _params) do
todos = list_todos()
render(conn, "index.json", todos: todos)
end
defp list_todos() do
Todo
|> Repo.all()
|> Repo.preload(:author)
end
# in your endpoint.ex
# set up Jason using this one.
plug Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"],
json_decoder: Jason
# in your TODO and AUTHOR schemas derived the fields that you need in each tables.
defmodule TodoElixir.User.Todo do
use Ecto.Schema
import Ecto.Changeset
# this is the key parsing them
@derive Jason.Encoder
defstruct %{
:date,
:description,
:title,
:author # -> This will show author. take note, if you do not preload author via TODO, this will cause error
}
schema "todos" do
field :date, :date
field :description, :string
field :title, :string
belongs_to :author, TodoElixir.User.Author
timestamps()
end
end
# since we call AUTHOR inside TODO, we also need to derived fields from Author. # Otherwise it will cause error.
defmodule TodoElixir.User.Author do
use Ecto.Schema
import Ecto.Changeset
# you can also call fields that you want to parse.
@derive Jason.Encoder
defstruct %{
:email,
:name,
:id
}
schema "authors" do
field :email, :string
field :name, :string
field :password, :string, virtual: true
field :hash, :string
has_many :todos, TodoElixir.Main.Todo
timestamps()
end
end
def render("index.json", %{todos: todos}) do
todos
end
其他说明:如果您不想派生架构中的字段,但仍想将其解析为json,则可以这样做。
# in your CONTROLLER,
alias TodoElixir.User.{Author, Todo} # -> your tables
alias TodoElixir.Repo # -> call your repo
def index(conn, _params) do
todos = list_todos()
render(conn, "index.json", todos: todos)
end
defp list_todos() do
Todo
|> Repo.all()
|> Repo.preload(:author)
end
# In your VIEW, you can manipulate the transformation you want.
def render("index.json", %{todos: todos}) do
todos
|> Enum.map(fn f ->
%{
# you can add additional fields in here.
title: f.title,
author: f.author.name
}
end)
end
答案 2 :(得分:0)
问题在这里:)
上的has_many :todos, Main.Todo
。应该是
TodoElixir.Repo.Migrations.CreateAuthors
然后您可以在预加载数据后查询
defmodule TodoElixir.Repo.Migrations.CreateAuthors do
use Ecto.Migration
def change do
create table(:authors) do
add :name, :string
add :email, :string
add :hash, :string
timestamps()
end
end
end
另外,您应该使用def list_todos do
Repo.all(Todo)
|> preload(:author)
end
代替TodoElixir.Main.Todo
,并使用Main.Todo
代替TodoElixir.User.Author