Search code examples
ruby-on-railsrubyroutessimple-formnested-routes

Why does simple_form raise an "No route matches [POST] "/tenants"" error with nested resources


I am trying to make a simple form for creating a new Tenant, knowning that when you create a tenant, a Devise User(with "role: "tenant") has to be created at the same time. But when I submit the form it raises a "Routing Error" (No route matches [POST] "/tenants"). (I'm using Devise and Pundit)

I have a Property model having many Tenants models

class Property < ApplicationRecord
  belongs_to :owner
  has_many :tenants, dependent: :destroy

  validates :address, presence: true
end

class Tenant < ApplicationRecord
  belongs_to :user
  belongs_to :property
  has_many :incidents, dependent: :destroy

  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :email, presence: true, uniqueness: true
  validates :phone, presence: true
  validates :rental_price, presence: true
  validates :start_date, presence: true
  validates :rental_time, presence: true
end

Here are the routes:

Rails.application.routes.draw do
  root to: 'pages#home'
  devise_for :users

  resources :agencies do
    resources :owners, only: [:index, :new, :create]
  end
  resources :owners, only: [:show, :edit, :update, :destroy] do
    resources :properties, only: [:new, :create]
  end
  resources :properties, only: [:index, :show, :edit, :update, :destroy] do
    resources :tenants, only: [:new, :create]
  end
  resources :tenants, only: [:index, :show, :edit, :update, :destroy] do
    resources :incidents, only: [:new, :create]
  end
  resources :incidents, only: [:index, :show, :edit, :update, :destroy]
  resources :workers

the tenants controller:

class TenantsController < ApplicationController
  before_action :set_tenant, only: [:show, :edit, :update, :destroy]

  def create
    @tenant = Tenant.new(tenant_params)
    @tenant.user = User.create(email: @tenant.email, password: random_password, role: "tenant")
    @tenant.property = Property.find(params[:property_id])
    if @tenant.save
      redirect_to :root
    else
      render :new
    end
    authorize(@tenant)
  end

  def new
    @tenant = Tenant.new
    @tenant.user = User.new
    @tenant.property = Property.find(params[:property_id])
    authorize(@tenant)
  end

  def show
    authorize(@tenant)
  end

  def index
    @tenants = policy_scope(Tenant).order(created_at: :desc)
  end

  def edit
    authorize(@tenant)
  end

  def update
    authorize(@tenant)
  end

  def destroy
    authorize(@tenant)
    @tenant.destroy
    redirect_back(fallback_location: root_path)
  end

  private

  def random_password
    (('0'..'9').to_a + ('A'..'Z').to_a + ('a'..'z').to_a).shuffle.first(6).join
  end

  def set_tenant
    @tenant = Tenant.find(params[:id])
  end

  def tenant_params
    params.require(:tenant).permit(:first_name, :last_name, :email, :phone, :rental_price, :start_date, :rental_time)
  end
end

the new.html.erb

<%= simple_form_for [@property, @tenant] do |f| %>
  <%= f.input :first_name, label: false, placeholder: 'Prénom' %>
  <%= f.input :last_name, label: false, placeholder: 'Nom' %>
  <%= f.input :email, label: false, placeholder: 'Email' %>
  <%= f.input :phone, label: false, placeholder: 'Téléphone' %>
  <%= f.input :rental_price, label: false, placeholder: 'Montant du loyer en euros' %>
  <%= f.input :start_date, placeholder: 'Date de début de location' %>
  <%= f.input :rental_time, label: false, placeholder: 'Durée de location' %>

  <%= f.submit "  OK  " %>
<% end %>

and the policies

class TenantPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      scope.all
    end
  end

  def show?
    admin? || (agency? && user.agency.tenants.include?(record)) || (tenant? && user.tenant == record)
  end

  def index?
    admin? || (agency? && (record - user.agency.tenants).empty?)
  end

  def create?
    admin? || (agency? && user.agency.properties.include?(record.property))
  end

  def update?
    admin?
  end

  def destroy?
    admin? || (agency? && user.agency.tenants.include?(record))
  end
end

I would really like to understand why it doesnt work here as i made exactly the same thing for the property model (belonging to owner) and it worked. Thanks!


Solution

  • I found what was wrong:

    I forgot to instantiate @property in my controller methods.