I am updating a legacy Rails App from 3.2.5 to Rails 7. In the App I have a multistep form, that follows Railscasts # 217. This means I have an Order model with:
attr_writer :current_step
# Associations
belongs_to :bill_to_land, class_name: "Land", foreign_key: :bill_to_land_id
belongs_to :ship_to_land, class_name: "Land", foreign_key: :ship_to_land_id
belongs_to :order_status
belongs_to :shipping_service
has_many :order_items
has_many :products, through: :order_items
# Validations
validates_presence_of :shipping_name, :if => lambda { |o| o.current_step == "shipping" }
validates_presence_of :ship_to_land, if: -> { current_step == "shipping" }
validates_presence_of :billing_name, :if => lambda { |o| o.current_step == "billing" }
validates_presence_of :bill_to_land, if: -> { current_step == "billing" }
def current_step
@current_step || steps.first
end
def steps
%w[shipping billing confirmation]
end
def next_step
self.current_step = steps[steps.index(current_step) 1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
def all_valid?
steps.all? do |step|
self.current_step = step
valid?
end
end
In my Order Controller I have something like:
def new
session[:order_params] ||= {}
@order = Order.new(session[:order_params])
@order.current_step = session[:order_step]
end
def create
session[:order_params].deep_merge!(params[:order]) if params[:order]
@order = Order.new(session[:order_params])
@order.current_step = session[:order_step]
if @order.valid?
if params[:back_button]
@order.previous_step
elsif @order.last_step?
@order.save if @order.all_valid?
else
@order.next_step
end
session[:order_step] = @order.current_step
end
if @order.new_record?
render "new"
else
session[:order_step] = session[:order_params] = nil
flash[:notice] = "Order saved!"
redirect_to @order
end
end
with a view such as this:
<% form_for @order, data: { turbo_confirm: 'Are you sure to order?' } do |f| %>
<%= f.error_messages %>
<%= render "#{@order.current_step}_step", :f => f %>
<p><%= f.submit "Continue", data: { turbo: false } %></p>
<p><%= f.submit "Back", :name => "back_button" unless @order.first_step?, data: { turbo: false } %></p>
<% end %>
This used to work before upgrading. Now the multistep is broken, because bill_to_land
is validated already on the first step. I guess the problem are not the validations per se, but that all belongs_to associations are validated already on the first step of the form. I can get the multistep proceeding by commenting out the if @order.valid?
in my controller, but that deactivates all validations.
The only adjustment I had to make to the original code during upgrade was to add data: { turbo: false }
to the submit tags in my view.
Does anyone know why this implementation of multistep worked before, why it stopped working and how I should proceed in order to get it working again?
Thank you very much in advance.
CodePudding user response:
Since Rails 5.0
version belongs_to
is required by default
You can turn this option off:
# config/application.rb
config.active_record.belongs_to_required_by_default = false
Or add optional: true
to your belongs_to
associations:
belongs_to :bill_to_land, class_name: "Land", foreign_key: :bill_to_land_id, optional: true
belongs_to :ship_to_land, class_name: "Land", foreign_key: :ship_to_land_id, optional: true
belongs_to :order_status, optional: true
belongs_to :shipping_service, optional: true