.references to polimorfic

Mam takie modele i asocjacje:

class CreateActivities < ActiveRecord::Migration
  def change
    create_table :activities do |t|
      t.references :owner, polymorphic: true, index: true
      t.string :action
      t.text :parameters

      t.timestamps null: false
    end
  end
end


class User < ActiveRecord::Base
  has_many :activities, as: :owner, :source_type => "User", dependent: :nullify
  ...
end

class Activity < ActiveRecord::Base
  belongs_to :owner, polymorphic: true
  ....
end

Chciałbym zbudować zapytanie typu

a = Activity.includes(:owner).order(:kolumna_z_tabeli_USER)

ale nie wychodzi mi to :frowning:

Zapytanie :

a = Activity.includes(:owner)

jest OK, ale już

a = Activity.includes(:owner).references(:owner)

(bo chyba to references będzie mi potrzebne, by sortować po kolumnie z “ownera”) ciągle wywala mi błąd:
“ActiveRecord::EagerLoadPolymorphicError: Cannot eagerly load the polymorphic association :owner”

Co robię źle?

Jako że jest to asocjacja polimorficzna, nie da się zrobić zapytania sortowanego po kolumnie z ownera, bo nie wiadomo, do jakiej właściwie tabeli owner będzie należał. Rozwiązaniem jest po prostu sortowanie tego w Rubym.

Nawet jeżeli wprost zadeklaruję, że owner_type: ‘User’ (lub source_type: ‘User’) ?

A może pwinienem dodać coś w stylu

class Activity < ActiveRecord::Base
  belongs_to :owner, polymorphic: true
  belongs_to :user_owner, -> { where(owner: {owner_type: 'User'}) }
  ....
end

I napisać
a = Activity.includes(:user_owner).references(:user_owner) ???

To po co Ci wtedy polimorficzna asocjacja? :smile:
BTW source_type jest używane przy asocjacjach polimorficznych i has_many :through, czyli używanie tego tutaj jest niepotrzebne. Jeśli chcesz tylko te activities, które mają User jako owner_type, możesz zrobić coś takiego:

Activity.where(owner_type: 'User').joins('left outer join users on activities.owner_id = users.id').order('users.column ASC')

Uprzedzam, że taka konstrukcja nie działa niestety jako pre-load, więc jeśli chcesz zainicjować jeszcze te obiekty User, to musisz zrobić na tym scopie dodatkowo includes(:owner) (lub, bardziej zgodnie z prawdą, preload(:owner).

tonący brzytwy się chwyta. :wink:

mogę napisać:
u = User.last
u.activity

a = Activity.last
a.owner

i ładnie mi to łączy i wybiera.

Dlaczego Railsy radzą sobie z konstrukcją:
Activity.includes(:owner) a już nie dają rady z Activity.includes(:owner).references(:owner) ?

Dlatego że Activity.includes(:owner) jest równoważne Activity.preload(:owner) i nie robi joina (którego zrobić się nie da), tylko tyle zapytań, ile trzeba do wyciągnięcia rekordów i asocjacji. Natomiast jeśli robisz references(:owner), wymuszasz zrobienie konstrukcji Activity.eager_load(:owner), który próbuje zrobić left joina, a nie może go zrobić, bo asocjacja jest polimorficzna. Przeczytaj:

Dzięki za wyjaśnienia i link

Pozdrawiam :slight_smile: