Я сейчас использую Rails 2.3.9. Я понимаю, что указание параметра :joins
в запросе без явного :select
автоматически делает любые записи, которые возвращаются только для чтения. У меня есть ситуация, когда я хотел бы обновить записи, и, хотя я читал о различных способах приближения к ней, мне было интересно, какой путь является предпочтительным или «правильным».
В частности, моя ситуация такова, что у меня есть следующая модель User
с именованной областью действия active
, которая выполняет JOIN с таблицей subscriptions
:
class User < ActiveRecord::Base
has_one :subscription
named_scope :active, :conditions => { :subscriptions => { :status => 'active' } }, :joins => :subscription
end
Когда я вызываю User.active.all
, все возвращаемые пользовательские записи доступны только для чтения, поэтому, если, например, я вызову update_attributes!
для пользователя, ActiveRecord::ReadOnlyRecord
будет вызвано.
Благодаря чтению различных источников, кажется, что популярный способ обойти это, добавив :readonly => false
к запросу. Однако мне было интересно следующее:
- Это безопасно? Я понимаю причину, по которой Rails в первую очередь устанавливает его только для чтения, потому что, согласно документации Rails , "они будут иметь атрибуты, которые не соответствуют столбцам таблицы. " Однако в SQL-запросе, сгенерированном по этому вызову, в любом случае используется
SELECT `users`.*
, который выглядит безопасным, , так что же в первую очередь пытается защитить Rails? Может показаться, что Rails должен защищать против случая, когда :select
фактически явно указан, что является обратным фактическому поведению, , поэтому я неправильно понимаю цель автоматической установки флага только для чтения на :joins
?
- Похоже ли это на хак? Неправильно, что определение именованной области должно явно касаться установки
:readonly => false
. Я также боюсь побочных эффектов, если именованная область связана с другими именованными областями. Если я попытаюсь указать его вне области действия (например, с помощью User.active.scoped(:readonly => false)
или User.scoped(:readonly => false).active
), он не будет работать.
Еще один способ, который я прочитал, чтобы обойти это, - изменить :joins
на :include
. Я понимаю поведение этого лучше, но Есть ли какие-либо недостатки в этом (кроме ненужного чтения всех столбцов в таблице subscriptions
)?
Наконец, я также мог бы снова получить запрос, используя идентификаторы записей, вызвав User.find_all_by_id(User.active.map(&:id))
, но я считаю, что это скорее обходной путь, нежели возможное решение, поскольку он генерирует дополнительный запрос SQL.
Есть ли другие возможные решения? Какое решение было бы предпочтительным в этой ситуации? Я прочитал ответ, данный в предыдущем вопросе StackOverflow об этом , но, похоже, он не дает конкретных указаний относительно того, что будет считаться правильным .
Заранее спасибо!