Ruby on Rails:嵌套属性,belongs_to关系

我有一个具有当前位置字段(城市和国家/地区)的用户实体。 为了保存这些信息,我创建了一个名为Location的实体has_many Users。

我不完全确定我是否应该在用户模型中添加“has_one”或“belongs_to”,但是如果我想要它拥有该位置的外键我应该放入“belongs_to”。 我还希望能够在编辑用户时编辑用户的当前位置。 所以我使用的是嵌套属性。 但是当我编辑用户时,我每次都会添加一个新的位置,而不会将其与已编辑的用户相关联。 你能帮我吗?

我的代码如下:

#User Model class User  'Location' accepts_nested_attributes_for :current_location end #Location Model class Location  :edit end end 

正如其他人所指出的那样,您遇到的确切问题是您的控制器没有收到应有的位置ID。 在我看来,位置ID正在通过错误的参数传递。 遗憾的是,新记录中不存在位置ID,因此在表单中无法实现。

您的问题源于在belongs_to关系上使用accepts_nested_attributes_for。 行为没有明确定义。 这似乎是一个记录在案的错误。 所以accepts_nested_attributes_for应该是一个有一个或有很多关系的一面。

这是一些可能的解决方案:

  1. 将accepted_nested_attributes_for移动到Location模型并以相反的方式构建表单。

     -form_for @location do |location_form| ... =location_form.fields_for @user do |user_form| .... 

    不幸的是,这不允许提供信息的逻辑方式。 并且使编辑成为合适的用户很困难。

  2. 使用连接模型,并建立一个:通过关系。

    老实说,我不确定accept_nested_attributes_for对于:通过关系有多好,但它肯定会解决你的链接记录问题。

  3. 忽略accepts_nested_attributes_for并以旧式方式处理控制器中的关联。

    实际上保留accepts_nested_attributes_for。 它提供了一些方便的便捷方法,只是不要让它到达update_attributes / create语句。

     def update @user = @current_user completed = false location_params = params[:user].delete(:current_location_attributes) User.transaction do @location = Location.find_or_create_by_id(location_params) @user.update_attributes(params[:user]) @user.current_location = @location @user.save! completed = true end if completed flash[:notice] = "Account updated!" redirect_to account_url else render :action => :edit end end 

如果字段不是在创建新位置,则会自动填充current_location_attributes哈希中的id字段。 但是,find_or_create_by_id需要在哈希中使用:id条目才能使其工作。 如果id不在数据库中,它将使用正确的自动递增id创建。 如果要创建新位置,则需要添加它。 最简单的方法是使用=location_form.hidden_field :id, 0 unless current\_location.new\_record?将其添加到表单中=location_form.hidden_field :id, 0 unless current\_location.new\_record?

但是,您可能希望减少重复的位置创建,并将Location.find_or_create_by_id行更改为Location.find_or_create_by_location。 这也将减少唯一性validation失败的任何错误。

您没有提供嵌套属性的id。 所以铁路认为这是一个新的。

 - form_edit.fields_for :current_location do |location_form| = location_form.label :location, "Current Location" = location_form.text_field :location = location_form.hidden_field :id unless location_form.new_record? 

不确定以前的答案是否真的正确。 您需要的是指定位置的用户ID,而不是位置本身。

 - form_edit.fields_for :current_location do |location_form| = location_form.label :location, "Current Location" = location_form.text_field :location = location_form.hidden_field :user_id 

默认情况下, belongs_to :current_location, :class_name => 'Location'将期望Users表具有current_location_id字段。 一旦你有了这个,你应该能够做到这样的事情:

 @user = @current_user @user.update_attributes(params[:user]) @location = @user.current_location or @user.build_current_location @location.update_attributes(params[:location]) @user.current_location.save! @user.save!