问题描述
我如何与ecto 2建立多对多关系?作为示例应用程序,我想创建可以属于多个类别的帖子.类别已经存在.例如:
How can I make a many to many relation with ecto 2? As an example app I want tocreate a Post which can be in multiple categories. The categories already exist. For example:
[%Category{id: "1", name: "elixir"}, %Category{id: "2", name: "erlang"}]
Im使用Ecto 2 beta0.示例项目称为Ecto2.
Im using Ecto 2 beta 0. The example project is called 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
我试图这样做:
post = Repo.get!(Post, 1) |> Repo.preload(:categories)
changeset = Post.changeset(post, %{"title"=> "bla", "categories"=> [%{id: "1"}]})
Repo.update!(changeset)
但是Post.changeset中的cast_assoc不适合此任务,它想创建一个全新的Category而不是associate.我应该怎么用呢? build_assoc?但是build_assoc文档没有提到它对many_to_many很有用.如何使用?那么我应该将build_assoc放在Post.changeset中,还是应该在phoenix控制器中使用它.
But cast_assoc in Post.changeset is not suitable for this task, it wants to create a whole new Category instead of associate one.What should I use instead? build_assoc? But build_assoc docs do not mention it is useful with many_to_many. How do I use it? Should I put build_assoc in Post.changeset then, or should i use it in a phoenix controller.
推荐答案
您可以通过传递诸如"posts_categories"之类的字符串来通过表进行联接,也可以通过诸如MyApp.PostCategory之类的方案来通过一种方案进行联接.我更喜欢通过模式加入,因为可以包含时间戳.假设您选择通过模式而不是表进行连接:
You can join through a table by passing a string like "posts_categories", or through a schema by passing through a schema like MyApp.PostCategory. I prefer joining through schema as timestamps can be included. Let say you choose join through a schema instead of a table:
- 您需要为要加入的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
- 为在步骤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
Ecto beta 2已将:empty更改为空映射,并将cast \ 4更改为cast \ 3.检查变更日志.
Ecto beta 2 has changed :empty to empty map and change cast\4 to cast \3. Check changelog.
-
将此行添加到您的帖子架构中:
Add this line to your post schema:
many_to_many :categories, Ecto2.Category, join_through: Ecto2.PostCategory
many_to_many :categories, Ecto2.Category, join_through: Ecto2.PostCategory
将此行添加到您的类别架构:
Add this line to your category schema:
many_to_many :posts, Ecto2.Post, join_through: Ecto2.PostCategory
many_to_many :posts, Ecto2.Post, join_through: Ecto2.PostCategory
就是这样!现在您可以像这样更新```
That's it! Now you can update like```
post1 = Repo.get!(Post, 1)
category1 = Repo.get!(Category, 1)
post1
|> Repo.preload(:categories)
|> Post.changeset(%{})
|> put_assoc(:categories, [category1])
|> Repo.update!
```
这篇关于Elixir ecto 2创建many_to_many关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!