Getting belongs_to and has_many working: good plan?

I just added these linkage statements to my Expense and Vendor models, respectively.

I found a "HOWTO: Beginning Relationships" webpage (http://www.railsforum.com/viewtopic.php?id=265).

That leads me to think that I need to: 1. Create migration Add_Vendor_ID_to_Expense and then add column vendor_id of type integer, or something like that. 2. Create migration Add_Expense_ID_to_Vendor and then add column expense_id of type integer, or something like that. 3. Create migration Create_Expense_Vendor_Table and then add expense_id and vendor_id columns.

Then I can rake db:migrate.

Is that a sensible start?

Thanks in advance, Richard

RichardOnRails wrote:

I just added these linkage statements to my Expense and Vendor models, respectively.

Which? You neglected to say.

I found a "HOWTO: Beginning Relationships" webpage (http://www.railsforum.com/viewtopic.php?id=265).

Why are you following tutorials from 2006, before Rails 1.0 even existed?

And what have you been doing all this time? You've been posting on this list for a long time; have you really never looked at associations before?

That leads me to think that I need to: 1. Create migration Add_Vendor_ID_to_Expense and then add column vendor_id of type integer, or something like that. 2. Create migration Add_Expense_ID_to_Vendor and then add column expense_id of type integer, or something like that.

You don't need both.

3. Create migration Create_Expense_Vendor_Table and then add expense_id and vendor_id columns.

You may not need this at all.

And it should all be in one migration class if it represents one conceptual change.

Then I can rake db:migrate.

Right.

Is that a sensible start?

Thanks in advance, Richard

Best,

RichardOnRails wrote: > I just added these linkage statements to my Expense and Vendor models, > respectively.

Which? You neglected to say.

Change the Expense model:: app\models\expense.rb class Expense < ActiveRecord::Base   belongs_to :vendor # Added end

Change the Vendor model:: app\models\ vendor.rb class Vendor < ActiveRecord::Base   has_many :expenses # Added

  def name       #{qbname} # QuickBooks name   end end

> webpage (http://www.railsforum.com/viewtopic.php?id=265).

Why are you following tutorials from 2006, before Rails 1.0 even existed?

Because it was the simplest one I stumbled across.

And what have you been doing all this time? You've been posting on this list for a long time; have you really never looked at associations before?

Actually, I have read about them but I didn't think of them because I started with f.collection_select(:vendor, :vendor_id, ... as the means of adding a vendor to a new expense.

But I knew adding actual vendor-names (rather than vendor-ids) to expense records was poor design. So then I was focused on getting the vendor-id out of that select in order to add it to the expense record ... and so on until I took some time off to think of something better. Then Active Record association popped in my head, so I'm back on this problem.

> > That leads me to think that I need to: > 1. Create migration Add_Vendor_ID_to_Expense and then add column > vendor_id of type integer, or something like that. > 2. Create migration Add_Expense_ID_to_Vendor and then add column > expense_id of type integer, or something like that.

You don't need both.

Why not? And if not, does it matter which one I use?

> 3. Create migration Create_Expense_Vendor_Table and then add > expense_id and vendor_id columns.

You may not need this at all.

Really. You mean Active Record is smart enough to decipher this from the mere fact that I added those one-liners to the respective models?

And it should all be in one migration class if it represents one conceptual change.

I thought of that but decided to go with my first thought.

Again, best wishes, Richard

This is a better place to start, ruby on rails guide. Basically all you need is a foreign key pointing both directions in the join table.

http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association

I forgot to mention that my son seems mighty disappointed with my lack of performance on this for the last perhaps four weeks. But if I get this relationship stuff working (which will apply to a number of tables), I may be able to wrangle my way back into his good graces.

Active Record Associations — Ruby on Rails Guides

This is an excellent reference. Much, much better than anything I found with Google searches! Thank you very much.

Best wishes, Richard

RichardOnRails wrote:

RichardOnRails wrote: > I just added these linkage statements to my Expense and Vendor models, > respectively.

Which? �You neglected to say.�

Change the Expense model:: app\models\expense.rb class Expense < ActiveRecord::Base   belongs_to :vendor # Added end

Change the Vendor model:: app\models\ vendor.rb class Vendor < ActiveRecord::Base   has_many :expenses # Added

  def name       #{qbname} # QuickBooks name   end end

#{} syntax is only valid within double-quoted strings.

> webpage (http://www.railsforum.com/viewtopic.php?id=265).

Why are you following tutorials from 2006, before Rails 1.0 even existed?

Because it was the simplest one I stumbled across.

Who cares how simple it is if it's obsolete?

[...]

> That leads me to think that I need to: > 1. Create migration Add_Vendor_ID_to_Expense and then add column > vendor_id of type integer, or something like that. > 2. Create migration Add_Expense_ID_to_Vendor and then add column > expense_id of type integer, or something like that.

You don't need both.�

Why not? And if not, does it matter which one I use?

The Rails associations documentation explains which one you need. Perhaps you should go read that (it's the docs for the module that contains the has_many and belongs_to methods).

> 3. Create migration Create_Expense_Vendor_Table and then add > expense_id and vendor_id columns.

You may not need this at all.�

Really. You mean Active Record is smart enough to decipher this from the mere fact that I added those one-liners to the respective models?

No, I mean that you don't need a join table in this case, for reasons related to relational DB design and having nothing to do with Rails or ActiveRecord at all.

I *would* however advise using the Foreigner plugin to put foreign key constraints in the DB.

The fact that you're asking these questions makes me think that you know very little about relational database design. So put aside Rails for a day or two and go learn. In particular, learn about the proper use of foreign keys, and about normalization, especially as far as the Third Normal Form (3NF). Wikipedia has some excellent articles on these topics.

And it should all be in one migration class if it represents one conceptual change.�

I thought of that but decided to go with my first thought.

...because...?

Again, best wishes, Richard

Best,

I built the migration: class ExpensesVendorJoinTable < ActiveRecord::Migration   def self.up       create_table :expenses_vendor, :id=>false do |t|     t.integer :expenses_id     t.integer :vendor_id     t.timestamp :created_at       end   end

  def self.down     drop_table :expenses_vendor   end end

I applied the migration and checked the resulting join table:

I built the migration: class ExpensesVendorJoinTable < ActiveRecord::Migration def self.up create_table :expenses_vendor, :id=>false do |t| t.integer :expenses_id t.integer :vendor_id t.timestamp :created_at end end

That's setting yourself up for has_and_belongs_to_many (a single vendor can have multiple expenses and a single expense can have multiple vendors). Is that what you want ? (the earlier snippet from your models suggests no)

How do I modify my Expense.new from vendor-lookup via <%# @vendors = Vendor.all -%> <%# def name; Vendor.nickname; end -%> <%#= f.collection_select(:vendor, :vendor_id, @vendors, :id, :name) -%> so that the user can select a vendor from a drop-down and only the selected vendor's ID will be saved in the join-table, rather than the vendor's name saved in the expense record.

Assuming Vendor belongs_to :expense then f.collection_select :vendor_id, @vendors, :id, :name would do the trick - you're setting the vendor_id attribute of your object, using the collection of vendors named @vendors, the possible attribute values are given by their id and they should be displayed using their name (there is an example here:

)

Fred

Hi Fred,

Thanks for looking into my problem.

That's setting yourself up for has_and_belongs_to_many ... (the earlier snippet from your models suggests no)

Right, I don’t want that. I want: An Expense has a Vendor. A Vendor has many Expenses.

I believe that’s reflected in: app\models\vendor.rb:     class Vendor < ActiveRecord::Base       has_many :expenses     end and in app\models\ expense.rb:     class Expense < ActiveRecord::Base       belongs_to :vendor     end:

Assuming Vendor belongs_to :expense then f.collection_select :vendor_id, @vendors, :id, :name would do the trick

Au contraire: My concept is that every Expense belongs to some Vendor. However, every Vendor can have many different Expenses (incurred, as it were, at various times).

But I like your f.collection statement, though I feel I need to set @vendors somewhere to: @vendors = Vendor.find( :all, order=:>”nickname ASC”), perhaps just before the collection_select statement or in the Vendor’s controller (but within what method?)

Here’s what I wound up with in app\views\expenses\new.html.erb:     <%# New version of vendor selection -%>     <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%>     <%= f.collection_select(:vendor, :vendor_id, @current_vendors, :id, :nickname) %>     <%# End of New version -%>

That gave me:     undefined method `merge' for :nickname:Symbol (referencing the collection_select line)

But I think “nickname” is defined for Vendor, to wit:

> Assuming Vendor belongs_to :expense then > f.collection_select :vendor_id, @vendors, :id, :name > would do the trick

Au contraire: My concept is that every Expense belongs to some Vendor. However, every Vendor can have many different Expenses (incurred, as it were, at various times).

Sorry I worded that one back to front

But I like your f.collection statement, though I feel I need to set @vendors somewhere to: @vendors = Vendor.find( :all, order=:>”nickname ASC”), perhaps just before the collection_select statement or in the Vendor’s controller (but within what method?)

you would indeed, typically within whatever action this is for (so in this case the new method)

Here’s what I wound up with in app\views\expenses\new.html.erb: <%# New version of vendor selection -%> <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%> <%= f.collection_select(:vendor, :vendor_id, @current_vendors, :id, :nickname) %> <%# End of New version -%>

That gave me: undefined method `merge' for :nickname:Symbol (referencing the collection_select line)

It's getting confused because the first argument to collection_select(:vendor) shouldn't be there (so it thinks that your last parameter is a hash of options, but it's the symbol nick_name instead)

Hey Fred,

Thanks for you additional guidance on my introduction of belongs_to, etc.

> > Assuming Vendor belongs_to :expense then > > f.collection_select :vendor_id, @vendors, :id, :name > > would do the trick

> Au contraire: My concept is that every Expense belongs to some > Vendor. However, every Vendor can have many different Expenses > (incurred, as it were, at various times).

Sorry I worded that one back to front

No problem: I make so many mistakes that I amazed I get anything to work.

> That gave me: > undefined method `merge' for :nickname:Symbol (referencing the > collection_select line)

It's getting confused because ...

I coded a corrected (I hope) version in app\views\expenses \new.html.erb:   <p>     <%= f.label :vendor %><br />

    <%# New version of vendor selection -%>     <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%>     <%= f.collection_select(:vendor, @vendors, :id, :nickname) %>     <%# End of New version -%>

    <%= link_to 'New Vendor', :controller=>'vendors', :action=>'new' %>   </p>

I got the following error msgs which I suspect are due to the fact that I added one-to-many, etc after I already had a running version that saved the vendor-name in the expense record.

ActiveRecord::AssociationTypeMismatch in ExpensesController#create Vendor(#37938940) expected, got String(#21132310) [snip] K:/_Utilities/ruby186-26_rc2/ruby/lib/ruby/gems/1.8/gems/ activerecord-2.3.5/lib/active_record/base.rb:2438:in `initialize' K:/_Projects/Ruby/_Rails_Apps/_EIMS/RTS/app/controllers/ expenses_controller.rb:44:in `new' K:/_Projects/Ruby/_Rails_Apps/_EIMS/RTS/app/controllers/ expenses_controller.rb:44:in `create'

Request Parameters: {"commit"=>"Create", "expense"=>{"category"=>"junk", "account"=>"the account", "mode"=>"check", "tran_date(1i)"=>"2010", "tran_date(2i)"=>"7", "description"=>"7/13-1227 attempt", "amount"=>"123.45", "tran_date(3i)"=>"13", "vendor"=>"65", "user_id"=>"rlm"}, "authenticity_token"=>"d7GwyUWfO8eyfwkiEbquQ4SQZr+zf4UQNJ4h6LxVDmE="}

Expense Controller, expenses_controller.rb, lines 43-44 are:   def create     @expense = K:\_Projects\Ruby\_Rails_Apps\_EIMS\RTS\app\controllers \Expense.new(params[:expense])

Can you suggest how this should be coded in light of the vendor- expenses linkage?

Thanks again in advance for any guidance you have time to offer.

Best wishes, Richard

the first argument should be the name of the attribute ie vendor_id, not vendor

Fred