从 has_many 更改为 has_one 关系轨
Posted
技术标签:
【中文标题】从 has_many 更改为 has_one 关系轨【英文标题】:Changing from has_many to has_one relation rails 【发布时间】:2021-03-10 01:20:26 【问题描述】:这里personaldetails belongs_to user,给定的关系是has_many,这是错误的。我想将has_many关系转换为has_one关系,即用户has_one个人详细信息。当我直接更改关系时,我收到错误“未初始化的常量 User::Personaldetails。请指导我如何转换关系。
个人详情.rb
class Personaldetail < ApplicationRecord
belongs_to :user
end
用户.rb
class User < ApplicationRecord
has_many :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end
routes.rb
resources :users, except: [:new] do
resources :personaldetails
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps : :personaldetails
def show
@user = current_user
@personaldetails = @user.personaldetails.build
render_wizard
end
def update
@user = current_user
@user.update!(user_params)
render_wizard @user
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end
personaldetails.html.erb
<%= form_with(model: @user, url: wizard_path, local: true) do |form| %>
<%= form.fields_for :personaldetail,Personaldetail.new do |info| %>
<%= render 'personaldetails_field', form: info %>
<% end %>
<%= form.submit %>
<% end %>
_personaldetails_field.html.erb
<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>
所以解决办法是:
个人详情.rb
class Personaldetail < ApplicationRecord
belongs_to :user
end
用户.rb
class User < ApplicationRecord
has_one :personaldetails, dependent: :destroy
accepts_nested_attributes_for :personaldetails, reject_if: :all_blank, allow_destroy: true
end
routes.rb
resources :users, except: [:new] do
resources :personaldetail
end
user_steps_controller.rb
class UserStepsController < ApplicationController
include Wicked::Wizard
steps : :personaldetails
def show
@user = current_user
render_wizard
end
def update
@user = current_user
@user.update!(user_params)
render_wizard @user
end
private
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :user_id,
personaldetails_attributes: [:id,:first_name, :last_name, :gmail, :mobile_no, :city, :state, :pin_code, :_destroy])
end
end
personaldetail.html.erb
<%= form_with(model: @user, url: wizard_path, local: true) do |form| %>
<%= form.fields_for :personaldetail,@user.personaldetail || @user.build_personaldetail do |info| %>
<%= render 'personaldetail_field', form: info %>
<% end %>
<%= form.submit %>
<% end %>
_personaldetail_field.html.erb
<div class="field">
<%= form.label :First_name %><br />
<%= form.text_field :first_name %>
</div>
<div class="field">
<%= form.label :Last_name %><br />
<%= form.text_field :last_name %>
</div>
<div class="field">
<%= form.label :email %><br />
<%= form.text_field :gmail %>
</div>
<div class="field">
<%= form.label :Mobile_number %><br />
<%= form.text_field :mobile_no %>
</div>
<div class="field">
<%= form.label :City %><br />
<%= form.text_field :city %>
</div>
<div class="field">
<%= form.label :State %><br />
<%= form.text_field :state %>
</div>
<div class="field">
<%= form.label :Pincode %><br />
<%= form.text_field :pin_code %>
</div>
【问题讨论】:
Rails 命名约定要求has_one
是单数形式。这意味着在代码中引用personal_details
数组的每个地方,您现在都需要直接引用personal_detail
实例。
那么现在是@user.personaldetail 吗?
【参考方案1】:
尝试:has_one :personaldetail, dependent: :destroy
Rails 从名称和关联类型中猜测类名,因此使用 has_many 他们会尝试单数化关联名称 (personaldetails => Personaldetail) 但使用 has_one 他们会尝试按原样访问它 (personaldetails => Personaldetails)
【讨论】:
恐怕我没有完全理解。你能详细说明一下吗【参考方案2】:正如 spickermann 的评论,has_many
关系需要复数形式,has_one
需要单数形式。
也就是说,您应该已经能够从以下方面推断出关系:
@user.personaldetails # user has many personal details
@user.personaldetail # user has one personal detail
只是一个考虑:当对象/模型没有正确命名时,会出现许多奇怪的情况。根据经验,您应该为需要命名的对象使用最合适和最精确的英文名词。在这种情况下,这将对您有很大帮助。在正常的英语中,说“用户有个人详细信息”有点奇怪,但你当然会说“有个人详细信息”。特别是在涉及 ActiveRecord 关联时,Rails 语法应该尽可能接近英语,以避免以后的误解。例如,如果模型不是“PersonalDetail”,而是称为“Account”或“Profile”,我想就不会出现这种混淆。
【讨论】:
非常感谢您的回复,我其实有一个疑问,如果我使用has_one并创建一个personaldetail对象,然后创建另一个personaldetail的新对象,新对象会取代旧对象吗? 在 Rails 控制台中尝试一下,看看会发生什么。错误消息将是您最好的朋友,可帮助您了解 ActiveRecord 关系的工作原理:) 是的,我试过了,但没有出现错误。我其实以为我会得到一个错误 感谢您的建议,是的,您是对的,我们应该将其命名为个人资料或帐户。 不客气 - 假设personaldetail 对象是针对同一用户的 - 只需检查 user.personaldetail 并查看您创建的两个中的哪一个:)【参考方案3】:一些建议/cmets
-
将模型名称保留为 CamelCase,例如
PersonalDetail
而不是 Personaldetail
和关联名称 has_one :personal_detail
使用has_one
关系,您可以使用user.build_personal_detail.save
创建对象
当您再次运行第二步时,它将在personal_details
表中创建另一条记录,并在该事务中返回新记录。但是,之后当您尝试查询时,它将返回第一个创建的personal_details 记录而不是新记录。这是因为ActiveRecord
默认按 id 排序,has_one
关系限制为 1
【讨论】:
以上是关于从 has_many 更改为 has_one 关系轨的主要内容,如果未能解决你的问题,请参考以下文章
Rails ActiveRecord 关联“has_many,每个都有 has_one”
处理一个用户模型的 has_one 和 has_many 关联