首页 > 解决方案 > 修改变更集中的第一条相关记录

问题描述

有没有办法通过变更集仅更改关系中的第一项?也就是如果我有:

可以执行以下操作:

schema "accounts" do 
    ...
    has_many(:emails, StockrtApi.Accounts.Email)
end

def changeset(account, params) do
    account
    ...
    |> cast_assoc(:emails, required: true, with: &Email.changeset(&1, &2))
    |> put_change(first_email_only, {primary: true}) # <- might have to go in email schema
end

或者解决此类问题的最佳方法是什么?

标签: elixirphoenix-frameworkecto

解决方案


处理数据库时不应依赖任何事物的顺序(除非提供了显式ORDER BY子句。)虽然大多数数据库驱动程序将返回一个没有显式ORDER BY子句的可预测的有序集,但这绝不是保证。也就是说,总是更喜欢显式而不是隐式:只需引入另一个through关联:

schema "accounts" do 
  ...
  has_many :emails, StockrtApi.Accounts.Email
  has_one :primary_email, through: [:primary_email, :email]
end

并把这个联想与平常Ecto.Changeset.put_assoc/4


根据评论,当您只需要确保有电子邮件时,请执行以下操作:

defp ensure_email(
  %Ecto.Changeset{errors: errors, changes: %{emails: emails}} = changes
) do
  case emails do  
    [] ->
      new_errors = [{:emails, {"can’t be empty", [validation: :emails]}}]
      %{changes | errors: new_errors ++ errors, valid?: false}
    _ -> changes
  end
end

并明确使用它:

def changeset(account, params) do
  account
  ...
  |> cast_assoc(:emails, required: true, with: &Email.changeset(&1, &2))
  |> ensure_email()
end

这样您就可以确保至少存在一封电子邮件 - 只需将第一封用作primary.


推荐阅读