读高见龙的《为你自己学Ruby on Rails》后半部分提取笔记

Model 基本操作

  1. 当我们需要引用外部数据库时,假如我们想改名字,可以这样做:

      class User < ActiveRecord::Base
        self.table_name = "example"
      end
    
  2. 什么是主键(primary key):每個資料表的流水編號欄位(ID).

    当然这个主键也可以自定义

    • class User < ActiveRecord::Base
        self.primary_key = "user_id"
      end
      
    
    
  3. 什么是ORM?

    我們如果想要存取資料庫裡的內容,以前必需透過資料庫查詢語言(SQL)向資料庫進行查詢,但透過 ORM 的包裝之後,可以讓我們用操作「物件」的方式來操作資料庫。

  4. 平均数求和最大值最小值 直接让数据库来计算!

    $ bin/rails console
    >> Candidate.sum(:age)
       (0.2ms)  SELECT SUM("candidates"."age") FROM "candidates"
    => 44
    
    >> Candidate.average(:age).to_f
       (0.1ms)  SELECT AVG("candidates"."age") FROM "candidates"
    => 14.6666666666667
    
    $ bin/rails console
    >> Candidate.maximum(:age)
       (0.2ms)  SELECT MAX("candidates"."age") FROM "candidates"
    => 22
    >> Candidate.minimum(:age)
       (0.2ms)  SELECT MIN("candidates"."age") FROM "candidates"
    => 2
    
  5. 更新数据的方法有 saveupdateupdate_attributeupdate_attributes

    • # 使用 save 方法
      candidate.name = "剪彩倫"
      candidate.save
      
      # 使用 update_attribute 方法更新單一欄位的值(注意:方法名字是單數)
      candidate.update_attribute(:name, "剪彩倫")
      
      # 使用 update 更新資料,可一次更新多個欄位,且不需要再呼叫 save 方法
      candidate.update(name: "剪彩倫", age: 20)
      
      # 使用 update_attributes 方法
      candidate.update_attributes(name: "剪彩倫", age: 20) 
      
      單數的 `update_attribute` 方法會跳過驗證(Validation),等於是 `save(validate: false)` 的效果,在使用的時候要稍微注意一下。
    
    - 如果想整个资料表一起修改:
    
    
    Candidate.update_all(name: "剪彩倫", age: 18)
    
    
    
  6. scope可以嵌套,利用嵌套,可以方便我们:

    • 使代码整洁。
    • 方便我们修改。

    还可以:预设scope,

      class Product < ActiveRecord::Base
        default_scope { order('id DESC') }
        scope :available, -> { where(is_available: true) }
      end
    

    要取消預設的 scope,必須使用 unscope 方法:

    $ bin/rails console
    >> Product.unscope(:order)
      Product Load (0.2ms)  SELECT "products".* FROM "products"
    
    >> Product.unscope(:order).order(:title)
      Product Load (0.3ms)  SELECT "products".* FROM "products" ORDER BY "products"."title" ASC
    
    

    這樣才能把預設的 scope 的效果移除。

Model Migration

  1. 什么是migration: 描述数据表长什么样的档案。

  2. 为什么要这样设计:一切为了方便多人协作!

  3. 刚执行rake db:migrate发现要改一下栏位,怎么办?

    • 可以用rollback : rake db:rollback (这样一次可以退回一个migration文件。)
    • 新增migration文件来修改。(推荐!)
  4. 新增migration文件时,原来可以直接上index索引啊,酷!:

    $ bin/rails g migration add_candidate_id_to_articles candidate_id:integer:index
    Running via Spring preloader in process 7765
      invoke  active_record
      create    db/migrate/20170101081538_add_candidate_id_to_articles.rb
    
      class AddCandidateIdToArticles < ActiveRecord::Migration[5.0]
        def change
          add_column :articles, :candidate_id, :integer
          add_index :articles, :candidate_id
        end
      end
    
  5. rake db:setup可以一口氣把資料表建完,順便把預設資料寫入。

