ActiveRecord uses confusing defaults

Hi!

I have an AR model called Submission
In my submissions table (mysql 5) I have a column called postcode
(varchar, not null) and a column street (varchar, not null)

s = Submission.new
s.postcode # => 0

now I would expect this to return nil

s.street # => ''
I would expect this to return nil

Is there a reason for this?

Jeroen

It goes by the defaults set in your database. What's your schema look like?

Here's a snippet from one of my apps:

  create_table "contents", :force => true do |t|
    t.column "article_id", :integer
    t.column "comments_count", :integer, :default => 0
  end

Content.new.article_id

=> nil

Content.new.comments_count

=> 0

Hey that's interesting.

    t.column "email", :string, :default => "", :null => false

So my point was, setting the column default to "" means it's default
upon AR model initialization. If you set this up with :null => false
(or NOT NULL), your DB probably won't let you save NULL to the
attribute. Quit setting these confusing defaults, and you'll stop
seeing them in AR.

Jeroen,

You aren’t going nuts - it’s just AR trying to make it’s best interpretation of what it gets and this may very well be an edge case that could be up for discussion.

First, the migrations are simply used to create your table - they bear not on the actual model classes used later on. AR makes a call to “show fields” on the database and creates the column classes from there. Where you are running into an issue is that show fields returns a blank string for both of your example fields and AR makes it’s best guess from there based on the field type. For the string field - AR would have no choice but to do as it does because a blank string is a perfectly valid option for a string field default and therefore it needs to handle that case in that way. The integer column is a different story and you raise what I think would be an interesting idea that since a blank string is not a zero - then AR should not be making that leap in it’s handling of the default values. You might want to see what “show fields” would give back if you specifically gave your columns a nil (null) default value.

The short term solution to your problem should you need the nil values would be to override the columns method of the base class and put in the default values you wish for the columns

For example (completely untested so don’t be surprised if there’s bugs)

Content.rb

class Content < ActiveRecord::Base

def columns
unless @columns
super
@columns.map! { |col|
if col.name == ‘email’
col = MysqlColumn.new(col.name, #default value#, col.sql_type, col.null)
end
col
}
end
@columns
end

Obviously the #default value# would get replaced by the exact value you wanted for the column. Note this is pretty brute force but it should work just fine for you if you needed it. It also locks the model into MySQL due to the need for the MysqlColumn class. It also just doesn’t have that smooth look of rails :slight_smile:

Hope this helps you understand how it works and why

John W Higgins
wishdev@gmail.com