ror:用户建模

承接上文。

用户模型

创建并切换分支

git checkout -b modeling-users

生成用户模型

rails generate model User name:string email:string

数据库迁移

rails db:migrate
# rails db:rollback(向下迁移)

模型使用

打开控制台:

➜  microblog git:(modeling-users) rails c --sandbox  # 沙盒模式启动控制台
Running via Spring preloader in process 23202
Loading development environment in sandbox (Rails 5.0.1)
Any modifications you make will be rolled back on exit

新建/保存用户:

2.3.3 :001 > User.new  # 默认方式新建用户
 => # 
2.3.3 :002 > User.new name:"Joshua", email:"[email protected]"  # 指定信息新建用户(Hash为最后一个参数,可以省略{ })
 => # 
2.3.3 :003 > user = User.new name:"Joshua", email:"[email protected]"  # 新建用户并赋值
 => # 
2.3.3 :004 > user.valid?  # 验证是否有效
 => true 
2.3.3 :005 > user.save  # 保存数据库
   (0.2ms)  SAVEPOINT active_record_1
  SQL (31.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Joshua"], ["email", "[email protected]"], ["created_at", 2017-01-06 14:55:02 UTC], ["updated_at", 2017-01-06 14:55:02 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true   # 返回true/false
2.3.3 :006 > user  # 再次查看用户信息(包含:ID/创建时间/更新时间)
 => # 
2.3.3 :007 > user.name  # 查看
 => "Joshua" 
2.3.3 :008 > user.email  # 
 => "[email protected]" 
2.3.3 :009 > user.updated_at  #
 => Fri, 06 Jan 2017 14:55:02 UTC +00:00 
2.3.3 :010 > user = User.create(name:"Joshuaber", email:"[email protected]")  # 使用create等于new+save
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Joshuaber"], ["email", "[email protected]"], ["created_at", 2017-01-06 14:58:04 UTC], ["updated_at", 2017-01-06 14:58:04 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => # 
2.3.3 :011 > user  # 
 => # 
2.3.3 :012 > user.destroy  # 销毁
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 2]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => # 
2.3.3 :013 > user  # 销毁后对象仍然在内存中
 => # 

查找用户:

2.3.3 :014 > User.find(1)  # 查找id=1的用户(从数据库)
  User Load (573.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => # 
2.3.3 :015 > User.find(2)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=2
... ...
2.3.3 :016 > user  # 可以看到id=2的用户仍然在内存
 => # 
2.3.3 :017 > User.find(2)  # 
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=2
... ...
2.3.3 :018 > User.find_by(email:"[email protected]")  # 使用find_by进行属性查找
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => # 
2.3.3 :019 > User.first  #
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
 => # 
2.3.3 :020 > User.last  #
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ?  [["LIMIT", 1]]
 => # 
2.3.3 :021 > User.all  #
  User Load (0.4ms)  SELECT "users".* FROM "users"
 => #]> 

修改用户信息:

2.3.3 :022 > user = User.find(1)  # 查找并赋值
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => # 
2.3.3 :023 > user.email = "[email protected]"  # 修改信息
 => "[email protected]" 
2.3.3 :024 > user.save  # 保存修改
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "[email protected]"], ["updated_at", 2017-01-07 03:33:54 UTC], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true 
2.3.3 :025 > user.email = "[email protected]"  # 修改信息
 => "[email protected]" 
2.3.3 :026 > user.reload.email  # 重新加载(放弃修改)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => "[email protected]" 
2.3.3 :027 > user  # 
 => # 
2.3.3 :028 > user.created_at  #
 => Fri, 06 Jan 2017 14:55:02 UTC +00:00 
2.3.3 :029 > user.updated_at  #
 => Sat, 07 Jan 2017 03:33:54 UTC +00:00 
2.3.3 :030 > user.update_attributes(name:"Joshuaber", email:"[email protected]")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.4ms)  UPDATE "users" SET "email" = ?, "name" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "[email protected]"], ["name", "Joshuaber"], ["updated_at", 2017-01-07 03:37:20 UTC], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true 
2.3.3 :031 > user.update_attributes(email:"[email protected]")  # 修改属性(直接保存)
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.2ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "[email protected]"], ["updated_at", 2017-01-07 03:38:10 UTC], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true 
2.3.3 :032 > User.find(1)  #
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => #  

代码比较简单,请看注释

用户数据验证

使用TDD进行开发

执行测试命令

rails test:models

有效性测试

# test/models/user_test.rb

... ...
  def setup
    @user = User.new(name: "Example User", email: "[email protected]")
  end

  test "should be valid" do
    assert @user.valid?
  end
... ...

存在性验证

# test/models/user_test.rb

