-->

Instance variable not set with redirect

2020-07-16 12:14发布

问题:

What would cause my instance variable @product to not be set/passed for the redirect. Product is an ActiveModel object, not ActiveRecord. to be more specific, the @product variable is not appearing in the redirect_to(new_services_path) or redirect_to(home_path) pages. As the @product variable need to populate a form in my footer that is on every page.

Application_controller:

class ApplicationController < ActionController::Base 
  before_filter :set_product

  private

  def set_product
    @product ||= Product.new
  end
end

Product_controller:

  def new
  end


 def create
    @product = Product.new(params[:product])

    if  @product.category == "wheels"
      redirect_to(new_services_path) 
    else
      redirect_to(home_path) 
    end
  end

Issue related to this original post.. Passing variables through multiple partials (rails 4)

回答1:

Instance variables are not passed on a redirect.

Consequently, you have no @product object at the time you are reaching the before_filter and so you're just creating the new and empty Product object each time.

ActiveModel objects can't persist from session to session, but you can keep the attributes in your session store and use that in your before_filter

def set_product
  @product = Product.new(session[:product]) if session[:product]
  @product ||= Product.new
end

And in your create method you move the form params to the session...

def create
  session[:product] = params[:product]
  set_product 
  if @product.category == 'wheels'
  ---

Notice that we called set_product explicitly in the create method because the session[:product] had been re-established.

In case you're wondering WHY the instance variable is lost... in the create method you are in an instance of ProductController and that instance has it's own instance variables. When you redirect, you are instructing rails to create a NEW instance of some other (or the same) controller, and that brand new controller object, it has no instance variables established.



回答2:

To add to SteveTurczyn's answer, you need to read up about object orientated programming. After I did, all the @instance variable stuff became a lot clearer.

Very good write-up


Ruby is object-orientated, meaning every time you send a request, it has to invoke all the relative objects (classes) for you to interact with:

The duration of this request is called an instance. Redirecting invokes a new request; hence a new instance of your various classes.

This is why you have to invoke new @instance variables each time your users interact with a new action:

#app/controllers/your_controller.rb
class YourController < ApplicationController
   def edit
      @model = Model.find params[:id]
   end

   def update
      @model = Model.find params[:id] #-> new request = new instance
   end
end

--

Thus, when you ask...

@product variable need to populate a form in my footer that is on every page.

You need to remember that this will be invoked every time your actions are rendered. You already do this; the problem is that you're not persisting the data:

#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base 
  before_filter :set_product

  private

  def set_product
    @product ||= Product.new #-> after "create", params[:product] = nil (new instance)
  end
end

@SteveTurczyn got it when he mentioned putting the data into the session. As per the docs...

HTTP is a stateless protocol. Sessions make it stateful.



回答3:

The solution mentioned by Steve will work for smaller amount of data but what about fetching some records and passing it with redirect_to. The session does not allow us that much space.

What i did is that i set those records in a flash object and when it redirected to the page flash rendered the data for me successfully.

flash[:products] = @products
redirect_to users_path

Let me know, how it works...