验证Ecto“多对多”关系

时间:2016-08-27 19:24:01

标签: validation associations elixir ecto

我正在尝试确定在Ecto 2中验证多对多关系的正确方法。我有一个Conversation模型,需要有很多成员,而且用户可以参与很多对话,所以我已经建立了这样的模型:

# 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肯定是正确的,并且它的参数似乎也是正确的。

我最好的猜测是,在调用我的自定义验证器之前,我需要在验证中执行某些操作,但我只是不知道应该做什么。

1 个答案:

答案 0 :(得分:1)

有两个错误:

  1. 您将MyApp.Conversation传递给validate_member_count,而不是Ecto.Changeset。您可以使用Ecto.Changeset

    将定义Struct的Ecto Schema转换为Ecto.Changeset.change/1
    def changeset(struct, _params) do
      struct
      |> change
      |> validate_member_count
    end
    
  2. Ecto.assoc/2接受Ecto架构结构,而不是Ecto.Changeset。您可以使用Ecto.Changeset

    .data访问基础结构
    members = Repo.all(assoc(changeset.data, :members))
    
  3. 最终代码:

    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