I'm new to RoR, and I'm trying to create my first association within my app between an Order model, and a star key that represents all of the users who have starred that particular order.
I was originally thinking of using a has_and_belongs_to_many association for this, but that doesn't make sense given that there aren't many stars coming from the one user to one order.
With that said, I've really confused myself and could use a little bit of direction. Perhaps a simple migration file would send me down the right path.
Cheers!
Assuming users can "star" more than one order, then you do indeed want has_and_belongs_to_many. You can readily enforce that any one user has only one relationship to any one order.
I would indeed create an in between table referencing both Order and User, as an order has many users that keyed the order, and a user can key many orders...
rails g migration CreateOrderUsers order:references user:references
Your order_users model becomes
belongs_to: order
belongs_to: user
Then put in your order model
has_many :order_users, dependent: :destroy
has_many users, through: :order_users
And in your user model
has_many :order_users, dependent: :destroy
has_many orders, through: :order_users
Hope this helps!
Related
I'm trying to figure out what the appropriate ActiveRecord associations would be for my models. I am building a meal customization and ordering system for a website where Users can select from a number of Items and build a Meal and repeat this multiple times for a single Order.
My current setup is as follow:
User
has_many :orders, :dependent => :destroy
has_many :meals, through: :orders
Order
belongs_to :user
has_and_belongs_to_many :meals
Meal
has_and_belongs_to_many :orders
has_and_belongs_to_many :items
Item
has_and_belongs_to_many :meals
I suppose my question then would come in two parts:
1) does my current setup allow for multiple items to be assigned to a single meal, and possibly to other meals for a single order.
2) will I be able to access a user's previous meals to provide a shortcut when making new orders.
thanks a bunch in advance from a first time question asker!
To your first question, yes, you have set up a many-to-many relationship between your meals and items (assuming you have the correctly named database columns as well).
To your second question, you may have to wright a custom query for it as I'm not sure if active record is setup to do the multi layer query you're asking it to do (i.e. get all the meals from all the orders for a given user). Something like
past_meals = Set.new
users.orders.each {|o| past_meals += o.meals} would certainly work but could be made much more efficient if you needed it to.
However, even though has_many_and_belongs_to does work for what you're doing, using it isn't recommended. The community is much more in favor of making a join table doing a has_many_through relationship with that.
You in no means have to do that but most people would suggest it and it could save you some work down the road.
I have two models: Reader and Magazine. I obviously want to have a join table, readers_magazines, to represent which magazines each reader is subscribed to.
So I create my Reader model (with fields like name, address and age) and my Magazine model (with fields like Title and Active?). In each model I write has_and_belongs_to_many of the other.
Then I write a migration, CreateReadersMagazinesJoin, and write:
create_join_table :readers, :magazines do |t|
t.index 'reader_id'
t.index 'magazine_id'
end
And migrate the database. All good.
My question is... what now? Do I create a model for the join table? That seems wrong, and yet I do need some model validations (I don't want to represent the same User-Subscription combo twice). So do I write a model for it and manually specify the database table to use?
What is the correct procedure in this situation?
From the rails guides:
A has_and_belongs_to_many association creates a direct many-to-many
connection with another model, with no intervening model.
That means that using a has_and_belongs_to_many association might be easier for you to setup (because there is no middle model) but you don't have any kind of control over the join table, validations, etc. HABTM are normally considered harmful and you would want to use a has_many :through relationship instead, that is the same but having a model to represent the join table, so you have control over everything.
More information here.
Do I create a model for the join table? That seems wrong, and yet I do need some model validations.
If you don't want to add an additional fields to the join table, you don't really need to create the model for it.
And in most cases the validation that are required by yours, can be applied to one or both the tables.
I don't want to represent the same User-Subscription combo twice.
What do you mean twice, I havent seen the cases for that, however since the User-Subscription is something external the for the join table, you can explain more crear.
Where you have the choice between a habtm relationship or separate model, I would consider whether you need a rich amount of data in the join. In the present case, I think you do need extra data in the join. For example, a subscription would have a start date and an end date. It may also have a billing date and billing amount. You would not normally populate this information in a join table but rather in a stand alone model.
I would call the join table subscriptions and thus create a model Subscription. Then you can do it like this:
class Reader < ActiveRecord::Base
has_many :subscriptions
has_many :magazines, through: :subscriptions
end
class Subscription < ActiveRecord::Base
beongs_to :reader
belongs_to :magazine
end
class Magazine < ActiveRecord::Base
has_many :subscriptions
has_many :readers, through: :subscriptions
end
I have read the Choosing Between has_many :through and has_and_belongs_to_many on the Rails website, however I am a bit confused since I have a different case to the ones given on the website.
I have two models: Prop and CharacterCostume, and the character's costume can have multiple props associated to it, but a prop doesn't belong to that character and it can be used by any number of characters in the scene, too.
Right now I have has_and_belongs_to_many :props inside my CharacterCostume model, which does exactly what I want it to do: it fetches all the props associated with the costume using a table named character_costumes_props when I call CharacterCostume#props
However the association name is putting me off because of the "belongs to many" part. The costume does not belong to any of the props, so there's no has_and_belongs_to_many :character_costumes inside the Prop model.
I know that it can all function fine without it, but it got me thinking that maybe I should use a has_many :through association, but that requires me to create a superfluous model (it is superfluous, right?) and the model would look like this:
class CharacterCostumeProp < ActiveRecord::Base
belongs_to :character_costume
has_one :prop
end
Also, would has_one instead of belongs_to work here?
I want the code to be as semantic as possible, but I am not sure if this will increase the requirement for resources or decrease performance in some way, since there's an intermediate model.
Are there certain quirks/benefits attached to either approach? Is mine good enough? Or is my thinking completely wrong from what I need to do?
Thanks!
I think you want to use a :has_many, :through because you're going to want to work directly with the relation model - what scene(s), consumed or damaged, etc.
But, the reason it reads funny to you is that, for the most part, has_many and belongs_to don't really mean what they mean in English. What they really mean is "They have the foreign keys" and "I have the foreign key", respectively; the exception being the :dependent => :destroy behavior.
That still doesn't really help with has_and_belongs_to_many, since you're then saying, "They have the foreign keys and I have the foreign keys` - except that you can think of it sort of adding a new part both to "I" and "They" that happens to be the same part for each, and has those keys.
Does that help?
The single most important question you must ask yourself when deciding between HABTM and has_many :through is this:
Do I want to store any information specific to the association?
Example 1: magazine subscriptions
A many-to-many relationship between readers and magazines might conceivably be structured as a HABTM or a has_many :through. However, the latter makes far more sense in this case because we can easily think of information specific to the association that we might want to store.
A reader is related to a magazine through a subscription, and every subscription can be described by fields such as price, starting date, issue frequency and whether it's active or not.
Example 2: tags
The relationship between an existing Tag model and, say, an Article model is clearly of the many-to-many kind. The fact that one particular tag has been associated to any particular article must have no influence on whether the same tag will be able to be similarly associated to other articles in the future.
But, differently from the previous example, here the association itself is all the information we need. We just need to know which tags are associated to any given article. It doesn't matter when the association was formed. It doesn't matter how long it lasted.
It may matter to us how many articles a tag is associated with. But that information is stored in the Tag model since it's not specific to an association. There is even a Rails feature that takes care of that called counter_cache.
has_one wouldn't work, you'd need belongs_to
it is not superfluous if you have logic in your association model
has_and_belongs_to_many is good enough for you
See example below
class Student
has_and_belongs_to_many :courses
has_many :teachers, through: :courses
end
class Teacher
has_many :courses
has_many :students, through: :courses
end
class Course
has_and_belongs_to_many :students
belongs_to :teacher
def boring?
teacher.name == 'Boris Boring'
end
end
In the example above, I make use of both versions. See how Course would have its own logic? See how a class for CourseStudent might not? That's what it all comes down to. Well, to me it is. I use has_many through for as long as I can't give a proper name to my association model and/or the model doesn't need extra logic or behavior.
I have one model say user, that can live in multiple towns (represented as another model). If I create a new user I have to choose (and edit) the different towns that they live in. Due to time constraints, I often end up with a "hackyier than I would like" solution involving something like: http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off.
Any nice solutions that are popular with SO?
cheers...
Slothishtype
The has_and_belongs_to_many association was built for this very situation. Here is the documentation on it: http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_and_belongs_to_many
Otherwise, if you need to store information abotu the association itself (fields that would not exist in the city table or the user table, but in between), you might just want to set up two, parallel 'has_many_through' associations, and set up a seperate 'user_city' table. So it would be in the user table
has_many :user_cities
has_many :cities, :through => :user_cities
and in the cities table
has_many :user_cities
has_many :users, :through => :user_cities
Then, you CAN just call: user.cities, and get a list of the cities the user lives in.
Rails has a has_one :through association that helps set up a one-to-one association with a third model by going through a second model. What is the real use of that besides making a shortcut association, that would otherwise be an extra step away.
Taking this example from the Rails guide:
class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ActiveRecord::Base
belongs_to :account
end
might allow us to do something like:
supplier.account_history
which would otherwise be reached as:
supplier.account.history
If it's only for simpler access then technically there could be a one-to-one association that connects a model with some nth model going through n-1 models for easier access. Is there anything else to it that I am missing besides the shortcut?
Logic, OK it might sound a bit weak for this but it would be logical to say that "I have a supplier who has an account with me, I want to see the entire account history of this supplier", so it makes sense for me to be able to access account history from supplier directly.
Efficiency, this for me is the main reason I would use :through, simply because this issues a join statement rather than calling supplier, and then account, and then account_history. noticed the number of database calls?
using :through, 1 call to get the supplier, 1 call to get account_history (rails automatically uses :join to retrieve through account)
using normal association, 1 call to get supplier, 1 call to get account, and 1 call to get account_history
That's what I think =) hope it helps!
I'm surprised no one has touched on Association Objects.
A has_many (or has_one) :through relationship facilitates the use of the association object pattern which is when you have two things related to each other, and that relation itself has attributes (ie a date when the association was made or when it expires).
This is considered by some to be a good alternative to the has_and_belongs_to_many ActiveRecord helper. The reasoning behind this is that it is very likely that you will need to change the nature of the association or add to it, and when you are a couple months into a project, this can be very painful if the relationship were initially set up as a has_and_belongs_to_many (the second link goes into some detail). If it is set up initially using a has_many :through relationship, then a couple months into the project it's easy to rename the join model or add attributes to it, making it easier for devs to respond to changing requirements. Plan for change.
Inverse association: consider the classic situation user-membership-group. If a user can be a member in many groups, then a group has many members or users, and a user has many groups. But if the user can only be a member in one group, the group still has many members: class User has_one :group, :through => :membership but class Group has_many :members, :through => memberships. The intermediate model membership is useful to keep track of the inverse relationship.
Expandability: a has_one :through relationship can easy be expanded and extended to a has_many :through relationship