Rails - using `rails generate model` to specify non-nullable field type

4.8k views Asked by At

According to the rails documentation

http://guides.rubyonrails.org/migrations.html

2.3 Supported Type Modifiers says it should be possible to modify fields to allow or disallow NULL in the column, and that it's possible to do this on the terminal

This is what I want to appear in the migration file

class CreateTestModels < ActiveRecord::Migration
  def change
    create_table :test_models do |t|
      t.string:non_nullable, :null => false

      t.timestamps
    end
  end
end

On the terminal, I've tried

rails generate model TestModel non_nullable:string{null}
rails generate model TestModel 'non_nullable:string{null: false}'

I can't think of any other way to express it

Note: I already know you can go into the migration file and manually add it. That's not what I'm looking for.

4

There are 4 answers

0
duleorlovic On

You can open editor by utilising https://github.com/rails/rails/pull/38870 (available for Rails versions > 6.1.0)

To create migration with null: false from command line, first you need to enable EDITOR_FOR_GENERATOR

# config/application.rb
    # https://github.com/rails/rails/pull/38870#issuecomment-609018444
    config.generators.after_generate do |files|
      if ENV["EDITOR_FOR_GENERATOR"]
        files.each do |file|
          system("#{ENV["EDITOR_FOR_GENERATOR"]} #{file}")
        end
      end
    end

Than use sed to append to specific columns.

For example that you want to create a model with jti and exp columns with not null constrains and add index to them (index is supported on command line using :index). We need to match line t.string :jti and append to it so end result is t.string :jti, null: false

Here is command I use:

# rails g model CreateJwtDenylist jti:index exp:datetime:index

# replace jti and exp with your column names
EDITOR_FOR_GENERATOR='sed -i "" -r -e "/^[[:space:]]*t.*(jti|exp)$/ s/$/, null: false/"' rails g model CreateJwtDenylist jti:index exp:datetime:index

This works both for rails g migration and rails g model.

Resulting migration is

# db/migrate/20230121091319_create_jwt_denylist.rb
class CreateJwtDenylist < ActiveRecord::Migration[7.0]
  def change
    create_table :jwt_denylists do |t|
      t.string :jti, null: false
      t.datetime :exp, null: false

      t.timestamps
    end
    add_index :jwt_denylists, :jti
    add_index :jwt_denylists, :exp
  end
end
2
pyron_orion On

You can do it in your model class like this-

class TestModel < ActiveRecord::Base
  validates_presence_of :non_nullable
end
0
user2903934 On

The closest I can get to your solution is something like this:

rails generate model TestModel non_nullable,null:string

I couldn't work out what comes after the , but that should give you a start

1
devpuppy On

The docs mention that

Some commonly used type modifiers can be passed directly on the command line. They are enclosed by curly braces and follow the field type

but they don't give details about which "commonly used" modifiers willl work.

As pointed out by mr rogers there are only three supported options:

  • length for string/text/binary/integer (name:string{255})
  • precision,scale for decimal (dollar_fragment:decimal{3,2})
  • polymorphic for references/belongs_to (agent:references{polymorphic})

As mentioned by user2903934 it may be possible to make this work from the command line as a hack.

NOTE: this is a hack. i wouldn't recommend doing this but it does answer your question.

rails generate model TestModel 'non_nullable, null => false:string'

It looks like it splits on the first colon, so we can use a hashrocket syntax to sneak options in there. This yields:

class CreateTestModels < ActiveRecord::Migration
  def change
    create_table :test_models do |t|
      t.string :non_nullable, null => false

      t.timestamps
    end
  end
end

That obviously isn't officially supported, it just happens to work.