Ecto 中 OR 的条件验证 - 需要 2 个字段中的 1 个
Conditional Validation in Ecto for OR - 1 of 2 fields is required
我如何对 OR 逻辑进行条件验证,我们在其中检查是否存在 2 个值中的 1 个或两个值都存在。
因此,例如,如果我想检查以确保 email
或 mobile
字段已填充...我希望能够将列表传递到 [= validate_required_inclusion
的 13=] 以验证列表中至少有 1 个字段不为空。
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:email, :first_name, :last_name, :password_hash, :role, :birthdate, :address1, :address2, :city, :state, :zip, :status, :mobile, :card, :sms_code, :status])
|> validate_required_inclusion([:email , :mobile])
end
def validate_required_inclusion(changeset, fields, options \ []) do
end
如何进行条件 OR 验证?
怎么样:
def validate_required_inclusion(changeset, fields, options \ []) do
if Enum.any?(fields, fn(field) -> get_field(changeset, field) end),
do: changeset,
else: add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}")
end
get_field
为您提供更改集接受的字段,包括更改(转换)和 non-changed,以及 Enum.any?将确保其中至少有一个字段。
这是一个简单的方法。您可以自定义它以支持更好的错误消息:
def validate_required_inclusion(changeset, fields) do
if Enum.any?(fields, &present?(changeset, &1)) do
changeset
else
# Add the error to the first field only since Ecto requires a field name for each error.
add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}")
end
end
def present?(changeset, field) do
value = get_field(changeset, field)
value && value != ""
end
使用 Post 模型和 |> validate_required_inclusion([:title , :content])
进行测试:
iex(1)> Post.changeset(%Post{}, %{})
#Ecto.Changeset<action: nil, changes: %{},
errors: [title: {"One of these fields must be present: [:title, :content]",
[]}], data: #MyApp.Post<>, valid?: false>
iex(2)> Post.changeset(%Post{}, %{title: ""})
#Ecto.Changeset<action: nil, changes: %{},
errors: [title: {"One of these fields must be present: [:title, :content]",
[]}], data: #MyApp.Post<>, valid?: false>
iex(3)> Post.changeset(%Post{}, %{title: "foo"})
#Ecto.Changeset<action: nil, changes: %{title: "foo"}, errors: [],
data: #MyApp.Post<>, valid?: true>
iex(4)> Post.changeset(%Post{}, %{content: ""})
#Ecto.Changeset<action: nil, changes: %{},
errors: [title: {"One of these fields must be present: [:title, :content]",
[]}], data: #MyApp.Post<>, valid?: false>
iex(5)> Post.changeset(%Post{}, %{content: "foo"})
#Ecto.Changeset<action: nil, changes: %{content: "foo"}, errors: [],
data: #MyApp.Post<>, valid?: true>
您也可以在数据库中创建约束,例如通过编写迁移:
create(
constraint(
:users,
:email_or_mobile,
check: "(email IS NOT NULL) OR (mobile IS NOT NULL)"
)
)
并使用 check_constraint
验证变更集:
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:email, :first_name, :last_name, :password_hash, :role, :birthdate, :address1, :address2, :city, :state, :zip, :status, :mobile, :card, :sms_code, :status])
|> check_constraint(
:users_table,
name: :email_or_mobile,
message: dgettext("errors", "can't be blank")
)
end
我如何对 OR 逻辑进行条件验证,我们在其中检查是否存在 2 个值中的 1 个或两个值都存在。
因此,例如,如果我想检查以确保 email
或 mobile
字段已填充...我希望能够将列表传递到 [= validate_required_inclusion
的 13=] 以验证列表中至少有 1 个字段不为空。
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:email, :first_name, :last_name, :password_hash, :role, :birthdate, :address1, :address2, :city, :state, :zip, :status, :mobile, :card, :sms_code, :status])
|> validate_required_inclusion([:email , :mobile])
end
def validate_required_inclusion(changeset, fields, options \ []) do
end
如何进行条件 OR 验证?
怎么样:
def validate_required_inclusion(changeset, fields, options \ []) do
if Enum.any?(fields, fn(field) -> get_field(changeset, field) end),
do: changeset,
else: add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}")
end
get_field
为您提供更改集接受的字段,包括更改(转换)和 non-changed,以及 Enum.any?将确保其中至少有一个字段。
这是一个简单的方法。您可以自定义它以支持更好的错误消息:
def validate_required_inclusion(changeset, fields) do
if Enum.any?(fields, &present?(changeset, &1)) do
changeset
else
# Add the error to the first field only since Ecto requires a field name for each error.
add_error(changeset, hd(fields), "One of these fields must be present: #{inspect fields}")
end
end
def present?(changeset, field) do
value = get_field(changeset, field)
value && value != ""
end
使用 Post 模型和 |> validate_required_inclusion([:title , :content])
进行测试:
iex(1)> Post.changeset(%Post{}, %{})
#Ecto.Changeset<action: nil, changes: %{},
errors: [title: {"One of these fields must be present: [:title, :content]",
[]}], data: #MyApp.Post<>, valid?: false>
iex(2)> Post.changeset(%Post{}, %{title: ""})
#Ecto.Changeset<action: nil, changes: %{},
errors: [title: {"One of these fields must be present: [:title, :content]",
[]}], data: #MyApp.Post<>, valid?: false>
iex(3)> Post.changeset(%Post{}, %{title: "foo"})
#Ecto.Changeset<action: nil, changes: %{title: "foo"}, errors: [],
data: #MyApp.Post<>, valid?: true>
iex(4)> Post.changeset(%Post{}, %{content: ""})
#Ecto.Changeset<action: nil, changes: %{},
errors: [title: {"One of these fields must be present: [:title, :content]",
[]}], data: #MyApp.Post<>, valid?: false>
iex(5)> Post.changeset(%Post{}, %{content: "foo"})
#Ecto.Changeset<action: nil, changes: %{content: "foo"}, errors: [],
data: #MyApp.Post<>, valid?: true>
您也可以在数据库中创建约束,例如通过编写迁移:
create(
constraint(
:users,
:email_or_mobile,
check: "(email IS NOT NULL) OR (mobile IS NOT NULL)"
)
)
并使用 check_constraint
验证变更集:
def changeset(struct, params \ %{}) do
struct
|> cast(params, [:email, :first_name, :last_name, :password_hash, :role, :birthdate, :address1, :address2, :city, :state, :zip, :status, :mobile, :card, :sms_code, :status])
|> check_constraint(
:users_table,
name: :email_or_mobile,
message: dgettext("errors", "can't be blank")
)
end