Home > database >  Rails: belongs_to association breaks multistep form
Rails: belongs_to association breaks multistep form

Time:06-13

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
  • Related