My route :
patch '/projects/:project_id/budgets/:id', to: 'budgets#update', as:'patch_budget' #update
when i send a patch method to this route, :project_id should be my project_id, like:
/projects/test_project/budgets/14
but it turns to budget.id, like:
/projects/14/budgets/14
_form.html.erb
<%= form_with model: budget do |f| %>
<div>
<%= f.label :name, "預算名稱" %><br>
<%= f.text_field :name %>
</div>
<div>
<%= f.label :description, "說明" %><br>
<%= f.text_field :description %>
</div>
<div>
<%= f.label :category, "預算類別" %><br>
<%= f.text_field :category %>
</div>
<div>
<%= f.label :quantity, "數量" %><br>
<%= f.text_field :quantity %>
</div>
<div>
<%= f.label :unit_cost, "單位成本" %><br>
<%= f.text_field :unit_cost %>
</div>
<div>
<%= f.label :issue_id, "議題" %><br>
<%= f.select(:issue_id, Issue.all.collect { |i| [ i.subject, i.id] }) %>
</div>
<div>
<%= f.submit "送出" %>
</div>
<% end %>
edit.html.erb
<h1>編輯預算</h1>
<%= render "form", budget: #budget %>
<br/>
<%= link_to "回預算列表", budgets_path %>
budgets_controller.rb
class BudgetsController < ApplicationController
before_action :find_project, :authorize
def index
#budgets = Budget.all
#sum = Budget.all.sum(:total_cost).round(1)
end
def new
#budgets = Budget.new
end
def create
#budgets = Budget.new(budget_params)
#budgets.project_id = #project.id
#budgets.total_cost = #budgets.quantity * #budgets.unit_cost
#budgets.created_by_id = User.current.id
if #budgets.save
redirect_to budgets_path, notice: "新增預算成功!"
else
render :new
end
end
def edit
#budget = Budget.find(params[:id])
end
def update
#budget = Budget.find(params[:id])
if #budget.update(budget_params)
redirect_to budgets_path, notice: "修改成功!"
else
render :edit
end
end
def destroy
#budget = Budget.find(params[:id])
#budget.destroy if #budget
redirect_to budgets_path, notice: "預算已刪除!"
end
def show
#budget = Budget.find(params[:id])
#create_by = User.find(#budget.created_by_id)
if #budget.modified_by_id != nil
#modified_by = User.find(#budget.modified_by_id)
end
end
private
def find_project
# #project variable must be set before calling the authorize filter
#project = Project.find(params[:project_id])
end
def budget_params
params.require(:budget).permit(:name, :description, :category,
:quantity, :unit_cost, :issue_id)
end
end
Create and delete are running normally, I try many ways in my form, but it didn't work, thanks for the help.
You need to pass an array to generate nested routes:
<%= render "form", budget: #budget, project: #project %>
<%= form_with model: [project, budget] do |f| %>
See https://api.rubyonrails.org/v7.0/classes/ActionDispatch/Routing/PolymorphicRoutes.html
i have no idea why this error come out, can anyone help me? thanks
It shown when i comment out the product controller( def add) there.
And also if possible is that can help me check the add.html.erb there is that my option_for_select to get all the outlets correct?
Errors show in website
Errors show in console
ActionView::Template::Error (undefined method `errors' for nil:NilClass
#virtual_path = "shared/_error_messages";object = local_assign
s[:object]; object = object;; if object.errors.any?
^^^^^^^):
1: <% if object.errors.any? %>
2: <div id="error_explanation">
3: <div class="alert alert-danger">
4: The form contains <%= pluralize(object.errors.count, "error")
%>.
app/views/shared/_error_messages.html.erb:1
app/views/products/add.html.erb:5
app/views/products/add.html.erb:4
Add.html.erb
<h1>Add to outlet</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #product, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :quantity %>
<%= f.number_field :quantity, class: 'form-control' %>
<%= f.label :price %>
<%= f.number_field :price, class: 'form-control' %>
<%= f.label :outlet %>
<%= f.select :outlet, options_for_select(#outlets), :include_blank => true %>
<%= f.hidden_field :category_id, value: 1 %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
_error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Product Controller
class ProductsController < ApplicationController
def category
#category = Category.find(params[:id])
end
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
#product.category_id = params[:category_id]
end
def create
#product = Product.new(product_params)
#category_id = Category.find(params[:product] [:category_id])
if #product.save
flash[:success] = "Succesful create!"
redirect_to #product
else
render 'new'
end
end
def outlet
#outlet = Outlet.find(params[:id])
end
def add
##product = Product.find(params[:id])
##outlet = Outlet.find(params[:outlet_id])
end
def update
#product = Product.find(params[:id])
#outlet = Outlet.find(params[:outlet][:name])
if #product.update(product_params)
flash[:success] = "Product updated"
redirect_to #product
else
render 'add'
end
end
private
def product_params
params.require(:product).permit(:name, :quantity, :price,
:category_id)
end
end
I'm trying to implement a simple uniqueness validation on a Rails Ajaxified form. Unfortunately, my validation error is not printed when the validation is not accepted. Can you help me to figure why?
model.rb
class WaitingList < ApplicationRecord
validates :email, uniqueness: true
end
controller.rb
def create_concours
#email = params[:email]
#waiting_user = WaitingList.new(email: #email, source: params[:source], concours_city: params[:choosen_city])
#First Ajax of Weekendr History snif
respond_to do |format|
if #waiting_user.save
UserMailer.welcome(#email).deliver_now
format.html { redirect_to concours_path }
format.js
else
format.html
format.js
end
end
end
_form.html.erb
<%= form_tag concours_path, {id: 'concours-form', remote: true} do %>
<%= text_field_tag :email,
value = nil,
class: "landing-form-input",
autocomplete: "off",
required: true,
pattern: '[^#]+#[^#]+\.[a-zA-Z]{2,6}',
placeholder: "Renseignez ton email ici"
%>
<i class="fa fa-envelope"></i>
<%= hidden_field_tag :choosen_city, '', id: 'c-choosen-city'%>
<%= hidden_field_tag 'source', params[:source] %>
<%= button_tag :submit, class: "landing-form-button" do %>
Je tente ma chance !
<% end %>
<% end %>
create_concours.js.erb
function refreshForm(innerHTML) {
const form = document.getElementById("concours-form");
form.innerHTML = innerHTML;
}
<% if #waiting_user.errors.any? %>
refreshForm('<%= j render "form" %>');
<% end %>
I have two models, Roaster and Roast
I want to have the user select the value of :roaster in the new roast form, from the Roaster model. I am using a collection_select which displays the list of roasters in the dropdown ok, but it doesn't insert the value into the table. From the console, it actually looks like it's trying to pass the roaster_id
"roast"=>{"roaster_id"=>"1", "name"=>"Rugby", "beans"=>"", "countries_attributes"=>{"0"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "1"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "2"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}}, "bestfor"=>"", "roast"=>"", "tastingnotes"=>""}, "commit"=>"Create Roast"}
My select:
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
I've tried
<%= form.collection_select(:roaster_name, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
but this gives and undefined method error.
My roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
Adding in :roaster_name doesn't solve either.
As requested full form:
<%= form_with(model: roast, local: true, multipart: true) do |form| %>
<% if roast.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger" role="alert">
<h2><%= pluralize(roast.errors.count, "error") %> prohibited this roast from being saved:</h2>
<ul>
<% roast.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<form>
<div class="row">
<div class="col-6">
<div class="field form-group">
<%= form.label :roaster, class: 'control-label' %>
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
</div>
</div>
<div class="col-6">
<div class="form-group">
<%= form.label :name, class: 'control-label' %>
<%= form.text_field :name, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= form.label :beans, "Blend", class: 'control-label' %><br />
<%= form.select :beans, [ 'Single Origin','Two Country Blend', 'Three Country Blend' ], :prompt => 'Select One', id: :roast_beans, class: "form-control" %>
</div>
<div class="row">
<%= form.fields_for :countries do |countries_form| %>
<div class="col-6">
<div class="form-group">
<%= countries_form.label :country %>
<%= countries_form.text_field :country_name, class: "form-control" %>
</div>
</div>
<div class="col-6">
<!-- note the appending of `countries_` to form.fields to allow for deeper nested to work-->
<%= countries_form.fields_for :regions do |regions_form| %>
<%= regions_form.label :region %>
<%= regions_form.text_field :region_name, class: "form-control" %>
<% end %>
<br />
</div>
<% end %>
</div>
<div class="form-group">
<%= form.label :bestfor, "Style", class: 'control-label' %><br />
<%= form.select :bestfor, [ 'Espresso','Filter' ], :prompt => 'Select One', id: :roast_bestfor, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :roast, "Strength", class: 'control-label' %><br />
<%= form.select :roast, [ 'Light','Medium','Dark' ], :prompt => 'Select One', id: :roast_roast, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :tastingnotes, "Tasting Notes (separate with commas, e.g chocolate, citrus)", class: 'control-label' %><br />
<%= form.text_area :tastingnotes, id: :roast_tastingnotes, class: "form-control" %>
</div>
<br />
<div class="form-group">
<%= form.label :avatar, "Upload image...", class: 'control-label' %>
<%= form.file_field :avatar %>
</div>
<div class="actions">
<%= form.submit class: "btn btn-success" %> <%= link_to "Cancel", "/roasts", class: "btn btn-secondary"%>
</div>
<% end %>
</form>
roast_controller.rb
class RoastsController < ApplicationController
before_action :set_roast, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:create, :edit, :update, :destroy]
before_action :set_search
# GET /roasts
# GET /roasts.json
def index
#q = Roast.ransack(params[:q])
#roastsalpha = #q.result.order(:name)
#roastcount = Roast.count(:country)
#roasts = Roast.order(:name).count
#countroastschart = Roast.order("roaster DESC").all
end
# GET /roasts/1
# GET /roasts/1.json
def show
#roast = Roast.friendly.find(params[:id])
#commentable = #roast
#comments = #commentable.comments
#comment = Comment.new
#sameroaster = Roast.where(roaster: #roast.roaster)
#samecountry = Roast.where(country: #roast.country)
#roastcount = Roast.where(roaster: #roast.roaster)
end
# GET /roasts/new
def new
#roast = Roast.new
3.times {#roast.countries.build.regions.build}
end
# GET /roasts/1/edit
def edit
3.times {#roast.countries.build.regions.build}
end
# POST /roasts
# POST /roasts.json
def create
#roast = Roast.new(roast_params)
respond_to do |format|
if #roast.save
format.html { redirect_to #roast, notice: 'Roast was successfully created.' }
format.json { render :show, status: :created, location: #roast }
else
format.html { render :new }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /roasts/1
# PATCH/PUT /roasts/1.json
def update
respond_to do |format|
if #roast.update(roast_params)
format.html { redirect_to #roast, notice: 'Roast was successfully updated.' }
format.json { render :show, status: :ok, location: #roast }
else
format.html { render :edit }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# DELETE /roasts/1
# DELETE /roasts/1.json
def destroy
#roast.destroy
respond_to do |format|
format.html { redirect_to roasts_url, notice: 'Roast was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_roast
#roast = Roast.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
end
end
I think you are doing many wrong things. By looking your other questions I saw your models. I put some important things:
class Roast < ApplicationRecord
has_many :countries
accepts_nested_attributes_for :countries
end
class Country < ApplicationRecord
has_many :regions, inverse_of: :country
accepts_nested_attributes_for :regions
belongs_to :roast
end
class Region < ApplicationRecord
belongs_to :country, inverse_of: :regions
end
In these models I didn't see the Roaster. I assume a Roast belongs_to :roaster.
So: your Roast has many countries and each country has many regions. But you are passing country names and region names in your view to the create controller. You need to pass the ids, so that you save references to these models.
You have many unnecessary field in params, and some missing ones. This is how it should be:
def roaster_params
params.require(:roast).permit(:roaster_id, :name, :bestfor, :beans, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:id, regions_attributes: [:id]])
end
You don't need roast, roaster, country_name, region_name. You need the id of the country (and not the country_id), and the id of the region (and not the region_id)
In your form you should ask for country and region ids:
<%= countries_form.collection_select(:id, Country.all, :id, :name, :prompt => 'Select Country') %>
<%= regions_form.collection_select(:id, Region.all, :id, :name, :prompt => 'Select Region') %>
In fact this is more difficult, because a region belongs to a country, but here you are showing all regions. You should only show regions for the selected country (which is dynamic).
I'm trying to create a record in a join table named Interventions. Basically in my application a user can do many interventions on an incident, and an incident can have interventions by many users. I pass the needed strong parameters, but the application gives the following errors when I try to save: "Incident must exist" and "User must exist". I spent hours on this, and can't figure out what is the problem. Can you please help me? I post the relevant code here:
user.rb (model)
has_many :interventions
has_many :incidents, through: :interventions
incident.rb (model)
has_many :interventions
has_many :users, through: :interventions
intervention.rb (model)
belongs_to :incident_priority
belongs_to :intervention_status
interventions_controller.rb
def new
#incident = Incident.find(params[:incident])
#user = User.find(current_user.id)
#intervention = Intervention.new(:user_id => #user, :incident_id => #incident)
#project = #incident.channel.project
#mirth = Mirth.find_by server_id: #incident.mirth_server_id
end
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
#intervention = Intervention.create(intervention_params)
#project = #incident.channel.project
#mirth = Mirth.find_by server_id: #incident.mirth_server_id
respond_to do |format|
if #intervention.save
format.html { redirect_to new_intervention_path(#incident), notice: 'Intervention was successfully created.' }
format.json { render :show, status: :created, location: #intervention }
else
format.html { render :new, incident: :incident_id }
format.json { render json: #intervention.errors, status: :unprocessable_entity }
end
end
end
< .... >
def intervention_params
params.require(:intervention).permit(:user_id, :incident_id, :incident_priority_id, :begin_date, :end_date, :description,
:intervention_status_id, :forwarded_to)
end
In my view (interventions_form.html.erb):
<%= form_for(#intervention) do |f| %>
<% if #intervention.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#intervention.errors.count, "error") %> prohibited this intervention from being saved:</h2>
<ul>
<% #intervention.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="form-group form-inline">
<%= hidden_field_tag :user_id, #user.id %>
<%= hidden_field_tag :incident_id, #incident.id %>
<strong>Interveniente:</strong>
<%= #user.first_name %> <%= #user.last_name %>
</div>
<div class="form-group form-inline">
<%= f.label 'Prioridade' %>
<%= f.collection_select :incident_priority_id, IncidentPriority.all, :id, :description, {}, {class: "form-control"} %>
</div>
<div class="form-group form-inline">
<%= f.label 'Data início intervenção' %>
<%= f.datetime_select :begin_date %>
</div>
<div class="form-group form-inline">
<%= f.label 'Data fim intervenção' %>
<%= f.datetime_select :end_date, :include_blank => true %>
</div>
<div class="form-group form-inline">
<%= f.label 'Observações' %>
<%= f.text_area :description %>
</div>
<div class="form-group form-inline">
<%= f.label 'Estado' %>
<%= f.collection_select :intervention_status_id, InterventionStatus.all, :id, :description, {}, {class: "form-control"} %>
</div>
<div class="form-group form-inline">
<%= f.label 'Encaminhado para:' %>
<%= f.text_area :forwarded_to %>
</div>
<div class="actions" align="right">
<%= link_to 'Voltar', incidents_path(:mirth => #mirth, :project => #project), class: "btn btn-info" %>
<%= f.submit "Gravar", class: "btn btn-info" %>
</div>
I run debug and the values in the hidden_field_tags are correctly filled. Also in the controller the #user and #incident are correctly populated, but #intervention has nil in the foreign keys :user_id and :incident_id.
Thanks in advance!
You need to associate the user and the incident with the intervention.
in your model...
class Intervention
belongs_to :user
belongs_to :incident
in your create method...
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
#intervention = Intervention.create(intervention_params)
#intervention.incident = #incident
#intervention.user = #user
...
You need to change this:
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
# ...
end
For this:
def create
#incident = Incident.find(params[:incident_id])
#user = User.find(params[:user_id])
intervention_params.merge(user_id: #user.id, incident_id: #incident.id)
# ...
end
And your model:
class Intervention
belongs_to :user
belongs_to :incident