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.
Related
Consider this example in Rails 6.1 with postgresql:
An organization has users & pictures
An item has_and_belongs_to_many pictures and belongs_to an organization
A user has_and_belongs_to_many items and belongs_to an organization, has relationship has_many :pictures, through: :items
pictures belong_to an organization & has_and_belongs_to_many users
pictures can only be viewed by an admin of an organization and those users have have/belong to them. So for a user doing something as simple as current_user.pictures will show them all the pictures they are allowed to view.
(in this example an admin uploads pictures and shares them with certain users)
This is where my question comes in:
Pictures can also be marked as public, which would allow any user to view them in their list. So essentially I want combine organization.pictures.where(public: true) and current_user.pictures (which uses a join table) into one query and also easily add any sql order or pagination.
I could simply use concatenation current_user.pictures + organization.pictures.where(public: true) but that returns an array and then I'd have to use array methods to sort and paginate, which is undesirable.
What other options do I have? I can't seem to get an .or to work either.
Update: A solution I had figured would 'work' originally does and is what I'm going with for now, to pluck the ids from the two joins tables has_many :pictures, through: :items, but I am still curious of a better way;
In the user model:
def viewable_pictures
pictures_ids = pictures.pluck(:id)
organization.pictures.public_to_daycare.or(Picture.where(id: pictures_ids))
end
In this case, I would think the query from the Picture model instead of the organization object. Something like:
Picture.where(public: true, organization_id: organization.id).or(Picture.where(user_id: current_user.id)
I'm wondering if there's such a thing as a best practice for one to many relationships. I'm planning a simple rails app that will have a relational DB with either of the following scenarios:
Scenario A ERD: A user can have zero or many lists, and a list can have zero to many items. I know that this means a user cannot directly create an item. An item can only be created through a list. An item actually belongs to a list, and a list belongs to a user.
Scenario B ERD: A user can have zero or many lists, and the user can also have zero or many items. This means everything is directly associated to the user. Items will be associated to a list due to a foreign key via a list ID.
Is there a best practice to use/not use either scenario?
Thanks in advance!
Scenario A is a better approach.
class User < ActiveRecord::Base
has_many :lists
has_many :items, :through => :lists
end
class List < ActiveRecord::Base
has_many :items
end
How do I meld together getting all of the associated data for all of the records of a given model?
I have the following models:
User --N:1--> Reservation <--1:N-- Concert
So pseudo-code:
Reservation belongs_to User
Reservation belongs_to Concert
User has_many Reservations
User has_many Concerts through Reservations
Concert has_many Reservations
Concert has_many Users through Reservations
How do I make a single big array of everything?
I can get all my Reservations via Reservation.all
I can get the User for a particular Reservation via Reservation.find(25).user
I can get the Concert for a particular Reservation via Reservation.find(25).concert
But how do I get it for all of them? If I do
Reservation.all.each do |res|
res.user.name+","+res.concert.name+","+res.concert.date # etc.
end
Then it will do two new database queries for each reservation as it loops through. For 10 records, it might not matter, but for thousands, it can be very painful. Add to it other associations (e.g. Concert belongs_to venue, User has_one email, etc.)...
Is there any way to say, "Get all of the reservations and the following attached info" so it loads in a single SQL query?
What you're trying to accomplish is called eager loading, and can be done using includes in ActiveRecord. See below:
N + 1 queries problem
Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the includes method of the Model.find call. With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
In your example you could use the following:
Reservation.all.includes(:user, :concert)
It is also a good idea to specify the :inverse_of option for your :belongs_to relations. This optimizes object loading and makes sure that cross-referencing a model will point back to the same object in memory, i.e.:
#user == #user.reservations.first.user # true
More information available here:
If you are using a belongs_to on the join model, it is a good idea to set the :inverse_of option on the belongs_to ...
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
In your example:
# app/models/reservation.rb
belongs_to :user, :inverse_of => :reservations
belongs_to :concert, :inverse_of => :reservations
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!
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.