Search code examples
ruby-on-railsdevisemany-to-manypgpundit

Unable to detect error in many to many relationship


I have a bug, that is driving me crazy for a few days now....I set up a many to many relationship between users (created by devise) and parks through table user_parks. When I want to create a new park with a certain user, I get the error message: "undefined method `user=' for # Did you mean? users= users". Unfortunately, I am not able to find the mistake in my code that prevents me from creating the relationship between user and parks.

Schema

create_table "parks", force: :cascade do |t|
    t.text "name"
end

create_table "user_parks", force: :cascade do |t|
    t.bigint "park_id"
    t.bigint "user_id"
    t.index ["park_id"], name: "index_user_parks_on_park_id"
    t.index ["user_id"], name: "index_user_parks_on_user_id"
end

create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.integer "role"
end

  add_foreign_key "user_parks", "parks"
  add_foreign_key "user_parks", "users"
end

models

class Park < ApplicationRecord
  has_many :user_parks
  has_many :users, :through => :user_parks
end

class UserPark < ApplicationRecord
  belongs_to :park
  belongs_to :user
end

class User < ApplicationRecord
  has_many :user_parks
  has_many :parks, :through =>  :user_parks
  enum role: [:owner, :admin, :employee, :accountant]

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
end

Park policy

class ParkPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
     if user.admin?
        scope.where(user: user)
      end
    end
  end

  def index?
    user.admin?
  end

  def new?
    user.admin?
  end

  def create?
    user.admin?
  end
end

parks_controller

class ParksController < ApplicationController
  skip_before_action :authenticate_user!

  #create a new park
  def new
    @park = Park.new
    authorize @park
  end

  def create
    @user = current_user
    @park = Park.new(park_params)
    authorize @park
    @park.user = @user
    @park.save
    redirect_to parks_path
  end

  #edit park
  def edit
    @park = Park.find(params[:id])
  end

  def update
    @park = Park.find(params[:id])
    @park = Park.update(park_params)
    redirect_to parks_path
  end

  def index
    @user = current_user
    @parks = policy_scope(Park)
  end

private
  def park_params
    params.require(:park).permit(:name)
  end
end

new.html.erb (for park)

<%= simple_form_for ([@user, @park]) do |f|%>
  <%= f.input :name %>
  <%= f.submit "Add park", class: "btn btn-primary" %>
<% end %>

When I want to create a new park with a certain user, I get the error message: "undefined method `user=' for # Did you mean? users= users".


Solution

  • You have has many association. Instead of @user.park you need to use @user.parks

    The easiest way is to initialize park object before you initialize form

    parks_controller

    def new
      @park = current_user.parks.build
      authorize @park
    end
    
    def create
      @park = current_user.parks.create(park_params)
      redirect_to parks_path
    end
    

    new.html.erb (for park)

    <%= simple_form_for(@park) do |f|%>
      <%= f.input :name %>
      <%= f.submit "Add park", class: "btn btn-primary" %>
    <% end %>
    

    you can check methods available for object with has many association here: docs