Hello,
I am fairly new to RoR and I have a question I hope can be solved
elegantly (so many things are, so why not this one too?). I have a
working solution, but I want to be sure I am doing things the "rails
way" (having come from PHP). You could say I am looking for a little
validation (no flames please).
I have a table of units like so:
CREATE TABLE `units` (
`id` int(11) NOT NULL auto_increment,
`number` varchar(255) NOT NULL default '''',
`description` text NOT NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
that has_many :prices like so:
CREATE TABLE `prices` (
`id` int(11) NOT NULL auto_increment,
`price` decimal(10,2) NOT NULL default ''0.00'',
`unit_id` int(11) default NULL,
`created_at` datetime NOT NULL default ''0000-00-00
00:00:00'',
PRIMARY KEY (`id`),
KEY `unit_id` (`unit_id`)
) TYPE=MyISAM;
My goal is to be able to create a unit and set its price at the same
time. Then, when I update a unit, should I change the price, I want
to add a new row and time stamp to the price table.
My unit model looks like this:
-----------
class Unit < ActiveRecord::Base
has_many :prices
has_one :most_recent_price, :class_name => "Price", :order =>
''created_at DESC''
end
-----------
The has_one :most_recent_price lets me grab the most recent price for
display and comparison purposes.
I can currently accomplish my aims in the controller like so:
-----------
class AdminUnitsController < ApplicationController
#snip
def create
@unit = Unit.new(params[:unit])
@unit.prices.create("price" => params[:price])
@levels = Level.find(:all, :order => "LPAD
(`levels`.`elevator_number`,5,\"0\") ASC")
@unit_types = UnitType.find(:all, :order => "`unit_types`.`name`
ASC")
if @unit.save
Unit.find(@unit.id).update_attributes(params[:unit])
flash[:notice] = ''Unit was successfully created.''
redirect_to :action => ''list''
else
render :action => ''new''
end
end
def update
@unit = Unit.find(params[:id])
unless params[:price] == @unit.most_recent_price.price.to_s
@unit.prices.build("price" => params[:price])
end
@levels = Level.find(:all, :order => "LPAD
(`levels`.`elevator_number`,5,\"0\") ASC")
@unit_types = UnitType.find(:all, :order => "`unit_types`.`name`
ASC")
if @unit.update_attributes(params[:unit])
flash[:notice] = ''Unit was successfully updated.''
redirect_to :action => ''show'', :id => @unit
else
render :action => ''edit''
end
end
#snip
end
-----------
and a _form.rhtml like so:
-----------
<%= error_messages_for ''unit'' %>
<!--[form:unit]-->
<p><label
for="unit_number">Number</label><br/>
<%= text_field ''unit'', ''number''
%></p>
<p><label
for="unit_description">Description</label><br/>
<%= text_area ''unit'', ''description''
%></p>
<p><label for="unit_price">Base Price</label><br
/>
$<%= text_field_tag ''price'', @unit.most_recent_price.nil? ?
"0.00" :
@unit.most_recent_price.price %>
</p>
<!--[eoform:unit]-->
-----------
Is this the "rails way"? Is there a better way?
Specifically, is the text_field_tag ''price'' construct the best
way to
show/capture the price value or is there a "rails way" of showing/
capturing the price value and capturing it with params[] that I don''t
know about? If there isn''t, would it be best to move the ternary
operator statement into the model or is kosher to keep it in the view?
Thanks for any input,
Nick
Hi ! 2006/3/9, Nicholas P. Mueller <railsdev@restechservices.net>:> class AdminUnitsController < ApplicationController > def create > @unit = Unit.new(params[:unit]) > @unit.prices.create("price" => params[:price]) > @levels = Level.find(:all, :order => "LPAD > (`levels`.`elevator_number`,5,\"0\") ASC") > @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` > ASC") > if @unit.save > Unit.find(@unit.id).update_attributes(params[:unit]) > flash[:notice] = ''Unit was successfully created.'' > redirect_to :action => ''list'' > else > render :action => ''new'' > end > endHmm, this is too complicated. If you look at your log, you''ll see two updates to the database: SQL INSERT - corresponds to "if @unit.save" SQL UPDATE - corresponds to Unit.find(@unit.id).update_attributes(params[:unit]) You don''t really need the second line - notice you did Unit.new(params[:unit]) ? This has the same effect as an update_attributes to an existing object.> def update > @unit = Unit.find(params[:id]) > unless params[:price] == @unit.most_recent_price.price.to_s > @unit.prices.build("price" => params[:price]) > end > @levels = Level.find(:all, :order => "LPAD > (`levels`.`elevator_number`,5,\"0\") ASC") > @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` > ASC") > if @unit.update_attributes(params[:unit]) > flash[:notice] = ''Unit was successfully updated.'' > redirect_to :action => ''show'', :id => @unit > else > render :action => ''edit'' > end > endThat looks fine.> Is this the "rails way"? Is there a better way?I wouldn''t say there''s a Rails way for this case. Your solution is pragmatic, and it works. I would have probably done it the same way. One thing you might want to investigate. Two times you have this code:> @levels = Level.find(:all, :order => "LPAD > (`levels`.`elevator_number`,5,\"0\") ASC") > @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` > ASC")You should extract that to a filter, like this: class AdminUnitsController < ApplicationController before_filter :load_lookup_data, :except => [:index, :list, :destroy] protected def load_lookup_data @levels = Level.find(:all, :order => "LPAD(`levels`.`elevator_number`,5,\"0\") ASC") @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` ASC") end end Hope that helps ! -- Fran?ois Beausoleil http://blog.teksol.info/
Thanks for the advice! RE: the insert followed by update, that was from some code I had cut/ paste from another project. RE: the filter - I was looking for a way to do something like that! Thanks for pointing it out to me! I''ve written a few projects where I was cutting and pasting the same code blocks all over and thought there was probably a way to do something, but hadn''t yet bothered to look. Thanks so much, Nicholas P. Mueller On Mar 9, 2006, at 9:21 PM, Francois Beausoleil wrote:> Hi ! > > 2006/3/9, Nicholas P. Mueller <railsdev@restechservices.net>: >> class AdminUnitsController < ApplicationController >> def create >> @unit = Unit.new(params[:unit]) >> @unit.prices.create("price" => params[:price]) >> @levels = Level.find(:all, :order => "LPAD >> (`levels`.`elevator_number`,5,\"0\") ASC") >> @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` >> ASC") >> if @unit.save >> Unit.find(@unit.id).update_attributes(params[:unit]) >> flash[:notice] = ''Unit was successfully created.'' >> redirect_to :action => ''list'' >> else >> render :action => ''new'' >> end >> end > > Hmm, this is too complicated. If you look at your log, you''ll see two > updates to the database: > > SQL INSERT - corresponds to "if @unit.save" > SQL UPDATE - corresponds to Unit.find(@unit.id).update_attributes > (params[:unit]) > > You don''t really need the second line - notice you did > Unit.new(params[:unit]) ? This has the same effect as an > update_attributes to an existing object. > >> def update >> @unit = Unit.find(params[:id]) >> unless params[:price] == @unit.most_recent_price.price.to_s >> @unit.prices.build("price" => params[:price]) >> end >> @levels = Level.find(:all, :order => "LPAD >> (`levels`.`elevator_number`,5,\"0\") ASC") >> @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` >> ASC") >> if @unit.update_attributes(params[:unit]) >> flash[:notice] = ''Unit was successfully updated.'' >> redirect_to :action => ''show'', :id => @unit >> else >> render :action => ''edit'' >> end >> end > > That looks fine. > >> Is this the "rails way"? Is there a better way? > > I wouldn''t say there''s a Rails way for this case. Your solution is > pragmatic, and it works. I would have probably done it the same way. > > One thing you might want to investigate. Two times you have this > code: > >> @levels = Level.find(:all, :order => "LPAD >> (`levels`.`elevator_number`,5,\"0\") ASC") >> @unit_types = UnitType.find(:all, :order => "`unit_types`.`name` >> ASC") > > You should extract that to a filter, like this: > > class AdminUnitsController < ApplicationController > before_filter :load_lookup_data, :except => > [:index, :list, :destroy] > > protected > def load_lookup_data > @levels = Level.find(:all, :order => > "LPAD(`levels`.`elevator_number`,5,\"0\") ASC") > @unit_types = UnitType.find(:all, :order => > "`unit_types`.`name` ASC") > end > end > > Hope that helps ! > -- > Fran?ois Beausoleil > http://blog.teksol.info/ > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails