options_for_select on simple_form - ruby-on-rails

I'm probably using this incorrectly and I cant figure it out.
I am using an options_for_select on my simple_form. It renders fine with no errors, but the selected option does not save to the database. All other fields save no problems.
The select_tag is
<%= select_tag :experiment_type, options_for_select(['AOV', 'Conversion']), :prompt => "Select a Project Type" %>
Controller:
class ExperimentsController < ApplicationController
before_action :find_advertiser
before_action :find_experiment, only: [:edit, :update, :show, :destroy]
def index
#experiments = Experiment.all.order("created_at DESC")
end
def show
end
def new
#experiment = Experiment.new
#advertisers = Advertiser.all.map{ |c| [c.name, c.id] }
end
def create
#experiment = Experiment.new(experiment_params)
#experiment.advertiser_id = params[:advertiser_id]
if #experiment.save
redirect_to advertiser_path(#advertiser)
else
render 'new'
end
end
def edit
#projects = Project.all.map{ |c| [c.name, c.id] }
end
def update
#experiment.advertiser_id = params[:id]
if #experiment.update(experiment_params)
redirect_to experiment_path(#experiment)
else
render 'edit'
end
end
def destroy
#experiment.destroy
redirect_to root_path
end
private
def experiment_params
params.require(:experiment).permit(:advertiser_id, :name, :experiment_type, :hypothesis, :priority, :status, :launch_date,
:description, :baseline_url, :test_url, :baseline_aov_60, :baseline_aov_30, :baseline_aov_mtd,
:baseline_conversion_60, :baseline_conversion_30, :baseline_conversion_mtd)
end
def find_advertiser
#advertiser = Advertiser.find(params[:advertiser_id])
end
def find_experiment
#experiment = Experiment.find(params[:id])
end
end

I would need your full form layout to tell you for sure, but according to your experiment_params method, experiment_type field is a part of experiment. However, when you use just select_tag it is not connected to your main object. You need to use just select. Similar to this:
<%= simple_form_for :experiment do |f| %>
...
<%= f.select ... %>
...
<% end %>
or in the simple_form format:
<%= f.input :experiment_type, collection: ['AOV', 'Conversion'] %>
My guess is also based on your hash:
"experiment_type"=>"AOV", "experiment"=>{"name"=>"Test" ....
The experiment_type is outside of your "experiment".

Related

Rails - Passing a variable while using Simple Form

When creating a new model object using simple form, I need to pass the id of one object to the new object as it relies on the id for its URL. The routing is:
resources :coins do
resources :questions
end
I am attempting to do this using a hidden field tag but its not working. The ID is not passed and as a result, the new object does not save.
<%= simple_form_for #question, url: coin_questions_path(#coin.id) do |f| %>
<%= f.input :ques_num %>
<%= f.input :content %>
<%= hidden_field_tag(:coin_id, #coin.id) %>
<%= f.button :submit, 'Submit' %>
<% end %>
Prior to this, I was using collection_select in simple form to manually enter the ID and it worked, however I need it to happen automatically. Is there a better way of doing this that will do what I am looking for?
Question model:
class QuestionsController < ApplicationController
before_action :find_question, only: [:show, :edit, :update, :destroy ]
before_action :find_coin
before_action :authenticate_user!, except: [:index, :show]
def index
#questions = Question.where(coin_id: #coin.id).order("created_at DESC")
end
def show
end
def new
#coin
#question = current_user.questions.build
end
def create
#question = current_user.questions.build(question_params)
if #question.save
redirect_to coin_question_path(#coin.id, #question.id)
else
render 'new'
end
end
.
.
.
private
def find_question
#question = Question.find(params[:id])
end
def find_coin
#coin = Coin.find(params[:coin_id])
end
def question_params
params.require(:question).permit(:content, :ques_num, :coin_id)
end
end
You could easily use hidden_field with a value - read more
For example:
<%= f.hidden_field :coin_id, value: #coin.id %>
And then the value will be in the params[:question][:coin_id] on create, so the rest should work as it is now. :)
<%= hidden_field_tag(:coin_id, #coin.id) %> will generate a field with a name of just "coin_id"; you want "question[coin_id]"
You should, in turn, assign this value to the object in your controller. #question.coin_id = #coin.id, or #question = Question.new(:user => current_user, :coin => #coin) or #question = current_user.questions.new(:coin => #coin)
<%= f.input :coin_id, :as => :hidden %>

How can I create a form without using resources (action :new, :create) in Rails?

This is my controller
class SchoolsController < ApplicationController
def teacher
#teacher = Teacher.new
end
def form_create
#teacher = Teacher.new(teacher_params)
if teacher.save
redirect_to schools_teacher_path
else
flash[:notice] = "error"
end
end
private
def teacher_params
params.require(:teacher).permit(:name)
end
end
This is my views/schools/teacher.html.erb
<%= form_for :teacher do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
I am new to Ruby on Rails, and not sure how to proceed.
You should move this to a TeachersController let me show you how:
First you need to create the controller, you can get this done by typing this on the terminal at the project root directory:
$ rails g controller teachers new
Then into your route file (config/routes.rb):
resources :teachers, only: [:new, :create]
After that go to the teachers_controller.rb file and add the following:
class TeachersController < ApplicationController
def new
#teacher = Teacher.new
end
def reate
#teacher = Teacher.new(teacher_params)
if #teacher.save
redirect_to schools_teacher_path
else
redirect_to schools_teacher_path, notice: "error"
end
end
private
def teacher_params
params.require(:teacher).permit(:name)
end
end
Then you can have the form at views/teachers/new.html.erb:
<%= form_for :teacher do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
Please let me know how it goes!

Trying to have 2 forms pass to 2 different controllers from one view

I have 2 forms in one view one is displayed if the user is a moderator and the other if it is a normal user and they both send the information to 2 different controllers. My problem is that if its a normal user, the form that is displayed for them uses the wrong controller.
Here is the coding
categories/new.html.erb
<% if current_user.mod_of_game? #guide %>
<%= form_for([#guide, #category], url: guide_categories_path) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name, "Category name" %>
<%= f.text_field :name %>
<%= f.submit "Next" %>
<% end %>
<% else %>
<%= form_for([#guide, #check_category], url: check_category_post_path) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name, "Category name" %>
<%= f.text_field :name %>
<%= f.submit "Next" %>
<% end %>
<% end %>
Categories controller
before_action :mod_checker, only: [:create]
def new
#guide = Guide.friendly.find(params[:guide_id])
#category = Guide.friendly.find(#guide.id).categories.new
#check_category = CheckCategory.new
end
def create
#guide = Guide.friendly.find(params[:guide_id])
#category = Guide.friendly.find(#guide.id).categories.new(category_params)
if ((#category.save) && (current_user.mod_of_game? #guide))
flash[:info] = "guide category added succesfully!"
redirect_to #guide
else
render 'new'
end
end
private
def category_params
params.require(:category).permit(:name)
end
def mod_checker
#guide = Guide.friendly.find(params[:guide_id])
unless current_user.mod_of_game? #guide
flash[:danger] = "Sorry something went wrong!"
redirect_to root_path
end
end
check_categories controller
def new
end
def create
if #check_category.save
flash[:info] = "Game category added successfully. A mod will apporve it shortly."
redirect_to #guide
else
render 'new'
end
end
private
def check_category_params
params.require(:check_category).permit(:name)
end
and the routes
resources :guides do
resources :categories, only: [:new, :create, :edit, :update]
end
resources :check_categories, only: [:new, :edit, :update]
match 'guides/:guide_id/categories/' => 'check_categories#create', :via => :post, as: :check_category_post
sorry the coding is a bit messy, the 4 spaces to put it in a code block was spacing my coding weird.
When i have a non moderator user submit the form, the before action in the categories controller is run and I'm redirected to the homepage. I don't know why it does this because the submit path should go to the check_categories controller for non moderator users, the check_categories controller doesn't have the before filter.
Why does it use the before filter in the controller I'm not using for that form? How can I fix it?
Building this app to learn rails better. So I can only assume lack of rails knowledge is causing me to do something wrong.
Bad practice to have two forms with identical code (apart from the path) - goes against DRY Don't Repeat Yourself.
As mentioned by #Akash, this sounds like a job for authorization.
Further, it also denotes that you have issues with the underlying structure of your code. Specifically, you have an antipattern with CheckCategory (you can put it all into the Category model):
#config/routes.rb
resources :guides do
resources :categories, only: [:new, :create, :edit, :update] do
patch :approve, on: :member
end
end
#app/models/category.rb
class Category < ActiveRecord::Base
before_action :set_guide
def new
#category = current_user.categories.new
flash[:notice] = "Since you are not a moderator, this will have to be approved." unless current_user.mod_of_game? #guide
end
def create
#category = current_user.categories.new category_params
#category.guide = #guide
#category.save
end
def approve
#category = #guide.categories.find params[:id]
#category.approve
end
private
def set_guide
#guide = Guide.find params[:guide_id]
end
end
#app/views/categories/new.html.erb
<%= form_for [#guide, #category] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name, "Category name" %>
<%= f.text_field :name %>
<%= f.submit "Next" %>
<% end %>
The above will solve most of your structural issues.
--
To fix the authorization issue, you'll be best denoting whether the category is "approved" in the model:
#app/models/category.rb
class Category < ActiveRecord::Base
enum status: [:pending, :approved]
belongs_to :user
belongs_to :guide
validates :user, :guide presence: true
before_create :set_status
def approve
self.update status: "approved"
end
private
def set_status
self[:status] = "approved" if self.user.mod_of_game? self.guide
end
end
--
If I understand correctly, you want to allow anyone to create a category, but none-mods are to have their categories "checked" by a moderator.
The code above should implement this for you.
You will need to add a gem such as CanCan CanCanCan to implement some authorization:
#app/views/categories/index.html.erb
<% #categories.each do |category| %>
<%= link_to "Approve", guide_category_approve_path(#guide, category) if category.waiting? && can? :update, Category %>
<% end %>
Use "Cancan" Gem and give authorization

Edit nested in a form

Thanks to help on stackoverflow I got my create nested models form working the other day but I can't for the life of me get the corresponding update form to work. I have read a lot and tried out as many solutions as I can find.
The form looks fine but the nested attributes of Manufacturer and Scale, which are select via drop down, don't have their current values. All non nested elements of the form work fine.
Whatever changes you make to the two nested drop downs, pressing save changes creates NEW lines in the corresponding tables and doesn't alter the existing.
Ultimately what I want is for the attributes to be editable and then i'll have an "add manufacturer" and "add scale" button or link for miniatures that need multiple listings.
Here are my form fields where I've tried and failed to pass a hidden field.
Form
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :material %>
<%= f.select 'material', options_from_collection_for_select(Miniature.select("DISTINCT material"), :material, 'material', #miniature.material) %>
<%= f.fields_for :sizes do |size_fields| %>
<%= size_fields.label :scale_id, "Scale".pluralize %>
<%= hidden_field "Miniature Scale", #miniature.sizes %>
<%= size_fields.select :scale_id, options_from_collection_for_select(Scale.all, :id, :name) %>
<% end %>
<%= f.fields_for :productions do |production_fields| %>
<%= production_fields.label :manufacturer_id, "Manufacturer".pluralize %>
<%= hidden_field "Miniature Manufacturer", #miniature.productions %>
<%= production_fields.select :manufacturer_id, options_from_collection_for_select(Manufacturer.all, :id, :name, #miniature.manufacturers) %>
<% end %>
<%= f.label :release_date %>
<%= f.date_select :release_date, :start_year => Date.current.year, :end_year => 1970, :include_blank => true %>
Here is the miniatures controller where I'm pretty sure I've filled the 'def update' with too much/the wrong stuff.
Miniatures Controller
class MiniaturesController < ApplicationController
before_action :signed_in_user, only: [:new, :create, :edit, :update]
before_action :admin_user, only: :destroy
def show
#miniature = Miniature.find(params[:id])
end
def new
#miniature = Miniature.new
#miniature.productions.build
#miniature.sizes.build
end
def create
#miniature = Miniature.new(miniature_params)
#production = #miniature.productions.build
#size = #miniature.sizes.build
if #miniature.save
redirect_to #miniature
else
render 'new'
end
end
def edit
#miniature = Miniature.find(params[:id])
end
def update
#miniature = Miniature.find(params[:id])
#production = #miniature.productions.find(params[:id])
#size = #miniature.sizes.find(params[:id])
if #miniature.update_attributes(miniature_params)
#production = #miniature.productions.update_attributes(:manufacturer_id)
#size = #miniature.sizes.update_attributes(:scale_id)
flash[:success] = "Miniature updated"
redirect_to #miniature
else
render 'edit'
end
end
def index
#miniatures = Miniature.paginate(page: params[:page])
end
def destroy
Miniature.find(params[:id]).destroy
flash[:success] = "Miniature destroyed."
redirect_to miniatures_url
end
private
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, productions_attributes: [:manufacturer_id], sizes_attributes: [:scale_id])
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
end
I won't attach the models as I'm pretty sure the relationships are all correct since they work fine for creating new nested models.
Miniatures have_many scales and manufactures through sizes and productions.
Any help or pointers very much appreciated.
Thanks to an answer on this Q I've solved it. What I had already was fine for CREATING but didn't work for UPDATES because I hadn't whitelisted the JOIN model ids in the 'miniature_params, so they couldn't retrieve the existing info.
Now I have productions_attributes: [:id, :manufacturer_id] instead of just productions_attributes: [:manufacturer_id]
as below
def miniature_params
params.require(:miniature).permit(:name, :release_date, :material, productions_attributes: [:id, :manufacturer_id], sizes_attributes: [:id, :scale_id])
end
I can also strip ALL of the references to the nested models out of my miniatures controller update method as it 'just works'.
def update
#miniature = Miniature.find(params[:id])
if #miniature.update_attributes(miniature_params)
flash[:success] = "Miniature updated"
redirect_to #miniature
else
render 'edit'
end
end
Hope this is useful to someone in future.

Bulk create records for one Model in Rails 4

i have view:
<%= form_tag mined_materials_url do %>
<%= render 'import_fields' %><br />
<%= render 'import_fields' %>
<%= button_tag :submit %>
<% end %>
_import_fields.html.erb:
<%= text_field_tag 'mined_material[][character_id]','' %>
<%= text_field_tag 'mined_material[][material_id]','' %>
<%= text_field_tag 'mined_material[][quantity]','' %>
<%= text_field_tag 'mined_material[][price]','' %>
(i will add more fields via JS)
and need to insert multiple records into same model at once, but it fail to save because of error: undefined method `permit' for Array:0x00000001f63230
Params:
{"utf8"=>"✓",
"authenticity_token"=>"fFR3nWwz2Q0ivED6XXyj4ICRBtj5Rdois4N++cNnCko=",
"mined_material"=>[{"character_id"=>"1",
"material_id"=>"1",
"quantity"=>"1",
"price"=>"1"},
{"character_id"=>"2",
"material_id"=>"2",
"quantity"=>"2",
"price"=>"2"}],
"button"=>""}
how could i permit bulk create?
EDIT:
MinedMaterialsController
class MinedMaterialsController < ApplicationController
before_action :set_mined_material, only: [:show, :edit, :update, :destroy]
before_action :load_mined_material, only: :create
load_and_authorize_resource
# GET /mined_materials/new
def new
#mined_material = MinedMaterial.new
end
# POST /mined_materials
def create
#mined_material = MinedMaterial.new(mined_material_params)
respond_to do |format|
if #mined_material.save
format.html { redirect_to new_mined_material_path, notice: 'Mined material was successfully created.' }
else
format.html { render action: 'new' }
end
end
end
def import
end
private
# Use callbacks to share common setup or constraints between actions.
def set_mined_material
#mined_material = MinedMaterial.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def mined_material_params
params.require(:mined_material).permit(:character_id, :material_id, :quantity, :price, :paid_out, :date, :corp_tax)
end
def load_mined_material
#mined_material = MinedMaterial.new(mined_material_params)
end
end
You can change your mined_material_params method to this:
params.permit({mined_material: [:character_id, :material_id, :quantity, :price, :paid_out, :date, :corp_tax]} ).require(:mined_material)
permit method only work if the value is a hash.

Resources