Elixir Ecto:如何验证外键约束?
Elixir Ecto: How to validate foreign key constraint?
我正在使用 Elixir 和 Phoenix web 框架,但现在我一直在尝试验证外键约束。所以,给定一个模型 Post
有 很多评论 ,我写了 Comment
模型如下:
defmodule MyApp.Comment do
use MyAPp.Web, :model
schema "comments" do
field :body, :text
belongs_to :post, MyApp.Post
timestamps
end
@required_fields ~w(body post_id)
@optional_fields ~w()
def changeset(model, params \ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> foreign_key_constraint(:post_id)
end
end
及其单元测试:
defmodule MyApp.CommentTest do
# [...]
test "changeset with non existent post" do
attrs = %{
body: "A comment."
post_id: -1 # some non-existent id?
}
refute Comment.changeset(%Comment{}, attrs).valid?
assert {:post_id, "does not exist"} in errors_on(%Comment{}, %{})
end
end
根据http://hexdocs.pm/ecto/Ecto.Changeset.html#foreign_key_constraint/3:
The foreign key constraint works by relying on the database to check
if the associated model exists or not. This is useful to guarantee
that a child will only be created if the parent exists in the database
too.
我希望我编写的代码能够工作,但它只检查是否存在(如 @required_fields ~w(body post_id)
中所定义)。我不排除我做错了什么或误解了文档中的声明。
有没有人已经偶然发现了这个?
更新:
为了完整起见,这里是迁移:
def change do
create table(:comments) do
add :body, :text
add :post_id, references(:posts)
timestamps
end
create index(:comments, [:post_id])
end
"relying on the database" 表示您的数据库模型中需要有一个 FOREIGN KEY CONSTRAINT。
在你的迁移中,你应该有这样的东西:
create table(:comments) do
add :post_id, references(:posts)
end
在父项和子项之间强制执行 FOREIGN KEY CONSTRAINT CHECK table。
由于依赖数据库,需要在迁移中添加引用,进行实际的数据库操作。您必须调用 Repo.insert/1
或 Repo.update/1
提供您的变更集,然后它将 return {:error, changeset}
.
记住,Elixir 和 Ecto 中都没有对象。因此 changeset.valid?
永远无法执行数据库操作,它只是反映一组要执行的更改的数据,并且此数据的状态会在您执行插入或更新等操作时发生转换。
最后一点,errors_on/2
总是会 return 一个新的变更集,而不是你目前一直在使用的变更集。你的最后一行应该是:
assert {:post_id, "does not exist"} in changeset.errors
我正在使用 Elixir 和 Phoenix web 框架,但现在我一直在尝试验证外键约束。所以,给定一个模型 Post
有 很多评论 ,我写了 Comment
模型如下:
defmodule MyApp.Comment do
use MyAPp.Web, :model
schema "comments" do
field :body, :text
belongs_to :post, MyApp.Post
timestamps
end
@required_fields ~w(body post_id)
@optional_fields ~w()
def changeset(model, params \ :empty) do
model
|> cast(params, @required_fields, @optional_fields)
|> foreign_key_constraint(:post_id)
end
end
及其单元测试:
defmodule MyApp.CommentTest do
# [...]
test "changeset with non existent post" do
attrs = %{
body: "A comment."
post_id: -1 # some non-existent id?
}
refute Comment.changeset(%Comment{}, attrs).valid?
assert {:post_id, "does not exist"} in errors_on(%Comment{}, %{})
end
end
根据http://hexdocs.pm/ecto/Ecto.Changeset.html#foreign_key_constraint/3:
The foreign key constraint works by relying on the database to check if the associated model exists or not. This is useful to guarantee that a child will only be created if the parent exists in the database too.
我希望我编写的代码能够工作,但它只检查是否存在(如 @required_fields ~w(body post_id)
中所定义)。我不排除我做错了什么或误解了文档中的声明。
有没有人已经偶然发现了这个?
更新: 为了完整起见,这里是迁移:
def change do
create table(:comments) do
add :body, :text
add :post_id, references(:posts)
timestamps
end
create index(:comments, [:post_id])
end
"relying on the database" 表示您的数据库模型中需要有一个 FOREIGN KEY CONSTRAINT。
在你的迁移中,你应该有这样的东西:
create table(:comments) do
add :post_id, references(:posts)
end
在父项和子项之间强制执行 FOREIGN KEY CONSTRAINT CHECK table。
由于依赖数据库,需要在迁移中添加引用,进行实际的数据库操作。您必须调用 Repo.insert/1
或 Repo.update/1
提供您的变更集,然后它将 return {:error, changeset}
.
记住,Elixir 和 Ecto 中都没有对象。因此 changeset.valid?
永远无法执行数据库操作,它只是反映一组要执行的更改的数据,并且此数据的状态会在您执行插入或更新等操作时发生转换。
最后一点,errors_on/2
总是会 return 一个新的变更集,而不是你目前一直在使用的变更集。你的最后一行应该是:
assert {:post_id, "does not exist"} in changeset.errors