<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 6.1 用戶模型 接下來的三章要實現網站的“注冊”頁面(構思圖如[圖 6.1](#fig-signup-mockup-preview) 所示),在此之前我們先要解決存儲問題,因為現在還沒地方存儲用戶信息。所以,實現用戶注冊功能的第一步是,創建一個數據結構,獲取并存儲用戶的信息。 ![signup mockup bootstrap](https://box.kancloud.cn/2016-05-11_5732bd08d8ea7.png)圖 6.1:用戶注冊頁面的構思圖 在 Rails 中,數據模型的默認數據結構叫“模型”(model,MVC 中的 M,參見 [1.3.3 節](chapter1.html#model-view-controller))。Rails 為解決數據持久化提供的默認解決方案是,使用數據庫存儲需要長期使用的數據。和數據庫交互默認使用的是 Active Record。[[1](#fn-1)]Active Record 提供了一系列方法,無需使用[關系數據庫](http://en.wikipedia.org/wiki/Relational_database)所用的“結構化查詢語言”(Structured Query Language,簡稱 SQL),[[2](#fn-2)]就能創建、保存和查詢數據對象。Rails 還支持“遷移”(migration)功能,允許我們使用純 Ruby 代碼定義數據結構,而不用學習 SQL “數據定義語言”(Data Definition Language,簡稱 DDL)。最終的結果是,Active Record 把你和數據存儲層完全隔開了。本書開發的應用在本地使用 SQLite,部署后使用 PostgreSQL(由 Heroku 提供,參見 [1.5 節](chapter1.html#deploying))。這就引出了一個更深層的話題——在不同的環境中,即便使用不同類型的數據庫,我們也無需關心 Rails 是如何存儲數據的。 和之前一樣,如果使用 Git 做版本控制,現在應該新建一個主題分支: ``` $ git checkout master $ git checkout -b modeling-users ``` ## 6.1.1 數據庫遷移 回顧一下 [4.4.5 節](chapter4.html#a-user-class)的內容,在我們自己創建的 `User` 類中為用戶對象定義了 `name` 和 `email` 兩個屬性。那是個很有用的例子,但沒有實現持久性最關鍵的要求:在 Rails 控制臺中創建的用戶對象,退出控制臺后就會消失。本節的目的是為用戶創建一個模型,讓用戶數據不會這么輕易消失。 和 [4.4.5 節](chapter4.html#a-user-class)中定義的 `User` 類一樣,我們先為用戶模型創建兩個屬性,分別是 `name` 和 `email`。我們會把 `email` 屬性用作唯一的用戶名。[[3](#fn-3)]([6.3 節](#adding-a-secure-password)會添加一個屬性,存儲密碼)在[代碼清單 4.13](chapter4.html#listing-example-user) 中,我們使用 Ruby 的 `attr_accessor` 方法創建了這兩個屬性: ``` class User attr_accessor :name, :email . . . end ``` 不過,在 Rails 中不用這樣定義屬性。前面提到過,Rails 默認使用關系數據庫存儲數據,數據庫中的表由數據行組成,每一行都有相應的列,對應數據屬性。例如,為了存儲用戶的名字和電子郵件地址,我們要創建 `users` 表,表中有兩個列,`name` 和 `email`,這樣每一行就表示一個用戶,如[圖 6.2](#fig-users-table) 所示,對應的數據模型如[圖 6.3](#fig-user-model-sketch) 所示。(圖 6.3 只是梗概,完整的數據模型如[圖 6.4](#fig-user-model-initial) 所示。)把列命名為 `name` 和 `email` 后,Active Record 會自動把它們識別為用戶對象的屬性。 ![users table](https://box.kancloud.cn/2016-05-11_5732bd08f0351.png)圖 6.2:`users` 表中的示例數據![user model sketch](https://box.kancloud.cn/2016-05-11_5732bd090ff29.png)圖 6.3:用戶數據模型梗概 你可能還記得,在[代碼清單 5.28](chapter5.html#listing-generate-users-controller) 中,我們使用下面的命令生成了用戶控制器和 `new` 動作: ``` $ rails generate controller Users new ``` 創建模型有個類似的命令——`generate model`。我們可以使用這個命令生成用戶模型,以及 `name` 和 `email` 屬性,如[代碼清單 6.1](#listing-generate-user-model) 所示。 ##### 代碼清單 6.1:生成用戶模型 ``` $ rails generate model User name:string email:string invoke active_record create db/migrate/20140724010738_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml ``` (注意,控制器名是復數,模型名是單數:控制器是 `Users`,而模型是 `User`。)我們指定了可選的參數 `name:string` 和 `email:string`,告訴 Rails 我們需要的兩個屬性是什么,以及各自的類型(兩個都是字符串)。你可以把這兩個參數與[代碼清單 3.4](chapter3.html#listing-generating-pages) 和[代碼清單 5.28](chapter5.html#listing-generate-users-controller) 中的動作名對比一下,看看有什么不同。 執行上述 `generate` 命令之后,會生成一個遷移文件。遷移是一種遞進修改數據庫結構的方式,可以根據需求修改數據模型。執行 `generate` 命令后會自動為用戶模型創建遷移,這個遷移的作用是創建一個 `users` 表以及 `name` 和 `email` 兩個列,如[代碼清單 6.2](#listing-users-migration) 所示。(我們會在 [6.2.5 節](#uniqueness-validation)介紹如何手動創建遷移文件。) ##### 代碼清單 6.2:用戶模型的遷移文件(創建 `users` 表) db/migrate/[timestamp]_create_users.rb ``` class CreateUsers < ActiveRecord::Migration def change create_table :users do |t| t.string :name t.string :email t.timestamps null: false end end end ``` 注意,遷移文件名前面有個時間戳,指明創建的時間。早期,遷移文件名的前綴是遞增的數字,在團隊協作中,如果多個程序員生成了序號相同的遷移文件就可能會發生沖突。除非兩個遷移文件在同一秒鐘生成這種小概率事件發生了,否則使用時間戳基本可以避免沖突的發生。 遷移文件中有一個名為 `change` 的方法,定義要對數據庫做什么操作。在[代碼清單 6.2](#listing-users-migration) 中,`change` 方法使用 Rails 提供的 `create_table` 方法在數據庫中新建一個表,用來存儲用戶。`create_table` 方法可以接受一個塊,塊中有一個塊變量 `t`(“table”)。在塊中,`create_table` 方法通過 `t` 對象創建 `name` 和 `email` 兩個列,均為 `string` 類型。[[4](#fn-4)]表名是復數形式(`users`),不過模型名是單數形式(`User`),這是 Rails 在用詞上的一個約定:模型表示單個用戶,而數據庫表中存儲了很多用戶。塊中最后一行 `t.timestamps null: false` 是個特殊的方法,它會自動創建兩個列,`created_at` 和 `updated_at`,這兩個列分別記錄創建用戶的時間戳和更新用戶數據的時間戳。([6.1.3 節](#creating-user-objects)有使用這兩個列的例子。)這個遷移文件表示的完整數據模型如[圖 6.4](#fig-user-model-initial) 所示。(注意,[圖 6.3](#fig-user-model-sketch) 中沒有列出自動添加的兩個時間戳列。) ![user model initial 3rd edition](https://box.kancloud.cn/2016-05-11_5732bd091f455.png)圖 6.4:[代碼清單 6.2](#listing-users-migration) 生成的用戶數據模型 我們可以使用如下的 `rake` 命令([旁注 2.1](chapter2.html#aside-rake))執行這個遷移(叫“向上遷移”): ``` $ bundle exec rake db:migrate ``` (你可能還記得,我們在 [2.2 節](chapter2.html#the-users-resource)用過這個命令。)第一次運行 `db:migrate` 命令時會創建 `db/development.sqlite3`,這是 SQLite [[5](#fn-5)]數據庫文件。若要查看數據庫結構,可以使用 [SQLite 數據庫瀏覽器](http://sqlitebrowser.org/)打開 `db/development.sqlite3` 文件,如[圖 6.5](#fig-sqlite-database-browser) 所示。(如果想從云端 IDE 把這個文件下載到本地電腦,可以在 `db/development.sqlite3` 上按右鍵,然后選擇“Download”。)和[圖 6.4](#fig-user-model-initial) 中的模型對比之后,你可能會發現有一個列在遷移中沒有出現——`id` 列。[2.2 節](chapter2.html#the-users-resource)提到過,這個列是自動生成的,Rails 用這個列作為行的唯一標識符。 ![sqlite database browser 3rd edition](https://box.kancloud.cn/2016-05-11_5732bd0931aa3.png)圖 6.5:在 SQLite 數據庫瀏覽器中查看剛創建的 `users` 表 大多數遷移,包括本書中的所有遷移,都是可逆的,也就是說可以使用一個簡單的 Rake 命令“向下遷移”,撤銷之前的操作,這個命令是 `db:rollback`: ``` $ bundle exec rake db:rollback ``` (還有一個撤銷遷移的方法,參見[旁注 3.1](chapter3.html#aside-undoing-things)。)這個命令會調用 `drop_table` 方法,把 `users` 表從數據庫中刪除。之所以可以這么做,是因為 `change` 方法知道 `create_table` 的逆操作是 `drop_table`,所以回滾時會直接調用 `drop_table` 方法。對于一些無法自動逆轉的操作,例如刪除列,就不能依賴 `change` 方法了,我們要分別定義 `up` 和 `down` 方法。關于遷移的更多信息請查看 [Rails 指南](http://guides.rubyonrails.org/migrations.html)。 如果你執行了上面的回滾操作,在繼續閱讀之前請再遷移回來: ``` $ bundle exec rake db:migrate ``` ## 6.1.2 模型文件 我們看到,執行[代碼清單 6.1](#listing-generate-user-model) 中的命令后會生成一個遷移文件([代碼清單 6.2](#listing-users-migration)),也看到了執行遷移后得到的結果([圖 6.5](#fig-sqlite-database-browser)):修改 `db/development.sqlite3` 文件,新建 `users` 表,并創建 `id`、`name`、`email`、`created_at` 和 `updated_at` 這幾個列。[代碼清單 6.1](#listing-generate-user-model) 同時還生成了一個模型文件,本節剩下的內容專門解說這個文件。 我們先看用戶模型的代碼,在 `app/models/` 文件夾中的 `user.rb` 文件里。這個文件的內容非常簡單,如[代碼清單 6.3](#listing-raw-user-model) 所示。 ##### 代碼清單 6.3:剛創建的用戶模型 app/models/user.rb ``` class User < ActiveRecord::Base end ``` [4.4.2 節](chapter4.html#class-inheritance)介紹過,`class User &lt; ActiveRecord::Base` 的意思是 `User` 類繼承自 `ActiveRecord::Base` 類,所以用戶模型自動獲得了 `ActiveRecord::Base` 的所有功能。當然了,只知道這種繼承關系沒什么用,我們并不知道 `ActiveRecord::Base` 做了什么。下面看幾個實例。 ## 6.1.3 創建用戶對象 和[第 4 章](chapter4.html#rails-flavored-ruby)一樣,探索數據模型使用的工具是 Rails 控制臺。因為我們還不想修改數據庫中的數據,所以要在沙盒模式中啟動控制臺: ``` $ rails console --sandbox Loading development environment in sandbox Any modifications you make will be rolled back on exit >> ``` 如提示消息所說,“Any modifications you make will be rolled back on exit”,在沙盒模式下使用控制臺,退出當前會話后,對數據庫做的所有改動都會回歸到原來的狀態。 在 [4.4.5 節](chapter4.html#a-user-class)的控制臺會話中,我們要引入[代碼清單 4.13](chapter4.html#listing-example-user) 中的代碼才能使用 `User.new` 創建用戶對象。對模型來說,情況有所不同。你可能還記得 [4.4.4 節](chapter4.html#a-controller-class)說過,Rails 控制臺會自動加載 Rails 環境,這其中就包括模型。也就是說,現在無需加載任何代碼就可以直接創建用戶對象: ``` >> User.new => #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil> ``` 上述代碼顯示了一個用戶對象的默認值。 如果不為 `User.new` 指定參數,對象的所有屬性值都是 `nil`。在 [4.4.5 節](chapter4.html#a-user-class),自己編寫的 `User` 類可以接受一個哈希參數初始化對象的屬性。這種方式是受 Active Record 啟發的,在 Active Record 中也可以使用相同的方式指定初始值: ``` >> user = User.new(name: "Michael Hartl", email: "mhartl@example.com") => #<User id: nil, name: "Michael Hartl", email: "mhartl@example.com", created_at: nil, updated_at: nil> ``` 我們看到 `name` 和 `email` 屬性的值都已經設定了。 數據的有效性對理解 Active Record 模型對象很重要,我們會在 [6.2 節](#user-validations)深入介紹。不過注意,現在這個 `user` 對象是有效的,我們可以在這個對象上調用 `valid?` 方法確認: ``` >> user.valid? true ``` 到目前為止,我們都沒有修改數據庫:`User.new` 只在內存中創建一個對象,`user.valid?` 只是檢查對象是否有效。如果想把用戶對象保存到數據庫中,我們要在 `user` 變量上調用 `save` 方法: ``` >> user.save (0.2ms) begin transaction User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE LOWER("users". "email") = LOWER('mhartl@example.com') LIMIT 1 SQL (0.5ms) INSERT INTO "users" ("created_at", "email", "name", "updated_at) VALUES (?, ?, ?, ?) [["created_at", "2014-09-11 14:32:14.199519"], ["email", "mhartl@example.com"], ["name", "Michael Hartl"], ["updated_at", "2014-09-11 14:32:14.199519"]] (0.9ms) commit transaction => true ``` 如果保存成功,`save` 方法返回 `true`,否則返回 `false`。(現在所有保存操作都會成功,因為還沒有數據驗證功能,[6.2 節](#user-validations)會看到一些失敗的例子。)Rails 還會在控制臺中顯示 `user.save` 對應的 SQL 語句(`INSERT INTO "users"…`),以供參考。本書幾乎不會使用原始的 SQL,[[6](#fn-6)]所以此后會省略 SQL。不過,從 Active Record 各種操作生成的 SQL 中可以學到很多知識。 你可能注意到了,剛創建時用戶對象的 `id`、`created_at` 和 `updated_at` 屬性值都是 `nil`,下面看一下保存之后有沒有變化: ``` >> user => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46"> ``` 我們看到,`id` 的值變成了 `1`,那兩個自動創建的時間戳屬性也變成了當前時間。[[7](#fn-7)]現在這兩個時間戳是一樣的,[6.1.5 節](#updating-user-objects)會看到二者不同的情況。 和 [4.4.5 節](chapter4.html#a-user-class)的 `User` 類一樣,用戶模型的實例也可以使用點號獲取屬性: ``` >> user.name => "Michael Hartl" >> user.email => "mhartl@example.com" >> user.updated_at => Thu, 24 Jul 2014 00:57:46 UTC +00:00 ``` [第 7 章](chapter7.html#sign-up)會介紹,雖然一般習慣把創建和保存分成如上所示的兩步完成,不過 Active Record 也允許我們使用 `User.create` 方法把這兩步合成一步: ``` >> User.create(name: "A Nother", email: "another@example.org") #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2014-07-24 01:05:24", updated_at: "2014-07-24 01:05:24"> >> foo = User.create(name: "Foo", email: "foo@bar.com") #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2014-07-24 01:05:42", updated_at: "2014-07-24 01:05:42"> ``` 注意,`User.create` 的返回值不是 `true` 或 `false`,而是創建的用戶對象,可直接賦值給變量(例如上面第二個命令中的 `foo` 變量). `create` 的逆操作是 `destroy`: ``` >> foo.destroy => #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2014-07-24 01:05:42", updated_at: "2014-07-24 01:05:42"> ``` 奇怪的是,`destroy` 和 `create` 一樣,返回值是對象。我不覺得什么地方會用到 `destroy` 的返回值。更奇怪的是,銷毀的對象還在內存中: ``` >> foo => #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2014-07-24 01:05:42", updated_at: "2014-07-24 01:05:42"> ``` 那么我們怎么知道對象是否真被銷毀了呢?對于已經保存而沒有銷毀的對象,怎樣從數據庫中讀取呢?要回答這些問題,我們要先學習如何使用 Active Record 查找用戶對象。 ## 6.1.4 查找用戶對象 Active Record 提供了好幾種查找對象的方法。下面我們使用這些方法查找創建的第一個用戶,同時也驗證一下第三個用戶(`foo`)是否被銷毀了。先看一下還存在的用戶: ``` >> User.find(1) => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46"> ``` 我們把用戶的 ID 傳給 `User.find` 方法,Active Record 會返回 ID 為 1 的用戶對象。 下面來看一下 ID 為 3 的用戶是否還在數據庫中: ``` >> User.find(3) ActiveRecord::RecordNotFound: Couldn't find User with ID=3 ``` 因為我們在 [6.1.3 節](#creating-user-objects)銷毀了第三個用戶,所以 Active Record 無法在數據庫中找到這個用戶,拋出了一個異常,這說明在查找過程中出現了問題。因為 ID 不存在,所以 `find` 方法拋出 `ActiveRecord::RecordNotFound` 異常。[[8](#fn-8)] 除了這種查找方法之外,Active Record 還支持通過屬性查找用戶: ``` >> User.find_by(email: "mhartl@example.com") => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46"> ``` 我們會使用電子郵件地址做用戶名,所以在學習如何讓用戶登錄網站時會用到這種 `find` 方法([第 7 章](chapter7.html#sign-up))。你可能會擔心如果用戶數量過多,使用 `find_by` 的效率不高。事實的確如此,我們會在 [6.2.5 節](#uniqueness-validation)說明這個問題,以及如何使用數據庫索引解決。 最后,再介紹幾個常用的查找方法。首先是 `first` 方法: ``` >> User.first => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46"> ``` 很明顯,`first` 會返回數據庫中的第一個用戶。還有 `all` 方法: ``` >> User.all => #<ActiveRecord::Relation [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2014-07-24 01:05:24", updated_at: "2014-07-24 01:05:24">]> ``` 從控制臺的輸出可以看出,`User.all` 方法返回一個 `ActiveRecord::Relation` 實例,其實這是一個數組([4.3.1 節](chapter4.html#arrays-and-ranges)), 包含數據庫中的所有用戶。 ## 6.1.5 更新用戶對象 創建對象后,一般都會進行更新操作。更新有兩種基本方式,其一,可以分別為各屬性賦值,在 [4.4.5 節](chapter4.html#a-user-class)就是這么做的: ``` >> user # 只是為了查看 user 對象的屬性是什么 => #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2014-07-24 00:57:46", updated_at: "2014-07-24 00:57:46"> >> user.email = "mhartl@example.net" => "mhartl@example.net" >> user.save => true ``` 注意,如果想把改動寫入數據庫,必須執行最后一個方法。我們可以執行 `reload` 命令來看一下沒保存的話是什么情況。`reload` 命令會使用數據庫中的數據重新加載對象: ``` >> user.email => "mhartl@example.net" >> user.email = "foo@bar.com" => "foo@bar.com" >> user.reload.email => "mhartl@example.net" ``` 現在我們已經更新了用戶數據,如在 [6.1.3 節](#creating-user-objects)中所說,自動創建的那兩個時間戳屬性不一樣了: ``` >> user.created_at => "2014-07-24 00:57:46" >> user.updated_at => "2014-07-24 01:37:32" ``` 更新數據的第二種常用方式是使用 `update_attributes` 方法:[[9](#fn-9)] ``` >> user.update_attributes(name: "The Dude", email: "dude@abides.org") => true >> user.name => "The Dude" >> user.email => "dude@abides.org" ``` `update_attributes` 方法接受一個指定對象屬性的哈希作為參數,如果操作成功,會執行更新和保存兩個操作(保存成功時返回值為 `true`)。注意,如果任何一個數據驗證失敗了,例如存儲記錄時需要密碼([6.3 節](#adding-a-secure-password)實現),`update_attributes` 操作就會失敗。如果只需要更新單個屬性,可以使用 `update_attribute`,跳過驗證: ``` >> user.update_attribute(:name, "The Dude") => true >> user.name => "The Dude" ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看