Rails Select boolean with NULL value
First the environment:
Rails 2.1.0, Ruby 1.8.7, Mysql 5.1.54, Ubuntu 11.04
I have a boolean field in my table which starts as NULL, but I can not find a good way to set it to NULL. (This field is basically a yes / no / unanswered field, which true / false / null seems about right for. The client specifically said he would like it to be able to remain null (unanswered).)
Here is my migration (specific names replaced):
class AddQuestionToClients < ActiveRecord::Migration
def self.up
add_column :clients, :question, :boolean
end
def self.down
remove_column :clients, :question
end
end
My controller uses a basic
@client = Client.find(params[:id])
@client.update_attributes(params[:client])
My view has a select (I think this is causing the problem, was never great with form helper selects):
<%= f.select :question, [["Yes", true], ["No", false]], { :include_blank => true, :selected => @client.question } %>
When selecting yes, true is passed; no, false is passed; blank, "" is passed. But "" seems to automatically convert to false when updating the开发者_如何学编程 database.
To get around this, I'm doing this:
params[:client][:question] = (params[:client][:question] == "")? nil : params[:client][:question]
But this can't be the right way to do it. What am I missing?
I just ran into the same problem, and I solved it by making the attribute convert blank to null. In your case, I would add a method to your model class.
class Client < ActiveRecord::Base
def question=(value)
value = nil if value == ''
super(value)
end
end
This works for both create and update. (I'm using Rails 2.0.5.)
I think that you can do that only in that way server side, because when a data is posted is always not nil. So your solution is correct.
If you want to avoid that ugly code you can do a little trick client side using javascript. In fact if you use a disabled element instead of a blank value that value won't be posted and you get nil
on server side.
To do that you can add a submit callback that checks whether the question field is blank and if so disable it before posting data. In that way it will work without any server side code.
To disable an element using jQuery you can see this link. So assuming your form has the #form
id you can use this code
$(function() {
$('#form').submit(function () {
if ($('question-selector').val() == "") {
$('question-selector').attr('disabled', true);
}
})
});
I had the same problem, i needed null value by default for boolean to represent "not answered"
With Rails 3.2, when i pass "" for a boolean value (blank option) is sets the column to NULL in db.
So problem solved with newer version of rails :)
<%= f.select :question, options_for_select([["Yes", true], ["No", false]], :question) %>
@vdaubry I would only add that it is not really solved ..it is masked because you are using 'select' in your UI. If you are foolish enough to use checkboxes, like me (after all, they are booleans, right?) then your nice default nil value in the db, which we mean to represent 'unknown' is converted to 'false' when rendered in the view, and converted to false in the db, even when it is not updated in the view, same as was described above. In other words, it's a bit brittle to continue to rely on a 3rd value for 'boolean' being consistent.
Furthermore, if ever reporting on this attribute, your reporting code cannot easily deduce the values in the db, if "unknown" is a valid meaning and is obscured.
So I chose to refactor all my booleans making them strings ('yes', 'no', 'unknown') and use radio buttons in the views. Note this only matters for things like data collection UIs, where a user has not got round to finding out the truth, so statistically it matters a lot if it's "unknown".
This isn't mentioned in previous posts, but in Rails 5 this should work if you want to name the nil option
<%= f.select :question, [["Yes", true], ["No", false]], { :include_blank => 'Name this whatever you want, it will be nil when submitted', :selected => @client.question } %>
Tested this on my own project
精彩评论