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