Display first record in rails - ruby-on-rails

Assuming I have a comments model and a posts model,
What code can I use in a view to display the first comment of the post being linked?

assuming:
post = Post.find(params[:id])
and post model contains:
has_many :comments
then you can:
comments = post.comments
first_comment = comments.first

Post.find(123).comments.first
Where 123 is your post ID.

#post.comments.first will normally work. (#post should be set up in your controller method)
However it is good to realise that 'first' means first in the association, which is normally ordered by id. Since ids autoincrement, this happens to be the same as 'first added' or 'earliest comment'. But it doesn't have to be.
If your association for comments specified a different ordering, then first will use this, e.g. if your association looked like this:
has_many :comments, :order=>'rating desc'
Then (assuming the 'rating' field is set up somehow to be some value that represents the average rating) post.comments.first would give you the highest rated comment, not the first to be added.
In that case, assuming your comments model has timestamps, then you'd need to do something like
#post.comments.find(:first, :order=>'created_at asc')

Related

Rails: Order activerecord object by attribute under has_many relation

class Post
has_many :commments
end
class Comment
belongs_to :post
end
I wish to display a list of posts ordered by date of post creation (submitted_at). I also want some post xyz to appear at the top if it has some new comment posted and yet to be reviewed by moderator. We will determine this by a boolean attribute/field at comments level (moderated = 1/0)
I tried
Posts.join(:comments)
.distinct
.order("submitted_at DESC, comments.moderated")
but this excludes posts that have no comments and results aren't sorted as expected. I am sure that we can do this at ruby level, but looking for a way to do this using AR.
For the join, use this:
Posts.join("LEFT JOIN comments ON comments.post_id = posts.id")
Which will include the ones with no comments.
Your sorting seems to suggest you want a count of moderated comments, in which case, try this:
.order("submitted_at DESC, COUNT(comments.moderated)")
Although, you may need to use group in some way too.

How to pass attribute of one model as a param to a different model?

I have a Favorite model and an Article model. The Favorite has an attribute fav_id that is equal to the the id of Article.
I want to create a link_to #article.title and pass a param of :id=>#favorite.fav_id.
favorites controller code:
def show
#favorite = Favorite.find(params[:id])
#article = Article.find(params[:id])
end
view/favorite/show code:
<%= link_to #article.title, article_path(:controller=>:article, :id=>#favorite.fav_id, :action=>'view')
When I load the page, the favorite id is used, which is fine. I just want to pass favorite.fav_id to the #article = Article.find(params[:id]). That way, the link will show the article title instead of a number (fav_id). In the future id like to be able to show #article.description and other attributes too.
I have also considered passing the article attributes to favorite, but this seems like itd be a heavier load on the database so I've so far avoided that
I've also tried a :through => association. Maybe I did it wrong, but :through didn't work when I tried it. Any suggestions?
Rails 4.2.0
Ruby 2.1.5
If your Favorite model has an attribute, pointing to another model (Article), that means, by definition, that there's an association between those models. So you should explicitely set that association in your models.
First of all you need to define the association type. In your case it an be either a has_one association in case an article may correnpond to only one favorite or a has_many association if one article may correspond to multiple favorits (multiple favourities can have the save fav_id).
Then you need to set up your association in both the Article and the Favorite models.
In your app/models/article.rb it will go like this:
has_one :favorite, foreign_key: :fav_id
In your app/models/favorite.rb it will go like this:
belongs_to :article, foreign_key: :fav_id
I recommend you though to simplify that by changing the field name from fav_id to article_id so that you can drop the foreign_key parameter in both definitions.
Make sure to read the API docs for more information on working with associations.
After you set up that association, you can easily use it in the controller.
def show
#favorite = Favorite.find params[:id]
#article = #favorite.article
end
And then just use link_to #article.title, #article in your view.
I cant clearly understand what you are going to get, but this:
`
def show
#favorite = Favorite.find(params[:id])
#article = Article.find(params[:id])
end
Is completely wrong. You are trying to use same :id parameter to find either Article and Favorite. I don`t think this is behavior you are waiting for.
The Favorite has an attribute of .fav_id
Also, why Favorite model has fav_id attribute? Is it Single Table Inheritance? Or what? Rails way would be, for example, Favorite to have article_id.

Ruby on rails implementing "repost" action for Post's model

I have a Userand Post model with the association one-to-many. I tried to implement a repost action, add a link has_and_belongs_to_many through a table reposts.
But I was faced with the following challenges:
1) Post to feed loaded as follows:
followed_users="SELECT followed_id FROM relationships WHERE follower_id = :user ";
replics_posts="SELECT micropost_id FROM replics_users WHERE user_id = (:user)"
reposts="SELECT post_id FROM reposts WHERE user_id = (:user)"
where("user_id IN(#{followed_users}) OR user_id= (:user) OR id IN(#{replics_posts}) OR id in (#{reposts})", user: user);
and sorted by date modified. Repost similarly sorted, from which there is a situation that is repost in the middle feed.
2) No additional effort, followers do not see reposts user.
These problems can be solved through the auxiliary array with the need to fast, but it looks ridiculous and non-optimal solution.
How can I get out of the situation?
P.S. I think the solution can be found by reference in the field "Content" in the Post model on the same field, another object. Then repost action will not need a separate table and will consist only of a new Post object with a pointer to the contents of the original post. But I do not know how to do this in Ruby on Rails.
Thank you for your help!
I corrected as follows:
1) In the Post model added a new field repost_id and reference to yourself:
has_many: reposts, class_name: "Post", foreign_key: "repost_id", dependent:: destroy;
(relation to the model User not changed)
2) Added to Post's controller method repost
def repost
orig_post=Micropost.find(params[:id]);
if(orig_post)
Micropost.create(user_id:current_user.id,
content: orig_post.content,
repost_id:orig_post.id);
respond_to do |format|
format.js
end
end
end
(Do not forget to realize meets both the route and validations creation of the post)
The result is a correct model of behavior actions repost with dependencies and correct display in the feed. But sadly, this approach involves storing duplicate data in the table Posts in the "Content" field.

