Proper way to prevent ActiveRecord::ReadOnlyRecord?

后端 未结 4 875
执念已碎
执念已碎 2021-01-03 19:03

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 re

相关标签:
4条回答
  • 2021-01-03 19:13

    I ran across this same issue and was not comfortable using :readonly => false

    As a result I did an explicit select namely :select => 'users.*' and felt that it seemed like less of a hack.

    You could consider doing the following:

    class User < ActiveRecord::Base
      has_one :subscription
    
      named_scope :active, :select => 'users.*', :conditions => { :subscriptions => { :status => 'active' } }, :joins => :subscription
    end
    
    0 讨论(0)
  • 2021-01-03 19:16

    I believe that it would be customary and acceptable in this case to use :include instead of :join. I think that :join is only used in rare specialized circumstances, whereas :include is pretty common.

    If you're not going to be updating all of the active users, then it's probably wise to add an additional named scope or find condition to further narrow down which users you're loading so that you're not loading extra users & subscriptions unnecessarily. For instance...

    User.active.some_further_limiting_scope(:with_an_argument)
      #or
    User.active.find(:all, :conditions => {:etc => 'etc'})
    

    If you decide that you still want to use the :join, and are only going to update a small percentage of the loaded users, then it's probably best to reload just the user you want to update right before doing so. Such as...

    readonly_users = User.active
    # insert some other code that picks out a particular user to update
    User.find(readonly_users[@index].id).update_attributes(:etc => 'etc')
    

    If you really do need to load all active users, and you want to stick with the :join, and you will likely be updating most or all of the users, then your idea to reload them with an array of IDs is probably your best choice.

    #no need to do find_all_by_id in this case. A simple find() is sufficient.
    writable_users_without_subscriptions = User.find(Users.active.map(&:id))
    

    I hope that helps. I'm curious which option you go with, or if you found another solution more appropriate for your scenario.

    0 讨论(0)
  • 2021-01-03 19:25

    Regarding your sub-question: so am I not properly understanding the purpose of automatically setting the read-only flag on :joins?

    I believe the answer is: With a joins query, you're getting back a single record with the User + Subscription table attributes. If you tried to update one of the attributes (say "subscription_num") in the Subscription table instead of the User table, the update statement to the User table wouldn't be able to find subscription_num and would crash. So the join-scopes are read-only by default to prevent that from happening.

    Reference: 1) http://blog.ethanvizitei.com/2009/05/joins-and-namedscopes-in-activerecord.html

    0 讨论(0)
  • 2021-01-03 19:28

    I think the best solution is to use .join as you have already and do a separate find()

    One crucial difference of using :include is that it uses outer join while :join uses an inner join! So using :include may solve the read-only problem, but the result might be wrong!

    0 讨论(0)
提交回复
热议问题