Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Validation 使用EXTO,验证具有两个不同相关模型的变更集是否具有相同的父模型_Validation_Elixir_Ecto_Changeset - Fatal编程技术网

Validation 使用EXTO,验证具有两个不同相关模型的变更集是否具有相同的父模型

Validation 使用EXTO,验证具有两个不同相关模型的变更集是否具有相同的父模型,validation,elixir,ecto,changeset,Validation,Elixir,Ecto,Changeset,在我的应用程序中,我有一个创建新响应的方法。响应与玩家和比赛都有归属关系 此外,球员和对手都与球队有归属关系 看起来是这样的: 当插入一个新的响应时,我想验证在变更集中具有player_id和match_id外键的player和match是否属于同一个团队 目前,我正在实现以下目标。首先,定义检查属于外键的记录的自定义验证: def validate_match_player(changeset) do player_team = Player |> Repo.get(

在我的应用程序中,我有一个创建新响应的方法。响应与玩家和比赛都有归属关系

此外,球员和对手都与球队有归属关系

看起来是这样的:

当插入一个新的响应时,我想验证在变更集中具有player_id和match_id外键的player和match是否属于同一个团队

目前,我正在实现以下目标。首先,定义检查属于外键的记录的自定义验证:

def validate_match_player(changeset) do
  player_team =
    Player
    |> Repo.get(get_field(changeset, :player_id))
    |> Map.get(:team_id)

  match_team =
    Match
    |> Repo.get(get_field(changeset, :match_id))
    |> Map.get(:team_id)

  cond do
    match_team == player_team -> changeset
    true -> changeset |> add_error(:player, "does not belong to the same team as the match")
  end
end
并将验证用作变更集的一部分:

def changeset(model, params \\ %{}) do
  model
  |> cast(params, [:player_id, :match_id, :message])
  |> validate_required([:player_id, :match_id, :message])
  |> foreign_key_constraint(:match_id)
  |> foreign_key_constraint(:player_id)
  |> validate_match_player()
  |> unique_constraint(
    :player,
    name: :responses_player_id_match_id_unique,
    message: "already has an response for this match"
  )
end
这很好,但是需要两个额外的SQL查询来查找相关记录,以便获得他们的team_id外键来比较它们


有没有更好的方法来做到这一点,也许是使用约束来避免额外的查询?

我有两个可能的改进:

应用程序级解决方案:不需要两个查询,只需查询一次。 数据库级解决方案:为数据库中的检查创建触发器。 应用程序级解决方案 现在,您有两个查询用于检查球员和比赛是否属于同一队。这意味着两次往返数据库。如果只使用一个查询(例如,给定以下查询),则可以将其减少一半:

选择计数* 作为p的球员 内部联接匹配为m 在p.team\u id=m.team\u id上 其中p.id=NEW.player\u id和m.id=NEW.match\u id 您将按如下方式更改您的函数:

def validate_match_player(changeset) do
  player_id = get_field(changeset, :player_id)
  match_id = get_field(changeset, :match_id)

  [result] =
    Player
    |> join(:inner, [p], m in Match, on: p.team_id == m.team_id)
    |> where([p, m], p.id == ^player_id and m.id == ^match_id)
    |> select([p, m], %{count: count(p.id)})
    |> Repo.all()

  case result do
    %{count: 0} ->
      add_error(changeset, :player, "does not belong to the same team as the match")
    _ ->
      changeset
  end
end
数据库级解决方案 我假设您正在使用PostgreSQL,因此我的答案将与您在PostgreSQL手册中可以找到的内容相对应

在执行此操作的表中,没有明确的方法来定义约束。约束只能访问定义它们的表。某些约束只能从其定义的内容访问列,而不能再使用检查约束

最好的方法是编写一个触发器来验证两个字段,例如:

创建或替换功能触发器\u验证\u匹配\u播放器 将触发器返回为$$ 如果 选择计数* 作为p的球员 内部联接匹配为m 在p.team\u id=m.team\u id上 其中p.id=NEW.player\u id和m.id=NEW.match\u id = 0 然后 RAISE“与比赛不属于同一队” 使用错误代码“无效匹配玩家”; 如果结束; 归还新的; $$语言plpgsql; 创建触发器响应\u验证\u匹配\u玩家 在插入或更新响应之前 每行 执行程序触发\验证\匹配\玩家; 前一个触发器失败时将引发异常。这也意味着EXTO将引发异常。您可以看到如何处理此异常

最后,维护触发器并不容易,除非您使用类似的方法进行数据库迁移

PS:如果你好奇的话,在CHECK约束中这样做的最肮脏的方式是定义一个基本上绕过限制的PostgreSQL函数。我不推荐


我希望这有帮助:

AFAIK,约束不能访问其他表的数据。因此,要么在SQL中编写一个触发器,在不满足该条件时阻止编辑,要么像这样保留它。回答不错,谢谢!我认为你关于维护触发器困难的观点是正确的。我确实考虑过一个带有连接的单一查询,这看起来是一个不错的选择。