简体   繁体   中英

Ruby on Rails: Insert values via form into array field in db

How would you insert values from a form into an array in the database? I'm using simple form, rails 4.1, postgresql db.

Here is my form, all fields update correctly except for the impact and likelihood fields (both arrays). They are the focus of this question:

<%= simple_form_for(@risk) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="form-group">
    <%= f.input :title, required: false, :error => false, input_html: { class: 'form-control' } %>
  </div>

  <div class="form-group">
    <%= f.input :description, required: false, :error => false,  as: :text, input_html: { class: 'form-control description' } %>
  </div>

  <div class="form-group">
    <%= f.input :area, :collection => ['Operations', 'IT', 'Finance'], required: false, :error => false, input_html: { class: 'form-control' } %>
  </div>

  <div class="form-group">
    <%= f.input :owner, :collection => User.all, required: false, :error => false, input_html: { class: 'form-control' } %>
  </div>

  <div class="form-group">
    <%= f.input :action, required: false, :error => false, input_html: { class: 'form-control' } %>
  </div>

  <div class="form-inline">
    <span class="date-of-action-input">
    <%= f.input :date_of_action, :as => :date, :start_year => Date.today.year - 10, :end_year => 2030,
        :order => [ :day, :month, :year], :required => false, :error => false, input_html: { class: 'form-control' } %>
    </span>
    <span class="action-completed-input">
    <%= f.input :action_completed, as: :boolean, required: false, :error => false, input_html: { class: 'form-control' } %>
    </span>

    <span class="impact-input">
    <%= f.input :impact, required: false, :error => false, input_html: { class: 'form-control' } %>
    </span>
    <span class="likelihood-input">
    <%= f.input :likelihood, required: false, :error => false, input_html: { class: 'form-control' } %>
    </span>
    <span class="submit-risk">
    <%= f.button :submit, :error => false, :error => false, input_html: { class: 'form-control' } %>
    </span>
  </div>
<% end %>

Here the associated controller (risks_controller.rb):

class RisksController < ApplicationController
before_action :signed_in_user
before_action :correct_user, only: [:destroy, :update]


def create
    @risk = current_user.risks.build(risk_params)
    if @risk.save
      flash[:success] = "Risk created!"
      redirect_to root_url
    else
      render 'new'
    end
end

def new
  @risk = Risk.new
end

def destroy
  @risk.destroy
  redirect_to root_url
end

def edit
  @risk = Risk.find(params[:id])
end

def update
  @risk = Risk.find(params[:id])
    @risk.assign_attributes(risk_params)
    if @risk.changed? == false
      flash[:info] = "No changes were made"
      redirect_to root_url
    elsif @risk.update_attributes(risk_params)
      flash[:success] = "The risk has been updated."
      redirect_to root_url
    else
        render 'new'
    end
end

private

    def risk_params
        params.require(:risk).permit(:description, :title, :area, :owner, :action, :date_of_action, :action_completed, :impact, :likelihood)
    end

  def correct_user
    if current_user.admin?
      @risk = Risk.find_by(id: params[:id])
      redirect_to root_url if @risk.nil?
    else
      @risk = current_user.risks.find_by(id: params[:id])
      redirect_to root_url if @risk.nil?
    end
  end

end

My db schema file:

ActiveRecord::Schema.define(version: 20140617165640) do

  create_table "risks", force: true do |t|
    t.integer  "user_id"
    t.string   "description"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "title"
    t.string   "area"
    t.string   "owner"
    t.string   "action"
    t.date     "date_of_action"
    t.boolean  "action_completed", default: false
    t.integer  "impact",           default: [],    array: true
    t.integer  "likelihood",       default: [],    array: true
  end

  add_index "risks", ["user_id", "created_at"], name: "index_risks_on_user_id_and_created_at", using: :btree

  create_table "users", force: true do |t|
    t.string   "name"
    t.string   "email"
    t.boolean  "admin",           default: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "password_digest"
    t.string   "remember_token"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
  add_index "users", ["remember_token"], name: "index_users_on_remember_token", using: :btree

end

I imagined the code would look something like this, but alas, my first rails project and I need a bit of guidance. In risks_controller.rb:

def create
  @risk = current_user.risks.build(risk_params)
  @impact = risk_params[:impact]
  @userid = current_user.id
  @impacttoupdate = Risk.select(:impact).where(id: risk_params[:id])
  @impacttoupdate.insert(@userid, @impact)
    if @risk.save
      flash[:success] = "Risk created!"
      redirect_to root_url
    else
      render 'new'
    end
end

I want to insert the value according to the user id. So if the user id is 3, the value from the form they are submitting should go into the 3rd slot in the array.

You have to define in strong params that post values are arrays.

def risk_params
    params.require(:risk).permit(:description, :title, :area, :owner, :action, :date_of_action, :action_completed, {:impact => []}, {:likelihood=>[]})
end

For Seong and anyone else out there - here's some rudimentary code on how I got this to work. It's not very elegant, I was pretty new to Rails back then.

def update
@risk = Risk.find(params[:id])
#1. capture the existing arrays from the db before anything 
#has happened to them
@impactarray = @risk.impact
@likelihoodarray = @risk.likelihood
#2. update ALL attributes, including impact and likelihood fields incorrectly
@risk.assign_attributes(risk_params)
if @risk.changed? == false
  flash[:info] = "No changes were made"
  redirect_to root_url
elsif @risk.update_attributes(risk_params)
  #3. delete the existing value in the array, even if it is nil
  @impactarray.delete_at(current_user.id)
  #4. repeat this for the likelihood field
  @likelihoodarray.delete_at(current_user.id)
  #5. capture the value that was entered, it would be the first 
  #and only value in the incorrectly formed array
  @impactscore = @risk.impact[0]
  #6. insert the new value correctly according to user id by inserting it 
  #back into the original arrays we captured at the start of the method
  @impactarray.insert(current_user.id, @impactscore)
  #7. do the same for the likelihood column
  @likelihoodscore = @risk.likelihood[0]
  @likelihoodarray.insert(current_user.id, @likelihoodscore)
  #8. officially make these changes on the @risk object and save them
  @risk.update_column("impact", @impactarray)
  @risk.update_column("likelihood", @likelihoodarray)
  #9. update the score columns to save computation on the risk matrix
  averageimpactscore = @impactarray.compact.inject(0) {|sum,x| sum += x.to_i } / @impactarray.compact.length
  averagelikelihoodscore = @likelihoodarray.compact.inject(0) {|sum,x| sum += x.to_i } / @likelihoodarray.compact.length
  overallscore = (averageimpactscore + averagelikelihoodscore) / 2
  @risk.update_column("average_impact", averageimpactscore)
  @risk.update_column("average_likelihood", averagelikelihoodscore)
  @risk.update_column("overall_score", overallscore)
  @risk.save!
  #N.B. remember we got simple form to recall the user's values so they 
  #do not get put back to 0 when other fields are edited (see the risk form view)
  flash[:success] = "Risk #{@risk.id} has been updated."
  redirect_to root_url
else
    render 'new'
end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM