Некорректное добавление имени столбца при создании внешнего ключа
У меня странная проблема с Rails 4.2.4. Я создаю новую таблицу, которая ссылается на некоторые другие, например:
t.references :local, index: true, foreign_key: true, null: false
t.references :serie, index: true, foreign_key: true, null: false
при выполнении миграции возникает ошибка при создании ограничения внешнего ключа:
PG::UndefinedColumn: ERROR: нет существующего столбца "серийный_идентификационный номер").
что по-испански
PG::UndefinedColumn: ERROR: столбец "series_id", на который ссылается ограничение внешнего ключа, не существует
это означает, что в созданной таблице нет столбца "series_id". Конечно, это не должен быть какой-либо столбец с таким именем.
Правильное имя столбца, которое должно искать поколение FK, - "serie_id", и оно существует.
Теперь самое странное в том, что он не ошибается: например, локальный. Он ищет не "locales_id", а "local_id", который является правильным, и создается соответствующий FK.
У меня есть пользовательские интонации испанского языка, и правильные множественные числа:
местные -> локали
серия -> серия
однако я не понимаю, почему поколение ФК, похоже, плюрализуется в одном случае, а не в другом.
В этом ответе я нашел работающее решение, которое объявляет конкретно внешние ключи, например:
add_foreign_key :turnos_registrados, :series, column: :serie_id
но я хотел бы знать, почему это происходит.
Заранее спасибо.
2 ответа
Кажется, что Rails делает эту процедуру, чтобы получить имя столбца внешнего ключа:
ссылочная модель в миграции → (преобразовать символ в строку) → множественное число → единственное число
При определении имени таблицы для данной ссылочной модели выполняется множественное число, см. Эту строку в исходном коде. Затем выполняется сингуляризация при получении фактического столбца внешнего ключа из ранее умноженного имени таблицы (см. Источник здесь).
Тем не менее, в английском языке "серия" является собирательным существительным, поэтому формы единственного и множественного числа совпадают. Rails обрабатывает это правильно по умолчанию:
"series".pluralize
# => "series"
"series".singularize
# => "series"
Но также обратите внимание на это:
"serie".pluralize
# => "series"
Итак, приложение "s" сначала добавляется в serie
упомянутое имя модели, а затем сингуляризация приводит к тому же слову - "серия". Т.е. Rails выдает следующее для :serie
Ссылочная модель из вашей миграции:
:serie → "serie" (conversion to string) → "series" (pluralize) → "series" (singularize)
Вот почему Rails пытается искать series_id
столбец внешнего ключа, но не для locales_id
,
Вы пишете, что вам удалось добавить правильный ключ вручную, используя add_foreign_key
, Я подозреваю, что позже вы столкнетесь с большим количеством проблем при настройке, так как Rails все равно будет пытаться получить внешний ключ как series_id
,
Либо вам нужно указать правильный (serie_id
) foreign_key везде в ваших ассоциациях или вы должны определить пользовательское правило плюрализации для вашего случая. Правило должно быть добавлено в качестве инициализатора, который содержит:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'serie', 'series'
end
Если у вас есть это, плюрализация будет работать, как и ожидалось для вашего конкретного случая:
"series".singularize
# => "serie"
И с этим пользовательским правилом я думаю, что даже ваша оригинальная миграция должна работать без проблем.
Благодаря ответу BoraMa, я пошел дальше и выяснил "почему":
кажется, что некоторые высокоприоритетные английские перегибы Rails по умолчанию мешали.
У меня есть правило перегиба
inflect.singular(/((?<![aeiou][rndlj])e|a|i|o|u)s([A-Z]|_|$)/, '\1\2')
который должен поймать
"series".singularize
поскольку
2.2.1 :006 > "series".gsub(/((?<![aeiou][rndlj])e|a|i|o|u)s([A-Z]|_|$)/,'\1\2')
=> "serie"
но это не сработало. Как-то, добавив предложенный
inflect.irregular 'serie', 'series'
заставил это работать, но это должно работать без слишком. Итак, я заподозрил какое-то предопределенное правило, которое можно подтвердить (еще один вклад BoraMa) в консоли, потому что ответ на предложение
ActiveSupport::Inflector.inflections.singulars
напечатает среди прочего: [/(s)eries$/i, "\1eries"].
Для того, чтобы избавиться от этих значений по умолчанию, добавив
inflect.clear
чтобы inflections.rb сделал это. Теперь это работает с более общим правилом, которое у меня было изначально.