Topic: Foreign Key not setting

This is my first stab at Ruby and I seem to be repeatedly doing something very wrong. My foreign keys never get anything in them.

My current test application has 2 simple tables.

USE testguy2_development;
DROP TABLE IF EXISTS firsttables;

CREATE TABLE firsttables (
  id INT NOT NULL AUTO_INCREMENT,
  firstfield VARCHAR(10) NOT NULL UNIQUE,
  PRIMARY KEY (id)
);

CREATE TABLE secondtables (
  id INT NOT NULL AUTO_INCREMENT,
  secondfield VARCHAR(10) NOT NULL UNIQUE,
  firsttable_id INT NOT NULL
  PRIMARY KEY (id)
);

I generate the system with scaffold and updated the models
class Firsttable < ActiveRecord::Base
  has_many :secondtables
end
class Secondtable < ActiveRecord::Base
  belongs_to :firsttable
end


I can create records into either table fine but when I create records in secondtables, the field 'firsttable_id' is always null. I can see from the logs that I am passing the id to the controller if I call the 'new' method of secondtable explicitly from the firsttable show view.

Isn't the foreign key supposed to fill in automatically because it is named firsttable_id? Am I missing something really stupid here?

Re: Foreign Key not setting

How are you creating the secondtable entry? Can you post that code?

Railscasts - Free Ruby on Rails Screencasts

Re: Foreign Key not setting

I have actually made progress but I still don't think I am doing it correctly. I send over the firsttable's key in the NEW view and then get it back in the CREATE and stick it in but I was expecting Ruby to figure this stuff out for itself from the model

Secondtable_controller.rb

cat secondtable_controller.rb
class SecondtableController < ApplicationController
  def index
    list
    render :action => 'list'
  end

  # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
  verify :method => :post, :only => [ :destroy, :create, :update ],
         :redirect_to => { :action => :list }

  def list
    @secondtable_pages, @secondtables = paginate :secondtables, :per_page => 10
  end

  def show
    @secondtable = Secondtable.find(params[:id])
  end

  def new
    @firsttable = Firsttable.find(params[:id])
    @secondtable = @firsttable.secondtables.new params[:secondtable]

  end

  def create
   @firsttable = Firsttable.find(params[:id])
    @secondtable = @firsttable.secondtables.new params[:secondtable]
    @secondtable.firsttable_id = @firsttable.id

    if @secondtable.save
      flash[:notice] = 'Secondtable was successfully created'
      redirect_to :action => 'list'
    else
      render :action => 'new'
    end
  end

  def edit
    @secondtable = Secondtable.find(params[:id])
  end

  def update
    @secondtable = Secondtable.find(params[:id])
    if @secondtable.update_attributes(params[:secondtable])
      flash[:notice] = 'Secondtable was successfully updated.'
      redirect_to :action => 'show', :id => @secondtable
    else
      render :action => 'edit'
    end
  end

  def destroy
    Secondtable.find(params[:id]).destroy
    redirect_to :action => 'list'
  end
end

VIEW secondtable/new.rhtml

<h1>New secondtable</h1>

<%= start_form_tag :action => 'create', "id" => params[:id] %>
  <%= render :partial => 'form' %>
  <%= submit_tag "Create" %>
<%= end_form_tag %>
<%= text_field 'secondtable', 'id', :value=> @secondtable.firsttable_id %>
<%= text_field 'secondtable', 'id', :value  => params[:id] %>


<%= link_to 'Back', :action => 'list' %>

Re: Foreign Key not setting

Try removing this line in your create action:

@secondtable.firsttable_id = @firsttable.id

It shouldn't be necessary since you're building the model through the association on the previous line.

Edit: oh, just noticed you need to use the "build" method instead of the "new" method.

@secondtable = @firsttable.secondtables.build(params[:secondtable])

Railscasts - Free Ruby on Rails Screencasts

Re: Foreign Key not setting

That did it! Thank you very much. However I still don't really understand it and can not find any documentation that explains 'build' vs 'new'. Could you point me to something please?

Re: Foreign Key not setting

#new creates a blank new instance, filling attributes with the ones handed in. It is only used on a Model class:

 @article = Article.new(attributes)

#build is only used on association proxies, not on classes, and it does the same as #new, but besides that,it sets the foreign key to the id of the parent element:
@comment = @article.comments.build(attributes_for_comment)
#@comment will have the id of @article already set in its article_id column

Thats the whole magic wink

Last edited by Duplex (2007-09-19 14:00:15)

Re: Foreign Key not setting

Does any of this change on a RESTful design with nested routes?

This is what I have:

Asset belongs_to client
Client has_many assets

clients/2/assets/ (index.html - I am creating new assets in the index.)
[code: ruby]
<% form_for(:asset, :url => assets_path, :html => { :multipart => true }) do |f| -%>
<%= f.text_field :description %>
<%= submit_tag 'Create' %>

<%= f.text_field :client_id, :value => @asset.client_id %>
<%= f.text_field :parent_id, :value => params[:id]  %>
[/code]

assets controller
[code: ruby]
  def create
    @asset = Asset.new(params[:asset])
    @asset = @client.assets.build(params[:asset])
[/code]

With the code like this I get a standard nil error as shown below.
"You have a nil object when you didn't expect it! The error occurred while evaluating nil.client_id"