【问题标题】:Best way to assign a AssociatedModel or AssociatedModel.id to an ActiveRecordModel?将 AssociatedModel 或 AssociatedModel.id 分配给 ActiveRecordModel 的最佳方法是什么?
【发布时间】:2021-10-21 11:37:48
【问题描述】:

我正在尝试构建一个辅助方法,该方法将采用关键字 args 并基于某些逻辑对它们进行处理,这是一个示例:

def update_with_logic(widget:, user:)
  # do some logic here
  widget.user = user
end

现在这工作得很好,但是,我想支持 user 也被设置为用户的 ID。

但是,如果我们将 user: 3 发送到我们的 update_with_logic 方法中,我会收到以下错误:

ActiveRecord::AssociationTypeMismatch: User(#47287814961100) expected, got 3 which is an instance of Integer(#47287745166420)

如果您换一种方式尝试:widget.user_id = useruserActiveRecord::Model,它会将 user_id 设置为 nil(并且不会引发异常)

这让我很吃惊,因为我可以在 ActiveRecord::Query 设置中毫无问题地执行以下操作:

userModel = User.find 3

# Looking up by user_id while sending a user model as the value
Widget.where(user_id: userModel).to_sql
# => SELECT "widgets".* FROM "widgets" WHERE "widgets"."user_id" = 3

# Looking up by user association while sending a user_id (integer) as the value
Widget.where(user: 3).to_sql
# => SELECT "widgets".* FROM "widgets" WHERE "widgets"."user_id" = 3

我尝试过使用widget.attributes = { user: user },但是使用user = 3 时效果相同。 (引发 ActiveRecord::AssociationTypeMismatch)。

我找到的唯一解决方案是执行以下操作:

widget.user_id = user.is_a?(User) ? user.id : user

上面的解决方案有效,但是,它并不像一个干净的实现。我希望有人可以推荐一些更符合“The Rails Way®” ;)

【问题讨论】:

  • 经过更多考虑,不确定您是否想要此功能。因为如果您支持通过user_id 保存user,您会鼓励开发人员发送不同的对象。假设您需要访问user.full_name,但是,user = 3,您必须进行大量检查才能看到user 是什么类型的对象。这似乎会增加代码的复杂性(总体而言)。
  • 尽管我不同意使用这种方法,但我确实想看看我是否可以建立一个支持所讨论的实现的关注点(包括@konstantin-strukov 建议以确保模型是预期的模型)签出:gist.github.com/anlek/9f888518d3273508caffafdb1afea07c

标签: ruby-on-rails ruby activerecord


【解决方案1】:

您可以提供ActiveRecord 这样一团糟,只是因为它确实some heavylifting 来构建正确的查询。实际上并没有什么魔法——所有这些方便的多态性都以代码复杂性为代价。

您的带有try 的代码有点危险——如果提供的不是用户实例而是任何其他模型(它也会响应id)怎么办?例如,你可以让它更防弹

case user
when Integer
  widget.user_id = user
when User
  widget.user = user
else
  raise "BOOM!"
end

但这也不理想 - 如果提供的 id 用户不存在怎么办?当然,我们可以很容易地添加检查(只是像widget.user = User.find(user) 那样做),但我们真的需要所有这些意外的复杂性吗?

如果我是你,我会停下来问自己一个问题:为什么这个方法的调用不一致?如果我们希望它为一个小部件分配一个用户,为什么我们要提供的不是用户而是其他东西(反之亦然)?真的无法避免吗?

在大多数实际情况下,答案都是“否”:可以在调用此方法的位置进行必要的更改,以便它始终接收一致的参数 - 无论是用户或有效的用户 ID,- 并保持方法本身简洁明了。

【讨论】:

  • 感谢您的回复!您为什么支持多个 user 值的问题的答案主要是我们有一个由 5 名开发人员组成的团队,我们喜欢“Ruby 方式”,如果您认为它应该以这种方式工作,它就会。这是 Rails 做得很好的事情,所以我们努力保持下去。大多数时候,我们使用预期的签名运行(在此示例中,它将是一个User 模型。但是,有时我们不想加载用户,尤其是当方法的逻辑部分没有时'真的不需要有一个用户模型。给它一个 ID 就好了。
  • 我也很欣赏关于try 的安全问题,我从未想过它。你说得对,我想做widget.user_id = user.is_a?(User) ? user.id : user 之类的事情。这样,如果user 是不同的模型(或对象),就会引发错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-04
  • 1970-01-01
  • 2019-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-20
相关资源
最近更新 更多