2013-04-05 14 views
13

tôi có mô hình sau:Rails Hội has_one mới nhất Ghi

class Section < ActiveRecord::Base 
    belongs_to :page 
    has_many :revisions, :class_name => 'SectionRevision', :foreign_key => 'section_id' 
    has_many :references 

    has_many :revisions, :class_name => 'SectionRevision', 
         :foreign_key => 'section_id' 

    delegate :position, to: :current_revision 

    def current_revision 
    self.revisions.order('created_at DESC').first 
    end 
end 

đâu current_revision là gần đây nhất là tạo revision. Có thể biến current_revision thành một liên kết để tôi có thể thực hiện truy vấn như Section.where("current_revision.parent_section_id = '1'") không? Hoặc tôi nên thêm một cột current_revision vào cơ sở dữ liệu của tôi thay vì cố gắng tạo nó gần như hoặc thông qua các liên kết?

Trả lời

2

Tôi hiểu rằng bạn muốn nhận các phần mà bản sửa đổi cuối cùng của từng phần có parent_section_id = 1;

Tôi có tình huống tương tự, trước tiên, đây là SQL (hãy xem các danh mục dưới dạng phần cho bạn, bài đăng dưới dạng sửa đổi và user_id dưới dạng parent_section_id-lo ngại nếu tôi không chuyển mã theo nhu cầu của bạn nhưng tôi phải đi):

SELECT categories.*, MAX(posts.id) as M 
FROM `categories` 
INNER JOIN `posts` 
ON `posts`.`category_id` = `categories`.`id` 
WHERE `posts`.`user_id` = 1 
GROUP BY posts.user_id 
having M = (select id from posts where category_id=categories.id order by id desc limit 1) 

Và đây là truy vấn trong Rails:

Category.select("categories.*, MAX(posts.id) as M").joins(:posts).where(:posts => {:user_id => 1}).group("posts.user_id").having("M = (select id from posts where category_id=categories.id order by id desc limit 1)") 

này hoạt động, nó là xấu xí, tôi nghĩ cách tốt nhất là để "cắt" truy vấn, nhưng nếu bạn có quá nhiều phần đó sẽ là một vấn đề trong khi vòng lặp máng chúng; bạn cũng có thể đặt truy vấn này vào một phương thức tĩnh và ý tưởng đầu tiên của bạn, có một revision_id bên trong bảng phần của bạn sẽ giúp tối ưu hóa truy vấn, nhưng sẽ giảm bình thường hóa (đôi khi cần) và bạn sẽ phải cập nhật trường này khi sửa đổi mới được tạo cho phần đó (vì vậy nếu bạn định thực hiện nhiều sửa đổi trong cơ sở dữ liệu khổng lồ thì có thể sẽ là một ý tưởng tồi nếu bạn có máy chủ chậm ...)

CẬP NHẬT tôi đã trở lại hehe, tôi đã làm một số xét nghiệm và kiểm tra này:

def last_revision 
    revisions.last 
end 

def self.last_sections_for(parent_section_id) 
    ids = Section.includes(:revisions).collect{ |c| c.last_revision.id rescue nil }.delete_if {|x| x == nil} 

    Section.select("sections.*, MAX(revisions.id) as M") 
     .joins(:revisions) 
     .where(:revisions => {:parent_section_id => parent_section_id}) 
     .group("revisions.parent_section_id") 
     .having("M IN (?)", ids) 
end 

tôi đã thực hiện truy vấn này và làm việc với các bảng của tôi (hy vọng tôi đặt tên tốt params, nó là cùng một truy vấn Rails từ trước nhưng tôi thay đổi truy vấn trong việc tối ưu hóa); xem ra nhóm; bao gồm làm cho nó tối ưu trong datasets lớn, và xin lỗi tôi không thể tìm thấy một cách để làm cho một mối quan hệ với has_one, nhưng tôi sẽ đi với điều này, nhưng cũng xem xét lại các lĩnh vực mà bạn đề cập ở đầu.

+0

Tuyệt vời, Cảm ơn! –

14

Bạn có thể thay đổi thành liên kết nhưng thông thường, đặt hàng cho liên kết has_one hoặc belongs_to luôn được diễn giải sai khi được sử dụng trên truy vấn. Trong câu hỏi của bạn, khi bạn biến chúng thành một hiệp hội, đó sẽ là

has_one :current_revision, class_name: 'SectionRevision', foreign_key: :section_id, order: 'created_at DESC' 

Vấn đề ở đây là khi bạn cố gắng kết hợp này với các truy vấn khác, nó sẽ bình thường cung cấp cho bạn một bản ghi sai.

>> record.current_revision 
    # gives you the last revision 
>> record.joins(:current_revision).where(section_revisions: { id: 1 }) 
    # searches for the revision where the id is 1 ordered by created_at DESC 

Vì vậy, tôi khuyên bạn nên thêm current_revision_id để thay thế.

+0

Đẹp đó chính xác là những gì tôi đang tìm kiếm! –

+0

Nếu bạn có một ràng buộc null trên 'section_revisions.section_id', hãy chắc chắn không sử dụng helper has_one 'record.create_current_revision' (hoặc' record.build_current_revision') hoặc bạn sẽ nhận được 'Failed to remove current_revision được kết hợp hiện có. Bản ghi không lưu được sau khi khóa ngoại được đặt thành nil.' Thay vào đó, luôn sử dụng 'record.section_revisions.create', sau đó sẽ là 'current_revision' mới –

21

Để có được cuối cùng trên một has_many, bạn sẽ muốn làm điều gì đó tương tự như @jvnill, ngoại trừ thêm một phạm vi với một trật tự để các hiệp hội:

has_one :current_revision, -> { order created_at: :desc }, 
    class_name: 'SectionRevision', foreign_key: :section_id 

này sẽ đảm bảo bạn sẽ có được sửa đổi gần đây nhất từ cơ sở dữ liệu.