How to change type of a record and run validations of the correct class in rails using the type column?
I have a model which uses Single table inheritance and has different types (school year of type either semester or quarter) and each type has its own validations. If a user tries to change the type of the record, he can select which academic year type it is from a drop down and make changes. however, if the user changes the type, i cannot figure out how to make new class validations run and not old validations. For instance, my code is as follows:
@schoo开发者_开发知识库l_year = SchoolYear.find(params[:id])
respond_to do |format|
if SchoolYear::SUBCLASSES.include? params[:school_year]['type']
@school_year[:type] = params[:school_year]['type']
else
raise "Invalid type"
end
if @school_year.update_attributes(params[:school_year])
# done
else
# validation problem?
end
now if the year's type was semester, and the user changes it to quarter, i expect the QuarterSchoolYear's validations to run and not of those of semester. How do i make changes to code to make it work?
I guess you should reload the object after you change the type. Just assigning a new value to the 'type' attribute will not change the ruby class of the object. Of course, when you save the object just after the type change, the old validations will be used.
You may try to update the type attribute in the database, and then load the object. Something like:
[..]
if type_differs_and_is_acceptable_to_change
SchoolYear.update_all(
['type = ?', params[:shool_year][:type] ],
['id = ?',@school_year.id ]
)
@school_year = SchoolYear.find(@school_year.id)
end
if @school_year.update_attributes...
Be sure to have :type NOT in attr_accessible for this class.
Besides that, I find it a little disturbing to change the type of the object, but that may be just my personal fear. :)
This is old, but cleaning up and improving upon @Arsen7's suggestion, the best way to reload your object as its new type is to use the following ActiveRecord method (which is made for this very reason), and it doesn't require you to save the object after changing its type. This way, you're not hitting the database twice:
[..]
if type_differs_and_is_acceptable_to_change
@school_year.type = params[:school_year][:type]
@school_year = @school_year.becomes(@school_year.type.constantize)
end
if @school_year.update_attributes...
You need to instantiate with the right type of model to run the validations. I don't see a way to do this except to save with the new type and find it again:
new_type = params[:school_year]['type']
@school_year[:type] = new_type
@school_year.save!
@school_year = new_type.constantize.find(@school_year.id)
@school_year.update_attributes(params[:school_year])
You'd probably want to put this in a transaction so you could rollback the change of type if update_attributes
fails.
精彩评论