2011-02-12 51 views
5

繼承人我試圖實現的:如何在使用rails3創建新記錄之前檢查記錄是否存在?

  • 我有一個標記系統。
  • 當創建帖子時,會創建標籤(帖子has_many:tags,:through =>:tag_joins。
  • 當使用標籤創建帖子時,會自動創建標籤連接)。

我想檢查標籤是否已經存在。如果是這樣,我想爲tag_join記錄使用現有的標記,而不是創建新的標記記錄。

這是我目前的代碼,這是行不通的。

class Tag < ActiveRecord :: Base 
    belongs_to :user 
    belongs_to :tag_join 
    belongs_to :post 

    before_create :check_exists 

    def check_exists 
    tag = Tag.where(:name => self.name, :user_id => current_user.id) 
    if tag.nil? 
     tag = Tag.create(:name => self.name, :user_id => current_user.id) 
    end 
    end 

end 

這不工作,雖然,在創建任務,我得到一個錯誤......(服務器實際上只是超時 - 我沒有收到具體的錯誤)。

任何想法?

Tokland說我告訴它再次創建標籤創建一個無限循環 - 所以我想這:

def check_exists 
     tag = Tag.find_by_name_and_user_id(:name => self.name, :user_id => current_user.id) 
     if tag != nil 
     self.id = tag.id 
     end 
    end 

,仍然可以得到服務器超時

編輯:我不知道這很重要,但標籤添加的方式類似於「http://railscasts.com/episodes/73-complex-forms-part-1

它們嵌套在帖子表單中,並使用某些東西像這樣:

def tag_attributes=(tag_attributes) 
    tag_attributes.each do |attributes| 
    tags.build(attributes) 
    end 
end 

我想知道這是否阻止了整個工作?此外,在模型中使用current_user.id肯定似乎是一個問題......

編輯:

東西我已經想通了:這 不得不改變,我們使用之前的格式不正確,語法 - 通常用於.where方法。

def check_exists 
    @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) 
    if @tag != nil 
     #return false 
     #[email protected] 
    end 
    end 

現在的問題是這個,我可以知道它的標籤是否已經存在。但那又如何?如果我使用返回false選項,創建後會出現錯誤,並且不會創建連接記錄......另一個選項「self = @ tag」顯然不起作用。

+3

代碼審查時間:當命名例程時,你想使用一個名字來表示它的功能。當它實際上可以創建記錄時,調用一個方法「check_exists」可能會更難找到「該記錄來自哪裏?」一旦你在應用中獲得了很多代碼。也許「get_tag」或「find_or_create_tag」會更好?像這樣的微妙事物會累積在應用程序中,並隨着時間的推移確定其可讀性和可維護性。 –

+0

服務器超時可能與代碼無關。檢查它是否真的在運行,如果它在另一臺機器上,是否有連接。 –

+0

其實只是發生在我的本地環境中。每次嘗試通過rails管理員手動創建標記時,服務器都會凍結。 – Elliot

回答

11

你會很難從標籤模型中找到它。這似乎是你想要的是更新後使用嵌套的屬性,就像這樣:

post = Post.create 
post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 

這實際上是非常簡單的,通過使用虛擬屬性setter方法做:

class Post < AR::Base 
    has_many :tags 

    def tags_attributes=(hash) 
    hash.each do |sequence,tag_values| 
     tags << Tag.find_or_create_by_name_and_user_id(tag_values[:name],\ 
     tag_values[:user_id]) 
    end 
    end 

> post = Post.create 
> post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 
> Tag.count # => 1 
# updating again does not add dups 
> post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 
> Tag.count # => 1 
+1

非常感謝Zetetic,我一整天都在苦苦掙扎! – Elliot

+0

經過5個小時的搜索後我發現了這個,你是我的英雄 – cubny

8

你知道里面有一個find_by_or_create_by_函數,它適合Rails,對不對?

# No 'Summer' tag exists 
Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") 

# Now the 'Summer' tag does exist 
Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer") 

http://api.rubyonrails.org/classes/ActiveRecord/Base.html(下動態的基於屬性的發現者)

+0

如果我基於名稱和user_id檢查,但是如何使用這個?我應該在哪裏使用這個?在check_exists方法中? – Elliot

+0

是的,請參閱Rob Di Marco提供的一個簡單示例,請參見下文。 – Bjorn

6

你想用魔術方法find_or_create_by

def check_exists 
    tag = Tag.find_or_create_by_name_and_user_id(:name => self.name, :user_id => current_user.id) 
end 

退房的ActiveRecord::Base文檔的更多信息

+0

使用此工具獲取服務器超時 – Elliot

+1

問題是,check_exists方法是作爲before_create調用運行的,但它本身試圖創建標記。所以如果你想使用Tag.find_or_create_by_name_and_user_id,你應該從調用代碼(你的控制器可能?)那樣做。如果您試圖強制標籤只顯示一次名稱/用戶標識組合,請檢查validates_uniqueness_of以確保這一點。 –

+0

嘿羅布,這很有道理 - 我剛剛更新了這個問題。標籤創建的方式似乎很複雜(使用railscast ep - 如上面鏈接),我想知道是否應該在上面添加的方法中使用find_or_create? – Elliot

0

試試這個

def check_exists 
    tag = Tag.where(:name => self.name, :user_id => current_user.id).first 
    tag = Tag.new({:name => self.name, :user_id => current_user.id}) unless tag 
    end 

使用Tag.new而不是Tag.create

+0

Tag.where將返回集合,你可以使用 ''Tag.where(:name => self.name,:user_id => current_user.id).first'' – Pavel

+0

是的,你是對的。我只是複製粘貼該部分的問題,並沒有檢查該行。將進行編輯。 – rubyprince

4

我最初問的問題最終被扭曲了。所以我將它分開。

人們誰是試圖做什麼,我原來問可以試試這個:

before_create :check_tag exists 

private 

def check_tag_exists 
    @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) 
    if @tag != nil 
     # 
    end 
    end 

這將使你檢查你的記錄已經被創建。任何進一步的邏輯,你可以放在那裏,如果陳述。

+1

好的交易回來澄清。 – Mosselman

-2

那裏返回找不到匹配項時清空ActiveRecord。

3

我相信其他答案有點過時。下面是你應該如何實現Rails 4的代碼4

tag = Tag.first_or_initialize(:name => self.name, :user_id => current_user.id) 
if !tag.new_record? 
    tag.id = self.id 
    tag.save 
end 
相關問題