Rails 5.1.5 Admin命名空间关联模型报错:BsController#create NoMethodError
Hey there, let's figure out how to fix that NoMethodError in your Admin::BsController#create action and get your associated models working smoothly within the Admin namespace. I'll break this down step by step based on Rails 5.1.5 conventions.
1. First, Confirm Your Controller Structure
Make sure your Admin namespace controller is set up correctly. It should inherit from either a dedicated Admin base controller (best practice for shared admin logic) or ApplicationController:
# app/controllers/admin/bs_controller.rb class Admin::BsController < Admin::BaseController # If you don't have an Admin::BaseController yet, use this fallback: # class Admin::BsController < ApplicationController end
2. Set Up Nested Routes (The Secure, Rails-Way Approach)
Since B belongs to A, using nested routes in the Admin namespace will automatically pass the a_id to your controller, avoiding manual hidden fields and reducing error chances. Update your config/routes.rb:
namespace :admin do resources :as do resources :bs, only: [:new, :create] # Add other actions like :edit, :destroy if needed end end
This generates routes like /admin/as/:a_id/bs which includes the parent A's ID directly in the request parameters.
3. Fix the #create Action in Your Controller
The most common cause of that NoMethodError is calling bs on a nil @a instance. Here's the correct way to build and save your B record using the association:
def create # Fetch the parent A record using the a_id from nested routes @a = A.find(params[:a_id]) # Build a new B directly associated with @a (automatically sets b.a_id) @b = @a.bs.build(b_params) if @b.save # Redirect back to the parent A's admin show page with a success message redirect_to admin_a_path(@a), notice: 'B was successfully created!' else # Re-render the new form with validation errors render :new end end private # Strong params to permit only allowed attributes for B (update with your actual fields) def b_params params.require(:b).permit(:name, :description, :any_other_b_attribute) end
If you still see undefined method 'bs' for nil:NilClass, debug by adding puts params.inspect to the create action—this will show if params[:a_id] is missing, or if the A record with that ID doesn't exist.
4. Update Your Form to Use the Nested Association
In your app/views/admin/bs/new.html.erb form, pass both the parent @a and new @b as an array to the form helper. This tells Rails to use the nested route we set up:
<%# Using form_with (Rails 5.1+ default) %> <%= form_with(model: [@a, @b], local: true) do |form| %> <% if @b.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@b.errors.count, "error") %> prohibited this B from being saved:</h2> <ul> <% @b.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <!-- Add your B model fields here --> <div class="field"> <%= form.label :name %> <%= form.text_field :name %> </div> <div class="actions"> <%= form.submit %> </div> <% end %>
Don't forget to set both @a and @b in your #new action:
def new @a = A.find(params[:a_id]) @b = @a.bs.new end
5. What If You Don't Want Nested Routes?
If nested routes don't fit your workflow, you can manually pass the a_id via a hidden field. Just adjust your controller and form:
Controller Adjustment:
def create @b = B.new(b_params) if @b.save redirect_to admin_b_path(@b), notice: 'B created successfully!' else render :new end end private def b_params params.require(:b).permit(:a_id, :name, ...) # Include a_id in permitted params end
Form Adjustment:
<%= form_with(model: @b, local: true) do |form| %> <%= form.hidden_field :a_id, value: @a.id %> <!-- Rest of your B fields --> <% end %>
Note: This approach is less secure, as users could modify the a_id value in the browser. Nested routes are always the better choice for ownership-based associations.
内容的提问来源于stack exchange,提问作者amarradi

