So I have a model in Rails, with globalize
installed and configured:
model.rb
class Model < ApplicationRecord
include TranslationWrites
translates :column
...
end
I want to Eager Load "English" and "French" translations only, to prevent an N+1 query problem while looping. I've tried a few ways, but the query will not work as expected. Here's what I've tried:
Model.includes(:translations).first
Which executes the following SQL:
Model Load (...)
SELECT * FROM models ORDER BY id ASC LIMIT 1
Model::Translations Load (...)
SELECT * FROM model_translations WHERE model_translations.model_id = 1
That's perfect for Eager Loading all translations, but I only want "English" and "French". I tried this code:
Model.includes(:translations).where(translations: { locale: ['en', 'fr'] }).first
That runs the following incorrect SQL:
Model Load (...)
SELECT * FROM models LEFT OUTER JOIN model_translations ON model_translations.model_id = models.id WHERE translations.locale IN ('en', 'fr')
ActiveRecord::StatementInvalid (Mysql2::Error: Unknown column 'translations.locale' ...)
As you can see, conditional eager loading (as see on this tutorial) doesn't seem to work...
Lastly, Globalize has the with_translations(['en', 'fr'])
method, but that scopes the Model
s returned to only those that have the specified translations. I need those Models regardless of whether or not they have "English" or "French". If you're familiar with Laravel, it's the difference between with()
and whereHas()
:
// Returns all `Model` instances, and each `$model->translations` will have `en` and `fr` only.
Model::with(['translations' => function($query) {
$query->whereIn('locale', ['en', 'fr'])
})->get();
// Only returns `Model` instances that have `en` or `fr` translations, but `$model->translations` will include all translations
$models = Model::whereHas('translations', function($query) {
$query->whereIn('locale', ['en', 'fr'])
})->get();
Has anyone come across this? I need an efficient way to only load English and French translations for each Model in the database, regardless of whether or not that translations actually exists.
Generally speaking, in your .where
method you should use the real table name, not the relationship's name between your models, so try with:
Model.includes(:translations).where(model_translations: { locale: ['en', 'fr'] }).first