2014-12-02 61 views
2

第一貼這裏,所以我希望它是有道理的,但我一直在旋轉我的車輪太久了。 首先是一個小背景。我正在構建一個包含3個模型的Wiki應用程序:用戶,Wiki和協作(連接表)。評論家的政策範圍有很多通過關係

我正在使用Devise和Pundit,並有4類用戶應根據他們的狀態查看維基的不同子集。

這裏的規則:

  • 公衆用戶(未登錄) - 應查看(無需編輯)僅公共維基 (:隱藏=>假)
  • 身份驗證的用戶(:角色=>「標準「) - 應 查看,編輯&僅刪除公共維基。
  • 高級用戶(:角色=> 「溢價」) - 查看,編輯,刪除公共wiki和創造 私人維基的能力(:隱藏=>真),並添加合作者的私人 維基這給他們編輯維基的權利。
  • Admin(:role =>「admin」)完全控制所有記錄。

所以我在政策範圍內(用戶檢查狀態)一個漫長scope.joins條件我維基索引視圖給予CURRENT_USER的基於角色維基列表的一個子集。

信念使噴出了這樣的錯誤:

Started POST "/__better_errors/59802a57d82fd17e/variables" for 127.0.0.1 at 2014-12-02 09:05:41 -0800 
    Wiki Load (0.4ms) SELECT "wikis".* FROM "wikis" INNER JOIN "collaborations" ON "collaborations"."wiki_id" = "wikis"."id" WHERE (hide = 'f' or user_id = 2 or collaborations.user_id = 2) 
SQLite3::SQLException: ambiguous column name: user_id: SELECT "wikis".* FROM "wikis" INNER JOIN "collaborations" ON "collaborations"."wiki_id" = "wikis"."id" WHERE (hide = 'f' or user_id = 2 or collaborations.user_id = 2) 

這裏的政策

class WikiPolicy < ApplicationPolicy 

    # What collections a user can see users `.where` 
    class Scope < Scope 

    def resolve 
     if user && user.role == 'admin' 
     scope.all 
     elsif user 
     scope.joins(:collaborations).where("hide = :hide or user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id}) 
     else 
     scope.where hide: false 
     end 
    end 
    end 


    #Policies are boolean logic true or false to determine if a user has access to a controller action. 
    def update? 
    (user && user.role == 'admin') || (user && record.users.pluck(:id).include?(user.id)) || (user && user.id == record.owner.id) 
    end 

    def show? 
    (user.present? && user.admin?) or not record.hide? 
    end 

    def premium? 
    user.admin? or user.premium? 
    end 

    def edit? 

    end 

end  

我的模型

class Wiki < ActiveRecord::Base 
    belongs_to :user 
    has_many :collaborations, dependent: :destroy 
    has_many :users, through: :collaborations 

    def owner 
    user 
    end 

    def collaborators 
    users 
    end 

    validates :title, length: { minimum: 5 }, presence: true 
    validates :body, length: { minimum: 20 }, presence: true 

end 

class User < ActiveRecord::Base 
    devise :database_authenticatable, :registerable, 
     :recoverable, :rememberable, :trackable, :validatable, :confirmable 
    has_many :wikis 
    has_many :collaborations 
    has_many :cowikis, through: :collaborations, source: :wiki 

    after_initialize :init 

    def admin? 
    role == 'admin' 
    end 

    def premium? 
    role == 'premium' 
    end 

    def standard? 
    role == 'standard' 
    end 

private 
    def init 
    if self.new_record? && self.role.nil? 
     self.role = 'standard' 
    end 
    end 
end 

class Collaboration < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :wiki 
end 

維基控制器

class WikisController < ApplicationController 
    def index 
    @wikis = policy_scope(Wiki) 
    # authorize @wikis 
    end 

    def show 
    @wiki = Wiki.find(params[:id]) 
    authorize @wiki 
    end 

    def new 
    @wiki = Wiki.new 
    authorize @wiki 
    end 

    def create 
    @wiki = Wiki.new(wiki_params) 
    authorize @wiki 
    if @wiki.save 
     flash[:notice] = "Post was saved." 
     redirect_to @wiki 
    else 
     flash[:error] = "There was an error saving the post. Please try again." 
     render :new 
    end 
    end 

    def edit 
    @user = current_user 
    @users = User.all 
    @wiki = Wiki.find(params[:id]) 
    authorize @wiki 
    end 

def update 
    @wiki = Wiki.find(params[:id]) 
    authorize @wiki 
    @wiki.collaborators = params[:wiki][:user_ids] 
    if @wiki.update_attributes(wiki_params) 
    flash[:notice] = "Post was updated." 
    redirect_to @wiki 
    else 
    flash[:error] = "There was an error saving the post. Please try again." 
    render :edit 
    end 
end 

    def destroy 
    @wiki = Wiki.find(params[:id]) 
    authorize @wiki 
    title = @wiki.title 

    if @wiki.destroy 
     flash[:notice] = "\"#{title}\" was deleted successfully." 
     redirect_to wikis_path 
    else 
     flash[:error] = "There was an error deleting the wiki." 
     render :show 
    end 
    end 

    private 

    def wiki_params 
    params.require(:wiki).permit(:title, :body, :hide) 
    end 
end 

我希望你是不是不知所措的代碼,但我想提供儘可能多的信息成爲可能。

感謝您的幫助!

哦,這是最有可能的違規代碼,但我包括上下文的一切。

scope.joins(:collaborations).where("hide = :hide or user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id}) 
+0

萬一有人正在做一個大的查詢不同的表長範圍內沒有返回預期值。我必須將範圍分爲2(一個用於本地和另一個連接)。我確信有一個更優雅的方式來做到這一點,但這是我的工作代碼。 '(scope.where(「hide =:hide or wikis.user_id =:owner_id」,{hide:false,owner_id:user.id})+ scope.joins(:collaborations).where(「collaborations.user_id =:collaborator_id 「,{collaborator_id:user.id}))。uniq' – 2014-12-02 23:46:14

回答

0

這是錯誤信息的最有價值的部分:

SQLite3::SQLException: ambiguous column name: user_id 

當SQL結果集具有相同名稱的多個列可能發生的不明確的列名錯誤。在這種情況下,列是user_id

user_id列位於wiki表和協作表中。這兩個表都用在SQL查詢中。

這裏的SQL代碼片段引起的問題:

# DB can't figure out which table "user_id = 2" refers to: 
WHERE (hide = 'f' or user_id = 2 or collaborations.user_id = 2) 

我們需要指定對user_id列的表名前綴。

要做到這一點,請嘗試更改此行WikiPolicy

scope.joins(:collaborations) 
    .where("hide = :hide or user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id}) 

到:

scope.joins(:collaborations) 
    .where("hide = :hide or wikis.user_id = :owner_id or collaborations.user_id = :collaborator_id", {hide: false, owner_id: user.id, collaborator_id: user.id}) 
+0

謝謝!那就是訣竅。它返回一組記錄。我的邏輯有點偏離,因爲我得到的是重複的,沒有公共記錄,但我可以解決這個問題。 – 2014-12-02 20:40:31