Where NOT IN () on a join? - ruby-on-rails

I have the following scope in a Rails model.
class Suggestion < ActiveRecord::Base
has_many :favourites
def self.favoured_by(user)
joins(:favourites).where(favourites: { user_id: user.id })
end
end
That works perfectly. It will return all the suggestions which a particular user has favourited.
How can I retrieve all the suggestions which are either not favourited at all or which are favourited but not by this particular user?
def self.not_favoured_by(user)
# ...
end
My Favourite model looks like this:
class Favourite < ActiveRecord::Base
belongs_to :suggestion
belongs_to :user
end

favorited_suggestions_ids = joins(:favourites).where(favourites: { user_id: user.id }).map(&:id)
return scoped if favorited_suggestion_ids.empty?
where('id not in (?)',favorited_suggestions_ids)

How about this:
def self.not_favoured_by(user)
sql = <<-SQL
SELECT suggestions.* FROM suggestions
WHERE NOT EXISTS
(SELECT id FROM favourites WHERE user_id = #{user.id} AND favourites.suggestion_id = suggestions.id);
SQL
find_by_sql(sql)
end

Related

Caching association that was in where clause

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

Access rails includes with has_many

I have a model called "Match" and a model called "Bet"
class Match < ActiveRecord::Base
...
has_many :bets
end
And my Model Bet:
class Bet < ActiveRecord::Base
attr_accessible :match_id, :user_id, :bet
...
belongs_to :match
belongs_to :user
end
I'm using the following code to select some matches and user's bets together:
#matches = Match.includes(:bets).where("bets.user_id = ? or bets.user_id is NULL", #user.id)
How can I access user bets with this query?
Using this does not work:
#matches.each do |match|
match.bet.bet
...
How to access bet attribute inside match?
Thanks!!
Trying #sevenseacat answer with this code:
#user ||= User.find_by_id(params[:user_id]) if params[:user_id]
if #user
#matches = Match.includes(:home_team, :away_team, :bets).where("bets.user_id = ? or bets.user_id is NULL", #user.id) #.group_by{ |match| match.date.strftime("%d/%m/%y")}
#matches.each do |match|
match.bets.each do |bet|
bet.bet
end
end
end
I've changed it to match.bets.first (I only have 1 bet for each match_id and user_id so it works).
You would access each match's bets by doing simply match.bets inside your block.
If you wanted to iterate over those bets, use another each.
#sevenseacat is right
#match.bet.each { |bet| bet.attr } maybe good for you

How do I count records that satisfy a condition in a associated model?

Okay, what I've got is two models...
Jiraissue:
class Jiraissue < ActiveRecord::Base
# JIRA uses a singular table name for this model
set_table_name 'jiraissue'
has_one :severity
end
Severity:
class Severity < ActiveRecord::Base
belongs_to :jiraissue
end
What I'm trying to do is get a count of all Jiraissues for which jiraissue.severity = "S1"
Now it turns out that the jiraissue table has a column for priority so I can pull this trick in the model...
Jiraissue:
class Jiraissue < ActiveRecord::Base
# JIRA uses a singular table name for this model
set_table_name 'jiraissue'
has_one :severity
def self.count_priority(priority)
where("PRIORITY = ?",priority).count()
end
end
And then in the view do something like...
<%= (1..4).map {
|priority| Jiraissue.biit.bugs.recent.count_priority(priority)
}.inspect %>
How do I do something similar for Jiraissue to get a count_severity method?
This just doesn't work (nor would I expect it to)...
def self.count_severity(severity)
where("severity = ?",severity).count()
end
But I'm totally confused.
Jiraissue.joins(:severities).where(:severities => {:severity => "S1"}).count
model
def self.count_priority(priority)
where("PRIORITY = ?",priority).size
end
controller
def index
#jiraissues = Jiraissue.count_priority('S1')
end
Doesn't it work?

Impossible to save product_id key in has_and_belongs_to_many table ? Key is auto-incremeting

I'm getting confused with the creation of a new link in a has_and_belongs_to_many table.
I think my use of the .build(...) is not correct but I can't find a way to fix it.
I wrote :
#user = User.find(1)
if (params[:product_id])
#user.products.build(params[:product_id])
end
logger.debug "product id is #{params[:product_id]}"
respond_to do |format|
if #user.save
...
And the saved relationship in my table products_users is auto-incrementing ???
Example of the content of my table : (user_id ; product_id) = { (1;16) (1;17) (1;18) ...}
And it create blanck lines in the products table with these new id ... ? Is it a cause of build ?
But in the logger, I saw the correct value of :params[:product_id]... so what did I forgot ? :-s
The model is :
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
belongs_to :group
has_and_belongs_to_many :authors
has_and_belongs_to_many :users
end
Instead of using
#user.products.build(params[:product_id])
try
#user.product_ids << params[:product_id]
I think that may do the trick for you.
This is what worked for me
#user = User.find(1)
if (params[:product_id])
#product = Product.find(params[:product_id])
unless #user.products.include?(#product)
#user.products << #product
end
end

Bulk update attribute on multiple models in ActiveRecord?

I have a simple has_many association, and I want to change an attribute from public to private the associated object. What's the best way to do this:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
end
user = User.first #=> #<User...>
user.posts.count #=> 100
# something like this:
user.posts.bulk_update_attribute("privacy", "private") #=> 1 DB call
I believe you are looking for update_all.
In your example, you'd rewrite it to be something like
Post.update_all("privacy = 'private'", ["user_id = ?", user.id])
Or as #jenjenut233 points out
user.posts.update_all("privacy = 'private'")

Resources