【问题标题】:Validating Ecto "many-to-many" relationships验证 Ecto“多对多”关系
【发布时间】:2016-08-27 19:24:01
【问题描述】:

我正在尝试确定在 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 绝对正确,而且它的参数似乎也正确。

我最好的猜测是,在调用我的自定义验证器之前,我需要在验证中做一些事情,但我就是不知道我应该做什么。

【问题讨论】:

  • 尝试在MyApp.Conversation.changeset/2 中将struct |> validate_member_count 替换为struct |> change |> validate_member_count
  • 我现在得到一个不同的错误:** (UndefinedFunctionError) function Ecto.Changeset.__schema__/2 is undefined or private
  • 尝试用Repo.all(assoc(changeset.data, :members))替换Repo.all(assoc(changeset, :members))
  • 是的!做到了!非常感谢。请提交正确的答案,以便我接受!

标签: validation associations elixir ecto


【解决方案1】:

有两个错误:

  1. 您将MyApp.Conversation 传递给validate_member_count,而不是Ecto.Changeset。您可以使用 Ecto.Changeset.change/1 将定义 Struct 的 Ecto Schema 转换为 Ecto.Changeset

    def changeset(struct, _params) do
      struct
      |> change
      |> validate_member_count
    end
    
  2. Ecto.assoc/2 接受 Ecto Schema 结构,而不是 Ecto.Changeset。您可以使用.dataEcto.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

【讨论】:

  • 太棒了!非常感谢!
猜你喜欢
  • 2015-12-30
  • 1970-01-01
  • 2018-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-17
相关资源
最近更新 更多