1

我正在尝试学习rails,但遇到了一些问题。

我有一个旧的 sqlite3 数据库,它有一个Accounts我之前用一些 GUI 程序制作的表。我想看看是否可以通过数据库迁移重新创建该表。

这是我之前的描述(我的目标):

-- desired
CREATE TABLE "accounts" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL DEFAULT (0),
  "username" TEXT NOT NULL COLLATE NOCASE,
  "password_hash" BLOB NOT NULL,
  "creation_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now')),
  "expiration_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now') + 2592000)
)

我当前的迁移代码将无法正常工作。我在某个地方读到了可以:options用来指定 ActiveRecord 无法封装的东西的地方,但我可能做错了。当我使用:default => Time.now.to_i时,它只是硬编码了一些默认值;当我使用时:default => "strftime('%s', 'now')",它根本不起作用。

class CreateAccounts < ActiveRecord::Migration
  def change
    create_table :accounts do |t|
      t.column 'username', :text, :null => false, :options => 'COLLATE NOCASE'
      t.column 'password_hash', :binary, :null => false
      t.column 'creation_time', :integer, :null => false, :options => "DEFAULT(strftime('%s', 'now'))"
      t.column 'expiration_time', :integer, :null => false, :options => "DEFAULT(strftime('%s', 'now') + 2592000)"
    end
  end
end

我最终得到下表。看起来所有的:option值都被忽略了。

-- what i actually get
CREATE TABLE "accounts" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  "username" text NOT NULL,
  "password_hash" blob NOT NULL,
  "creation_time" integer NOT NULL,
  "expiration_time" integer NOT NULL
)

创建列时如何指定 SQL 位?任何帮助将不胜感激。

4

3 回答 3

3

您可以做的最简单的事情就是执行原始 SQL 来定义表。这看起来像:

class CreateAccounts < ActiveRecord::Migration
  def up
    execute <<-SQL
    CREATE TABLE "accounts" (
      "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL DEFAULT (0),
      "username" TEXT NOT NULL COLLATE NOCASE,
      "password_hash" BLOB NOT NULL,
      "creation_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now')),
      "expiration_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now') + 2592000)
    )
    SQL
  end

  def down
    remove_table :accounts
  end
end

这样,您最终将得到与以前相同的表。我不完全确定您是否可以使用当前的迁移 DSL 来表达这样的表定义。不过,您应该能够验证它。如果您使用上述原始 SQL 进行迁移,则可以运行rake db:migrate. 这应该会生成一个新的db/schema.rb. 然后,您可以打开该文件并搜索帐户表并查看定义。如果所有内容都包含在内,您可以将该定义复制回迁移中。

于 2012-12-31T14:07:50.787 回答
1

默认设置为:

t.column :creation_time, :integer, :null => false, :default => "strftime('%s', 'now'))"

collat​​e 和 nocase 无法通过 ruby​​ 完成,但可以使用 execute 方法运行原始 sql:

execute "ALTER TABLE accounts ADD COLUMN username TEXT NOT NULL COLLATE NOCASE AFTER id

或者,您可以简单地将排序规则替换为:

class Account < ActiveRecord::Base
  def username=(username)
    write_attribute :username, username.downcase
  end
end
于 2012-12-31T14:07:48.733 回答
1

您的迁移看起来不错。

既然您确实想真正学习 Rails,我会坚持使用它并尝试解决具体问题。

For rails, for created and updated date/time stamps what you do is create fields called updated_at and created_at (or _on for each) and then when rails uses active record to do a database table row update it will update those fields appropriately and automatically (or automagically as some would say).

This is a basic tenant of Rails - convention over configuration> Pick the right names and strictures and it will do the 'heavy-lifting' for you.

Your issue both using a default of now but it being hardcoded makes sense on reflection, but the above will address it

于 2012-12-31T14:26:58.647 回答