ruby on rails polymorphic Rails: включает в себя полиморфную ассоциацию




rails sti (5)

Изменить 2 : теперь я использую рельсы 4.2, а нетерпимый полиморфизм загрузки теперь является функцией :)

Редактирование : похоже, это работает на консоли, но по какой-то причине мое предложение об использовании с частицами ниже все еще генерирует предупреждения N + 1 Query Stack с маркером bullet. Мне нужно исследовать ...

Хорошо, я нашел решение ([edit] или сделал?), Но предполагает, что вы знаете все типы предметов.

class Activity < ActiveRecord::Base
  belongs_to :subject, polymorphic: true

  belongs_to :event, -> { includes(:activities).where(activities: { subject_type: 'Event' }) }, foreign_key: :subject_id
  belongs_to :image, -> { includes(:activities).where(activities: { subject_type: 'Image' }) }, foreign_key: :subject_id
end

И теперь вы можете сделать

Activity.includes(:part, event: :guests, image: :tags).order(created_at: :desc).limit(10)

Но для активной загрузки на работу вы должны, например, использовать

activity.event.guests.first

и не

activity.part.guests.first

Таким образом, вы, вероятно, можете определить способ использования вместо темы

def eager_loaded_subject
  public_send(subject.class.to_s.underscore)
end

Итак, теперь вы можете иметь представление с

render partial: :subject, collection: activity

Частично с

# _activity.html.erb
render :partial => 'activities/' + activity.subject_type.underscore, object: activity.eager_loaded_subject

И две (фиктивные) частицы

# _event.html.erb
<p><%= event.guests.map(&:name).join(', ') %></p>

# _image.html.erb
<p><%= image.tags.first.map(&:name).join(', ') %></p>

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

В итоге у нас что-то вроде

class Activity < ActiveRecord::Base
  belongs_to :subject, polymorphic: true
end

Теперь, если два из этих предметов, например:

class Event < ActiveRecord::Base
  has_many :guests
  after_create :create_activities
  has_one :activity, as: :subject, dependent: :destroy 
end

class Image < ActiveRecord::Base
  has_many :tags
  after_create :create_activities
  has_one :activity, as: :subject, dependent: :destroy 
end

С create_activities определяется как

def create_activities
  Activity.create(subject: self)
end

И с гостями и тегами, определяемыми как:

class Guest < ActiveRecord::Base
  belongs_to :event
end

class Tag < ActiveRecord::Base
  belongs_to :image
end

Если мы запрашиваем последние 20 зарегистрированных действий, мы можем:

Activity.order(created_at: :desc).limit(20)

У нас есть первая проблема с запросом N + 1, которую мы можем решить с помощью:

Activity.includes(:subject).order(created_at: :desc).limit(20)

Но тогда, когда мы вызываем гостей или теги, у нас возникает другая проблема с запросом N + 1.

Каков правильный способ решить это, чтобы иметь возможность использовать разбивку на страницы?


Это, мы надеемся, будет исправлено в рельсах 5.0. Для этого уже существует проблема и запрос на перенос.

https://github.com/rails/rails/pull/17479

https://github.com/rails/rails/issues/8005

Я разветвил рельсы и применил патч к 4.2-стабильному, и он работает для меня. Не стесняйтесь использовать свою вилку, хотя я не могу гарантировать синхронизацию с восходящим потоком на регулярной основе.

https://github.com/ttosch/rails/tree/4-2-stable


Означает ли это, что генерирует действительный SQL-запрос или он терпит неудачу, потому что events не могут быть JOIN с tags а images не могут быть JOIN с guests ?

class Activity < ActiveRecord::Base
  self.per_page = 10

  def self.feed
    includes(subject: [:guests, :tags]).order(created_at: :desc)
  end
end

# in the controller

Activity.feed.paginate(page: params[:page])

Это будет использовать will_paginate .


image_activities = Activity.where(:subject_type => 'Image').includes(:subject => :tags).order(created_at: :desc).limit(20)
event_activities = Activity.where(:subject_type => 'Event').includes(:subject => :guests).order(created_at: :desc).limit(20)
activities = (image_activities + event_activities).sort_by(&:created_at).reverse.first(20)

После того, как я прочитал статью « Полиморфные отношения с нетерпением загрузки в Rails», я получил следующее решение:

class ActivitiesController < ApplicationController
  def index
    activities = current_user.activities.page(:page)

    @activities = Activities::PreloadForIndex.new(activities).run
  end
end

class Activities::PreloadForIndex
  def initialize(activities)
    @activities = activities
  end

  def run
    preload_for event(activities), subject: :guests
    preload_for image(activities), subject: :tags
    activities
  end

  private

  def preload_for(activities, associations)
    ActiveRecord::Associations::Preloader.new.preload(activities, associations)
  end

  def event(activities)
    activities.select &:event?
  end

  def image(activities)
    activities.select &:image?
  end
end




polymorphic-associations