... ...
  test "name should be present" do
    @user.name = "  "
    assert_not @user.valid?  # 应当无效
  end
... ...

此时,测试是失败的,需要添加存在性验证:

# app/models/user.rb

validates :name, presence: true

同样的道理,添加email的存在性验证。

长度验证

# test/models/user_test.rb

... ...
  test "name should not be too long" do
    @user.name = "a" * 51  # 限制长度为50
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 256
    assert_not @user.valid?
  end

此时的测试是失败的,添加长度限制:

# app/models/user.rb

    validates :name, presence: true, length: { maximum: 50 }
    validates :email, presence: true, length: { maximum: 255 }

格式验证

# test/models/user_test.rb

  test "email validation should accept valid addresses" do
    valid_addresses = %w[[email protected] [email protected] [email protected] [email protected] [email protected]]
    valid_addresses.each do |valid_address|
      @user.email = valid_address
      assert @user.valid?, "#{valid_address.inspect} should be valid"
    end
  end

  test "email validation should reject invalid addresses" do
    invalid_addresses = %w[user@example,com user_at_foo.org [email protected]@bar_baz.com foo@bar+baz.com]
    invalid_addresses.each do |invalid_address|
      @user.email = invalid_address
      assert_not @user.valid?, "#{invalid_address.inspect} should be invalid"
    end
  end

添加格式限制:

# app/models/user.rb

    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i  # 简单的邮箱验证正则表达式
    validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }

唯一性验证

# test/models/user_test.rb

... ...
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    duplicate_user.email = @user.email.upcase
    @user.save
    assert_not duplicate_user.valid?
  end

添加唯一性限制:

# app/models.user.rb

# 保存前将邮件转变为小写
before_save { self.email = email.downcase }
... ...
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }

添加索引

生成迁移文件:

rails generate migration add_index_to_users_email

添加唯一性约束的迁移:

# db/migrate/20170107075525_add_index_to_users_email.rb

def change
  add_index :users, :email, unique: true
end

执行数据库迁移:

rails db:migrate

添加安全密码

添加密码列

rails generate migration add_password_digest_to_users password_digest:string
rails db:migrate

添加gem

# Gemfile

... ...
gem 'bcrypt'
... ...

执行bundle:

bundle

添加代码

# app/models/user.rb

... ...
  has_secure_password
end

修改测试

# test/models/user_test.rb

... ...

  def setup
    @user = User.new(name: "Example User", email: "[email protected]", password: "joshua", password_confirmation: "joshua")
  end
... ...

密码最短长度

添加测试:

# test/models/user_test.rb

... ...
  test "password should have a minimum length" do
    @user.password = @user.password_confirmation = "a" * 5  # max_length = 6
    assert_not @user.valid?
  end
... ...

添加密码长度限制:

# app/models/user.rb

validates :password, length: { minimum: 6 }

创建用户

2.3.3 :001 > User.all  # 
  User Load (17.5ms)  SELECT "users".* FROM "users"
 => # 
2.3.3 :002 > User.create(name:"Joshua", email:"[email protected]", password:"joshua", password_confirmation:"joshua")  # 创建用户
   (0.1ms)  begin transaction
  User Exists (24.0ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
  SQL (12.9ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?)  [["name", "Joshua"], ["email", "[email protected]"], ["created_at", 2017-01-08 04:40:15 UTC], ["updated_at", 2017-01-08 04:40:15 UTC], ["password_digest", "$2a$10$vWuRpVTzhdwVHzRlJBBJi.0jVUSuUJvIUnzybK6QQ1t8FQavFoz5i"]]
   (123.8ms)  commit transaction
 => # 
2.3.3 :003 > User.find_by(email:"[email protected]")  # 大小写敏感
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => nil 
2.3.3 :004 > User.find_by(email:"[email protected]")  # 查询
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => # 
2.3.3 :005 > user = User.find_by(email:"[email protected]")  #
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => # 
2.3.3 :006 > user.password_  # 使用Tab补全命令
user.password_confirmation                
... ...   
2.3.3 :006 > user.password_digest  # 查询
 => "$2a$10$vWuRpVTzhdwVHzRlJBBJi.0jVUSuUJvIUnzybK6QQ1t8FQavFoz5i" 
2.3.3 :007 > user.authenticate("wrongpwd")  # 错误认证
 => false 
2.3.3 :008 > user.authenticate("joshua")  # 正确认证
 => # 

收尾

合并删除分支

git checkout master
git merge modeling-users
git branch -d modeling-user
git branch

部署到Heroku并测试

git push heroku master
heroku rake db:migrate
heroku run console --sandbox

参考

《Rails Tutorial 3th》

你可能感兴趣的:(ror:用户建模)