adamscott
adamscott

Reputation: 853

how do rails associations get set?

I always seem to have trouble with this concept. I get what associations allow you to do, I just never seem to be able to tell if the associations are set in an application.

For example, I generated a scaffold for line_items and before I ran my migration, I set the belongs_to and has_many methods in the correct models, and then ran my migration.

After running my migration, I look at my schema and I can't tell if there are any associations set. To me it does not seem like it because I don't see the schema setting any relationships.

Do the has_many and belongs_to methods actually set the association? Or are they there for developers reading the code to understand the relationship?

How would my schema look if the associations were set properly? Do I need to rollback my last migration and include the correct indexes?

create_table "carts", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "line_items", force: :cascade do |t|
    t.integer  "product_id"
    t.integer  "cart_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "products", force: :cascade do |t|
    t.string   "title"
    t.text     "description"
    t.string   "image_url"
    t.decimal  "price",       precision: 8, scale: 2
    t.datetime "created_at",                          null: false
    t.datetime "updated_at",                          null: false
  end

Upvotes: 0

Views: 675

Answers (2)

Zhiliang Xing
Zhiliang Xing

Reputation: 1057

it's very simple to tell if the association is set up properly or not through your schema. basically the model has belongs_to should have the corresponding table that contains the foreign_id. For example in the schema you post, it's easy to see that the lineItem belongs to 'Product' and Cart, because it has cart_id and product_id(two foreign id).

activerecord is not a black magic, all has_many and belongs_to do is just dynamically adds a method to the model class, which translate the query to raw sql for you and map the result to ruby object. however it is your responsibility to set up the database table correctly. Because after all, activerecord use SQL to query data following rails convention.

update

I think what you mean is the helper method in your migration file such as

t.belongs_to product, index: true, foreign_key: true

this line of code is also not a black magic, it is a helper method rails provide to make your life much easier. It is equivalent to do three simple things to create your database table.

  1. create a foreign_id depends on your input, in the above example, it will be product_id. equivalent to t.integer product_id
  2. add an index on the foreign_id, because usually you query out the associated data a lot by using the foreign_id, add an index will improve your efficiency. equivalent to add_index "xxx", ["product_id"], name: "index_xxxs_on_product_id"
  3. at the end the foreign_key: true is going to set up an database constrains for your foreign_id, so that if there is still one row in your child table related to one row in your parent table, the row in your parent table will not be accidentally deleted. this is equivalent to add_foreign_key :xxxs, :products

so as a conclusion, using add_foreign_key :articles, :authors will not make anything look special in your schema, because ActiveRecord is just a translator to make your life easier when you deal with sql database, it can only do the thing sql database can do, not anything special. the idea of association in database is just saving a foreign_id in one table so that you can query the related data in another table by using the foreign_id.

Upvotes: 2

SteveTurczyn
SteveTurczyn

Reputation: 36860

The "association" is a rails concept that is implemented by has_many and belongs_to... so those lines are not just for documentation, they create the association.

You have product_id and cart_id in the line_items table and I assume

class LineItem
  belongs_to :cart
  belongs_to :product
  ...
end

class Product
  has_many :line_items
  ...
end

class Cart
  has_many :line_items
  ...
end

The has_many and belongs_to do set the associations for rails and means rails now knows that there are the associations...

my_line_item.cart
my_line_item.product
my_cart.line_items
my_product.line_items

If you didn't have the has_many and belongs_to it wouldn't work.

The columns card_id and product_id are needed for the associations to work as they're the way that records are linked and need to be present on the belongs_to side of the relationship. They don't have to be called what they're called but if you don't specifically use a different foreign_key name in the belongs_to and has_many then these field names are what will be expected by rails. And it's best to use the expected names... "Convention over configuration" is the preference.

If you did decide to call the foreign key vegetable_id instead of product_id that's fine, but then you'd define the association as

class LineItem
  belongs_to :product, foreign_key: :vegetable_id

and

class Product
  has_many :line_items, foreign_key: :vegetable_id

You can go an extra step and have...

class Cart
  has_many :line_items
  has_many :products, through: :line_items
  ...
end 

This auto-magically gives you the ability to do..

my_cart.products

As rails knows how to build the SQL command to get all products for a cart via the line_items.

Because you need the foreign key in the belongs_to table, you would need to specify that foreign key when you create the table.

create_table :line_items do |t|
  t.integer :product_id

There is a helper_method called references (also aliased as belongs_to) which basically does the same thing... creates the :product_id field...

create_table :line_items do |t|
  t.belongs_to :product

or

create_table :line_items do |t|
  t.references :product

The three versions above basically do the same thing... they create the integer column :product_id

You can also index on the :product_id field to improve retrieval performance, so you'll occastionally see index: true but this isn't essential.

Upvotes: 1

Related Questions