model的关联性

  1. 什么是外部键:用于对应其他model主键的栏位,例如:user_id product_id

  2. 可以用user.create_post(:title => "hello")来新建对应的资料,生成的资料自动对应好user_id。

  3. 之前商店大赛加油站,无法先新建address再对应user_id,现在终于找到原因和解决方法!:

    • 原因:rails5之后,必须先建立才能建立

    • 解法:加多一个optional: true

        class Store < ApplicationRecord
          belongs_to :user, optional: true
        end
      
  4. has_one 跟 belongs_to 方法需要同時設定嗎?

    不一定,端看需求,一樣以我們上面 User 跟 Store 的例子來看,如果你不需要「從 Store 反查 User」的功能的話,那 belongs_to 是不需要設定的。

  5. 其实多对多没有我们rails101教材上的那么复杂,其实可以这么简单:

      class WareHouse < ApplicationRecord
        belongs_to :store
        belongs_to :product
      end
    
      class Store < ApplicationRecord
        belongs_to :user
    
        has_many :ware_houses
        has_many :products, through: :ware_houses
      end
    
      class Product < ApplicationRecord
        has_many :ware_houses
        has_many :stores, through: :ware_houses
      end
    

    看到没有,第二个has_many只要写两个字段就可以啦!之前是这样的:

    读高见龙的《为你自己学Ruby on Rails》后半部分提取笔记_第1张图片
    image

    有点让新手摸不着头脑。。。

  6. 酷!还有一种叫HABTM(has_and_belongs_to_many)!

      # Store Model
      class Store < ActiveRecord::Base
        has_and_belongs_to_many :products
      end
    
      # Product Model
      class Product < ActiveRecord::Base
        has_and_belongs_to_many :stores
      end
    

    就這樣,不需要另外新增第三方 Model 即可完成多對多關連。注意,我是說「不需要第三方 Model」,不是「不需要第三方資料表」,畢竟還是要有一個資料表存放雙方的資訊,只是這個資料表因為不重要也不會存取它,所以可以不需要 Model 對應。

    這個第三方資料表的名字是有規定的,預設是「兩個資料表依照英文字母先後順序排序,中間以底線分格」,所以以我們這個例子來說,這個資料表的名字就是「products_stores」。

Model 驗證及回呼

  1. 资料验证:有三个方法:

    • 前端驗證:在 HTML 頁面使用 JavaScript 在使用者填寫資料的時候就先檢查。
    • 後端驗證:資料傳進來在寫入資料庫之前之後再檢查。
    • 資料庫驗證:直接由資料庫本身所提供的功能來做資料驗證。

    推荐model层来做这件事!

  2. 验证是否为空:

    • class Article < ApplicationRecord
        validates :title, presence: true
      end
      
    - ```
        class Article < ActiveRecord::Base
          validates_presence_of :title
        end
    

    这两种效果是一样的。

  3. 想查看错误的提示信息?

    • 检查是否有错误提示:a1.errors.any?
    • 检查错误提示是什么:a1.errors.full_messages
  4. 只有这些方法会触发验证:

    • create
    • create!
    • save
    • save!
    • update
    • update!

    其它方法不會經過驗證流程喔

    有驚嘆號版本的,如果驗證未通過會產生錯誤訊息,而沒有驚嘆號版本則僅會回傳該 Model 的一個空物件。这大概就是惊叹号的好处了!

  5. 如何跳过验证?

      user1 = User.new
      user1.save(validate: false)
    
  6. 检查是否能通过验证?

    >> user1.valid?
    => false
    
  7. 回呼(Callback):

    其中,顏色比較深的那幾個流程是有機會可以掛上一些方法,又稱之回呼(Callback)

    注意:before_savebefore_create 的差別,在於 before_save 是每次存檔的時候都會經過,但 before_create 只有在「新增」的時候才會觸發。

    除了這樣的寫法,如果內容單純的話,也是可以使用 Block 的方式來寫:

      require 'digest'
      class User < ActiveRecord::Base
        before_create do
          self.email = Digest::MD5.hexdigest(email)
        end
      end
    

寄發信件

  1. 之前的奇怪,现在不奇怪了,寄送信件,rails已内置好这个功能了,使用很容易!

背景工作及工作排程(异步任务)

慌然大悟,原来这就是我之前一直想学的异步任务!!!!相见恨晚!

如果我要写一个异步任务,可以这样做:

  1. 执行rails g job user_confirm_email

  2. 设置 app/jobs :

      class UserConfirmEmailJob < ApplicationJob
        queue_as :default
    
        def perform(user)
          # 在這裡寄發確認信...
        end
      end
    
  3. 在controller里作用:

        def create
          @user = User.new(user_params)
          if @user.save
            UserConfirmEmailJob.perform_later(@user)
            redirect_to @user, notice: 'User was successfully created.'
          else
            render :new
          end
        end
    
搞定!

一些小技巧:

  • 其中,queue_as 方法是指這件工作急不急,預設值是 :default,如果這件工作不急,可把 :default 改成 :low_priority,如果是急件則可設定成 :urgent

  • # 這樣是 5 秒之後做
    UserConfirmEmailJob.set(wait: 5.seconds).perform_later(@user)
    
    # 這樣是「明天下午有空再做」
    UserConfirmEmailJob.set(wait_until: Date.tomorrow.noon).perform_later(@user)
    
- 通常异步任务会被放在缓存中,如果宕机,重开机这些资料会不见。安全起见,可以用第三方gem:(把任务放入资料库)常用:有 `Sidekiq` 跟 `Delayed Job`

  下面以`Delayed Job`为例:

  - 在 *Gemfile.rb* 中增加:

  gem 'delayed_job_active_record'
​```
  • $bundle
    
  • 修改:app/configs/application.rb

