Topic: Passing conditions from multiple controllers to one Model find method

If I have a find method in a Model, is there a way to specify different conditions from the controllers which access it?

For example, I have a bunch of events and in the case of artists or venues I just want to retrieve events specific to them. However, I also have a towns controller and want to retrieve any events within a certain distance of that town, so I need to pass some conditions to the find method. What is the best way to go about doing this?

Thanks in advance...

Models (needs an option to take ':conditions' somehow?):

class Event < ActiveRecord::Base
  belongs_to :artist
  belongs_to :venue

  def self.find_upcoming
            find(:all,
                 :include => [:artist, {:venue => :town}],
                 :order => "events.date",
                 :limit => 30)
  end
end

class Artist < ActiveRecord::Base
  has_many :events
end

class Town < ActiveRecord::Base
  has_many :venues
  has_many :events, :through => :venues
end

class Venue < ActiveRecord::Base
  belongs_to :town
  has_many :events
end


Artist/Venue Controllers:
  def show
    @artist = Artist.find(params[:id])
    @events = @artist.events.find_upcoming
  end

  def show
    @venue = Venue.find(params[:id])
    @events = @venue.events.find_upcoming
  end


Towns Controller (Needs refactoring with the Events model to be able to pass through the conditions)
  def show
    @town = Town.find(params[:id])

    @distance = 30000 #will eventually be set by param
    @xmax = @town.x + @distance
    @xmin = @town.x - @distance
    @ymax = @town.y + @distance
    @ymin = @town.y - @distance

    @events = Event.find(
             :all,
             :include => [:artist, {:venue => :town}],
             :conditions => ["towns.x BETWEEN ? AND ? AND towns.y BETWEEN ? AND ?", @xmin, @xmax, @ymin, @ymax],
             :order => "events.date",
             :limit => 30
              )
  end

Re: Passing conditions from multiple controllers to one Model find method

Try using named scopes. It makes it easier to reuse find options.
http://api.rubyonrails.org/classes/Acti … ml#M002120

class Event < ActiveRecord::Base
  named_scope :upcoming, :conditions => ['date >= ?', Date.today]
  named_scope :nearby, lambda {|xmin, xmax, ymin, ymax| {:include => [:venue => :town], :conditions => ["towns.x BETWEEN ? AND ? AND towns.y BETWEEN ? AND ?", xmin, xmax, ymin, ymax]}}
end

class ArtistsController < ApplicationController
  def show
    @artist = Artist.find(params[:id])
    @events = @artist.events.upcoming
  end
end

class VenuesController < ApplicationController
  def show
    @venue = Venue.find(params[:id])
    @events = @venue.events.upcoming
  end
end

class TownController < ApplicationController
  def show
    @distance = 30000 #will eventually be set by param
    @xmax = @town.x + @distance
    @xmin = @town.x - @distance
    @ymax = @town.y + @distance
    @ymin = @town.y - @distance
   
    @town = Town.find(params[:id])
    @events = @venue.events.upcoming.nearby(@xmin, @xmax, @ymin, @ymax)
  end
end

Vincent Woo Ruby on Rails Blog

Re: Passing conditions from multiple controllers to one Model find method

Ah yes, I'd heard about named scopes but never looked into them much - very handy!

One further question though - using named scopes appears to be quite a bit slower (more than 3x) than a standard find, is there any particular reason why?

Re: Passing conditions from multiple controllers to one Model find method

This is purely based on observation but it seems that passing the variables to the Model slows it down for some reason...

i.e. it seems slower to use this:

@events = Event.upcoming.nearby(@xmin, @xmax, @ymin, @ymax)

than this:
@events = Event.upcoming.find(:all, :conditions => [ "towns.x BETWEEN ? AND ? AND towns.y BETWEEN ? AND ?", @xmin, @xmax, @ymin, @ymax ])

I have no idea why, but I would be interested if anyone can shed any light...

Re: Passing conditions from multiple controllers to one Model find method

Sorry, I'm not sure why it is slower. Out of curiosity, would it speed up if you remove the lambda in the nearby named_scope and hard code xmin, xmax, ymin, ymax?

Vincent Woo Ruby on Rails Blog

Re: Passing conditions from multiple controllers to one Model find method

Are your named scope and the standard find really the same? If you use the named scope suggested by vwoo, there is a :include => [:venue => :town] in it. In the find you said to be faster, there is not.

It's the nature of a forum to contain lots of information. That is because old threads don't get deleted:
Tutorials: http://railsforum.com/viewforum.php?id=20
Extremely Powerful Tool: http://railsforum.com/search.php

Re: Passing conditions from multiple controllers to one Model find method

Good point, I forgot about the include option. It is in the standard find in the original post so I'm assuming mooch took that into account.

But that suggests that you try using :select => 'events.*' in the nearby named_scope. That might help but it is hard to say.

Try looking at the logs to see what SQL is being generated for named scope and standard find.

Vincent Woo Ruby on Rails Blog

Re: Passing conditions from multiple controllers to one Model find method

Hi guys! I've read the topic and i would say that it helped me alot. But i have a question. How can i change this code using named_scope?

 def show
    @order = Order.find(params[:id])
    @line_items = LineItem.find(:all,:conditions => {:order_id => @order} )
end

I want to pass @order variable to the order_id. Order and LineItem are different models.

Thanx
Kostas

Last edited by little_kostas (2009-08-12 13:01:15)

Re: Passing conditions from multiple controllers to one Model find method

Kostas,

Assuming you have the proper associations set up between Order and LineItem, you should be able to rewrite that as this (although it doesn't really utilize named_scope, but rather the basics of associations):

def show
  @order = Order.find(params[:id])
 
  # you can call this in the view instead even
  @line_items = @order.line_items
end

If not, read up here about associations:
http://guides.rubyonrails.org/association_basics.html

Chris