Find multiple database objects by attribute in Rails?

I have a Track table and a Section table. A track has many sections. Sections are connected to their respective task by the Section's :track_id, which corresponds to the Track's :id attribute.
<% #track = Track.find(params[:id]) %>
<% #sections = Section.find_by_track_id(#track.id) %>
In the code above I'm trying to find multiple sections that share the same :track_id attribute, but find_by_track_id() only returns the first. What's the best way to get all of them?
Thanks!
If your tracks and sections are related in this way, then the best way to relate them is by using the methods that come automatically from Rails' associations.
in this case, I expect in your model files, you have the following:
class Track < ActiveRecord::Base
has_many :sections
end
class Section < ActiveRecord::Base
belongs_to :track
end
Then you can get the sections for a track like this:
#track = Track.find(params[:id])
#sections = #track.sections
You're looking for where, which finds all records where a specific set of conditions are met.
#sections = Section.where(track_id: #track.id)
This is unrelated to your question, but you should set #sections and #track in your controller. As it seems like you're new to Rails, I'd highly recommend reading through the Rails Guides. They will help you immensely on your journey.
EDIT: I was solving for the general question of "Find multiple database objects by attribute in Rails?", which is how to find multiple database objects in the general case. #TarynEast's method is the way to go to find all of the sections for a track, or more generally, all of the objects that belong to the desired object. For the specific case you're asking for above, go with #TarynEast's solution.
Association
To extend Taryn East's answer, you need to look into ActiveRecord Associations.
In your model, if you have the following has_many relationship:
#app/models/track.rb
Class Track < ActiveRecord::Base
has_many :sections
end
#app/models/section.rb
Class Section < ActiveRecord::Base
belongs_to :track
end
This will set up a relational database association between your tracks and sections datatables.
--
Associative Data
The magic of Rails comes into play here
When you call the "parent" object, you'll be able to locate it using its primary key (typically the ID). The magic happens when Rails automatically uses this primary_key as a foreign_key of the child model - allowing you to call all its data as an append to the parent object:
#track = Track.find params[:id] #-> find single Track by primary key
#sections = #track.sections #-> automagically finds sections using the track primary key
This means if you call the following, it will work exactly how you want:
#sections.each do |section|
section.name
end
Where
Finally, if you wanted to look up more than one record at a time, you should identify which ActiveRecord method you should use:
find is to locate a single record by id
finy_by key: "value" is to locate a single record by your defined key/column
where is to return multiple items using your own conditions
So to answer your base line question, you'll want to use where:
#sections = Section.where track_id: params[:id]
This is not the right answer, but it should help you
<% #sections=#track.sections%>
Use find when you are looking for one specific element identified by it's id.
Model.find is using the primary key column. Therefore there is always exactly one or no result.

Deleting a record in a has_and_belongs_to_many table - Rails

I've been looking, and can't find a good answer for how to delete records in a HABTM table. I assume a lot of people have this same requirement.
Simply, I have Students, Classes, and Classes_Students
I want a student to be able to drop a class, or delete the HABTM record that has signed that student up for that class.
There must be a simple answer to this. Does anyone know what it is?
The reason why .destroy or .delete does not work on this situation is due to the missing primary key in the middle table. However, our parent objects have this really cool method called {other_obj}_ids. It is a collection of ids on the left table object, of the right table object. This information is of course populated from our middle table.
So with that in mind, we have 2 object classes (Student, and Classes). Active record magic can generally figure out the middle table if you are not doing anything fancy, but it is recommended to use has_many :through.
class Student < ActiveRecord::Base
has_and_belongs_to_many :classes
end
class Classes < ActiveRecord::Base
has_and_belongs_to_many :students
end
What we can now do in terms of the middle table with this setup...
student = Student.find_by(1)
student.classes # List of class objects this student currently has.
student.class_ids # array of class object ids this student currently has
# how to remove a course from the middle table pragmatically
course = Course.find_by({:name => 'Math 101'})
# if this is actually a real course...
unless course.nil?
# check to see if the student actually has the course...
if student.class_ids.include?(course.id)
# update the list of ids in the array. This triggers a database update
student.class_ids = student.class_ids - [course.id]
end
end
I know this is a little late to answer this, but I just went through this exact situation tonight and wanted to share the solution here.
Now, if you want this deleted by the form, since you can now see how it is handled pragmatically, simply make sure the form input is nested such that it has something to the effect of:
What kind of trouble are you having? Do you have the appropriate :dependent=>:destroy and :inverse_of=>[foo] on your relations?
Let's say a class had a course title. You can do:
student.classes.find_by_course_title("Science").delete
So the proper answer here is to do something like this in your view:
<%= link_to 'Remove', cycle_cycles_group_path(#cycle, cycle), method: :delete %><br />
cycle is from a block the above code is within.
#cycle is an instance variable from the join models controller.
cycle_cycles_group_path is the nested join table "cycles_groups" under the model "Cycle" in the routes.rb file:
resources :cycles do
resources :cycles_groups do
end
end
and the join model controller looks like this:
def destroy
#cycles_group = CyclesGroup.find(params[:id])
#cycle = #cycles_group.cycle
#cycles_group.destroy
puts "cycle: #{#cycle}"
respond_to do |format|
format.html {redirect_to cycle_path(#cycle), notice: 'Training Week was successfully removed!'}
end
end

Resources