Alright, so here's the deal. I have two tables and a join table since it's a many-to-many relationship. I have an order and an order can have many products. Obviously it goes the other way since products can be on many orders. I've got the following classes:
class Order < ActiveRecord::Base
has_many :orders_products
has_many :products, :through => :orders_products
end
class OrderProduct < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :orders_products
has_many :orders, :through => :orders_products
end
I'm getting a page to display and I'm able to enter stuff and when I go to interact with the products on the saved order via #order.products I'm getting the following error:
SQLite3::SQLException: no such table: order_products: SELECT "products".* FROM "products" INNER JOIN "order_products" ON "products".id = "order_products".product_id WHERE (("order_products".order_id = 1))
My join table is named orders_products, but as you can see it's trying to join through order_products. My limited knowledge of Rails naming conventions tells me that orders_products is the correct way to name it, and then name my model as OrderProduct. I'm really pounding my head against a wall on this one.
EDIT: I see that even though it saved my order it and I selected multiple checkboxes it did not save any values in the orders_products table, presumably for the same reason as it is erroring now.
orders_products is not the correct naming convention for a has_many :through relationship--it is correct for a has_and_belongs_to_many relationship. In a has_many :through, the "join" model is not just for joining--it is also its own model that has its own data, that also happens to join two other models together.
If your OrderProduct model doesn't have any of it's own data, logic, or constraints, then you could use a has_and_belongs_to_many relationship instead, remove the model completely, and then your join table is named right. Otherwise, it is named according to regular model naming conventions, namely order_products.
Related
if we have data in a table and table that has a has_and_belongs_to_many association and we want to change the association to has_many through and add a foreign key to that table and we also want the previous data of the table to be saved with foreign key what will we do?
You would have to add a model to your application with the same name as your join table, or rename the join table with a migration.
What are the names of your models?
For example if you now have countries and companies. A country can have multiple companies and a company can have locations in multiple countries. You start with a model for Company and Country and a join table companies_countries.
You can create a model: CompaniesCountry and use it for the has_many :through
class Company < ApplicationRecord
has_many :companies_countries
has_many :countries, through: :companies_countries
end
class CompaniesCountry < ApplicationRecord
has_many :companies
has_many :countries
end
I think it would be nicer here to rename the join table to something more sensible like company_locations with a migrations and make a model for this instead. You could also store additional info like address or such on the model which you weren't able to do with the join table
The rails guides are a nice read on this as well: https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
I have a Match and User model with a has_and_belongs_to_many between them.
How do I retrieve match.users.first and match.users.second based on when the MatchUser association was created, rather than by when the User itself was created?
You don't want to be using has_and_belongs_to_many in the first place. has_and_belongs_to_many relations are headless - there is no join model. The only columns that are ever used on the join table are the two foreign keys. Even if you added a created_at column to the join table there is no way to access it or use it to order the records. And AR won't set the timestamps anyways.
While you can kind of assume that a has_and_belongs_to_many association is ordered in the same order that the records where inserted you can't really order it.
You want to use has_many through: which uses a model to join the two records:
class User < ApplicationRecord
has_many :user_matches
has_many :matches, through: :user_matches
end
class Match < ApplicationRecord
has_many :user_matches
has_many :users, through: :user_matches
end
class UserMatch < ApplicationRecord
belongs_to :user
belongs_to :match
end
You can then order the association by:
match.users.order("user_matches.created_at DESC")
match.users.first
will return the first user by :id.
If you want to it ordered by created_at then you must do something like
user_id = matches_users.where(match_id: match.id).first.user_id
user.find(user_id)
Hope this is what you are looking at.
In my project, I have this simple association set up:
class Episode < ActiveRecord::Base
belongs_to :game_master, :class_name => 'CastMember'
has_and_belongs_to_many :players, :class_name => 'CastMember'
end
class CastMember < ActiveRecord::Base
has_and_belongs_to_many :episodes
end
I was wondering what the join table for these might be.
At first, I thought a cast_members_episodes table would be enough, but after thinking about it for a bit, it wouldn't make sense, as it wouldn't be able to differentiate between game_master and players.
Any ideas?
Yes, cast_members_episodes table to handle the many-to-many relation of players and episodes.
And, add a column game_master_id to episodes table to handle the relation of game_master and episodes.
I have two tables with has_and_belongs_to_many relationship: categories and raw_categories
Should the table be called categories_raw_categories?
Yes, the join table is named after the two tables to be joined listed in alphabetical order. Since categories is higher in the alphabet than raw_categories, the join table is called categories_raw_categories. Note that if you are doing migrations, you need to create a separate migration for this join table.
See here for more details on HABTM relationships and the join tables required for them: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Also note that you can set a custom name for the join table if you want. Example (if you want to call the join table category_associations):
# Category model
has_and_belongs_to_many :raw_categories, :join_table => 'category_associations'
# RawCategory model
has_and_belongs_to_many :categories, :join_table => 'category_associations'
You can also always explicitly make the join table a first-class model by using has_many :though on the models to be joined. Following the example above, you could make CategoryAssociation an actual model and join it to the other two like this:
# CateogoryAssociation model
belongs_to :category
belongs_to :raw_category
# Category model
has_many :category_associations
has_many :raw_categories, :through => :category_associations
# RawCategory model
has_many :category_associations
has_many :categories, :through => :category_associations
I'm creating a model called Chats. And I want to assign users to a discussion. They are either a part of the Chats or they aren't...
So I create one model Chats.
What's the standard Rails naming convention for the other table?
ChatUsers?
While has_and_belongs_to_many is an ok option here, I recommend going with has_many :through instead.
In essence you will have an explicit join model, which you can call something like ChatSession.
class Chat < ActiveRecord::Base
has_many :chat_sessions
has_many :users, :through => :chat_sessions
end
class User < ActiveRecord::Base
has_many :chat_sessions
has_many :chats, :through => :chat_sessions
end
class ChatSession < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
Now you will need a table called chat_sessions with columns :user_id, and :chat_id in it. This is your join table.
Advantage
You get a model which is fully under your control, and isn't just a dumb join table managed by rails. So for example, if you want to track number of messages particular user left in particular chat, it could be a column in chat_sessions table. Presence of :through renders habtm unneeded in most cases. There is no complexity overhead either.
If it is a join table, it would be both table names joined by '_' and in alphabetical order of table names:
chats_users
This is called a has_and_belongs_to_many association in rails. You basically have two models that call has_and_belongs_to_many and create a linking table that uses the two models in the name (alphabetical and plural).
models:
class Chat < ActiveRecord::Base
has_and_belongs_to_many :users
end
class user < ActiveRecord::Base
has_and_belongs_to_many :chats
end
Then your tables would be
chats
users
chats_users