Association with scope in Ruby on Rails

Asked

Viewed 531 times

9

I have a Ruby on Rails application in which I have licenses, items that can be licensed, and a table that lists the two (Which items do you have in the license?). Analogously to items in a shopping cart.

Some of the items will no longer be marketed, but I intend to keep them in the database. I then created a soft-delete and I used a standard scope for the model and for the relationships. But when I try to change existing records using the related model, I get an exception: ActiveRecord::ReadOnlyRecord

My models are like this:

class Item < ActiveRecord::Base
  default_scope { where(deleted:false) }
end

class LicenseItem < ActiveRecord::Base
  belongs_to :license, touch: true
  belongs_to :item
end

class License < ActiveRecord::Base
  has_many :license_items,  
          -> { joins(:item).where(items: {deleted: false} ) }, 
          dependent: :destroy
end

In this way:

pry(main)> License.find(0).license_items[0].readonly?
=> true

Is there any way to make this relationship not just read?

I’ve tried to add readonly(false) at the end of the has_many in License unsuccessful.

  • What version of Rails are you using?

  • Was using 4.0.0, update to 4.0.2 solved!

2 answers

6


According to this discussion on Github, this is a bug of Rails 4.0, which was fixed in version 4.0.1. By updating your Rails, you can include the readonly(false) within its scope and will work:

class License < ActiveRecord::Base
    has_many :license_items,  
          -> { joins(:item).where(items: {deleted: false}).readonly(false) }, 
          dependent: :destroy
end

To update the Rails version, edit your Gemfile and then perform bundle update.

In the latter case, if you have no way to update the Rails version, you can pass the option readonly: false for the method find (unsaved):

License.find(1).license_items.find(1, readonly: false).update_attributes(amount: 5)
  • Update to 4.0.2 solved! Thanks!

1

What version of Ror are you using? According to that answer in the English OS, when the "read-only" feature was introduced (in version 1.12.10) the default behavior was to infer :readonly => true whenever :joins was used (what I see to be the case with your license_items). Translating freely from the CHANGELOG (my emphasis):

1.12.0 (October 16th, 2005)

  • Introduced read-only records. If you call object.readonly! it will mark the object as read-only and launch ReadOnlyRecord if you call object.save. object.readonly? indicates whether or not the object is read-only. Passing :readonly => true for any method Finder will mark the returned records as read-only. The option :joins now implies in :readonly, so if you use this option, saving the same record will fail. Use find_by_sql as an alternative route.

However, as of versions 2.3.4 and 3.0.0, the behavior has changed: readonly is only inferred if the option :joins is used without a :select explicit or without the option :readonly [attributed as false] also explicit (which, if I understand correctly, you have tried). In some cases (has_and_belong_to_many), the :readonly is ignored - explicit or not - so the only output is to use :select.

The answer referenced above contains more details, but unfortunately I don’t have enough experience with the :joins to comment. I suggest using it as a starting point if the above steps do not resolve.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.