2

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

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
4

1 回答 1

1

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

因为,您的代码的方式是,您只检查state变更集中的更改,但如果您尝试expires_at单独更新,changes变更集中的 将不包括state可能已经设置为"draft"的函数cond中的块validate_expires_at将始终匹配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从变更集的数据中获取时,您可能需要检查是否可能是字符串

于 2020-06-30T15:12:57.980 回答