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

#ruby-on-rails #ruby #mobility

Вопрос:

У меня простая проблема с драгоценным камнем мобильности. У меня есть простое соотношение в моих моделях. Допустим, в компании много сотрудников, и у сотрудников есть переведенный атрибут :job_function , который используется backend: :table .

 class Company < ApplicationRecord
    has_many :employees
end

class Employee < ApplicationRecord
    extend Mobility

    translates :job_function, type: :string, locale_accessors: true, backend: :table
end
 

Если я попытаюсь сделать:

 Company.first.employees.map(amp;:job_function)
 

Я понимаю проблему n 1. Каждый из переводов :job_function загружается индивидуально.

Как мне сказать Mobility, чтобы он загрузил их все за один раз, прежде чем я начну сопоставлять коллекцию?

Я не смог найти ни одного примера этого в документации…

Комментарии:

1. Я верю, что вы можете сделать: Company.first.employees.pluck(:job_function) .

Ответ №1:

Вы можете просто использовать pluck , что поддерживается мобильностью:

 Company.first.employees.i18n.pluck(:job_function)
 

Комментарии:

1. я пытался, но pluck отдача нулевая

2. но Company.first.employees.i18n.pluck(:job_function) работа. ты промахнулся i18n

3. Спасибо! Это правильно, обновил ответ.

4. Зальцберге, у вас было какое-нибудь простое решение на случай, если мы захотим предварительно загрузить сотрудников их переводами (и избежать N 1)?, я имею в виду, что, возможно, оператору не только нужно запросить функции перевода, но и нужно что-то сделать с сотрудниками, отвага не позволит загружать сотрудников.

Ответ №2:

Вы могли includes бы перевести сотрудников их job_function, это n 2 запроса

вот моя демонстрационная версия, модель продукта содержит множество версий модели, которая была настроена для атрибута name, соответствующих вашей компании, сотруднику, функции job_function соответственно.

 class Version < ApplicationRecord
  belongs_to :product

  extend Mobility
  translates :name, locale_accessors: [:en, :ja], backend: :table
end

class Product < ApplicationRecord
 has_many :versions

 has_many :translation_versions, -> { 
   i18n{name.not_eq(nil)}
   .select("versions.*, 
            version_translations_#{Mobility.locale}.name AS translate_name")
  }, class_name: "Version"
end
 

С языковым стандартом по умолчанию :en

 Product.includes(:translation_versions).first.translation_versions
   .map(amp;:translate_name)

# SELECT "products".* FROM "products" ...
# SELECT versions.*, version_translations_en.name AS translate_name 
#  FROM "versions" LEFT OUTER JOIN "version_translations" "version_translations_en" 
#  ON "version_translations_en"."version_id" = "versions"."id" 
#  AND "version_translations_en"."locale" = 'en' 
#  WHERE "version_translations_en"."name" IS NOT NULL AND "versions"."product_id" = ? ...

# => ["v1", "v2"]
 

С локализацией :ja

 Mobility.locale = :ja
Product.includes(:translation_versions).first.translation_versions
   .map(amp;:translate_name)
# ...
# => ["ja v1", "ja v2"]
 

Так что всего один вопрос.

В случае, если ваш backend параметр имеет значение KeyValue, таблицы перевода разделяют не только locale , но и type (Строка, текст,…), но вы уже решили, какой тип для атрибута, верно ? например: имя:строка. Так что вам все равно нужно только настроить динамическую локаль.

 class Product < ApplicationRecord
 has_many :translation_versions, -> { 
  i18n{name.not_eq(nil)}
   .select("versions.*, 
     Version_name_#{Mobility.locale}_string_translations.value AS translate_name")
  }, class_name: "Version"
end
 

запрос такой же, как и выше.

Конечно, вы могли бы сделать translation_versions это в более общем плане, разделив его на модуль, заменив Version именем класса, name целевым атрибутом и сделав динамическую функцию чем-то вроде translation_#{table_name} .