我正在尝试确定在Ecto 2中验证多对多关系的正确方法。我有一个会话模型,它需要有许多成员,用户可以成为许多会话的一部分,所以我建立了这样的模型:
# User Model
defmodule MyApp.User do
...
schema "users" do
....
many_to_many :conversations, Conversation, join_through: "conversations_users"
...
end
...
end
# Conversation Model
defmodule MyApp.Conversation do
...
schema "conversations" do
has_many :messages, Message
many_to_many :members, User, join_through: "conversations_users"
timestamps()
end
def changeset(struct, _params) do
struct
|> validate_member_count
end
defp validate_member_count(changeset) do
members = Repo.all(assoc(changeset, :members))
valid? = length(members) == 2
if valid? do
add_error(changeset, :members, "foo")
else
changeset
end
end
end
然而,我就是不能让这个工作。我已经编写了一个简单的测试来验证验证是否正确运行,但是我一直得到以下错误:
# Test
test "fails to validate a conversation with less than two members" do
changeset = Conversation.changeset(%Conversation{}, %{})
{message, []} = changeset.errors[:members]
assert message === "must have at least two members"
end
** (FunctionClauseError)在Ecto.Changeset.add_error/4中没有匹配的函数子句
我很难理解我做错了什么。似乎找不到函数,但我已经检查了文档,似乎Ecto.Changeset.add_error/4
绝对是正确的,并且它的参数似乎也是正确的。
我最好的猜测是,在调用自定义验证器之前,我需要在验证中做一些事情,但我只是不知道我应该做什么。
有两个错误:
-
您正在将
将Ecto模式定义结构转换为MyApp.Conversation
传递给validate_member_count
,而不是Ecto.Changeset
。您可以使用Ecto.Changeset.change/1
:Ecto.Changeset
def changeset(struct, _params) do struct |> change |> validate_member_count end
-
从Ecto.assoc/2
接受Ecto Schema Struct,而不是Ecto.Changeset
。您可以使用.data
:Ecto.Changeset
访问底层结构。members = Repo.all(assoc(changeset.data, :members))
def changeset(struct, _params) do
struct
|> change
|> validate_member_count
end
defp validate_member_count(changeset) do
members = Repo.all(assoc(changeset.data, :members))
valid? = length(members) == 2
if valid? do
add_error(changeset, :members, "foo")
else
changeset
end
end