2013-04-05 3 views
13

У меня есть следующие модели:рельсов Ассоциации HAS_ONE Последних записей

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 

Где current_revision является наиболее недавно созданным revision. Можно ли превратить current_revision в ассоциацию, чтобы я мог выполнить запрос, например, Section.where("current_revision.parent_section_id = '1'")? Или я должен добавить столбец current_revision в свою базу данных вместо того, чтобы пытаться создать его практически или через ассоциации?

ответ

2

Насколько я понимаю, вы хотите получить разделы, в которых последняя ревизия каждого раздела имеет parent_section_id = 1;

У меня такая же ситуация: во-первых, это SQL (пожалуйста, подумайте о категориях как разделы для вас, сообщения в виде ревизий и user_id как parent_section_id -sorry, если я не переведу код в вашу пользу, но я должен перейти):

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) 

И это запрос в 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)") 

Это работает, это некрасиво, я думаю, что лучший способ, чтобы «вырезать» запрос, но если у вас слишком много секций, которые были бы проблемой при прохождении через них; вы также можете разместить этот запрос в статическом методе, а также, ваша первая идея, иметь ревизию_id внутри вашей таблицы разделов, поможет оптимизировать запрос, но откажется от нормализации (иногда это необходимо), и вам придется обновляя это поле, когда новая редакция создается для этого раздела (так что если вы собираетесь делать много изменений в огромной базе данных, возможно, это будет плохая идея, если у вас медленный сервер ...)

UPDATE Я вернулся хех, я делал несколько тестов, и проверить это:

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 

Я сделал этот запрос и работал с моими таблицами (надеюсь, что я назвал хорошо params, это тот же запрос Rails, но я изменяю запрос в наличии для оптимизации); следить за группой; включение делает его оптимальным в больших наборах данных, и жаль, что я не мог найти способ установить связь с has_one, но я бы пошел с этим, но также пересмотреть поле, которое вы упомянули в начале.

+0

Awesome, Спасибо! –

14

Вы можете изменить его на ассоциацию, но, как правило, заказ на has_one или belongs_to ассоциации всегда интерпретируются неправильно при использовании по запросам. В вашем вопросе, когда вы включаете, что в ассоциацию, которая бы

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

Проблема в том, что, когда вы пытаетесь совместить это с другими запросами, то, как правило, дают вам неправильную запись.

>> 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 

Поэтому я предлагаю вам добавить current_revision_id вместо этого.

+0

Прекрасно, это именно то, что я искал! –

+0

Если у вас есть нулевое ограничение на 'section_revisions.section_id', не забудьте использовать has_one helper 'record.create_current_revision' (или' record.build_current_revision'), иначе вы получите 'Не удалось удалить существующий связанный current_revision. Запись не удалось сохранить после того, как ее внешний ключ был установлен на nil.' Вместо этого всегда используйте 'record.section_revisions.create', который затем будет новым' current_revision' –

21

Чтобы получить последний на has_many, вы хотели бы сделать что-то похожее на @jvnill, за исключением добавить область с упорядочением в ассоциации:

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

Это позволит вам получить самую последнюю версию из базы данных.

Смежные вопросы