Explanation on Rails Associations - Rails 4 - ruby-on-rails

i'm new to rails and your help and advise would be much appreciated as i am finding this challenging
Aim: i want the creator of the event to be able to select more than one user as
hosts for a created event (just like how facebook allows the creator of
a page to be be able to select users as admins of a created page). Is the below how my model and schema should be displayed?
i was aiming to build something like this image. Event1 i can select Ian & Jesse as hosts, Event2 i can also select Ian again as a host and select Emma
This is how i imagine it so far to be built (your guidance would be much appreciated):
models
user.rb
has_many events
event.rb
belongs_to user
host.rb
belongs_to user
has_many events
schema
users
name
email
events
title
address
user_id
hosts
user_id
event_id

Started writing this as a comment but realised it was getting too wordy.
your model is broken ... an event has many users .. it doesn't belong_to a single user.
What you have is a many to many relationship between users and events which needs resolving through a join table (aka associative/junction table). You have gone some way to resolving this with the hosts table though this goes against the rails convention.
What you want is something like:
models
user.rb
has_and_belongs_to_many :events
event.rb
has_and_belongs_to_many :users
and create a join table that references the two models
users table
name
email
events table
title
address
events_hosts table
user_id
event_id
The rails convention is for the join table to be named by joining the two names of the tables it is joining lexically ordered - i.e. events before hosts, concatenated together to give events_hosts.
Alternatively, you can also create a join model if you prefer:
EventHost
belongs_to :user
belongs_to :event
and modify the has_and_belongs_to_many to has_many :event_hosts in the other two models - the database schema will remain the same.

Related

[Rails][Postgres] How to order users by most posts in a category?

I am struggling to achieve this
I have 3 models,
User (has_many :posts)
Post (Belongs_to :user, HABTM :categories)
Category (HABTM :posts)
Now, let's say I want to find and order all users who have submitted most posts in a category, how do I achieve this.
Eg. For category 'Fashion' I want to fetch & order users by number of user's posts in fashion.
Desired result should give,
Mark (7 posts in fashion)
Dave (5 posts in fashion)
Carla (4 posts in fashion)
.. so on
Note: Would prefer a solution that is compatible with postgres
First of all, I think the has_and_belongs_to_many relationship for your purpose (or maybe almost any purposes) is inappropriate (or at least inconvenient) in this case. You should use has_many :through instead.
Why? Because has_and_belongs_to_many is not designed to achieve anything other than the bare-minimum basics, like what you want to do. For more in-depth justifications (of why you should use has_many :through for almost any many-to-many relationships), see, for example, "Why You Don't Need Has_and_belongs_to_many Relationships" by Flatiron School and
"Create a many-to-many ActiveRecord association in Ruby on Rails with has_many :through and has_and_belongs_to_many" at Development Simplified. For your reference, "Migration path from HABTM to has_many :through" by Christian Rolle may be helpful, which gives a migration guide for it.
Now, suppose you have migrated your model to "has_many :through". Then, in Rails 5+, which supports left_joins, (I think) the following will give the ordered User Relation based on the number of Posts each User has in a specified Category:
User.left_joins(:posts).
left_joins(posts: :post_category_joins).
where('post_category_joins.category_id = ?', YOUR_CHOSEN_CATEGORY_ID).
group(:id).
order('COUNT(post_category_joins.post_id) DESC')
where post_category_joins is your (chosen) join table name between Post and Category and YOUR_CHOSEN_CATEGORY_ID is the Category ID of your specified Category.
This answer is based on a Stackoverflow answer to a has_many relationship case.

Rails associations - orders

