Rails has_many conditions - ruby-on-rails

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.

Related

Ruby on rails: Has_many self referencial - List objects in view

i'm doing this tutorial http://cobwwweb.com/bi-directional-has-and-belongs-to-many-on-a-single-model-in-rails for doing a parent-child association with multiple parents for each child.
I can already associate parents with childs. But now I don't understand how can I list the parents and their children
This is my PerformanceIndicator model:
class PerformanceIndicator < ActiveRecord::Base
has_many :improvement_actions
has_ancestry
has_many :left_parent_associations, :foreign_key => :left_parent_id, :class_name => 'PerformanceIndicatorAssociation'
has_many :left_associations, :through => :left_parent_associations, :source => :right_parent
has_many :right_parent_associations, :foreign_key => :right_parent_id, :class_name => 'PerformanceIndicatorAssociation'
has_many :right_associations, :through => :right_parent_associations, :source => :left_parent
def associations
(left_associations + right_associations).flatten.uniq
end
end
And this is my PerformanceIndicatorAssociation model:
class PerformanceIndicatorAssociation < ActiveRecord::Base
belongs_to :left_parent, :class_name => 'PerformanceIndicator'
belongs_to :right_parent, :class_name => 'PerformanceIndicator'
end
How can I list the parents and their child like this?
Parent1
Child1
Child2
Parent2
Child1
Child2
Assuming left_associations are the parents of a PerformanceIndicator and right_associations are the children, your scope needs to look like this:
scope :without_parents, -> {
joins("LEFT JOIN performance_indicator_associations ON performance_indicator_associations.left_parent_id = performance_indicators.id")
.where(performance_indicator_associations: {right_parent_id: nil})
}
Now, you should get the following:
pi1 = PerformanceIndicator.create(name: 'parent1')
pi2 = PerformanceIndicator.create(name: 'parent2')
ci1 = PerformanceIndicator.create(name: 'child1')
ci2 = PerformanceIndicator.create(name: 'child2')
pi1.right_associations << ci1
pi1.right_associations << ci2
pi2.right_associations << ci2
PerformanceInficator.withour_parents.each do |parent| # => [p1, p2]
# do stuff with parent
parent.right_associations.each do |child|
# do stuff with child
# assume child is ci2
child.left_associations # => [p1, p2]
end
end

How to write this MYSQL query with multiple JOIN and OR in RoR ActiveRecord?

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.

Ruby on Rails: Is it right how I coded my Category-Tree (Is it ruby-like?)

I am new to Rails and just got my category tree working. Now I am not sure if what I have done is "ruby conform" or "ruby-like". I come from PHP and have to change some of my habits but this is not easy. I just want to check if I am on the right way.
The structure is done by the scaffold-command so I guess its correct.
So obvisoulythere is a model-class which is called Category and inherits from ActiveRecord::Base. This model has the following attributes/database fields:
*id,media,image,small_image,clicks,parent,active,description,name,created_at,updated_at*
This is the content of my model-class:
class Category < ActiveRecord::Base
has_many :articles,
:foreign_key => 'category'
belongs_to :parent_object,
:foreign_key => "parent",
:class_name => "Category"
has_many :children,
:foreign_key => "parent",
:class_name => "Category",
:order => "name ASC",
:dependent => :delete_all
#tree = Hash.new
#treepart = Hash.new
def self.category_tree
#root_categories = self.find(:all, :conditions => ["parent = ?", 0])
if #root_categories.length >= 1
#root_categories.each do |level|
#tree[level.id] = level.child_loop(level)
end
#tree
end
end
def child_loop(child)
#treepart = { :category => child }
#treepart[:children] = Hash.new
child.children.each do |child|
#treepart[:children][child.id] = child.child_loop(child)
end
#treepart
end
end
Categories can be nested therefore I have integrated a self-relating belongs_to and has_many function. I have called the parent *:parent_object* because only :parent does not work. Maybe it is in conflict with the attribute-name.
In the model I collect all categories with the method *category_tree* and *child_loop*. After this call I get an image of the category-tree in form of a hash.
Category.category_tree
I do this directly in the Articles´ *_form.html.erb* and pass it to my helper which is generating the html. Here is the call from the from-template:
<%= build_category_tree(Category.category_tree).html_safe %>
The helper is rendering as follows:
module CategoriesHelper
def build_category_tree(object_tree)
tree = object_tree
#treestring = "<ul>" + self.level_loop(tree) + "</ul>"
end
def level_loop(level)
#levelstring = ''
if !level.nil?
level.each do |id,item|
if item.has_key?(:category) && !item.nil?
#levelstring += "<li>" + item[:category].name + "</li>"
#levelstring += "<ul>" + self.level_loop(item[:children]) + "</ul>"
end
end
end
# in the end, return string to prevent a nil return
#levelstring += ""
end
end
Is this the ruby-way to code, can I shorten or change something completely?
Thanks for your help

ActiveRecord poly Appended Array vs Concatenated Array

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.

has_many association...class id not found

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)}'

Resources