Search code examples
ruby-on-railsrubyroutessimple-form

No route matches [POST] "/downtowns/1/properties/new"


I have an application that has a one to many relationship with downtowns and properties. What i've been having trouble with is creating new properties from the downtowns. I keep getting the error that I used as the title for this question.

I have a hunch that the problem is in my form, (which i'm using the simple form gem). I'm able to go to my new page, fill out the form, and then when I hit submit, I get this error.

I'll display my form file first. (I've had trouble understanding simple form's docs, and that's why i think this is where the problem is)

= simple_form_for (@property), method: 'POST', url: new_downtown_property_path do |f|
  = f.input :name
  = f.input :last_remodel
  = f.input :original_construction_year

My routes file

resources :downtowns do
  resources :properties
end

My downtown controller

def show
    @properties = Property.where(downtown: @downtown_id)
  end

  def new
    @downtown = Downtown.new
  end

  def create
    @downtown = Downtown.create(downtown_params)
    if @downtown.save
      redirect_to @downtown
    else
      render 'new'
    end
  end

  def downtown_params
    params.require(:downtown).permit(:name, :city)
  end

and my properties controller

  def new
    @property = Property.new
  end

  def create
    @downtown = property.find(id)
    @property = Property.create(params[:property_params])
    @property.downtown_id = @downtown.id

    if @property.save
      redirect_to @property
    else
      render 'new'
    end
  end

  def show
  end

Solution

  • = simple_form_for([@downtown, @property]) do |f|
      = f.input :name
      = f.input :last_remodel
      = f.input :original_construction_year
    

    Just pass an array to look up the nested route with the polymorphic route helpers. This will correctly use downtown_properties_path as the action. In Rails you always create resources by posting to the collection path (/downtowns/1/properties). The new route just renders a form. You don't need to specify the method as Rails detects if the model has been persisted and sets the method to POST or PATCH accordingly.

    You should also never add a space between the method name and the parens surrounding the argument list in Ruby:

    def add(a,b)
      a + b
    end
    
    add (1, 2) # syntax error
    add (1), 2 # this is what actually happens 
    

    Ruby behaves very differently then for example JS here since parens are optional for method calls.

    Your controller is also completely off. A nested resource controller should look something like.

    class PropertiesController < ApplicationController
      before_action :set_downtown
      before_action :set_property, only: [:show, :edit, :update, :destroy]
    
      # GET /downtowns/1/properties/new
      def new
        @property = @downtown.properties.new
      end
    
      # POST /downtowns/1/properties
      def create
        @property = @downtown.properties.new(property_params)
        if @property.save
          redirect_to @property 
        else
          render :new
        end
      end
    
      # ...
    
      private
    
      def set_downtown
        @downtown = Downtown.includes(:properties).find(params[:downtown_id])
      end
    
      def set_propery
        @property = Property.find(params[:id])
      end
    
      def property_params
        params.require(:downtown)
              .permit(:name, :city)
      end
    
      # ...
    end