How can I write the code below so that it passes the user.id. Given what I have, it throws Class id not found error. (User has many fights. And Fight belongs to user. User can either be a challenger in the fight, or a challengee in the other.)
has_many :fight_wins, :class_name => 'Fight', :foreign_key => 'challenger_id or challengee_id',
:conditions => ["(challenger_id = ? and challenger_won = ?) or (challengee_id = ? and challenger_won = ?)", self.id, true, self.id, false]
You can use the finder_sql option for complex has_many conditions:
has_many :fight_wins, :class_name => 'Fight', :finder_sql =>
'#{sanitize_sql_array(
"SELECT f.*
FROM fights AS f
WHERE (f.challenger_id = ? AND f.challenger_won = ?) OR
(f.challengee_id = ? AND f.challenger_won = ?)
", id, true, id, false)}'
Related
rails --version
2.3.16
ruby --version
1.8.7
Models:
class AToB
belongs_to :a
belongs_to :b
default_scope :include => [:a, :b]
end
class A
has_many :a_to_bs
has_many :bs, :through => :a_to_bs
named_scope :twos, :conditions => { :var => 2 }
named_scope :buzzed, :conditions => { :fizz => ['buzz'] }
end
class B
has_many :a_to_bs
has_many :as, :through => :a_to_bs
end
MYSQL query:
SELECT COUNT(DISTINCT a.id), COUNT(DISTINCT c.id)
FROM a_to_b
INNER JOIN a on a.id = a_to_b.a_id
INNER JOIN b on b.id = a_to_b.b_id
WHERE (a.var = 2 AND a.fizz in ('buzz') AND
(b.foo = TRUE OR b.bar = TRUE OR (b.moo = TRUE AND a_to_b.goo = FALSE))
)
Will also need this variation
SELECT COUNT(DISTINCT a.id), COUNT(DISTINCT c.id)
FROM a_to_b
INNER JOIN a on a.id = a_to_b.a_id
INNER JOIN b on b.id = a_to_b.b_id
WHERE (a.var = 2 AND a.fizz in ('buzz') AND
NOT (b.foo = TRUE OR b.bar = TRUE OR (b.moo = TRUE AND a_to_b.goo = FALSE))
)
I've already googled countless simpler examples, read rails docs, etc. to no avail.
Let me try answering this question. For example your models are as follow in Rails:
#a.rb
class A < ActiveRecord::Base
attr_accessible :var, :fizz
has_many :a_to_bs
end
#b.rb
class B < ActiveRecord::Base
attr_accessible :foo, :bar, :moo
has_many :a_to_bs
end
#a_to_b.rb
class AToB < ActiveRecord::Base
attr_accessible :goo, :a_id, :b_id, :a, :b
belongs_to :a
belongs_to :b
end
A typical query for the first one would be:
records = AToB.where("a.var = ? AND a.fizz in (?) AND (b.foo = ? OR b.bar = ? OR
(b.moo = ? AND a_to_b.goo = ?))", 2, 'buzz', true, true, true, false)
And for the second one it would be:
records = AToB.where("a.var = ? AND a.fizz in (?) AND NOT (b.foo = ? OR b.bar = ? OR
(b.moo = ? AND a_to_b.goo = ?))", 2, 'buzz', true, true, true, false)
The answer is true assuming the queries you mentioned are correct.
This kinda worked:
scope = A.twos.buzzed
scope.count(:joins => { :b => :a_to_b }, :conditions => "b.foo = TRUE OR b.bar = TRUE OR (b.moo = TRUE AND a_to_b.goo = FALSE)")
Going to test a bit more before I'm sure.
Okay, at the moment I have the following in my model:
has_many :current_monitorings, :class_name => "Monitoring",
:conditions => proc { [ 'monitorings.created_at > ?', Time.now.midnight ] }
I want to add a condition to this that checks if the outlet is_active attribute is set to false. I tried doing it like this:
has_many :current_monitorings, :class_name => "Monitoring",
:conditions => proc { [ 'monitorings.created_at > ? AND outlet.is_active = ?', Time.now.midnight, 'false' ] }
However, this doesn't work. I'm probably being stupid, but any help is greatly appreciated!
In your sql, outlet.is_active should be outlets.is_active. Assuming is_active is a boolean field, just pass false and not "false":
Try this:
has_many :monitorings
def current_monitorings
monitorings.joins(:outlets).where(
'monitorings.created_at > ? AND outlets.is_active = ?',
Time.now.midnight,
false
)
end
Why does the connections table get updated when I call #user.connections for the following?
Connection Model
class Connection < ActiveRecord::Base
belongs_to :left_nodeable, :polymorphic => true
belongs_to :right_nodeable, :polymorphic => true
# Statuses:
PENDING = 0
ACCEPTED = 1
named_scope :pending, :conditions => { :connection_status => PENDING }
named_scope :accepted, :conditions => { :connection_status => ACCEPTED }
end
User Model
class User < ActiveRecord::Base
has_many :left_connections, :as => :left_nodeable, :class_name => 'Connection', :conditions => {:left_nodeable_type => 'User', :right_nodeable_type => 'User'}
has_many :right_connections, :as => :right_nodeable, :class_name => 'Connection', :conditions => {:right_nodeable_type => 'User', :left_nodeable_type => 'User'}
def connections
self.left_connections << self.right_connections
end
end
If I use:
def connections
self.left_connections + self.right_connections
end
Then the model works ok but I cannot use any of my named_scope methods.
So I guess my questions boils down to...
What is the difference between the "<<" and "+" operator on an ActiveRecord? Why does using "<<" change the database, and using "+" cause named_scope methods to fail?
The model is updated because left_connections is updated with the << method. This makes left_connections = left_connections + right_connections.
arr = [1,2]
arr << [3,4]
arr #=> [1,2,3,4]
-------------------------
arr = [1,2]
arr + [3,4] #=> [1,2,3,4]
arr #=> [1,2]
self.left_connections + self.right_connections is the correct way to return a concatenation. As for your named_scope methods, I couldn't tell you why they're failing without seeing them.
c = "(f.profile_id = #{self.id} OR f.friend_id = #{self.id})"
c += AND + "(CASE WHEN f.profile_id=#{self.id} THEN f.friend_id ELSE f.profile_id END = p.id)"
c += AND + "(CASE WHEN f.profile_id=#{self.id} THEN f.profile_rejected ELSE f.friend_rejected END = 1)"
c += AND + "(p.banned = 0)"
I need this to be used in a has_many relationship like this:
has_many :removed_friends, :conditions => ???
how do i set there the self.id?, or how do i pass there the id?
Then i want to use the will_paginate plugin:
#profile.removed_friends.paginate(:page => 1, :per_page => 20)
Thanks for your help
EDIT:
class Profile < ActiveRecord::Base
has_many :friendships
has_many :removed_friends, :class_name => 'Profile', :through => :friendships, :conditions =>
"(friendships.profile_id = #{self.id} OR friendships.friend_id = #{self.id})"
"AND (CASE WHEN friendships.profile_id=#{self.id} THEN friendships.profile_rejected ELSE friendships.friend_rejected END = 1)" +
"AND (p.banned = 0)"
end
class Friendship < ActiveRecord::Base
belongs_to :profile
belongs_to :removed_friend, :class_name => 'Profile', :foreign_key => "(CASE WHEN friendships.profile_id = #{self.id} THEN friend_id ELSE profile_id END)"
end
Use single quotes to enclose the condition:
class Profile < ActiveRecord::Base
has_many :friendships
has_many :removed_friends, :class_name => 'Profile', :through => :friendships,
:conditions => '
( friendships.profile_id = #{self.id} OR
friendships.friend_id = #{self.id}
) AND
(CASE WHEN friendships.profile_id=#{self.id}
THEN friendships.profile_rejected
ELSE friendships.friend_rejected
END = 1
) AND
(p.banned = 0)'
end
You might want to break this down into a series of named scopes that can be applied in stages instead of all at once. As an example, extract the banned part:
class Friend < ActiveRecord::Base
named_scope :banned, lambda { |*banned| {
:conditions => { :banned => banned.empty? ? 1 : (banned.first ? 1 : 0) }
}}
end
#profile.friends.removed.banned(false).paginate(:page => 1, :per_page => 20)
Using heavy-duty conditions in relationships is bound to cause trouble. If possible, try denormalizing the table, creating derivative columns that have "easy" versions of the data, or other things to make querying it easier.
You really have two relationships here. You have:
A rejected friendship from the profile_id side
A rejected friendship from the friend_id side
I don't know why both sides can reject a friendship, and maybe you need to look at your model for a little bit here (which side is requesting it? Would it be better to consider that the requestor CANCELLED the request instead of saying it was rejected from the profile side?)
At any rate, I would model this as the two separate relationships that they are:
class Profile
has_many :rejected_friendships, :conditions => 'friendships.profile_rejected = 1'
has_many :canceled_friendships, :foreign_key => 'friend_id', :conditions => 'friendships.friend_rejected = 1'
named_scope :banned, lambda do |*banned|
{ :conditions => {:banned => banned.empty? ? 1 : (banned.first ? 1 : 0) } }
end
has_many :rejected_friends, :class_name => 'Profile', :through => :rejected_friendships
has_many :canceled_friends, :class_name => 'Profile', :through => :canceled_friendships
def removed_friends
(self.rejected_friends.banned(false).all + self.canceled_friends.banned(false).all).uniq
end
end
This is somewhat undesirable because removed_friends is not a relationship anymore so you can't do things like Profile.removed_friends.find(:all, :conditions => {:name => "bleh"}) anymore, but this is a pretty complicated case. That condition is quite complex.
Just out of curiosity, does anyone know a better way of building the following collection using named scopes (as opposed to find_by_sql)?
#available = Workflow.find_by_sql(["
SELECT workflows.id FROM workflows
WHERE workflows.project_id = ? AND workflows.status < 5 AND
( workflows.created_by = ? OR workflows.id IN
(
SELECT workflow_id FROM workflow_histories
INNER JOIN workflow_recipients on workflow_histories.id = workflow_recipients.workflow_history_id
WHERE workflow_recipients.recipient_id = ? AND workflow_recipients.recipient_type = ?
)
)", project.id, #current_user.id, #current_user.id , 'USER'])
I haven't tested this, but I think it would work:
named_scope :available, lambda { |user_id, project_id|
{ :select => :id,
:conditions => [ "project_id = :project_id AND status < 5 AND
(created_by = :user_id OR id IN (
SELECT workflow_id FROM workflow_histories
INNER JOIN workflow_recipients ON workflow_histories.id = workflow_recipients.workflow_history_id
WHERE workflow_recipients.recipient_id = :user_id AND workflow_recipients.recipient_type = :recipient_type
)",
{ :user_id => user_id,
:project_id => project_id,
:recipient_type => "USER"
}
]
}
}
(A previous version of my answer breaks the sub-select out into its own query, which I think is unnecessary.)