Reputation: 29258
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.
Upvotes: 1
Views: 773
Reputation: 12540
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
Upvotes: 1