I am making a Twitter clone using rails 4 just for practice. When a user is logged in, on the timeline I only want to display tweets of the people they follow (friends) and their own tweets in DESC order. I'm using tweets#index as my timeline. Currently I am displaying all tweets in the database to the user:
def index
#tweets = Tweet.all.order("created_at DESC")
end
I added an instance variable called #user_friendships that contains the current logged in users friends, witch I can then iterate through and access their tweets. Here is what it now looks like:
def index
#user_friendships = current_user.user_friendships.all
#tweets = Tweet.all.order("created_at DESC")
end
I don't want to loop through user_friendships and grab it's friend and then tweets in the view as well as loop through the current_users tweets.
In the controller, I want to have one instance variable that contains the tweets of both the current_user and each friends tweets in user_friendships...so in the View I only have to iterate through the new array.
Model Code
### User.rb
has_many :tweets
has_many :user_friendships
has_many :friends, through: :user_friendships
acts_as_voter
def to_param
username
end
### user_friendship.rb
belongs_to :user
belongs_to :friend, class_name: 'User', foreign_key: 'friend_id'
### tweet.rb
belongs_to :user
def index
#tweets = current_user.related_tweets.sort_by{|t| t.created_at}.reverse!
end
in model User
class User < ActiveRecord::Base
# ...
def related_tweets
friends_tweets = friends.map{|f| f.tweets}
return (tweets.to_a + friends_tweets).flatten
end
# ...
end
or other solution if you dont want to work with arrays
def index
#tweets = Tweet.for_user(current_user).order(cretead_at: :desc)
end
in model Tweet
class Tweet < ActiveRecord::Base
# ...
def self.for_user user
me_and_friends = [user.id] + user.friends.map(&:id)
where(:user_id => me_and_friends)
end
# ...
end
You didn't show the relations you have between tweets and users, but I assume you've got user has many tweets. Then, you can do something like this:
#tweets = Tweet.where(user_id: [current_user.id].concat(current_user.user_friendships.all.map(&:id)))
You can do something like this:
def index
#tweet_wall = Tweet.all.where(user_id: current_user.id).where(relationship between user and friend)
end
Second condition would depend on your model.
Related
Let me show an example:
I have 2 models:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
scope :created_in, ->(start_date, end_date) { where(created_at: start_date..end_date) }
end
What I want is to get users that created post during a specific period:
users = User.includes(:posts).joins(:posts).merge(Post.created_in(start_date, end_date))
Is it somehow possible to cache posts that are in the where clause? So after I do
users.first.posts
it will show me exactly those posts that match the condition without producing any additional queries.
No, I don't think this is possible. Depending on the context, what you can do is to do a lookup table which you memoize / cache. Something like
User.all.each do |user|
posts = posts_by_user_id[user.id]
end
def posts_by_user_id
#_posts_by_user_id ||= posts.group_by(&:user_id)
end
def posts
Post.created_in(start_date, end_date)
end
I am using Rails as a JSON API. My database has the following structure: the City model has_many Users, which in turn has_many Businesses.
When I send a GET request to businesses#index, I want Rails to return all the businesses in a given city, not just for a given user. What's the best way to do this?
I've already tried the given code below as a first pass, which is returning an internal server error (500).
def index
#city = City.find(params[:city_id])
#users = #city.users
#businesses = #users.businesses
render json: #businesses
end
Can you try this?
Because of the #user is an array. so the error will occuring.
# city.rb
class City < ApplicationRecord
has_many :users
end
#user.rb
class User < ApplicationRecord
belongs_to :invoice
has_many :businesses
end
#business.rb
class Business < ApplicationRecord
belongs_to :user
end
# your controller
def get_business
#city = City.find(params[:id])
#business = #city.users.includes(:business)
render json: #business
end
Add a new relationship to your City model:
has_many businesses, through: users
Then when you have a specific city you can get all businesses:
#city.businesses
You might also want to try starting with the Business model to make the query.
Business.joins(user: :city).where(cities: { id: params[:city_id] })
joins uses the association names
where uses the table names
I am implementing a basic tagging feature to my app. New to rails.
I have a listings model and I am working in the listings_controller # index. When the user goes to the listing page and sets the :tag param I want #users only to hold the users which match the tag.
So if they goto www.example.com/listings?tag=foo only pages which have been tagged with foo are loaded. This is what I have come up with so far but there is several problems with it.
def index
if params[:tag]
#id = Tag.where(:name => params[:tag]).first.id
#listingid = Tagging.where(:tag_id => #id)
#listingid.each do |l|
#users = User.find(l.listing_id)
end
else
#users = User.all
end
end
I am not sure how to loop and add each found user to #users. I think I may be going about this whole thing the wrong way.. My tag/tagging models look as follows:
tag.rb
class Tag < ActiveRecord::Base
has_many :taggings
has_many :listings, through: :taggings
end
tagging.rb
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :listing
end
Taggings has the following colums:
id, tag_id, listing_id
Tags has the following columns:
id, name
Any guidance would be appreciated, been trying to fix this for a while with no luck.
Trying with
def index
#tag = Tag.where(:name => params[:tag]).first
if #tag
#listings = #tag.listings.includes(:user)
#users = #listings.map{|l| l.user}
else
#users = User.all
end
end
I'm trying to create some kind of activity feed, and I'm having trouble getting the values I need to.
I have a controller with this action
def show
#user = User.find(params[:id])
#ideas = Idea.find(#user)
#lists = List.find(#user)
#li_array = (#lists + #ideas).sort{|a,b| -(a.created_at <=> b.created_at)}
end
I'm new to rails, and I'm trying to put together an array of values from the users of both ideas and lists. But I figured out that I can't do what I'm doing now because it will only pass the current user's id to match an id for Idea/List, but what I need to do is find the user's id column and search through based on #user.
I need to get all values from the users, what is the best method?
models
user.rb
has_many :lists
has_many :ideas, :through => :lists
list.rb
has_many :ideas
belongs_to :user
idea.rb
belongs_to :list
belongs_to :user
Thanks
You can simply do:
#user = User.find(params[:id])
#ideas = #user.ideas
#lists = #user.lists
class User
has_many :posts do
def latest(report_date)
order(:report_date).where('report_date <= ?', report_date).limit(1)
end
end
end
class Post
belongs_to :user
end
I would like to retrieve the records of user with the last post for each user.
I could do this:
users = User.all.map{|user| user.posts.latest(7.weeks.ago).first}.compact
Is there a better way to write this? something like:
users = User.posts.latest(7.weeks.ago).all
if that were valid?
I tend to add something like this. Would it work in your case? It's nice because you can 'include' it in list queries...
class User < ActiveRecord::Base
has_many :posts
has_one :latest_post, :class_name => 'Post', :order => 'report_date desc'
...
end
In practice, you would do something like this in the controller:
#users = User.include(:latest_post)
And then, in the view where you render the user, you could refer to user.lastest_post and it will be eager loaded.
For example - if this was in index.html.haml
= render #users
you can access latest_post in _user.html.haml
= user.name
= user.latest_post.report_date