【问题标题】:Elixir changeset custom validationElixir 变更集自定义验证
【发布时间】:2020-06-30 23:33:02
【问题描述】:

我在尝试实施自定义变更集验证时遇到了一些麻烦。 我的架构是:

defenum(VersionStateEnum, ["draft", "active"])
schema "versions" do
  field :expires_at, :utc_datetime
  field :state, VersionStateEnum
end

我正在尝试实施的验证是:只有在状态为草稿时才能设置 expires_at(这也应该对更新有效,如果状态仍然是草稿,我应该无法删除 expires_at) 我尝试了以下方法:

defp validate_expires_at(changeset) do                                                                                                                                                                           
    expires_at = get_change(changeset, :expires_at)                                                                                                                                                                
    cond do                                                                                                                                                                                                        
      get_change(changeset, :state) == :draft ->                                                                                                                                                                   
        case expires_at do                                                                                                                                                                                         
          nil -> add_error(changeset, :expires_at, "can't be blank when state is draft")                                                                                                                           
          _ -> changeset                                                                                                                                                                                           
        end                                                                                                                                                                                                        
      get_change(changeset, :state) == :active ->                                                                                                                                                                  
        case expires_at do                                                                                                                                                                                         
          nil -> changeset                                                                                                                                                                                         
          _ -> add_error(changeset, :expires_at, "cannot be set when state is not draft")                                                                                                                          
        end                                                                                                                                                                                                        
      true ->                                                                                                                                                                                                      
        changeset                                                                                                                                                                                                  
    end                                                                                                                                                                                                            
  end
end

但它并没有真正起作用,因为即使状态是草稿,我也可以将 expires_at 更新为 nil。任何帮助表示赞赏。

编辑 1: 我的变更集:

@required_fields [                                                                                                                                                                                                                                                                                                                                                                                               
    :state                                                                                                                                                                                                                                                                                                                                                                                                         
  ]                                                                                                                                                                                                                
@optional_fields [:expires_at]
def changeset(model, params \\ nil) do                                                                                                                                                                           
  model                                                                                                                                                                                                          
  |> cast(params, @required_fields ++ @optional_fields)                                                                                                                                                          
  |> validate_required(@required_fields)                                                                                                                                                                         
  |> validate_expires_at()                                                                                                                                                                                       
end

在哪里调用:

def create_document(attrs \\ %{}) do                                                                                                                                                                             
  %Document{}                                                                                                                                                                                                    
  |> Document.changeset(attrs)                                                                                                                                                                                   
  |> Repo.insert()                                                                                                                                                                                               
end

【问题讨论】:

  • 变更集中的更改是否包括 state 的值作为原子还是字符串?
  • 如果它作为原子传递给变更集。
  • 你能发布你的变更集函数以及你更新它的代码吗?
  • 那你什么时候更新呢?我试图重新创建问题,但如果我尝试将其创建为“草稿”,它会将错误添加到变更集中。在您说即使是草稿也可以更新expires_at 的问题中,您能否还包括您用于更新它的代码?如果你只是做create_document(%{state: "draft"}),它是否成功插入,因为我试过了,它确实返回了一个错误的变更集
  • 我的意思是状态已经是草稿并且已设置 expires_at,如果您尝试将 expires_at 更新为 nil 它将起作用,但它不应该起作用,因为它是没有 expires_at 的草稿在这种情况下没有意义。这有意义吗?

标签: elixir ecto phoenix changeset


【解决方案1】:

如果我正确理解您的问题,我认为要解决它,您可能还应该考虑赋予变更集的结构。

因为,按照您的代码方式,您只是从变更集中的更改中检查 state,但如果您尝试单独更新 expires_at,则变更集中的 changes 将不包括state 可能已经设置为 "draft",因此,validate_expires_at 函数中的 cond 块将始终匹配 true,因为该值将是 nil。

一种解决方法可能是更新函数,例如:

defp validate_expires_at(changeset) do                                                                                                                                                                           
    state = get_field(changeset, :state)
    expires_at = get_change(changeset, :expires_at)   
    case state do
      :draft ->
        case expires_at do                                                                                                                                                                                         
          nil -> add_error(changeset, :expires_at, "can't be blank when state is draft")                                                                                                                           
          _ -> changeset
        end
      :active ->
        case expires_at do
          nil -> changeset
          _ -> add_error(changeset, :expires_at, "cannot be set when state is not draft")
        end
      _ -> changeset
    end                                                                                                                                                                                                   
  end
end

使用get_field 而不是get_change 将尝试从更改中获取字段,但如果未更改,则可以从现有结构中获取,并且您的函数的其余部分应该正常工作

不确定在从数据库中插入和检索时原子/字符串处理是如何工作的。从变更集的数据中获取时,您可能需要检查 state 是否可以是字符串

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 2011-05-04
    相关资源
    最近更新 更多