Forum: Ruby on Rails Model Relations

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
E7c66d606ce1b7974322baa16e349bea?d=identicon&s=25 Christoffer Brodd-reijer (ephracis)
on 2009-04-10 13:22
Hi,

I am creating my databases and models and I need to use some
relationships. I am very new at this so any help or info at all would be
great. :)

The concept is:
There are three resources: people, items and tags.

* An item belongs to one person, the creator.
* A person can create many items.
* An item can have many tags.
* A tag can be used on many items.

Here my models:

-----------------------------------
/* models/item.rb */
class Item < ActiveRecord::Base
  belongs_to :person
  has_many :tags
end

-----------------------------------
/* models/person.rb */
class Person < ActiveRecord::Base
  has_many :items
end

-----------------------------------
/* models/tag.rb */
class Tag < ActiveRecord::Base
  /* should I use 'has_and_belongs_to_many' here? */
end



Here's the migrations:

-----------------------------------
/* db/migrate/1_create_items.rb */
class CreateItems < ActiveRecord::Migration
  def self.up
    create_table :items do |t|
      t.string :name
      t.belongs_to :person
      /* what should I put here to create a relation to tags? */
    end
  end

  def self.down
    drop_table :items
  end
end

-----------------------------------
/* db/migrate/2_create_people.rb */
class CreatePeople < ActiveRecord::Migration
  def self.up
    create_table :people do |t|
      t.string :name
      /* do I need something here to create a relation to items? */
    end
  end

  def self.down
    drop_table :people
  end
end

-----------------------------------
/* db/migrate/3_create_tags.rb */
class CreateTags < ActiveRecord::Migration
  def self.up
    create_table :tags do |t|
      t.string :name
      /* what should I put here to create a relation to items? */
    end
  end

  def self.down
    drop_table :tags
  end
end
81b61875e41eaa58887543635d556fca?d=identicon&s=25 Frederick Cheung (Guest)
on 2009-04-10 14:31
(Received via mailing list)
On Apr 10, 12:22 pm, Christoffer Brodd-reijer <rails-mailing-
l...@andreas-s.net> wrote:
> end
You could use has and belongs to many or has many through. Either way
you will need another table (the join table for the relationship
between tags and items), the existing tables are fine as is.
You might find http://guides.rubyonrails.org/association_basics.html
helpful.

Fred
E7c66d606ce1b7974322baa16e349bea?d=identicon&s=25 Christoffer Brodd-reijer (ephracis)
on 2009-04-10 17:24
Frederick Cheung wrote:
> On Apr 10, 12:22�pm, Christoffer Brodd-reijer <rails-mailing-
> l...@andreas-s.net> wrote:
>> end
> You could use has and belongs to many or has many through. Either way
> you will need another table (the join table for the relationship
> between tags and items), the existing tables are fine as is.
> You might find http://guides.rubyonrails.org/association_basics.html
> helpful.
>
> Fred

I went with using a joint table

-----------------------------------
/* db/migrate/3_create_tags.rb */
...
create_table :item_tags, :id => false do |t|
  t.integer :tag
  t.integer :item
end
...


Now I am coding the controller that creates the item. I am iterating
through each tag that the user typed in and I want this to happen:

* Create a new tag in the 'tags' table if it does not already exists
* Associate the tag and item by adding the pair to the 'item_tags' table

The idea here is that each tag should be only once in the tags table.
Here's the controller function so far:

-----------------------------------
/* controllers/items_controller.rb */
def create
  for tag in tags.split(' ')
    match = Tag.find(:first, :conditions => "name = '#{tag}'")
    unless match
      Tag.create(:name => tag)
    end

    /* make association here */

  end
end


Problem is, I don't know how to add the association in the controller.
Also, am I doing the rest of the stuff right? Or maybe Rails can
automate this whole process somehow? Thanks,
6883e5ef03484d4fcef507d7b4f1d243?d=identicon&s=25 Matt Jones (Guest)
on 2009-04-11 20:29
(Received via mailing list)
You can shorten this part up by using Tag.find_or_create_by_name, one
of the dynamic finder methods.
And I'd add a generic warning to NEVER use #{} interpolation directly
in anything you're feeding to an AR conditions parameter, unless you
want to relearn the lesson of little Bobby Tables...

The final code will look something like:

  def create
    # find item, put in @item
    @item.tags = params[:tags].split(' ').map do |tag|
      Tag.find_or_create_by_name(tag)
    end
    # do whatever else
  end

Alternatively, you could encapsulate this behavior in the Item model,
using a virtual tags_attr or the like to do the parsing. I'd recommend
that you take a look at some of the approaches taken by
acts_as_taggable and it's descendants.

--Matt Jones

On Apr 10, 11:24 am, Christoffer Brodd-reijer <rails-mailing-
This topic is locked and can not be replied to.