API 模式

  1. 直接用render json: @users会输出所有的json数据。如果想过滤一些数据的话,可以这样:

    • 新增app/views/users/index.json.jbuilder:

        json.array! @users, partial: 'users/user', as: :user
      
    • 新增app/views/users/_user.json.jbuilder:

        json.extract! user, :id, :name, :email, :created_at, :updated_at
        json.url user_url(user, format: :json)
      
  2. 有一个叫API-Only 的东西,在新增专案时,选择这种模式,可以帮rails减肥!

    $ rails new my_blog --api
    

寫測試讓你更有信心 Part 1

  1. 为什么要写测试:

    前期看起来有点花功能 ,但却是为后面多次开发的测试节省时间!

    不写测试可能还有各种偷懒的理由,但,跟着大神学着写吧~

    不要為了測試而測試,你寫的是「規格」(Spec)

  2. 开发前写一写“测试“,这里作者说是规格

下面以RSpec来实作:
  1. 安装RSpec:

    $ gem install rspec
    
  2. 把要有哪些项目要测试,先写出来:

      # 檔案:bank_account_spec.rb
    
      RSpec.describe BankAccount do
        describe "存錢功能" do
          it "原本帳戶有 10 元,存入 5 元之後,帳戶餘額變 15 元" do
          end
    
          it "原本帳戶有 10 元,存入 -5 元之後,帳戶餘額還是 10 元(不能存入小於等於零的金額)" do
          end
        end
    
        describe "領錢功能" do
          it "原本帳戶有 10 元,領出 5 元之後,帳戶餘額變 5 元" do
          end
    
          it "原本帳戶有 10 元,試圖領出 20 元,帳戶餘額還是 10 元,但無法領出(餘額不足)" do
          end
    
          it "原本帳戶有 10 元,領出 -5 元之後,帳戶餘額還是 10 元(不能領出小於或等於零的金額)" do
          end
        end
      end
    

    然后开始写内容:

    读高见龙的《为你自己学Ruby on Rails》后半部分提取笔记_第2张图片
    image
  3. 然后,开始$ rspec bank_account_spec.rb进行测试,自然会出错。

  4. 根据错误提示,把所有bug都解掉!

  5. 一些小技巧:

    • before(:each)
    • before(:all)
    • let(:account){BankAccount.new(10)}这个语句相当于动态地为整个方法提供一个区块变数。
  6. 寫測試是算是業界很常見的標準技能,所以:写吧!

代码重构:

  1. 页面上的逻辑可以这样处理:
    • 写进viewhelper
    • 在model里写义一个实体方法,然后在页面就只可以直接用啦!
  2. 善用继承,不能继承的就引入模组功能。

代码重构(进阶版):

  1. 設計 Service Object 類別,新增纯ruby的类别。
  2. 使用 Form Object

表示暂时看不太懂。。。先放过。。

后面的内容实操一遍,基本没有什么重要的知识了……。。

当然这个课程还有一些不错的内容在更新中……

读高见龙的《为你自己学Ruby on Rails》后半部分提取笔记_第3张图片
image

你可能感兴趣的:(读高见龙的《为你自己学Ruby on Rails》后半部分提取笔记)