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.

http://guides.rubyonrails.org/association_basics.html#the-has-and-bel

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:
http://guides.rubyonrails.org/form_helpers.html#option-tags-from-a-collection-of-arbitrary-objects
)

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