如何更新Ecto中的关系?

Joe*_*son 10 elixir ecto phoenix-framework

我有这样的订阅.

defmodule Rebirth.Subscription do
  use Rebirth.Web, :model

  schema "subscriptions" do
    ...
    belongs_to :user, Rebirth.User
    ...
  end

  ...

  def update_user(model, params \\ :empty) do
    model
    |> cast(params, @required_fields, @optional_fields)
    |> cast_assoc(:user, required: false)    
  end
end
Run Code Online (Sandbox Code Playgroud)

我想将用户与订阅相关联

所以我试过了

Rebirth.Subscription.update_user(subscription, %{user_id: 1})
Run Code Online (Sandbox Code Playgroud)

要么

Rebirth.Subscription.update_user(subscription, %{user: user})
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我收到以下错误:

** (ArgumentError) unknown assoc `user` in `cast_assoc`
Run Code Online (Sandbox Code Playgroud)

如何更新user_id?

谢谢!

tko*_*wal 13

cast_assoc用于"转换关联模型",只能用于has_onehas_many.belongs_torelation定义模型中调用它的外部id.has_manyhas_one依赖具有外键的"另一个"对象.

如果您正在创建具有许多其他对象的对象,则检查所有对象是否有效是有意义的.cast_assoccast在各自的模块中调用.

您的用户可以有许多订阅(可能,我在这里猜测),因此在创建订阅时创建用户并检查用户是否有效是没有意义的cast_assoc.通常在这种情况下,用户已经存在于数据库中.

在您的情况下,您只想检查演员表中是否存在关联模型,因此您应该使用:

|> assoc_constraint(:user)
Run Code Online (Sandbox Code Playgroud)

这不验证用户,但检查user_id数据库中是否存在.现在,当您想要更新用户的订阅时,您可以执行以下操作:

user = Repo.get(User, id)
subscription = Ecto.build_assoc(user, :subscriptions, other_fields_as_map)
Run Code Online (Sandbox Code Playgroud)

请注意它需要has_onehas_many在用户模型上.

或者您可以像以前一样尝试更新用户ID:

Rebirth.Subscription.update_user(subscription, %{user_id: 1})
Run Code Online (Sandbox Code Playgroud)

并且这次它将检查数据库是否存在给定的用户ID,但是您将无法在此处传递整个用户对象.

如果要更新关联用户,则必须分两步明确执行.a)获取用户,b)使用其模块中定义的变更集更新用户.

最后请注意,如果您没有为更新用户单独进行验证(我认为您不应该在这种情况下),那么将函数重命名update_userchangeset.相同的变更集可用于创建和更新模型.