Has_and_belongs_to_many woes

I’m still having a hard time wrapping my brain around many to many
relationships in rails. I understand what they are, and I’ve read all
kinds of code that explains how to update them after finding a specific
id. However, I can’t seem to figure out how to update a many to many
model from a form. I just can’t imagine what the syntax might be.

Here are my models:

class Item < ActiveRecord::Base
has_and_belongs_to_many :print_jobs
end

class PrintJob < ActiveRecord::Base
has_and_belongs_to_many :items
end

I have a table called “print_jobs_items” that contains the following
fields:

print_job_id
item_id
quantity
dimensions

… I won’t list all the fields, but there are about 15.

I have an ajax form that points to the method “save_item_pj_details”.
The form object is called “items_print_job”. What do I actually put in
that method to get rails to save the data from that form to the
database, and add a new record if necessary. Do I have to manually
create database objects for each table, or can I simply feed the form
object to the model just as I normally would with the .new method?

I just (obviously) don’t have a clear picture of how to go about this,
nor can I find any solid examples. Thanks very much.

MenDAKE wrote:

I’m still having a hard time wrapping my brain around many to many
relationships in rails. I understand what they are, and I’ve read all
kinds of code that explains how to update them after finding a specific
id. However, I can’t seem to figure out how to update a many to many
model from a form. I just can’t imagine what the syntax might be.

Here are my models:

class Item < ActiveRecord::Base
has_and_belongs_to_many :print_jobs
end

class PrintJob < ActiveRecord::Base
has_and_belongs_to_many :items
end

I have a table called “print_jobs_items” that contains the following
fields:

print_job_id
item_id
quantity
dimensions

… I won’t list all the fields, but there are about 15.

Ok, 2 things up front.

First, for the Rail magic to work, the table needs to be called
‘items_print_jobs’. The tablenames must be in alpha order.

Second, this table probably shouldn’t have any other attributes (aside
from item_id and print_job_id) as they’re hard to get at and update when
using has_and_belongs_to_many.

What you should probably consider instead is has_many :through.

There’s some more detail on the pros, cons and debates at the
many-to-many dance off on Josh S.s blog (
has_many :through - Many-to-many Dance-off! ).

In your case, we replace that join table with a real live model.

I’m not sure what model is represented by the link between print_job and
item in your case, so I’ll just call it a Printing.

Item has_many :printings
Item has_many :print_jobs, :through => :printings

PrintJob has_many :printings
PrintJob has_many :items, :through => printings

Printing has_one :item
Printing has_one :print_job

Thingy can then have as many attributes as it likes.

See Josh’s artuicle for more.

HTH

Alan

Thanks, Alan. I now have my models set up with the :through method, and
it seems to be working so far. My models now look like this:

class ItemsPrintJobs < ActiveRecord::Base
belongs_to :print_job
belongs_to :item
end

class Item < ActiveRecord::Base
has_many :items_print_jobs, :dependent => true
has_many :print_jobs, :through => :items_print_jobs
end

class PrintJob < ActiveRecord::Base
has_many :items_print_jobs, :dependent => true
has_many :items, :through => :items_print_jobs
end

The “ItemsPrintJobs” model references the table “items_print_jobs” and
looks like this now:

id
print_job_id
item_id
quantity
dimensions
… again, I won’t list all the records.

This works fine for displaying information (just as the
has_and_belongs_to_many relationship above did). However, I’m still not
clear on the best procedure for updating these records with a form. I
think this is just a basic AR/rails issue that I’m not clear on.

The problem is that when I go to save the items_print_jobs table I do
not have the value of the id. I only have the value of the foreign keys
“item_id” and “print_job_id”. I need to be able to save a record if one
already exists with those two foreign keys, or if not, create a new
record.

The code I’m using below just creates record every time, presumably
because it doesn’t know what the id (primary key) is.

Use the form data object (items_print_job) to create a new db object.

@items_print_job = ItemsPrintJobs.new(@params[:items_print_job])

Save the values of the two foreign keys.

@items_print_job.item_id = @params[:item_id]
@items_print_job.print_job_id = @params[:id]

Save the data to the database and add a flash message.

if @items_print_job.save
flash[:notice] = “Product successfully saved”
end

As an experienced programmer (though not in rails, obviously) I would do
some if statements or perhaps create a composite primary key, but
somehow I don’t think that’s how it’s supposed to work in rails. Can you
help?

Thanks for all your help. Looks like I finally have it working. Much
appreciated.

MenDAKE wrote:

Thanks, Alan. I now have my models set up with the :through method, and
it seems to be working so far. My models now look like this:

class ItemsPrintJobs < ActiveRecord::Base
belongs_to :print_job
belongs_to :item
end

class Item < ActiveRecord::Base
has_many :items_print_jobs, :dependent => true
has_many :print_jobs, :through => :items_print_jobs
end

class PrintJob < ActiveRecord::Base
has_many :items_print_jobs, :dependent => true
has_many :items, :through => :items_print_jobs
end

The “ItemsPrintJobs” model references the table “items_print_jobs” and
looks like this now:

id
print_job_id
item_id
quantity
dimensions
… again, I won’t list all the records.

This works fine for displaying information (just as the
has_and_belongs_to_many relationship above did). However, I’m still not
clear on the best procedure for updating these records with a form. I
think this is just a basic AR/rails issue that I’m not clear on.

The problem is that when I go to save the items_print_jobs table I do
not have the value of the id. I only have the value of the foreign keys
“item_id” and “print_job_id”. I need to be able to save a record if one
already exists with those two foreign keys, or if not, create a new
record.

The code I’m using below just creates record every time, presumably
because it doesn’t know what the id (primary key) is.

Use the form data object (items_print_job) to create a new db object.

@items_print_job = ItemsPrintJobs.new(@params[:items_print_job])

Save the values of the two foreign keys.

@items_print_job.item_id = @params[:item_id]
@items_print_job.print_job_id = @params[:id]

Save the data to the database and add a flash message.

if @items_print_job.save
flash[:notice] = “Product successfully saved”
end

As an experienced programmer (though not in rails, obviously) I would do
some if statements or perhaps create a composite primary key, but
somehow I don’t think that’s how it’s supposed to work in rails. Can you
help?

How about:

@item = Item.find(params[:item_id])
@print_job = @item.print_jobs.find(params[:print_job_id])

That gets the items in question and searches its list of print_jobs for
a match.

If @print_job is not null, the record exists and can be got via
@item.printing or @print_job.printing

If the @print_job is null, no relationship exists and you’ll need to
create one.

Printing.create( params )

Does this make sense ?

Alternatively, you may be able to use
Printing.find_by_item_id_and_print_job_id( params[:item_id],
params[:print_job_id])

A.
p.s. I’d definitely try and find a better name for that join model :slight_smile: