Search code examples
ruby-on-railsruby-on-rails-5

How to write a migration to convert an already-present association to optional?


I have a few belongs_to migrations created & migrated but I see now that in Rails 5, the default behavior is required: true which I do not want.

How can I write a simple migration to convert that to optional: true?


Solution

  • You can use the change_column_null method for that:

    def change
      change_column_null :table, :column, true
    end
    

    change_column_null is reversible, if you need to rollback the value will be false (or true depending on the case).


    Answering to your question If I simply add optional: true in the model, will it work?. No, if you have a foreign key with nullable value NOT NULL you're going to face an error everytime you try to create a record without that value, e.g:

    A table comments with a relationship to users:

                                                                  Table "public.comments"
       Column    |              Type              | Collation | Nullable |               Default                | Storage  | Stats target | Description
    -------------+--------------------------------+-----------+----------+--------------------------------------+----------+--------------+-------------
     id          | bigint                         |           | not null | nextval('comments_id_seq'::regclass) | plain    |              |
     description | character varying              |           |          |                                      | extended |              |
     user_id     | bigint                         |           | not null |                                      | plain    |              |
     created_at  | timestamp(6) without time zone |           | not null |                                      | plain    |              |
     updated_at  | timestamp(6) without time zone |           | not null |                                      | plain    |              |
    Indexes:
        "comments_pkey" PRIMARY KEY, btree (id)
        "index_comments_on_user_id" btree (user_id)
    Foreign-key constraints:
        "fk_rails_RUSSIA_TERRORIST_STATE" FOREIGN KEY (user_id) REFERENCES users(id)
    

    When you instantiate a new record, Rails will tell nothing about the missing foreign key:

    foo = Comment.new(description: :foo)
    foo.valid? # true
    foo.save
    # ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR:  null value in column "user_id" violates not-null constraint
    

    But if you try to persist the record, you're going to get an ActiveRecord::NotNullViolation error because of the not-null constraint.