Is it possible to unload an ActiveRecord association?
I encountered a situation where a model being saved in flash caused a marshall data too short
error, and discovered that the reason for this is that a huge association was being loaded and then when put into the session the data was too large for the field.
My classes look something like this:
class Location < ActiveRecord::Base
belongs_to :country
belongs_to :state
belongs_to :city
end
class Country < ActiveRecord::Base
has_many :states
has_many :locations
end
class State < ActiveRecord::Base
belongs_to :country
has_many :cities
has_many :locations
end
class City < ActiveRecord::Base
belongs_to :state
has_many :locations
end
Unfortunately, the validation in Location
accesses self.state.cities
, which loads all the cities for that state into memory (in this case, over 1000 records). As such, when the Location
object is passed 开发者_如何转开发through flash
, it causes the above error.
I realize that the code can be refactored to not access the association (in fact, that's what I've done at the moment), but I was wondering if there was any way to unload the association data from memory (without deleting it from the database, of course).
This project is using Rails 2.3.4 and unfortuately I am unable to upgrade it at the moment.
Part 1: Solution to the stated problem:
To empty the loaded association data, use the target
method on the association:
location.state.cities.target=[]
If you access the cities
now:
location.state.cities
=> []
Part 2: Clumsy way to avoid the problem:
Avoid loading the association while accessing cities
. Use
self.state.cities.all
Instead of
self.state.cities
Part 3: Something smells fishy:
Why are you storing objects in to flash? Flash is generally meant for sending text messages. If you assign non strings to flash you will run in to the cookie size limit in no time.
Also when you are validating why do you need to load all the cities? Does this mean you are validating in Ruby? Can you post the validation code. In most cases you can optimize this by moving validation to DB.
Edit: Extended the answer based on the comment
I follow this pattern in Rails 2.3.x. The variables I want to access in the view associated with edit
action is always a subset of variables available in update
action.
class ProductsController < ApplicationController
before_filter :init_data, :only => [:new, :create, :edit, :update,
:destroy, :show]
def update
@product.attributes = params[:product]
if @product.save
flash[:notice] = "Successfully saved the product."
redirect_to product_path(@product)
else
render :action => 'edit'
end
end
private
def init_data
switch(action.to_sym)
when :new, :create
@product = Product.new(params[:product])
when :edit, update, :destroy, :show
@product = Product.find(params[:id])
end
end
end
精彩评论