Elixir ecto 2创建了many_to_many关联

Roe*_*mer 13 elixir ecto phoenix-framework

如何与ecto 2建立多对多关系?作为一个示例应用程序,我想创建一个可以在多个类别中的帖子.这些类别已经存在.例如:

[%Category{id: "1", name: "elixir"}, %Category{id: "2", name: "erlang"}]
Run Code Online (Sandbox Code Playgroud)

我正在使用Ecto 2 beta 0.示例项目名为Ecto2.

我定义了两个模型:

defmodule Ecto2.Post do
  use Ecto2.Web, :model
  use Ecto.Schema

  schema "posts" do
    field :title, :string
    many_to_many :categories, Ecto2.Category, join_through: "posts_categories", on_replace: :delete
    timestamps
  end

  @required_fields ~w(title)
  @optional_fields ~w()
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> cast_assoc(:categories)  # not suitable?
  end
end

defmodule Ecto2.Category do
  use Ecto2.Web, :model

  schema "categories" do
    field :name, :string

    timestamps
  end

  @required_fields ~w(name)
  @optional_fields ~w()
  def changeset(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
  end
end
Run Code Online (Sandbox Code Playgroud)

我尝试这样做:

post = Repo.get!(Post, 1) |> Repo.preload(:categories)
changeset = Post.changeset(post, %{"title"=> "bla", "categories"=> [%{id: "1"}]})
Repo.update!(changeset)
Run Code Online (Sandbox Code Playgroud)

但是Post.changeset中的cast_assoc不适合这个任务,它想要创建一个全新的Category而不是关联的Category.我应该用什么呢?build_assoc?但是build_assoc文档没有提到它对many_to_many很有用.我该如何使用它?我应该将build_assoc放在Post.changeset中,还是应该在phoenix控制器中使用它.

Wil*_*hea 18

您可以通过传递类似"posts_categories"的字符串或通过传递MyApp.PostCategory之类的模式通过模式来加入表.我更喜欢通过模式加入,因为时间戳可以包括在内.假设您选择通过模式而不是表格加入:

  1. 您需要为要加入的many_to_many关系创建单独的表(例如:posts_categories).

```

def change do
  create table(:posts_categories) do
    add :post_id, references(:posts)
    add :category_id, references(:categories)
    timestamps
  end
end
Run Code Online (Sandbox Code Playgroud)
  1. 为在步骤1中创建的表创建架构.在web\models文件夹中,创建文件post_category.ex:

```

defmodule Ecto2.PostCategory do
use Ecto2.Web, :model

schema "posts_categories" do
  belongs_to :post, Ecto2.Post
  belongs_to :category, Ecto2.Category
  timestamps
end

def changeset(model, params \\ %{}) do
  model
  |> cast(params, [])
end
end
Run Code Online (Sandbox Code Playgroud)

Ecto beta 2已更改:空到空地图并将演员\ 4更改为演员\ 3.检查更改日志.

  1. 将此行添加到您的帖子架构:

    many_to_many :categories, Ecto2.Category, join_through: Ecto2.PostCategory

  2. 将此行添加到您的类别架构:

many_to_many :posts, Ecto2.Post, join_through: Ecto2.PostCategory

而已!现在你可以像```那样更新

post1 = Repo.get!(Post, 1)
category1 = Repo.get!(Category, 1)

post1
|> Repo.preload(:categories)
|> Post.changeset(%{})
|> put_assoc(:categories, [category1])
|> Repo.update!
Run Code Online (Sandbox Code Playgroud)

```