58

How do you declare an array column in Rails?

Detail

I have the following model

rails generate model User address:text

but I want a model which can store multiple addresses per user. The following declaration gives me an error

rails generate model User address[]:text 

How do you declare an array column in Rails?

notapatch
  • 6,569
  • 6
  • 41
  • 45
chandradot99
  • 3,616
  • 6
  • 27
  • 45
  • 1
    possible duplicate of [Storing arrays in database : JSON vs. serialized array](http://stackoverflow.com/questions/21312278/storing-arrays-in-database-json-vs-serialized-array) – Brad Werth Sep 05 '15 at 08:59
  • This should probably be a http://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many... – Brad Werth Sep 05 '15 at 09:00
  • 1
    I need make a note about the answers that have `default: []` on them. DON'T do this if you're adding a column to an existing table. You will lock your database down until the migration is finished running. You should instead add the column, populated it, then add the default. We need these three steps for hot compatibility. – Luke Jul 20 '21 at 17:55

4 Answers4

103

You can use following steps

rails g migration add_subjects_to_book subjects:text

And the migration file:

class AddSubjectsToBook < ActiveRecord::Migration
  def change
    add_column :books, :subjects, :text, array: true, default: []
  end
end

We can check it now:

2.1.2 :001 > b = Book.create
   (0.2ms)  BEGIN
  SQL (2.0ms)  INSERT INTO "books" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2014-10-17 08:21:17.870437"], ["updated_at", "2014-10-17 08:21:17.870437"]]
   (0.5ms)  COMMIT
 => #<Book id: "39abef75-56af-4ad5-8065-6b4d58729ee0", title: nil, created_at: "2014-10-17 08:21:17", updated_at: "2014-10-17 08:21:17", description: {}, metadata: {}, subjects: []>

2.1.2 :002 > b.subjects.class
 => Array

If you want to add array while creating table you can do as follows

create_table :products do |t|
  t.string :name, null: false
  t.references :category, null: false
  t.text :tags, array: true, default: []
end

Note: array column is supported by PostgreSQL

shrikant1712
  • 4,336
  • 1
  • 24
  • 42
32

If you are using Postgres, then this post http://blog.plataformatec.com.br/2014/07/rails-4-and-postgresql-arrays/ suggests using array: true option in migration script:

create_table :users do |t|
  # other columns
  t.text :addresses, array: true, default: []
end

In case you are not using Postgres, this answer might be helpful: Storing arrays in database : JSON vs. serialized array

ololobus
  • 3,568
  • 2
  • 18
  • 23
dimakura
  • 7,575
  • 17
  • 36
24

The examples using array: true would require Postgres or some other database capable of an arrays type. For MySQL, use generic serialization, which lets you store any arbitrary type in a column.

Database migration:

create_table :users do |t|
  t.text :addresses, default: [].to_yaml
  ...
end

Class with array attribute:

class User < ApplicationRecord
  serialize :addresses, Array
end

Using the attribute:

u = User.new
u.update_attributes addresses: ["123 Evergreen", "246 Main"]

The usual caveats apply to storing arrays in a database. It goes against the grain of relational databases to do this and will make it hard, slow, or impossible to do things like search for an individual item. However, it can be a fine solution for basic storage until you need to do those things.

idmean
  • 14,540
  • 9
  • 54
  • 83
mahemoff
  • 44,526
  • 36
  • 160
  • 222
4

If using Rails 5, follow the below steps to add an array of address in user table:

Run below command to generate a model with table,

rails generate model User address:text

The table will get create similar to the given below:

class CreateUser < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.text :address
      t.timestamps
    end
  end
end

Now add serialization for the address in a user model

class User < ApplicationRecord
 serialize :address, Array
end

Thank you.

Rahul2692
  • 334
  • 1
  • 10