【问题标题】:Rails: ActiveRecord::AssociationTypeMismatch errorRails:ActiveRecord::AssociationTypeMismatch 错误
【发布时间】:2017-04-01 01:48:04
【问题描述】:

是否有可能在一个表上有以下两列并用 active_record 保存它们:

  • user_name_id(这是一个关联)
  • user_name(纯文字)

演示尝试保存关联字段user_name_id 和文本字段user_name

以下是模型:

#app/models/blog.rb
class Blog < ApplicationRecord
  belongs_to :user_name
end

#app/models/user_name.rb
class UserName < ApplicationRecord
  has_many :blogs
end

形式:

<%= form_for(blog) do |f| %>
  <% if blog.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(blog.errors.count, "error") %> prohibited this blog from being saved:</h2>

      <ul>
      <% blog.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :user_name %>
    <%= f.text_field :user_name %>
  </div>

  <div class="field">
    <%= f.label :user_name_id %>
    <%= f.collection_select :user_name_id, UserName.all, :id, :name %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

强大的参数:

def blog_params
  params.require(:blog).permit(:user_name, :user_name_id)
end

当我提交表单以创建新的blog 时,它会出错。参数哈希看起来不错:

“blog”=>{“user_name_id"=>"1", “user_name"=>”foo”}

但是我收到以下错误:

ActiveRecord::AssociationType 不匹配 预期用户名(#70119900697480),得到字符串(#70119800086640)

更新:我理解,理想情况下:这些属性之一的列名应该改变。尽管如此:是否有可能使用 rails 来做到这一点?需要什么?

【问题讨论】:

  • 显示博客的两个模型和控制器
  • 博客是否属于一个名为 user_name 或 user 的表?
  • 属于一个名为 user_names 的实际表
  • 我同意盖茨 - 发布所有信息,并包括表格
  • 现在添加信息

标签: ruby-on-rails ruby activerecord


【解决方案1】:

从上面的答案中可以明显看出问题 - 将 user_name 属性更改为其他名称。

作为最后的手段,这可能是一个糟糕的建议,但您甚至可以尝试声明您正在使用的属性,例如:

class Blog < ApplicationRecord
  belongs_to :user_name, through: :user_name_id
end

但同样,您关联记录的原因是您可以随时通过关联调用 user_name,并获取存储在那里的所有信息。这意味着,您不需要将 user_name 与博客一起存储...因为您已经通过 user_name 关联。

【讨论】:

  • 也不需要通过;)
  • 如果您保留这两个属性,则肯定需要通过声明您关联的属性来将它们分开。这就是他首先遇到问题的原因。
  • 不幸的是,在应用程序中,两个表列名称无法在应用程序中更改。带有_id 的列代表一个关联(如果存在),而没有_id 的列代表一个名称,如果它没有在集合中列出。
  • 为什么不能改?似乎这两个选择是:将其中一个称为其他名称,或声明您将哪个属性与您的博客模型中的 user_name 相关联。
  • 我会删除博客中的用户名,因为将其存储在那里没有意义。我只是将 user_id 放在博客中。如果我想要名字,我会从用户表中得到它。如果用户更新他的个人资料,该博客将使用错误的名称(哈哈,除非它被处理)。而且这种架构是违背单一事实来源的!!!
【解决方案2】:
  1. 如果您已经使用 belongs_to 'user_name',则根本不应该使用 'user_name' 字段。
  2. 请不要调用 Model 'UserName' 这不好,这可能是 UserProfile 还是只是 User?
  3. 您确定要为您的博客缓存用户名吗?如果用户更改他的名字,将会产生很多问题。如果您仍然想为博客缓存用户名,您可以重命名“用户名缓存”上的列,并在“用户名”上更新它会在 before_save 回调中更新。

更新作者更新:

belongs_to :your_new_belons_to_name, class_name: 'NameUser', foreign_key: 'name_user_id'

【讨论】:

  • 请查看更新后的问题。这只是一个演示问题的快速演示代码。
  • 如果你想保护原始的 'user_name' 方法名,我认为你必须编写自己的 getter/setter 方法def user name; UserName.where(id: self.user_name_id).first || self.user_name; end
  • 但是在这种情况下你会得到奇怪的代码行为:你的变量可能是一个 AR 对象,也可能是一个字符串
【解决方案3】:

当你这样做时:

belongs_to :user_name

ActiveRecord 假定 Blog 有一个属性 user_name_id (看起来你有 - 到目前为止,很好)。正如Guide 中所述,您还将获得一个setter 方法user_name=,它希望您将传递给它一个UserName 实例。

当您执行以下操作时:

Blog.new(“user_name_id"=>"1", “user_name"=>”foo”)

ActiveRecord 期望 user_name 是类 UserName 的一个实例。但你只是给它传递了一个String。这正是错误告诉您的内容。

另外,我同意 Alexey 的观点。我不知道你为什么要在Blog 上坚持user_name(作为字符串)。如果UserName 类具有name 属性,那么您总是可以执行以下操作:

blog.user_name.name

我也同意 Alexey 的观点,即 UserName 是一个奇怪的类名。 Alexey 的命名建议不错,请考虑。

【讨论】:

  • 请查看更新后的问题。这只是一个演示问题的快速演示代码。
猜你喜欢
  • 1970-01-01
  • 2017-07-01
  • 2020-04-22
  • 1970-01-01
  • 1970-01-01
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多