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

sim*_*aob 9 attributes nested ruby-on-rails belongs-to

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

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

我的代码如下:

#User Model
class User < ActiveRecord::Base
  ## Relationships
  belongs_to :current_location, :class_name => 'Location'
  accepts_nested_attributes_for :current_location
end

#Location Model
class Location < ActiveRecord::Base
  #Relationship
  has_many :users
end

# part of the _form_edit.haml
- form_edit.fields_for :current_location do |location_form|
  = location_form.label :location, "Current Location"
  = location_form.text_field :location

#Application Helper
#nested attributes for user and location
def setup_user(user)
  returning(user) do |u|
    u.build_current_location if u.current_location.nil?
  end
end

#in the user controller (added after edit)
def update
    @user = @current_user
    if @user.update_attributes(params[:user])
      flash[:notice] = "Account updated!"
      redirect_to account_url
    else
      render :action => :edit
    end
  end
Run Code Online (Sandbox Code Playgroud)

EmF*_*mFi 9

正如其他人指出的那样,您面临的确切问题是您的控制器没有收到应有的位置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|
       ....
    
    Run Code Online (Sandbox Code Playgroud)

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

  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
    
    Run Code Online (Sandbox Code Playgroud)

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

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