Как создать динамическое отношение на основе значения столбца в рельсах - PullRequest
0 голосов
/ 11 апреля 2019

У меня есть простой случай, но я не могу понять, как его реализовать, используя ActiveRecord.Итак, у меня есть класс user, у которого может быть один profile, а также столбец role, который является перечислением.Выглядит примерно так

class User < ActiveRecord::Base
    enum role: { manager: 1, writer: 2 }

    has_one :profile
end

Точно так же у меня есть Writer и Manager, которые belongs_to пользователь

class Manager < ActiveRecord::Base
    belongs_to :user
end

class Writer < ActiveRecord::Base
    belongs_to :user
end

Я пытаюсь указать profile атрибут пользователя, чтобы исправитьпрофиль либо писатель или менеджер на основе роли enum.Я не могу использовать полиморфные отношения, так как пользователь является родителем, и от него зависят таблицы профиля (писатель и менеджер).

Будет оказана любая помощь

1 Ответ

1 голос
/ 11 апреля 2019

TL; DR:

Я не думаю, что вы могли бы определить has_many с динамическим «именем таблицы» (a.k.a. динамическая модель), главным образом потому, что нет динамической эквивалентной строки SQL для представления, скажем что-то вроде ниже:

# Let's pretend that you have a `Post` model `belongs_to :user` and has an attribute `is_enabled:boolean`
# and that `User` `has_one :post`...

User.joins(:post).where(posts: { is_enabled: true })
# would generate an SQL
# User Load (0.6ms) SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "posts"."is_enabled" = true LIMIT 1

# ^ This works fine because `has_one :posts` always is mapped to the `posts` table
# but since you are asking for a dynamic one, then see an equivalent below

User.joins(:profile).where(":managers or :writers": { is_enabled: true })
# User Load (0.6ms) SELECT "users".* FROM "users" INNER JOIN "what_table" on "what_table"."user_id" = "users"."id" WHERE "what_table"."is_enabled" = true LIMIT 1
# ^ which as you could see does not have an exact SQL equivalent because it is
# "trying to" INNER JOIN on a "dynamic" table. You can do this per `User` record,
# because you know what to INNER JOIN with, but for a collection of `User` records,
# there is no such SQL "dynamic-table-name-matching" equivalent.

Альтернативное решение:

class User < ActiveRecord::Base
  enum role: { manager: 1, writer: 2 }

  has_one :manager
  has_one :writer

  def profile
    case role
    when 'manager' then manager
    when 'writer' then writer
    else raise NotImplementedError
    end
  end

  # or if you prefer a dynamic-matching one:
  # def profile
  #   send(role.to_sym)
  # end
end

Пример использования

# rails console
user = User.first
puts user.profile
# => returns either <Manager...>, <Writer...>, or nil

Предостережение, хотя альтернативное решение выше определяет profile как метод , а не ассоциацию , поэтому вы потеряете способность делать INNER JOIN s (что вы, вероятно, не смогли бы сделать в любом случае; см. мой TL; DR выше, чтобы узнать почему)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...