我正在使用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:
外键约束通过依赖数据库来检查 如果相关模型存在与否。这对保证很有用 只有父数据库存在于子数据库中才会创建子项 太
我希望我编写的代码能够工作,而只是检查存在(如@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
答案 0 :(得分:9)
由于它依赖于数据库,因此需要在迁移中添加引用并执行实际的数据库操作。您必须致电Repo.insert/1
或Repo.update/1
提供变更集,然后返回{:error, changeset}
。
请记住,Elixir和Ecto中没有对象。因此changeset.valid?
永远不会执行数据库操作,它只是反映要执行的一组更改的数据,并且当您执行操作(如插入或更新)时,此数据的状态会发生变化。
最后一点,errors_on/2
总是会返回一个新的变更集,而不是到目前为止你一直在使用的变更集。你的最后一行应该是:
assert {:post_id, "does not exist"} in changeset.errors
答案 1 :(得分:1)
“依赖数据库”意味着您需要在数据库模型中使用FOREIGN KEY CONSTRAINT。
在你的迁移中,你应该有这样的东西:
create table(:comments) do
add :post_id, references(:posts)
end
在父表和子表之间强制执行FOREIGN KEY CONSTRAINT CHECK。