简体   繁体   中英

Rails: can't correctly invoke nested routes

I have the following routes:

shallow do
  resources :countries do
    resources :airports
  end
end

I'm having trouble with invoking two of the routes.

The airports_controller.rb file begins

def create
  Rails::logger.debug "!!! Building airport with parameters #{params}"
  @country = Country.find(params[:country_id])
  Rails::logger.debug "!!! Found airport #{@country.name}"
  @airport = @country.airports.build(params[:airport])

The last line gives the error Minitest::UnexpectedError: ActiveModel::ForbiddenAttributesError: ActiveModel::ForbiddenAttributesError , but the params I'm calling with are

!!! Building airport with parameters {"airport"=>{"iata_code"=>"CCC",
"icao_code"=>"CCCC", "name"=>"Airport 3", "city"=>"City 3", "latitude"=>"1.5",
"longitude"=>"1.5"}, "country_id"=>"980190962", "controller"=>"airports",
"action"=>"create"}

and as far as I can see all of those are in my permitted parameters:

def airport_params
  params.require(:airport).permit(:iata_code, :icao_code, :name, :city, :latitude, :longitude, :notes, :country_id)
end

Secondly, my airports\\_form.html.erb begins <%= form_for [@country, @airport] do |f| %> <%= form_for [@country, @airport] do |f| %> , but that gives an error Minitest::UnexpectedError: ActionView::Template::Error: undefined method 'airports_path' for #<#<Class:0x4b0ba30>:0x55a2e48> . Yes, that path is undefined, but I was trying to get to path country_airports_path , which is defined.

So what am I missing here?

In "def create"

def create
  ...
  @airport = @country.airports.build(airport_params)

You've correctly created the "airport_params" function, however you're not calling it. That should explain the ForbiddenAttributesError .

As for the undefined method, at first glance it looks like an incorrect association, are your models correctly related (ie belongs_to and has_many )? If so, you could try adding url: country_airports_path to the form_for field.

Attributes

The attributes error you've received is basically caused by your non-use of strong_params .

Rails 4 introduced strong_params to give you the ability to define specific attributes to pass to your model (prevents mass assignment). The problem you have is that you're using the Rails 3 way - passing all of the attributes without whitelisting them

As pointed out, you'll be better doing this:

#app/controllers/airports_controller.rb
Class AirportsController < ApplicationController
   def create
      @airport = Airport.new(airport_params)
   end

   private

   def airport_params
      params.permit(:airport).permit(:your, :params).merge(country_id: params[:country_id])
   end
end

However, I believe there's something deeper you need to consider.

You're currently using .build for your airport model object. Whilst there's nothing wrong with this, you need to consider what you're trying to achieve...

The Airport model object is a standalone object. You can easily associate it with your Country model by setting the foreign_key in your strong params (demonstrated above)

If you use this line: @airport = @country.airports.build , you're working with the Country object, which IMO will open yourself up to errors down the line. As mentioned, there's nothing "wrong" with what you're doing; it's just that I'd either work with the Airport model directly (as written above), or use accepts_nested_attributes_for to work with the Country model

--

Route

Secondly, your path error is going to be caused by your form_for

form_for builds a form from an ActiveRecord object - which it does by taking arguments such as model_name to build the "url" / "action" for the form.

This means every time you populate a form_for object with an ActiveRecord object (variable), Rails will build a route based off of what you pass it (it does not know whether the object is nested or not)

<%= form_for @airport do |f| %>
  # builds route using "airport_path"
<% end %>

If you want to create a nested form, you'll be much better using an array of ActiveRecord objects, as to provide Rails with the knowledge you're using a nested resource:

<%= form_for [@country, @airport] do |f| %>
   # builds route using "country_airport_path"
<% end %>

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM