在Rails中将列类型更改为较长的字符串

97

在第一次迁移时,我声明了一个名为content的列为字符串类型。使用annotate gem后,Activerecord将其设为string(255)。

在将应用程序推送到使用postgres的heroku之后,如果我在表单中输入长度超过255的字符串,则会出现错误。

PGError: ERROR: value too long for type character varying(255)

问题是,我需要该内容包含一个非常长的字符串(自由文本,可能有数千个字符)

  1. PostgreSQL会接受哪个变量(字符串对于此不适用)?
  2. 如何创建迁移以替换该列的类型?

谢谢

2个回答

229
如果您想使用没有长度限制的字符串,应该在Rails中使用text。像这样的迁移:
def up
  change_column :your_table, :your_column, :text
end
def down
  # This might cause trouble if you have strings longer
  # than 255 characters.
  change_column :your_table, :your_column, :string
end

应该对事情进行排序。您可能希望在末尾使用:null => false或其他选项。

当您使用没有明确限制的string列时,Rails会添加一个隐式的:limit => 255。但是如果您使用text,则会得到数据库支持的任意长度字符串类型。PostgreSQL允许您在没有长度的情况下使用varchar列,但大多数数据库都使用单独的类型,并且Rails不知道没有长度的varchar。您必须在Rails中使用text才能在PostgreSQL中获得text。在PostgreSQL中,text类型和varchar类型之间没有区别(但varchar(n)是不同的)。此外,如果您在PostgreSQL上部署,根本没有理由使用:string(又名varchar),除了varchar(n)的额外长度约束之外,数据库在内部处理textvarchar(n)是相同的;仅在列大小存在外部约束(例如政府表格上的第432个字段897/B将为23个字符长)时,才应该使用varchar(n)(又名:string)。
作为一个附注,如果你在任何地方使用了string列,你应该总是指定:limit,以提醒自己有一个限制,并且你应该在模型中进行验证以确保不超过限制。如果超过限制,PostgreSQL会抱怨并引发异常,MySQL会悄悄地截断字符串或抱怨(取决于服务器配置),SQLite会让它原样通过,其他数据库将做一些其他的事情(可能是抱怨)。
此外,你还应该在相同的数据库上开发、测试和部署(通常是Heroku上的PostgreSQL),甚至应该使用相同版本的数据库服务器。数据库之间还有其他差异(例如GROUP BY的行为),ActiveRecord无法保护你免受这些差异的影响。你可能已经在这样做了,但我还是想提一下。

更新:新版本的ActiveRecord可以理解没有限制的varchar,因此至少在PostgreSQL中,您可以使用以下语句:

change_column :your_table, :your_column, :string, limit: nil

将一个varchar(n)列更改为varchar。 就PostgreSQL而言,textvarchar仍然是相同的东西,但某些表单构建器会以不同的方式处理它们: varchar得到一个<input type="text">,而text得到一个多行的<textarea>

13
很好的回答。需要注意一点:Rails目前不支持在change方法中使用change_column(http://guides.rubyonrails.org/migrations.html#using-the-change-method);如果我没记错的话,如果这样做会创建一个不可逆转的迁移。最好使用旧的up/down方法来完成它。 - poetmountain
4
供其他读者参考,按照这种方式在Heroku上将字符串转换为文本,在Postgres中不会丢失数据。 - Marina Martin
如果你要在Heroku上部署应用,你也应该使用PostgreSQL进行开发。只是想指出,在Heroku上也可以使用MySQL。 - Dennis
2
@Dennis:也许“你应该使用同一个数据库进行开发、测试和部署”更准确一些。通常的问题是人们使用(荒谬的)Rails默认的SQLite设置,当他们在其他环境中部署时,一切都会崩溃。PostgreSQL仍然是Heroku的默认和最常见的选项,对吗? - mu is too short
4
顺便提一句,Rails假设未指定长度的字段应为255个字符,这很奇怪。在PostgreSQL中不必使用 text 就能获得无限长度;只需要使用未约束的 varchar 即可。Rails强加了这个奇怪的限制,而不是PostgreSQL。 - Craig Ringer
显示剩余4条评论

10
虽然已有的答案非常好,但我想在此添加一个答案,希望更好地解决原问题的第二部分,适合像我这样的非专家。
生成脚手架迁移
您可以通过在控制台中输入以下内容来生成用于保存更改的迁移(只需将“table”替换为您的表名,“column”替换为您的列名):
rails generate migration change_table_column

这将在您的Rails应用程序/db/migrate/文件夹中生成骨架迁移。 这个迁移是您的迁移代码的占位符。

例如,我想创建一个迁移,将名为TodoItems的表中的列类型从string更改为text

class ChangeTodoItemsDescription < ActiveRecord::Migration
  def change
     # enter code here
     change_column :todo_items, :description, :text
  end
end

运行迁移

一旦您输入了更改列的代码,只需运行以下命令:

rake db:migrate

应用您的迁移。如果出现错误,您可以使用以下命令还原更改:

rake db:rollack

Up和Down方法

接受的答案提到了UpDown方法,而不是更新的Change方法。自Rails 3.2以来,旧式的Up和Down方法相对于更新的Change方法有一些优势。'Up and Down'避免了ActiveRecord::IrreversibleMigration exception。自Rails 4发布以来,您可以使用reversible来避免此错误:

class ChangeProductsPrice < ActiveRecord::Migration
  def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end

享受 Rails :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接