I'm encountering a pundit response which I didn't expect and I was wondering if someone could tell me what is happening.
Issue
In my index action I would like to show attractions that below to a park. Currently, this resulted in the following unexpected behavior(for me at least):
--> result >> @attractions
=> #<ActiveRecord::Associations::CollectionProxy [#<Attraction id: 185, park_id: 109, name: "Spider", description: "", status: nil, persons_max: 4, persons_min: 4, persons_included: 4, attraction_count: 4, enabled_attraction_count: nil, thumb: nil, included_services: nil, created_at: "2019-10-10 11:00:07", updated_at: "2019-10-12 07:31:27", photo: nil>, #<Attraction id: 189, park_id: 109, name: "Throwing balls at cans", description: nil, status: nil, persons_max: 1, persons_min: 1, persons_included: 1, attraction_count: 1, enabled_attraction_count: nil, thumb: nil, included_services: nil, created_at: "2019-10-15 14:29:28", updated_at: "2019-10-15 14:29:28", photo: nil>]>
--> result >> @attractions ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column attractions.user does not exist
LINE 1: ...ERE "attractions"."park_id" = $1 AND "attracti...
^
: SELECT "attractions".* FROM "attractions" WHERE "attractions"."park_id" = $1 AND "attractions"."user" = $2 LIMIT $3
from /Users/xx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `prepare'
from /Users/xx/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.3/lib/active_record/connection_adapters/postgresql_adapter.rb:677:in `block in prepare_statement'[]
What I am doing wrong and how can I make sure to only display attractions of a park after pundit?
Code attractions controller
def index
@user = current_user
if params[:park_id]
@park = @user.parks.find(params[:park_id])
@attractions = @park.attractions
1. text to stop app
@attractions = policy_scope(@attractions)
2. text to stop app
end
end
policies/attraction_policy.rb
class AttractionPolicy < ApplicationPolicy
class Scope < Scope
def resolve
if user.admin?
scope.where(user: user)
else
raise Pundit::NotAuthorizedError
end
end
end
def index?
user.admin?
end
end
models
class Attraction < ApplicationRecord
belongs_to :park
end
class Park < ApplicationRecord
has_many :attractions, dependent: :destroy
has_many :user_parks, dependent: :destroy
has_many :users, through: :user_parks
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
has_many :user_parks, dependent: :destroy
has_many :parks, through: :user_parks
enum role: [:owner, :admin, :employee, :accountant]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :admin
end
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :invitable
end
class UserPark < ApplicationRecord
belongs_to :park
belongs_to :user
end
You try to scope Attraction
by user
:
scope.where(user: user)
but you have neither user
nor user_id
column in your attractions
table, so this obviously isn't gonna work. If you want to set up user
association on attraction
model, you need to do:
rails g migration add_user_id_to_attractions user:references
and add
belongs_to :user
to Attraction
model.
If you want to stick to your indirect association, you can also achieve your goal by modifying your scope a little bit, using joins
:
scope.joins(park: :user_parks).where(user_parks: { user_id: user.id })