Fralcon
Fralcon

Reputation: 489

Why is this a ReadOnly record?

So I am building an associated object through a main object like so: item.associated_items.build({name: "Machine", color: "Grey"})

and then in another method calling item.save. However I am getting an ActiveRecord::ReadOnlyRecord error. I read in the docs that

Records loaded through joins with piggy-back attributes will be marked as read only since they cannot be saved.

so I think that is what is happening here. But

  1. I dont't know why that is happening. I have called save on an object with a new associated record before and had no problems.

  2. What do the docs mean when they say "piggy-back attributes"?

  3. Is there a way to make the save happen by doing something like item.associated_items.readonly(false).build(attributes). I tried that and it didnt work, but I'm hoping there is another way.

edit: I just tried new_associated_item = AssociatedItem.new({attributes}) item.associated_items << new_associated_item

and the later method calls item.save and the read only exception still happens.

edit2: MurifoX asked me about how Item is being loaded. The above code is happening in a couple of service objects. The process is

Controller

owner = Owner.includes(:medallions).references(:medallions).find_by_id(params[:id])

later

creator = NoticeCreator.new(owner)
creator.run

NoticeCreator

def initialize #effectively
    medallion_notice_creators = []
    owner.medallions.some_medallion_scope.each do |medallion|
        medallion_notice_creator = MedallionNoticeCreator.new(medallion)
        medallion_notice_creator.prepare
        medallion_notice_creators << medallion_notice_creator
    end
end

later after looping through the medallion notice creators

def prepare
    medallion.notices.build(attributes)
end

later

medallion_notice_creators.each do |medallion_notice_creator|
    medallion_notice_creator.medallion.save
end

Apologies if the code seems convoluted. There is a bunch of stuff going on and I'm trying to condense the code and anonymize it.

Upvotes: 4

Views: 3440

Answers (1)

MurifoX
MurifoX

Reputation: 15089

Objects created with joins or includes, which is your case, are marked read-only because you are making a giant query with joins and stuff and preloading nested objects within your main one. (ActiveRecord can become confused with so many attributes and don't know how to build the main object, so it marks readonly on it.)

As you have noticed, this won't happen if you create you object with a simple find, as the only attributes received from the query are from the object itself.

I don't know why you are eager loading all of this associations, maybe it is from some rule in your code, but you should try to create a simple owner object using Owner.find(params[:id]), and lazy loading the associations when needed, so this way you can build nested associations on the simple object and save them.

Upvotes: 3

Related Questions