0

I have a self-referential relationship set up in my app so that an AppForm can belong_to another AppForm through Variations.

The behavior I am trying to create is a button on the AppForm's show page that can be clicked to create a new variation of the AppForm. This button would ideally create a duplicate of the current record and take the user to that new record's edit page to make changes before saving it.

I have the variations table set up like this in my schema.rb:

  create_table "variations", force: :cascade do |t|
    t.bigint "app_form_id"
    t.bigint "original_app_form_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.bigint "user_id"
    t.index ["app_form_id", "original_app_form_id"], name: "index_variations_on_app_form_id_and_original_app_form_id", unique: true
    t.index ["app_form_id"], name: "index_variations_on_app_form_id"
    t.index ["original_app_form_id"], name: "index_variations_on_original_app_form_id"
    t.index ["user_id"], name: "index_variations_on_user_id"
  end

With this as my variation.rb model:

class Variation < ApplicationRecord
  belongs_to :user
  belongs_to :app_form
  belongs_to :original_app_form, class_name: "AppForm"
end

And this in my app_form.rb model:

class AppForm < ApplicationRecord
  belongs_to :application_status, optional: true
  belongs_to :final_decision, optional: true
  belongs_to :funding_status, optional: true
  belongs_to :user, optional: true
  belongs_to :app_form, through: :variations
end

This seems pretty standard for a has_many :through relationship, but now I can't seem to Google my way to creating the duplicate variation of an AppForm. I've seen posts like this one that show a variety of different ways to do this. Some use routes, others need special gems...I'm lost. What's the "Rails-y" way to do this properly?

Liz
  • 1,369
  • 2
  • 26
  • 61

1 Answers1

0

There is no accepted "Rails-y" way to this. The Rails conventions don't actually cover every possible use case that you can find for the framework. If you can't find a "Rails-y" way there is a very good chance that it does not exist.

But this is something that's relatively straight forward to build with a nested route and nested resources.

# config/routes.rb
resources :app_forms do
  resources :variations, only: [:new, :create]
end

class AppForm < ApplicationRecord
  # ...
  def reproducable_attributes
    attributes.except('id', 'created_at', 'updated_at')
  end
end

class Variation < ApplicationRecord
  accepts_nested_attributes_for :app_form

  # ...
  # factory method to create a new instance from an existing AppForm
  def self.of(original_app_form)
    new(
      original_app_form: original_app_form,
      app_form_attributes: original_app_form.reproducable_attributes
    )
  end
end
class VariationsController < ApplicationController
  before_action :set_original_app_form

  # GET /app_forms/:app_form_id/variations/new
  def new
    @variation = Variation.of(@original_app_form)
  end

  # POST /app_forms/:app_form_id/variations
  def create
    @variation = @original_app_form.variations.new(variation_params) do |v|
      v.user = current_user
    end
    if @variation.save 
      redirect_to @variation.app_form
    else
      render :new
    end
  end

  private

  def set_original_app_form
    @original_app_form = AppForm.find(params[:app_form_id])
  end

  def variation_params
    params.require(:variation)
          .permit(app_form_attributes: [:foo, :bar])
  end
end
<%= form_for([@original_app_form, @variation]) do |f| %> 
  <%= f.fields_for :app_form do |app_form_fields|%>
    # ...
  <% end %>
  # ...
<% end %> 
max
  • 96,212
  • 14
  • 104
  • 165