Rails has_many没有在创建时填充

时间:2013-05-09 20:09:12

标签: ruby-on-rails-3.1

我已经开始迁移,以引入新的has_manyhas many :through关联。 “标准”通过“layers_assoc”具有_many“层”。新功能在现有标准上运行良好,但创建新标准,不会创建新图层或layer_assocs。

尽管创建了关联并且迁移具有默认值,但是在创建标准时是否必须创建关联实例?

以下是layers_assocs表的迁移

class CreateLayersAssocs < ActiveRecord::Migration
  def up
    create_table :layers_assocs do |t|
      t.timestamps
      t.integer :layer_id, :null => false      # has_many
      t.integer :standard_id, :null => false   # has_many
      t.boolean :visible, :default => true
    end

    add_index :layers_assocs, :layer_id
    add_index :layers_assocs, :standard_id

    LayersAssoc.reset_column_information

    puts "== Populating LayersAssoc table ============================"
    Standard.all.each do |standard|
      Layer.all.each do |layer|
        begin
          LayersAssoc.create!(
            standard_id: standard.id, 
            layer_id: layer.id, 
            visible: true
          )
        rescue
          puts "== Failed to populate standard_id: #{standard.id} with layer_id: #{layer.id} "
          continue
        end
      end
    end
    puts "== Finished Populating LayersAssoc table ==================="
  end

  def down
    drop_table :layers_assocs
  end
end

如果迁移在生产中出错,那里还有一些额外的东西可以提供帮助。创建标准后,它会显示undefined method 'visible'。然后检查数据库我可以看到最新的标准不在layers_assocs表中。

这是模特。标准.rb的重要部分:

has_many :layers_assocs
has_many :layers, :through => :layers_assocs
accepts_nested_attributes_for :layers_assocs

layers.rb:

has_many :layers_assocs
has_many :standards, :through => :layers_assocs

layers_assoc.rb:

belongs_to :standard
belongs_to :layer

更新2: 我几乎解决了这个问题。 LayersAssocs的默认设置很好,但我没有为标准建立默认的图层关联。我已经在新方法上将这些默认关联写入before_filter,它运行得很好。但是,尽管已填充layer_id,但在保存标准时,图层ID将消失。

LayersAssoc.new循环后的示例LayersAssoc:

#<LayersAssoc id: nil, created_at: nil, updated_at: nil, layer_id: 1, standard_id: nil, visible_authors: true, visible_reviewers: true>

在standard.save:

Mysql2::Error: Column 'layer_id' cannot be null: INSERT INTO `layers_assocs` (`created_at`, `layer_id`, `standard_id`, `updated_at`, `visible_authors`, `visible_reviewers`) VALUES ('2013-05-13 21:49:36', NULL, 112, '2013-05-13 21:49:36', 1, 1)

这真是令人困惑。

1 个答案:

答案 0 :(得分:0)

LayersAssocs对象可以使用它们的默认值创建,但我没有任何建立默认图层(通过图层关联)。在迁移中,此代码确定了现有标准的默认值:

Standard.all.each do |standard|
  Layer.all.each do |layer|
    begin
      LayersAssoc.create!(
        standard_id: standard.id, 
        layer_id: layer.id, 
        visible: true
      )
    rescue
      puts "== Failed to populate standard_id: #{standard.id} with layer_id: #{layer.id} "
      continue
    end
  end

该默认值需要转换为标准控制器上的新操作。我虽然错过了各种各样的Rails惯例,但我花了几天时间来弄清楚它。初始化的create操作将对象保存到数据库。新操作只是初始化对象,然后调用.save将其提交给数据库。因此,在before_filter的标准控制器中执行新操作:

@standard = Standard.new
@organization = Organization.find(params[:organization_id])
@standard.organization = @organization

# Create default layers associations
@default_layers = []
for i in 1..5 do
  @default_layers << LayersAssoc.new(layer_id: i)
end
@standard.layers_assocs = @default_layers

一旦启动了这些LayerAssocs个对象,您就可以在新页面上显示它们的表单:

<%= form_for(@standard) do |form| %>
  ...
 <%= form.label t('standard.layers') %>
 <table class="table table-striped span4">
   <thead>
     <tr>
       <td><strong><%= t('standard.layer') %></strong></td>
       <td><strong><%= t('type.authors') %></strong></td>
       <td><strong><%= t('type.reviewers') %></strong></td>
     </tr>
   </thead>
   <tbody>
     <% @standard.layers_assocs.each do |assoc| %>
       <%= form.fields_for :layers_assocs, assoc do |layer_field| %>
         <tr>
           <%= layer_field.hidden_field :layer_id, :value => assoc.layer_id %>
           <td><%= t(assoc.layer.name, :default => assoc.layer.name) %></td>
           <% if assoc.layer_id == 1 %>
             <td><%= layer_field.check_box :visible_authors, :disabled => true %></td>
             <td><%= layer_field.check_box :visible_reviewers, :disabled => true %></td>
           <% else %>
             <td><%= layer_field.check_box :visible_authors %></td>
             <td><%= layer_field.check_box :visible_reviewers %></td>
           <% end %>
         </tr>
       <% end %>
     <% end %>
   </tbody>
 </table>
 ...

注意:layer_id的隐藏字段。 提交给标准模型进行创建的唯一内容是表单的值。如果在before过滤器中初始化layer_id,则不要将其包含在表单中,{{{}调用1}}并且创建新标准将无效。

希望能帮助别人!