0

I added a has_and_belongs_to_many between Product and Brand tables/models

This is how the models look like:

class Brand < ActiveRecord::Base
  has_and_belongs_to_many :products

  default_scope { order('name asc')}
end

class Product < ActiveRecord::Base
  has_and_belongs_to_many :brands
end

These are the existing columns in the table:

[13] pry(main)> Brand
=> Brand(id: integer, name: string, created_at: datetime, updated_at: datetime, product_id: integer)

[11] pry(main)> Product
=> Product(id: integer, name: string, created_at: datetime, updated_at: datetime)

Join table db migration:

class CreateJoinTableProductsBrands < ActiveRecord::Migration
  def change
    create_join_table :products, :brands do |t|
      t.integer :product_id
      t.integer :brand_id
      t.index [:product_id, :brand_id]
      t.index [:brand_id, :product_id]
    end
  end
end

Questions:

  1. As you will notice, the Brand table already had the product_id column. Should I change it to an array product_ids column? (I am using postgres)
  2. Should I add brand_ids column to Product
  3. Should I add ProductBrand model. I tried it but seems like Rails console didnt recognize it

    class ProductBrand < ActiveRecord::Base

    end

  4. In ActiveAdmin what's the correct way of creating a new entry for Product or Brand such that a new record correctly links Product, Brand and ProductBrand entry?

codeObserver
  • 6,521
  • 16
  • 76
  • 121

1 Answers1

0

I would highly recommend reading some tutorials and documentation that explain the fundamental difference between HATBM or HMT relationships in ActiveRecord. I think this would go a long way to answering your own questions.

Take the following tables below: Teams and Users (which could be equivalent to your brands and products). They likewise have a HATBM relationship both ways between them.

create_table "teams", force: :cascade do |t|
  t.datetime "created_at",     null: false
  t.datetime "updated_at",     null: false
  t.integer  "wins"
  t.float    "win_percentage"
end

create_table "teams_users", id: false, force: :cascade do |t|
  t.integer "team_id", null: false
  t.integer "user_id", null: false
end

add_index "teams_users", ["team_id", "user_id"], name: "index_teams_users_on_team_id_and_user_id", using: :btree
add_index "teams_users", ["user_id", "team_id"], name: "index_teams_users_on_user_id_and_team_id", using: :btree

create_table "users", force: :cascade do |t|
  t.string   "first_name"
  t.string   "last_name"
  t.string   "user_name"
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
end

Note that there is no foreign key (FK) of a user_id in the teams table, and no FK in the users table. The purpose of the join table is to join these 2 models hence no FK is needed in each to link them.

To answer your first 3 questions:

  • I would not change your product_ids column to an array. There is simply no need. This is the purpose of the join table.
  • I would not add a brand_ids column to product. Use your join table instead.
  • Unless you have a specific reason for requiring a ProductBrand model then you do not need it. If did require it then I would advocate the use of have_many through relationships. Follow the Rails conventions and if using ActiveRecord's HATBM association you don't need/want this.

This question/answer will help: has_and_belongs_to_many vs has_many through

and an excellent post explaining join tables: http://nishacodes.tumblr.com/post/73484141822/join-tables-heres-the-deal

Community
  • 1
  • 1
Ben Hawker
  • 949
  • 8
  • 15
  • 1
    Totally agree with your strategy, but to be totally compliant with Rails conventions, you might want to rename the Join table to teammembers with a Teammember model. – bo-oz Nov 19 '15 at 07:01
  • I have searched for some more info on this but can't find anything - can you point me in the right direction in the docs please? – Ben Hawker Nov 19 '15 at 07:28
  • 1
    You're right, it's purely opinion based. But in my experience having clear names for models helps other people have a better understanding of your code. It also makes your code easier to read. In your case the model would need to be named TeamsUser which for me has a strange combination of a plural and a singular part. But your solution is completely valid and correct! – bo-oz Nov 19 '15 at 07:38