2011-05-31 86 views
0

我有兩個模型:ConversationMessage。消息belongs_to的對話和交談的has_many消息:急切加載一個有很多關係的最近項目

# conversation.rb 
class Conversation < ActiveRecord::Base 
    has_many :messages 
end 

# message.rb 
class Message < ActiveRecord::Base 
    belongs_to :conversation 
end 

我有一個要列出所有對話的界面,但我也希望包括通過渴望與對話相關的「最新」消息 - 上下。

我是否需要在會話和跟蹤最新消息的消息之間創建新的has_one或belongs_to關係?或者有沒有一種方法可以通過使用連接/包含的範圍來完成此操作?

編輯:的問題是,我不希望這個循環的每個迭代要執行一個SQL查詢:

# controller 
@conversations = Conversation.all 

# view 
@conversations.each do |conversation| 
    most_recent_message = conversation.messages.last 
    # ... 
end 

編輯2:我也試着做到以下幾點,不成功。目標是在會話表中添加last_message_id列,以便引用最後添加的消息。

# migration 
add_column :conversations, :last_message_id, :integer 

# conversation.rb 
belongs_to :last_message, :class_name => 'Message' 

# message.rb 
after_create :set_last_message_on_conversation 

def set_last_message_on_conversation 
    self.conversation.last_message = self 
    self.conversation.save! 
end 

但是,由於創建對話時last_message_id爲空,導致SQL錯誤。 (我希望一切都能發生在一個事務,該交易完成之前,last_message_id將被更新,但我認爲這是一個MySQL/MyISAM的問題,即交易不被使用。)

ActiveRecord::StatementInvalid: Mysql::Error: Column 'last_message_id' cannot be null: INSERT INTO `conversations` (`last_message_id`, `updated_at`, `created_at`) VALUES (NULL, '2011-05-31 16:57:11', '2011-05-31 16:57:11')

回答

1

您可以使用另一種聯想:

# conversation.rb 
class Conversation < ActiveRecord::Base 
    has_many :messages 
    has_one :last_message, :class_name => "Message", :order => "created_at DESC" 
end 

# controller 
@conversations = Conversation.includes(:last_message) 

# view 
@conversations.each do |conversation| 
    most_recent_message = conversation.last_messages 
    # ... 
end 

被警告last_messagemessages,並認爲是獨立的協會。如果使用conversation.messages,則將從數據庫加載消息。如果更改last_message的屬性,則在重新加載之前它不會影響messages.last

編輯:實際上,根據日誌,ActiveRecord仍然加載所有對話的消息。可能是因爲它不能像「不限量加載」那樣「限制1」。

因此,就SQL請求而言,此解決方案似乎等同於僅使用Conversation.includes(:messages)。但是,當使用has_one關聯時,Rails可能不會處理額外的結果。

+0

看來這是最接近的我會得到,我想!我看到的唯一問題是內存問題,因爲當我只想爲每個對話設置一個對話時,它會加載與對話關聯的所有消息。 – 2011-06-01 19:22:03

+0

如果這確實是一個問題,存儲'last_message_id'可以工作。這個答案顯示了一種方法:http://stackoverflow.com/questions/6008015/how-can-i-sort-my-records-by-average-rating/6025838#6025838 – 2011-06-01 23:01:05

0

在你是什​​麼意思懶加載?只是在會話加載時最近的消息未加載?試試這個:

class Conversation < ActiveRecord::Base 
    has_many :messages, :order => "created_at" 
end 

然後,你可以調用

@conversation.messages.last 
這是在你實際上問的最後一個消息時執行SQL喜歡

SELECT "messages".* FROM "messages" WHERE ("messages".conversation_id = 129) ORDER BY created_at DESC LIMIT 1 

重要:如果你調用@動作的執行過程中conversation.messages在此之前,它可能已經加載所有的郵件已經,所以當你調用@ conversation.messages可能不會再次命中數據庫。持續。所以如果在@ conversation.messages和@ conversation.messages.last的調用之間添加了一條消息,那麼您將不會獲得真正的最後一個消息。但我想這可能是好的?

+0

我更新了我試圖避免的問題,特別是爲什麼'@ conversation.messages.last'解決方案不起作用。 – 2011-05-31 03:17:41

+0

然後,你真正想要的是急切加載。您想要在一個或兩個SQL查詢中加載所有對話及其最後的消息。我稍後會刪除我的答案,也許再試一次,但我不認爲這很簡單。你可以做一個has_one關聯來獲得最後一條消息,但是當你爲所有對話而急切地加載它時,生成的SQL會拉取所有的消息,這是不好的。 – 2011-05-31 16:28:40

+0

感謝您的更正。我更新了我的問題和標籤以閱讀「急切加載」而不是「延遲加載」。 – 2011-05-31 16:59:24