I'm currently using Rails 2.3.9. I understand that specifying the :joins
option in a query without an explicit :select
automatically makes any records that are returned read-only. I have a situation where I would like to update the records and while I've read about different ways to approach it, I was wondering which way is the preferred or "proper" way.
Specifically, my situation is that I have the following User
model with an active
named scope that performs a JOIN with the subscriptions
table:
class User < ActiveRecord::Base
has_one :subscription
named_scope :active, :conditions => { :subscriptions => { :status => 'active' } }, :joins => :subscription
end
When I call User.active.all
, the user records that are returned are all read-only, so if, for instance, I call update_attributes!
on a user, ActiveRecord::ReadOnlyRecord
will be raised.
Through reading various sources, it seems a popular way to get around this is by adding :readonly => false
to the query. However, I was wondering the following:
- Is this safe? I understand the reason why Rails sets it to read-only in the first place is because, according to the Rails documentation, "they will have attributes that do not correspond to the table’s columns." However, the SQL query that is generated from this call uses
SELECT `users`.*
anyway, which appears to be safe, so what is Rails trying to guard against in the first place? It would appear that Rails should be guarding against the case when:select
is actually explicitly specified, which is the reverse of the actual behavior, so am I not properly understanding the purpose of automatically setting the read-only flag on:joins
? - Does this seem like a hack? It doesn't seem proper that the definition of a named scope should care about explicitly setting
:readonly => false
. I'm also afraid of side effects if the named scoped is chained with other named scopes. If I try to specify it outside of the scope (e.g., by doingUser.active.scoped(:readonly => false)
orUser.scoped(:readonly => false).active
), it doesn't appear to work.
One other way I've read to get around this is to change the :joins
to an :include
. I understand the behavior of this better, but are there any disadvantages to this (other than the unnecessary reading of all the columns in the subscriptions
table)?
Lastly, I could also retrieve the query again using the record IDs by calling User.find_all_by_id(User.active.map(&:id))
, but I find this to be more of a workaround rather than a possible solution since it generates an extra SQL query.
Are there any other possible solutions? What would be the preferred solution in this situation? I've read the answer given in the previous StackOverflow question about this, but it doesn't seem to give specific guidance of what would be considered correct.
Thanks in advance!