Hi, I''ve got a simple domain model with a list of Stocks where each stock can have 0 or more EarningsReports. I''ve got a controller for Stocks that handles list, create, edit, etc. And I''ve got a similar controller for EarningsReports. The class definition for Stock includes has_many :earnings_report and the class definition for EarningsReport has belongs_to :stock. Because I have a separate controller for each of these two domain objects, I''ve had to code the link_to from a Stock to its EarningsReports as: <%= link_to ''Earnings reports'', :controller => ''earnings'', :action => ''list'', :id => stock %> So far, so good (I think). I had to pass in the id of the selected stock so when I start creating EarningsReports I would know which Stock they belong to. In the list method of earnings_controller, I inserted a line into the scaffolding to retrieve the stock so the template would be able to provide feedback regarding which stock is being worked on: def list @stock = Stock.find(@params[:id]) @earnings_report_pages, @earnings_reports = paginate :earnings_report, :per_page => 10 end This way my earnings_report list template can set up a heading with something like @stock.name <http://stock.name>. So far this is working all right too. The problem is I''m not able to successfully insert an EarningsReport because its stock_id (required for RI) is never set at the time of insertion. I''m not sure what I must do in order to ensure the stock (or its id) is always available while I''m processing its collection of earnings reports. In the Depot example within "Agile Web Development with Rails" on page 101 it says "Notice that we didn''t have to do anything special with the various foreign key fields, such as setting the order_id column in the line item rows to reference the newly created order row. Rails does that knitting for us using the has_many( ) and belongs_to( ) declarations we added to the Order and LineItem models." I think I''ve done the necessary has_many( ) and belongs_to( ) declarations, so I wonder what I''ve missed that''s causing the this error when I attempt to create an earnings_report: Cannot add or update a child row: a foreign key constraint fails: INSERT INTO earnings_reports (`earnings_per_share`, `quarter`, `comments`, `date`, `year`, `stock_id`) VALUES(0.0, 0, '''', ''2005-09-21'', 0, 0) Any advice would be greatly appreciated! Thanks, . . . Phil _______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Phil Puccio wrote:> for EarningsReports. The class definition for Stock includes has_many > :earnings_report and the class definition for EarningsReport has > belongs_to :stock.Typo in code or post? Should be "has_many :earnings_reports".> def list > @stock = Stock.find(@params[:id]) > @earnings_report_pages, @earnings_reports = paginate > :earnings_report, :per_page => 10 > end > > This way my earnings_report list template can set up a heading with > something like @stock.name <http://stock.name>. So far this is working > all right too. > > The problem is I''m not able to successfully insert an EarningsReport > because its stock_id (required for RI) is never set at the time of > insertion. I''m not sure what I must do in order to ensure the stock (or > its id) is always available while I''m processing its collection of > earnings reports.At some point after creating a new @earnings_report object you have to do either: @stock.earnings_reports << @earnings_report or @earnings_report.stock = @stock in order to link the foreign keys. -- We develop, watch us RoR, in numbers too big to ignore.
>> Typo in code or post? Should be "has_many :earnings_reports".Yes, there was a typo in my code, so I''ve changed it to has_many : earnings_reports>> At some point after creating a new @earnings_report object you have >> to do either: >> @stock.earnings_reports << @earnings_report >> or >> @earnings_report.stock = @stockI understand that I must add the new earnings_report to the stock''s collection, but I didn''t think I had to explicitly set the back-reference (as per page 101 "Rails does that knitting for us using the has_many( ) and belongs_to( ) declarations..."). But I still missing the technique that should be used to make sure that the instance variable stock is defined throughout all method invocations of the earnings_controller. Or should I be putting the stock id in a hidden form field? Or should I be putting the stock in the session? Is there a Rails convention for this. It must be a common issue. Thanks! On 9/21/05, Mark Reginald James <mrj-bzGI/hKkdgQnC9Muvcwxkw@public.gmane.org> wrote:> > Phil Puccio wrote: > > > for EarningsReports. The class definition for Stock includes has_many > > :earnings_report and the class definition for EarningsReport has > > belongs_to :stock. > > Typo in code or post? Should be "has_many :earnings_reports". > > > > def list > > @stock = Stock.find(@params[:id]) > > @earnings_report_pages, @earnings_reports = paginate > > :earnings_report, :per_page => 10 > > end > > > > This way my earnings_report list template can set up a heading with > > something like @stock.name <http://stock.name> <http://stock.name>. So > far this is working > > all right too. > > > > The problem is I''m not able to successfully insert an EarningsReport > > because its stock_id (required for RI) is never set at the time of > > insertion. I''m not sure what I must do in order to ensure the stock (or > > its id) is always available while I''m processing its collection of > > earnings reports. > > At some point after creating a new @earnings_report object you have > to do either: > @stock.earnings_reports << @earnings_report > or > @earnings_report.stock = @stock > > in order to link the foreign keys. > > > > -- > We develop, watch us RoR, in numbers too big to ignore. > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Phil Puccio wrote:> >> At some point after creating a new @earnings_report object you have > >> to do either: > >> @stock.earnings_reports << @earnings_report > >> or > >> @earnings_report.stock = @stock > > I understand that I must add the new earnings_report to the stock''s > collection, but I didn''t think I had to explicitly set the > back-reference (as per page 101 "Rails does that knitting for us using > the has_many( ) and belongs_to( ) declarations...").For new earnings reports you still have to say to which stock it belongs. But you can do this by assiging objects to table relationship methods, in the way I''ve shown above, rather than by explicitly assigning foreign key values.> But I still missing the technique that should be used to make sure that > the instance variable stock is defined throughout all method invocations > of the earnings_controller. Or should I be putting the stock id in a > hidden form field? Or should I be putting the stock in the session? > > Is there a Rails convention for this. It must be a common issue.Well for existing EarningsReport objects its stock is always available as earnings_report_instance_object.stock. So when displaying a single earnings report, make the URL id that of the earnings report iteslf: GET host/earnings/show/id def show @earnings_report = EarningsReport.find(params[:id]) @stock = @earnings_report.stock end When listing all earnings reports for a stock, or when creating a new earnings report, the id should be that of the stock: GET host/earnings/list/id def list @stock = Stock.find(params[:id]) @earnings_reports = @stock.earnings_reports end If this is no good you can put stuff in the session, in hidden fields, or use routing to give you more than one id from a URL. -- We develop, watch us RoR, in numbers too big to ignore.
Hi Mark, Thanks for working through this with me.>> When listing all earnings reports for a stock, or when creating >>a new earnings report, the id should be that of the stock: >> >> GET host/earnings/list/id >> >> def list >> @stock = Stock.find(params[:id])>> @earnings_reports = @stock.earnings_reports>> endYes, that''s exactly what I''ve done to get earnings_controller''s list to show the name of the stock in the page''s title. But when the earnings_controller''s create is invoked, @stock is no longer defined (I suppose because the create is invoked on a new instance of earnings_controller). And doing @stock = Stock.find(params[:id]) in create no longer works because there is no longer a :id param. So I need an approach to keeping a reference to the parent object, stock, around for various methods of earnings_controller. This is particularly vexing in light of the book''s claim that "Rails does that knitting for us". I believe the book, so instead of using the session or hidden fields, I hope I can find what piece I''ve left out. Can anyone verify that I shouldn''t have to actively manage keeping alive a reference to a parent object within the methods of the controller for a child object? Thanks! . . . Phil On 9/21/05, Mark Reginald James <mrj-bzGI/hKkdgQnC9Muvcwxkw@public.gmane.org> wrote:> > Phil Puccio wrote: > > > >> At some point after creating a new @earnings_report object you have > > >> to do either: > > >> @stock.earnings_reports << @earnings_report > > >> or > > >> @earnings_report.stock = @stock > > > > I understand that I must add the new earnings_report to the stock''s > > collection, but I didn''t think I had to explicitly set the > > back-reference (as per page 101 "Rails does that knitting for us using > > the has_many( ) and belongs_to( ) declarations..."). > > For new earnings reports you still have to say to which stock it belongs. > But you can do this by assiging objects to table relationship methods, > in the way I''ve shown above, rather than by explicitly assigning foreign > key values. > > > > But I still missing the technique that should be used to make sure that > > the instance variable stock is defined throughout all method invocations > > of the earnings_controller. Or should I be putting the stock id in a > > hidden form field? Or should I be putting the stock in the session? > > > > Is there a Rails convention for this. It must be a common issue. > > Well for existing EarningsReport objects its stock is always available > as earnings_report_instance_object.stock. > > So when displaying a single earnings report, make the URL id that of > the earnings report iteslf: > > GET host/earnings/show/id > > def show > @earnings_report = EarningsReport.find(params[:id]) > @stock = @earnings_report.stock > end > > When listing all earnings reports for a stock, or when creating > a new earnings report, the id should be that of the stock: > > GET host/earnings/list/id > > def list > @stock = Stock.find(params[:id]) > @earnings_reports = @stock.earnings_reports > end > > If this is no good you can put stuff in the session, in hidden > fields, or use routing to give you more than one id from a URL. > > > -- > We develop, watch us RoR, in numbers too big to ignore. > > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails
Phil Puccio wrote:> Yes, that''s exactly what I''ve done to get earnings_controller''s list to > show the name of the stock in the page''s title. But when the > earnings_controller''s create is invoked, @stock is no longer defined (I > suppose because the create is invoked on a new instance of > earnings_controller). And doing @stock = Stock.find(params[:id]) in > create no longer works because there is no longer a :id param. So I > need an approach to keeping a reference to the parent object, stock, > around for various methods of earnings_controller. This is particularlyJust put the stock ID in the create earnings report link URL, which will be a link on either host/stock/show/id or host/earnings/list/id GET/POST host/earnings/create/id def create @stock = Stock.find( params[:id] ) @earnings_report = EarningsReport.new( params[:earnings_report] ) if request.post? @earnings_report.stock = @stock redirect_to :action => ''list'', :id => @stock.id if @earnings_report.save end end -- We develop, watch us RoR, in numbers too big to ignore.
Okay, I''ve found an approach that feels pretty clean -- The earnings_controller list method finds the stock and stores it in the session: def list @stock= Stock.find(@params[:id]) session[:stock] = @stock @earnings_report_pages, @earnings_reports = paginate :earnings_report, :per_page => 10 end Then earnings_controller''s create method retrieves it from the session: def create @stock= session[:stock] @earnings_report = EarningsReport.new(@params[:earnings_report]) @stock.earnings_reports << @earnings_report if @earnings_report.save flash[''notice''] = ''EarningsReport was successfully created.'' redirect_to :action => ''list'', :id => @stock.id <http://stock.id> else render_action ''new'' end end So a bit of Rails magic *is* happening in that I don''t have to explicitly set earning_report''s stock_id to @stock.id <http://stock.id>. The only other addition was to get Stock stored and retrieved within the session: class ApplicationController < ActionController::Base model :stock end Thanks for your help. . . . Phil On 9/21/05, Phil Puccio <poochio-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> > Hi Mark, > > Thanks for working through this with me. > > >> When listing all earnings reports for a stock, or when creating > >>a new earnings report, the id should be that of the stock: > >> > >> GET host/earnings/list/id > >> > >> def list > >> @stock = Stock.find(params[:id]) > >> @earnings_reports = @stock.earnings_reports > >> end > > Yes, that''s exactly what I''ve done to get earnings_controller''s list to > show the name of the stock in the page''s title. But when the > earnings_controller''s create is invoked, @stock is no longer defined (I > suppose because the create is invoked on a new instance of > earnings_controller). And doing @stock = Stock.find(params[:id]) in create > no longer works because there is no longer a :id param. So I need an > approach to keeping a reference to the parent object, stock, around for > various methods of earnings_controller. This is particularly vexing in light > of the book''s claim that "Rails does that knitting for us". I believe the > book, so instead of using the session or hidden fields, I hope I can find > what piece I''ve left out. > > Can anyone verify that I shouldn''t have to actively manage keeping alive a > reference to a parent object within the methods of the controller for a > child object? > > Thanks! . . . Phil > > > > > > > On 9/21/05, Mark Reginald James <mrj-bzGI/hKkdgQnC9Muvcwxkw@public.gmane.org> wrote: > > > > Phil Puccio wrote: > > > > > >> At some point after creating a new @earnings_report object you have > > > >> to do either: > > > >> @stock.earnings_reports << @earnings_report > > > >> or > > > >> @earnings_report.stock = @stock > > > > > > I understand that I must add the new earnings_report to the stock''s > > > collection, but I didn''t think I had to explicitly set the > > > back-reference (as per page 101 "Rails does that knitting for us using > > > > > the has_many( ) and belongs_to( ) declarations..."). > > > > For new earnings reports you still have to say to which stock it > > belongs. > > But you can do this by assiging objects to table relationship methods, > > in the way I''ve shown above, rather than by explicitly assigning foreign > > key values. > > > > > > > But I still missing the technique that should be used to make sure > > that > > > the instance variable stock is defined throughout all method > > invocations > > > of the earnings_controller. Or should I be putting the stock id in a > > > hidden form field? Or should I be putting the stock in the session? > > > > > > Is there a Rails convention for this. It must be a common issue. > > > > Well for existing EarningsReport objects its stock is always available > > as earnings_report_instance_object.stock. > > > > So when displaying a single earnings report, make the URL id that of > > the earnings report iteslf: > > > > GET host/earnings/show/id > > > > def show > > @earnings_report = EarningsReport.find(params[:id]) > > @stock = @earnings_report.stock > > end > > > > When listing all earnings reports for a stock, or when creating > > a new earnings report, the id should be that of the stock: > > > > GET host/earnings/list/id > > > > def list > > @stock = Stock.find(params[:id]) > > @earnings_reports = @stock.earnings_reports > > end > > > > If this is no good you can put stuff in the session, in hidden > > fields, or use routing to give you more than one id from a URL. > > > > > > -- > > We develop, watch us RoR, in numbers too big to ignore. > > > > _______________________________________________ > > Rails mailing list > > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > > http://lists.rubyonrails.org/mailman/listinfo/rails > > > >_______________________________________________ Rails mailing list Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org http://lists.rubyonrails.org/mailman/listinfo/rails