I try to reproduce in Rails 4 a functionality that was working fine in Rails 3: create a child record from the Show view of its parent using new_parent_child_path(@parent)
.
Unfortunately, when I validate the child creation, I get a message saying that the child.parent_id field cannot be null
. Reading around, it looks that this new_parent_child_path(@parent) functionality is indeed hardly used. Here are parts of my code...
Models:
class BusinessArea < ActiveRecord::Base
... some checks ...
validates :playground_id, presence: true
belongs_to :playground
belongs_to :owner, :class_name => "User", :foreign_key => "owner_id" # helps retrieving the owner name
belongs_to :status, :class_name => "Parameter", :foreign_key => "status_id" # helps retrieving the status name
has_many :business_flows
end
class BusinessFlow < ActiveRecord::Base
... some checks ...
validates :playground_id, presence: true
belongs_to :playground
belongs_to :owner, :class_name => "User", :foreign_key => "owner_id" # helps retrieving the owner name
belongs_to :status, :class_name => "Parameter", :foreign_key => "status_id" # helps retrieving the status name
has_many :business_processes
belongs_to :business_area
end
Routes:
ODQStep1::Application.routes.draw do
#static pages
get '/help', to: "static_pages#help"
get '/about', to: "static_pages#about"
get '/contact', to: "static_pages#contact"
get '/home', to: "static_pages#home"
#root definition
root to: "static_pages#home"
resources :sessions, only: [:new, :create, :destroy]
get '/signin', to: 'sessions#new' , via: :get
match '/signout', to: 'sessions#destroy', via: :delete
resources :parameters
resources :business_areas do
resources :business_flows, :only=>[:new, :create]
end
end
Link to create child business flow is at the end of the Business Area Show view:
<% provide(:title, 'Managing business areas') %>
<h1>Business area: <%= @business_area.name %></h1>
<ul class="mid_menu">
<li><%= link_to 'Edit', edit_business_area_path(@business_area) %> |</li>
<li><%= link_to 'Back to list', business_areas_path %> |</li>
<li><%= link_to 'Destroy', @business_area, confirm: 'Are you sure?', method: :delete %></li>
</ul>
<!-- <p id="notice"><%= notice %></p> -->
<div class="tabbable">
<ul class="nav nav-tabs">
<li class="active"><a href="#tab1" data-toggle="tab">Definition</a></li>
<li><a href="#tab2" data-toggle="tab">Ownership</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab1">
<!-- Tab content for Definition -->
<div class="row">
<div class="span2 text-right">Name:
</div>
<div class="span6"><%= @business_area.name%>
</div>
<div class="span1 text-right">Code:
</div>
<div class="span1"><%= @business_area.code%>
</div>
<div class="span1 text-right">Status:
</div>
<div class="span1"><%= @business_area.status.name%>
</div>
</div>
<div class="row">
<div class="span2 text-right">Description:
</div>
<div class="span10"><%= @business_area.description%>
</div>
</div>
<div class="row">
<div class="span2 text-right">Hierarchy:
</div>
<div class="span2"><%= @business_area.hierarchy%>
</div>
</div>
<div class="row">
<div class="span2 text-right">PCF index:
</div>
<div class="span2"><%= @business_area.PCF_index%>
</div>
<div class="span2 text-right">PCF reference:
</div>
<div class="span2"><%= @business_area.PCF_reference%>
</div>
</div>
<!-- Tab content -->
</div>
<div class="tab-pane" id="tab2">
<!-- Tab content for Ownership -->
<div class="row">
<div class="span2 text-right">Unique id:
</div>
<div class="span2"><%= @business_area.id%>
</div>
<div class="span2 text-right">Created by:
</div>
<div class="span2"><%= @business_area.created_by%>
</div>
</div>
<div class="row">
<div class="span2 text-right">Playground id:
</div>
<div class="span2"><%= @business_area.playground_id%>
</div>
<div class="span2 text-right">Created at:
</div>
<div class="span2"><%= @business_area.created_at%>
</div>
</div>
<div class="row">
<div class="span2 text-right">Owner:
</div>
<div class="span2"><%= @business_area.owner.login%>
</div>
<div class="span2 text-right">Updated by:
</div>
<div class="span2"><%= @business_area.updated_by%>
</div>
</div>
<div class="row">
<div class="span2 text-right">
</div>
<div class="span2">
</div>
<div class="span2 text-right">updated at:
</div>
<div class="span2"><%= @business_area.updated_at%>
</div>
</div>
<!-- Tab content -->
</div>
</div>
</div>
<table width=100%>
<tr><td><hr /></td></tr>
<tr align="left">
<th>Linked Business Flows</th>
<th></th>
</tr>
<tr>
<td>
<table class="table table-striped table-condensed">
<%@business_area.business_flows.each do |business_flow| %>
<tr align="left">
<td valign="top"> <%=link_to business_flow.code, business_flow%> </td>
<td valign="top"> <%=business_flow.name%> </td>
<td valign="top"> <%=business_flow.description%> </td>
<td> </td>
</tr>
<% end%>
</table>
</td>
</tr>
<tr>
<td>
<%= link_to 'Add business flow', new_business_area_business_flow_path(@business_area) %>
</td>
</tr>
</table>
Business flow form
<%= form_for [@business_area, @business_flow] do |f| %>
<% if @business_flow.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@business_flow.errors.count, "error") %> prohibited this business_flow from being saved:</h2>
<ul>
<% @business_flow.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<div class="span2 text-right">Name:
</div>
<div class="span10 field"><%= f.text_field :name, :class => "span8" %>
</div>
</div>
<div class="row">
<div class="span2 text-right">Code:
</div>
<div class="span2 field"><%= f.text_field :code %>
</div>
<div class="span2 text-right">Status:
</div>
<div class="span2 field"><%= f.collection_select :status_id, @statuses_list, :id, :name %>
</div>
</div>
<div class="row">
<div class="span2 text-right">Description:
</div>
<div class="span10 field"><%= f.text_area :description, :rows => 5, :class => "span8" %>
</div>
</div>
<div class="row">
<div class="span2 text-right">Hierarchy:
</div>
<div class="span2 field"><%= f.text_field :hierarchy %>
</div>
</div>
<div class="row">
<div class="span2 text-right">PCF index:
</div>
<div class="span2 field"><%= f.text_field :PCF_index %>
</div>
<div class="span2 text-right">PCF reference:
</div>
<div class="span2 field"><%= f.text_field :PCF_reference %>
</div>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Business Flows controller
class BusinessFlowsController < ApplicationController
# Check for active session
before_action :signed_in_user
# Retrieve current business flow
before_action :set_business_flow, only: [:show, :edit, :update, :destroy]
# Create the list of statuses to be used in the form
before_action :set_statuses_list, only: [:new, :edit, :update, :create]
# GET /business_flows
# GET /business_flows.json
def index
@business_flows = BusinessFlow.order("hierarchy ASC")
respond_to do |format|
format.html # index.html.erb
format.json { render json: @business_flows }
end
end
# GET /business_flows/1
# GET /business_flows/1.json
def show
### Retrieved by Callback function
end
# GET /business_flows/new
# GET /business_flows/new.json
def new
@business_flow = BusinessFlow.new
@business_flow.business_area_id = params[:business_area_id]
end
# GET /business_flows/1/edit
def edit
### Retrieved by Callback function
end
# POST /business_flows
# POST /business_flows.json
def create
@business_flow = BusinessFlow.new(business_flow_params)
@business_flow.business_area_id = params[:business_area_id]
@business_flow.updated_by = current_user.login
@business_flow.created_by = current_user.login
@business_flow.playground_id = current_user.current_playground_id
@business_flow.owner_id = current_user.id
respond_to do |format|
if @business_flow.save
format.html { redirect_to @business_flow, notice: 'Business flow was successfully created.' }
format.json { render json: @business_flow, status: :created, location: @business_flow }
else
format.html { render action: "new" }
format.json { render json: @business_flow.errors, status: :unprocessable_entity }
end
end
end
# PUT /business_flows/1
# PUT /business_flows/1.json
def update
### Retrieved by Callback function
@business_flow.updated_by = current_user.login
respond_to do |format|
if @business_flow.update_attributes(business_flow_params)
format.html { redirect_to @business_flow, notice: 'Business flow was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @business_flow.errors, status: :unprocessable_entity }
end
end
end
# DELETE /business_flows/1
# DELETE /business_flows/1.json
def destroy
### Retrieved by Callback function
@business_flow.destroy
respond_to do |format|
format.html { redirect_to business_flows_url }
format.json { head :no_content }
end
end
### private functions
private
### Use callbacks to share common setup or constraints between actions.
# Retrieve current business flow
def set_business_flow
@business_flow = BusinessFlow.includes(:owner, :status).find(params[:id])
end
### before filters
# Check for active session
def signed_in_user
redirect_to signin_url, notice: "You must log in to access this page." unless signed_in?
end
### strong parameters
def business_flow_params
params.require(:business_flow).permit(:code, :name, :hierarchy, :status_id, :PCF_index, :PCF_reference, :description)
end
end
Log when clicking the New business flow link
--- !ruby/hash:ActionController::Parameters
action: new
controller: business_flows
business_area_id: '2'
Log when validating business flow creation
--- !ruby/hash:ActionController::Parameters
utf8: ✓
authenticity_token: PwhUukDm3f0OIDhOZfeKxwgtH7M5GFoOuy63Mt7Tcw=
business_flow: !ruby/hash:ActionController::Parameters
name: aaaaaaa
code: bbbbbbb
status_id: '8'
description: ''
hierarchy: cccccccc
PCF_index: ''
PCF_reference: ''
commit: Create Business flow
action: create
controller: business_flows
I could imagine hiding a business_area_id
field in the form, but I hope for a smarter way to have the parameter passed to the new() function also available in the create() function even though they have to go through the strong parameters.
Being a beginner, I can't say if my analysis of the problem is correct, and I hope you can help me finding a nice solution.
Thanks a lot for your help,
Best regards,
Fred
I see that your new action does have the business_area_id: '2'
parameter but, in your controller's new action you aren't instantiating the business_area. It should look like this:
class BusinessFlowsController < ApplicationController
def new
@business_area = BusinessArea.find params[:business_area_id]
@business_flow = BusinessFlow.new
# not needed here since this isn't being created yet
# @business_flow.business_area_id = params[:business_area_id]
end
... rest of code ...
end
Now that you instantiated the business_area
, the form_for
should be able to correctly build the nested path which should include the business_area_id
. Now your business_flows controller's create action should look like this:
def create
@business_area = BusinessArea.find params[:business_area_id]
@business_flow = @business_area.business_flows.build(business_flow_params)
# assigning the parent id happens automatically in the build method
# @business_flow.business_area_id = params[:business_area_id]
@business_flow.updated_by = current_user.login
@business_flow.created_by = current_user.login
@business_flow.playground_id = current_user.current_playground_id
@business_flow.owner_id = current_user.id
... rest of code ...
end
To make sure the form's action includes the business_area_id
check the rendered html. The form's attributes should look like:
action="/business_areas/<:business_area_id>/business_flows" method="post"
where <:business_area_id>
is the integer id of the parent, in your case 2
.
You're using nested routes so that's good. You didn't post your view code so I'm assuming you aren't POSTing to the create action of your business_flows controller using the nested route . Your business_flow view's form should look something like:
# in your controller: @business_flow = BusinessFlow.new
form_for [@business_area, @business_flow] do |f|
... rest of code ...
This will make the form post to the nested create action of your business_flows controller. That way the parent's id will be in the route as params[:business_area_id]
. Then you can simply assign that id to your child model like you do in your create action.
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.