Let's say you have a persisted instance of a model, RedCar, that uses STI and
is descended from Car. You have an action that changes the type of the record
to BlueCar. You use #becomes to cast the record to the new type before
the save because you want to use the validations and callbacks from the new
model:
But wait! If you retrieve car from the database again you'll see that it's
still an instance of RedCar. In fact, you'll see that no changes are saved to
the database:
Doing an explicit update_column on type here doesn't help:
Also, note: no exceptions are raised. We'll save you any more head-scratching by pointing out the SQL that is generated:
</span><span class="n">cars</span><span class="o"> SET </span><span class="k">type</span><span class="o"> = 'BlueCar', </span><span class="n">brand</span><span class="o"> = 'Ford'
</span><span class="n">cars</span><span class="o">.</span><span class="k">type</span><span class="o"> IN ('BlueCar') AND </span><span class="n">cars</span><span class="o">.</span><span class="n">id</span><span class="o"> = 1
The problem is that Rails STI scopes the statement to WHERE `cars`.`type` IN
('BlueCar') even for UPDATES and even when you're trying to change the type
itself. Woops.
So what's the solution? As far as we can tell you have to do a separate update after you've passed validation. We ended up implementing this in a before_filter along these lines: