2016-03-18 82 views
9

我有这样的订阅。如何更新Ecto中的关系?

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 

我想用户当我运行它,我得到以下错误关联认购

所以,我想

Rebirth.Subscription.update_user(subscription, %{user_id: 1}) 

Rebirth.Subscription.update_user(subscription, %{user: user}) 

** (ArgumentError) unknown assoc `user` in `cast_assoc` 

如何更新user_id?

谢谢!

+0

我目前遇到同样的问题。:/ –

+0

我还没有找到一种方法来解决它,但挖掘周围,我孤立问题'Ecto.Changeset.cast_relation/4'。具体来说,'Map.get(types,key)'。该协会不是“类型”模式之一。 –

+0

那么,我的“这是半夜,我厌倦了这个”解决方案是删除'cast_assoc'并将'user_id'添加到所需的参数列表。这是一个体面的临时解决方法。 –

回答

11

cast_assoc用于“投射相关模型”,只能与has_onehas_many一起使用。 belongs_to关系在调用它的模型中定义外部标识。 has_manyhas_one依赖于具有外键的“其他”对象。

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

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

在你的情况,你只需要检查,如果中投存在关联的模型,所以你应该使用:

|> assoc_constraint(:user) 

如果给user_id在数据库中存在这不验证的用户,但检查。现在,当你想更新用户的订阅,你可以这样做:

user = Repo.get(User, id) 
subscription = Ecto.build_assoc(user, :subscriptions, other_fields_as_map) 

注意它需要用户模型has_onehas_many

或者你可以简单地更新用户ID,你尝试过:

Rebirth.Subscription.update_user(subscription, %{user_id: 1}) 

,这时候如果给定的用户ID存在,它会检查数据库,但您将无法通过整个用户对象这里。

如果你想更新关联的用户,你将不得不在两个步骤中明确地做到这一点。 a)获取用户,b)使用其模块中定义的变更集更新用户。

最后要注意的是,如果您没有对更新用户进行单独验证(我认为您不应该在这种情况下),最好将update_user的功能重命名为changeset。相同的变更集可用于创建和更新模型。

+0

谢谢!这是否意味着将用户设置为订阅而不是用户订阅的唯一方法是设置user_id?如果我没有错,这会产生一个新的订阅,正确的是:Ecto.build_assoc(user,:subscriptions,other_fields_as_map)? –

+0

这是正确的。将用户设置为订阅是没有意义的,因为它是可以有很多订阅的用户。是的,'build_assoc'会通过'user_id'集创建新的订阅。这只是获取id并在订阅时手动设置它的速记,也知道关联。 – tkowal