Rails 4 Nested Resources/Routes… almost there…?

2019-08-31 01:17发布

问题:

I have three models - users, manufacturers, and lines. I need users to have multiple manufacturers, and each manufacturer to have multiple lines. I'm to the point where lines is finally pulling the manufacturer_id on the _form, but now it's not saving the 'name' of the 'line', only the id. It'll save the 'name' as nil, like it's not passing in the name from the form. I've read the standard docs on this and I can't for the life of me get it working. It keeps hanging up on the final create stage in the controller, and I have a feeling that I've got a lot of things wrong in the paths of the form and the controller. Hoping someone will take it easy on me and help me get the whole shebang working.

user.rb

class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  has_many :manufacturers
end

manufacturer.rb

class Manufacturer < ActiveRecord::Base
  belongs_to :user
  has_many :lines
end

line.rb

class Line < ActiveRecord::Base
  belongs_to :manufacturer
end

lines_controller.rb

class LinesController < ApplicationController
  before_action :set_line, only: [:show, :edit, :update, :destroy]
  before_filter :load_manufacturer

  # GET /lines
  # GET /lines.json
  def index
    @lines = Line.all
  end

  # GET /lines/1
  # GET /lines/1.json
  def show
  end

  # GET /lines/new
  def new
    @manufacturer = Manufacturer.find(params[:manufacturer_id])
    @line = @manufacturer.lines.build
  end

  # GET /lines/1/edit
  def edit
  end

  # POST /lines
  # POST /lines.json
  def create
    @line = @manufacturer.lines.build

    respond_to do |format|
      if @line.save
        format.html { redirect_to @line, notice: 'Line was successfully created.' }
        format.json { render action: 'show', status: :created, location: @line }
      else
        format.html { render action: 'new' }
        format.json { render json: @line.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /lines/1
  # PATCH/PUT /lines/1.json
  def update
    respond_to do |format|
      if @line.update(line_params)
        format.html { redirect_to @line, notice: 'Line was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @line.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /lines/1
  # DELETE /lines/1.json
  def destroy
    @line.destroy
    respond_to do |format|
      format.html { redirect_to lines_url }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_line
      @line = Line.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def line_params
      params.require(:line).permit(:name, :manufacturer_id)
    end

    def load_manufacturer
      @manufacturer = Manufacturer.find(params[:manufacturer_id])
    end
end

lines/_form.html.erb

<%= form_for [@manufacturer,@line] do |f| %>
  <% if @line.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@line.errors.count, "error") %> prohibited this line from being saved:</h2>

      <ul>
      <% @line.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :manufacturer_id %><br>
    <%= f.text_field :manufacturer_id %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

routes.rb

Linecarder::Application.routes.draw do
  resources :manufacturers do
    resources :lines
  end

  devise_for :users
  root "pages#home"

  get "about" => "pages#about"

  match 'users/:id' => 'users#show', :via => "get"
  match '/users', :to => 'users#index', :as => "all_users", :via => "get"
  match '/users/:name' => 'users#show', via: :get, as: :public_profile
end

rake routes

                  Prefix Verb   URI Pattern                                              Controller#Action
      manufacturer_lines GET    /manufacturers/:manufacturer_id/lines(.:format)          lines#index
                         POST   /manufacturers/:manufacturer_id/lines(.:format)          lines#create
   new_manufacturer_line GET    /manufacturers/:manufacturer_id/lines/new(.:format)      lines#new
  edit_manufacturer_line GET    /manufacturers/:manufacturer_id/lines/:id/edit(.:format) lines#edit
       manufacturer_line GET    /manufacturers/:manufacturer_id/lines/:id(.:format)      lines#show
                         PATCH  /manufacturers/:manufacturer_id/lines/:id(.:format)      lines#update
                         PUT    /manufacturers/:manufacturer_id/lines/:id(.:format)      lines#update
                         DELETE /manufacturers/:manufacturer_id/lines/:id(.:format)      lines#destroy
           manufacturers GET    /manufacturers(.:format)                                 manufacturers#index
                         POST   /manufacturers(.:format)                                 manufacturers#create
        new_manufacturer GET    /manufacturers/new(.:format)                             manufacturers#new
       edit_manufacturer GET    /manufacturers/:id/edit(.:format)                        manufacturers#edit
            manufacturer GET    /manufacturers/:id(.:format)                             manufacturers#show
                         PATCH  /manufacturers/:id(.:format)                             manufacturers#update
                         PUT    /manufacturers/:id(.:format)                             manufacturers#update
                         DELETE /manufacturers/:id(.:format)                             manufacturers#destroy
        new_user_session GET    /users/sign_in(.:format)                                 devise/sessions#new
            user_session POST   /users/sign_in(.:format)                                 devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)                                devise/sessions#destroy
           user_password POST   /users/password(.:format)                                devise/passwords#create
       new_user_password GET    /users/password/new(.:format)                            devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format)                           devise/passwords#edit
                         PATCH  /users/password(.:format)                                devise/passwords#update
                         PUT    /users/password(.:format)                                devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)                                  devise/registrations#cancel
       user_registration POST   /users(.:format)                                         devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)                                 devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)                                    devise/registrations#edit
                         PATCH  /users(.:format)                                         devise/registrations#update
                         PUT    /users(.:format)                                         devise/registrations#update
                         DELETE /users(.:format)                                         devise/registrations#destroy
                    root GET    /                                                        pages#home
                   about GET    /about(.:format)                                         pages#about
                         GET    /users/:id(.:format)                                     users#show
               all_users GET    /users(.:format)                                         users#index
          public_profile GET    /users/:name(.:format)                                   users#show

回答1:

Update the create action as below:

 def create
    @line = @manufacturer.lines.build(line_params) ## Pass the params via line_params

    respond_to do |format|
      if @line.save
        format.html { redirect_to @line, notice: 'Line was successfully created.' }
        format.json { render action: 'show', status: :created, location: @line }
      else
        format.html { render action: 'new' }
        format.json { render json: @line.errors, status: :unprocessable_entity }
      end
    end
  end

You are not passing the params that you received from the form. Hence, name of line is not getting saved in database. Also, in order to permit the attributes for mass-assignment(strong parameters) pass them via line_params method.

UPDATE for OP's question in the comment to resolve undefined method 'line_url'... for 'format.html { redirect_to @line, notice: 'Line was successfully created.' }'

Use this

format.html { redirect_to manufacturer_line_path(@manufacturer, @line), notice: 'Line was successfully created.' }

instead of

format.html { redirect_to @line, notice: 'Line was successfully created.' }