Named scopes are a convenient way to filter record lists. When we want all future bookings of a given event, we can define a future
scope for our booking:
class Event
has_many :bookings
end
class Booking
named_scope :future, lambda {{ :conditions => ['date >= ?', Date.today] }}
end
It gets tricky when you want a form to edit all future bookings of an event using nested forms. Nested forms weren't designed with scopes in mind, they only play nice with has_many
associations.
Here is a little hack to make nested forms play along. Write your has_many
association like this:
class Event
attr_accessor :bookings_filter_sql
has_many :bookings, :conditions => '#{@bookings_filter_sql}'
end
The single quotes around the conditions are not a typo. It's to prevent Ruby from interpolating the string at compile time.
You can now set the bookings_filter_sql
attribute of an event to an SQL condition. All bookings of that event instance will then be scoped to that condition:
class EventController
def update
@event = Event.find(params[:id])
@event.bookings_filter_sql = "date >= '#{Date.today.to_s(:db)}'"
@event.attributes = params[:event]
@event.save!
end
end
I'd like to recommend two improvements to this hack. First, you need to be super careful to not allow SQL injections when bookings_filter_sql
gets directly piped into has_many
. Better make that attribute a getter that sanitizes the SQL:
class Event
has_many :bookings, :conditions => '#{bookings_filter_sql}'
attr_writer :bookings_filter
def bookings_filter_sql
self.class.send(:sanitize_sql_for_conditions, @bookings_filter)
end
end
You can now set the filter with all the sugar you're used to:
@event.bookings_filter = ['date >= ?', Date.today.to_s(:db)]
Secondly, the bookings
association as written above only works when a filter is set. So let's give it a alternate condition that is always true (like '1=1') for that case:
class Event
has_many :bookings, :conditions => '#{bookings_filter_sql}'
attr_writer :bookings_filter
def bookings_filter_sql
self.class.send(:sanitize_sql_for_conditions, @bookings_filter || '1=1')
end
end
Was this post helpful to you? Then let us know!