Luc
Luc

Reputation: 17072

Rails relationship

I'm trying to figure out something regarding rails relationships. I already posted a question regarding a specific items not long ago but I do not really understand what's done in the underlying DB.

I have a Project model and a Client model. A Project belongs_to :client => I need to manually add client_id in projects table (with a migration).
A Client has_many :projects => I do not need to do anything in the DB (no migration). The project.client and client.projects methods are both available.

I have a Group model and a User model.
A Group has_and_belongs_to_many :user
A User has_and_belongs_to_many :group I then need to create a migration to create a joint table with a user_id and a group_id pointers.

I do not really see where the border between rails and the relational database is. Why do I need to add foreign key sometimes but not always ? How is the has_many relationship handled as I did not do anything in the underlying DB for this particuliar guy ?

I am kind of lost sometimes :)

Thanks and Regards,

Luc

Upvotes: 2

Views: 322

Answers (2)

nagendra
nagendra

Reputation: 98

class Customer < ActiveRecord::Base
  has_many :orders, dependent: :destroy
end

class Order < ActiveRecord::Base
  belongs_to :customer
end

@order = @customer.orders.create(order_date: Time.now)

Upvotes: -1

Ryan Bigg
Ryan Bigg

Reputation: 107728

For a has_many <-> belongs_to assoication, you're defining that one project is owned (belongs_to) by one client. Therefore, that client has many (has_many) projects. For a project to determine what client it belongs to it needs to have an client_id column so that it can look it up. This client_id column is used by Rails when you call the client method, much like this:

Client.find(project.client_id)

That's how you can find a project's client. The client_id column is often referred to as a foreign key, because its a unique identifier ("key") in a table not of its origin ("foreign"). Boom.

When you call the other way around, finding all the projects a client has, i.e. client.projects, Rails does the equivalent of this:

Project.find_all_by_client_id(client.id)

This then returns all Project records which are associated with a particular client, based off the client_id field in the projects table.


With a has_and_belongs_to_many association, such as your users & groups example, you're declaring that a user has_and_belongs_to_many :groups.

Now if it were simply a has_many :groups, the foreign key would go in the groups table, or if it were a belongs_to it would go in the users table. Good thing to remember: the foreign key always goes in the table of the model that has the belongs_to.

You're also declaring that a group has_and_belongs_to_many :users, and so we come across the same problem. We can't declare the key in the users table because it doesn't belong there (because a user has many groups, you would need to store all the group ids the user belongs to) or the groups table for the same reasons.

This is why for a has_and_belongs_to_many we need to create what's known as a join table. This table has two and only two fields (both of them foreign keys), one for one side of the association and another for the other. To create this table, we would put this in a migration's self.up method:

create_table :groups_users, :id => false do |t|
  t.integer :group_id
  t.integer :user_id
end

A couple of things to note here:

  1. The table name is the two names of the two associations in alphabetical order. G comes before U and so the table name is groups_users.
  2. There's the :id option here which, when given the value of false generates a table with no primary key. A join table doesn't need a primary key because its purpose is to just join other tables together.
  3. We store the group_id and user_id as integer fields, just like we would on a belongs_to association.

This table will then keep track of what groups have what users and vice versa.

There's no need to define additional columns on either the users or groups table because the join table has got that under control.

Upvotes: 5

Related Questions