So I have been trying to create a dummy application to try and learn Rails. The app I thought I could create is a coffee ordering app for a group of people in work.
So the website will have many users.
A user can create a coffee_order.
A coffee order contains orders for other individual users.
Each user can have one or more coffee_shop_items (e.g. latte,
cappuccino,danish, muffin, etc)
A coffee order also has an assignee, this is the person who is tasked
with going and getting the order.
So as a user, I create a coffee order, select an assignee, add users to the order, and add one or more coffee shop items to each user,
I am really struggling with how the database should be, and what the associations need to be, along with any join tables?
I am also trying to use nested attributes for the form entry.
Thanks in advance for help.
Update with some code I have tried to create a coffee order:
#coffee_order = CoffeeOrder.new(coffee_order_params)
params[:coffee_order][:user_coffee_orders_attributes].each do |user_order|
order = #coffee_order.user_coffee_orders.new(user_id: user_order[1][:user_id].to_i)
user_order[1][:coffee_shop_items].each do |item|
coffee_shop_item = CoffeeShopItems.find(item) if item != ""
# this line fails! see error below
#coffee_order.user_coffee_orders.coffee_shop_items << coffee_shop_item if coffee_shop_item != nil
end
end
error:
NoMethodError (undefined method `coffee_shop_items' for #<UserCoffeeOrder::ActiveRecord_Associations_CollectionProxy:0x42c6180>):
The coffee_shop_items belong to the order, not the user. After all, a user could probably create another order another day? You should probably also check out the rails documentation, which, IIRC actually contains a walk-through of a shopping cart application.
User has_many :coffes_orders
User has_many :coffee_orders_he_needs_to_get, class_name: "CoffeeOrder", foreign_key: "assignee_id"
CoffeeOrder belongs_to :user
CoffeeOrder belongs_to :assignee, class_name: "User"
CoffeeOrder has_and_belongs_to_many :coffee_shop_items
Coffee_shop_items has_and_belongs_to_many :coffee_orders

User Model with Listings Rails - How to reach?

I have a Boat Model and its Models such as Brand, Model and Year. I have also User model and I would like to connect them by adding migrations to User model of boat_id and I added belongs_to :boat and has_many :boats to User model. But I can not reach User.first.boat.name from the console even though I am able to reach Boat.first.brand.name.
When I try User.first.boat.name. Console gives an error saying;
NoMethodError: undefined method `boat' for #<User:0x0000000665dc30>
Btw: Boat Model includes model_id brand_id and year_id.
EDIT1:
Or should i remove Boat model and add model_id brand_id and year_id to User model directly.
EDIT2:
I would like to be able to reach User.first.boat.brand.name or User.first.boat.year.nameor User.first.boat.model.name
EDIT3:
Every boat has one brand, year and model. But user can have many boats
EDIT4:
What i will do is;
User can sign up and login
Then User press the link list my boat.
He/she saves the boat then the page renders to User Profile
In the User profile I do not know how to get current user boat name year etc. That is why I am confused. Sorry for the misunderstanding
I think you're confused about how Rails associations work in conjunction with how they are stored in the database. If a User can have many boats, then the foreign key needs to be on the boats table. Currently you have boat_id in the users table, this should be removed and a user_id column needs to be added to the boats table as per Matt's answer.
Reference
To achieve what you're trying to do, you'll need to setup your models in the following manner:
class User
has_many :boats
...
end
class Boat
belongs_to :user # table has a user_id column
...
end
Then you can access a boat's brand using user.boats.first.brand.name
Run rails generate migration, then fill in the change method as follows:
def change
add_column :boats, :user_id, :integer
end
Then run rake db:migrate.
You user model has_many boats, so you need the boats table to refer to users. It's probably worth reading the Rails guide for ActiveRecord associations to get a better feel for how this works: http://guides.rubyonrails.org/association_basics.html#the-has-many-association

Forem gem: how to link a forum to other models

I have groups (Group model) in my app, which represent groups of people.
I want each group to have its own forum.
Should I just have the forum id in the groups table? It doesn't feel right. If I did it myself, the forum would have a polymorphic association to a "forumable" element (groups in this case, but I have other models that would need a forum).
Any opinions on what I should do? Modify the gem to fit my needs, or just have the forum_id in my models that need a forum? Or another solution maybe?
I'm the guy who started Forem (its the volunteers who did most of the hard work, though!), I think I can answer this question.
If you want only certain groups to have access to one and only one forum then you can put the forum_id field on the groups table and do it that way. What you can do then is override the can_read_forem_forum? method in your User model to perform a permission check for that user:
def can_read_forem_forum?(forum)
groups.where(:forum_id => forum.id).any?
end
This is used in Forem's ability model to determine whether or not a person can access a forum. What this method is going to do is that it will only return groups for that user that have link that specific forum. If there are any, then it's known that the user can access that forum.
Now if you're going the other route where a group may have access to many forums, well then you'd define a joins table between groups and forem_forums (called forum_groups) and define it as an association in your Group model like this:
has_many :forum_groups
has_many :forums, :through => :forum_groups, :class_name => "Forem::Forum"
You would need to also define a new model inside your application for this forum_groups association, it would be called ForumGroup and go a little like this:
class ForumGroup < ActiveRecord::Base
belongs_to :forum, :class_name => "Forem::Forum"
belongs_to :group
end
We're doing it this way so you have an easy way to manage the associations between forums and groups. If you did has_and_belongs_to_many, it generally only provides a gigantic pain in the ass when you want to delete one specific record from that join table.
Now, with that all nicely set up, the method you want to define in your User model is this one:
def can_read_forem_forum?(forum)
groups.joins(:forums).where("forem_forums.id = ?", forum.id).any?
end
Same thing, except this time we find all the groups that are linked to a specific forum through that association we set up earlier. This will do an INNER JOIN on the forum_groups table, and then another on the forem_forums table, getting the data required.
I hope this helps you, and thanks for using Forem!

Modeling polymorphic blog posts

Say I have a blog_posts table. But, the users writing posts belong to groups, and each group has its own blog.
For example:
Say, a user belongs to 3 groups: Marketing, Project Alpha, Administrators
He creates a blog post, but want that post to appear in the "Marketing Blog" and on the "Project Alpha" blog.
What would be the best way to model this?
Would it be a bad idea to have a field in the blog_posts table like: group_ids
and store a comma-delimited list of ids: 3,7 (where Marketing Group =3, and Project Alpha=7)
or should I create another table and store blog_posts id and groups?
Thanks.
Building on #minitech's answer i'd have 3 model's and 5 db tables
Models
rails g model <model_name>
Post (must have column user_id for FK)
User
Group
Additional tables
rails g migration <table_name>
groups_posts (columns group_id and post_id only)
groups_users (columns group_id and user_id only)
Then in your models configure the relations as follows
class Post < ActiveRecord::Base
has_and_belongs_to_many :groups
belongs_to :user
end
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
has_many :posts
end
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :posts
end
Then as per your example the code becomes:
user = User.find(<id of user>)
post = Post.create(:title => 'foo', :content => 'bar', :user => user)
post.groups << Group.find(3)
post.groups << Group.find(7)
I would recommend creating one table to hold all posts for all blogs, then have a table to store all the blogs' information, then a table to store all the blogs' posts, referenced by ID. Then you just add, for example:
Into posts: ID=(auto increment value), Title='The title', Content='The content'
Into blog posts: blogID=(the ID of blog #1 to insert the post into), postID=(the auto increment value from last time)
Into blog posts: blogID=(the ID of blog #2 to insert the post into), postID=(the auto increment value from last time)
That way, the post is linked in so when it is edited or removed changes are reflected on all blogs, and you can also get a quick list of all blogs a post is in using something like SELECT * FROM blogPosts WHERE postID=id_of_post.
Using comma delimited lists as a way around making all data items atomic is a bad idea.
It's basically a way of taking data that is not in first normal form and transfomring so that it looks like first normal form data even though it's not.
If you do this, for certain queries you will have to do a full scan of one of the tables instead of an indexed lookup. This will be monstrously slow, and get worse as data volume increases.
A better solution is to have a junction table that relates group_id to user_id.
Create Table Group_user
(group_id,
user_id primary key is (gorup_id, user_id))
Then do a three way join when you want all the data together. This is the basic way you model a many-to-many relationship.

Resources