Rails5 Model Document

创建: 2017/06/09

更新: 2017/06/21

更新: 2017/06/23 对待未完成的追加# TODO:
更新: 2017/06/29 修正文件名db/seed.rb ---> db/seeds.rb
                          改变默认主值 set_prime_key '...'
更新: 2017/08/06 纠正错误:where返回找到的全部
                          belongs_to 写在参照的一方 ---> 写在被参照的一方 
                          增加生成数据库指令      rake db:create:all
更新: 2017/08/17 对rails db:setup 详细解释
更新: 2017/10/15 增加了AddXxxToXxx的例子
更新: 2017/10/28 修正[RemoveXxxxFrom表格名](To ---> From)
更新: 2017/10/29 修正拼写错误 destory--->destroy
更新: 2018/01/08 增加find_or_create_by
                         补充 new=build, create = new + save
更新: 2018/01/16 增加迁移的选项after
更新: 2018/01/17 增加创建时间
更新: 2018/02/09 增加change_table下能利用的方法的写法例: t.index
更新: 2018/02/10 大幅度补充了迁移文件相关内容 
更新: 2018/02/13 增加association的相关opt
更新: 2018/02/28 更新迁移文件里的选项是否允许null值 [NULL] -> [null]
更新: 2018/04/02 补充migration相关说明
更新: 2018/04/17 纠正 [have] -> [having]
                         补充model的结合搜索
更新: 2018/04/22 补充关联的dependent选项的说明
                      补充delete, destory区别
          补充includes, joins相关注意: [参数是关联名, 检索套上的是表格名]
更新: 2018/05/02  增加 has_attribute?
更新: 2018/8/28 补充migration相关细节
更新: 2018/09/28 在migration处补充rails g migration的位置
更新: 2019/01/16 补充where里not的写法
更新: 2019/02/07 补充belongs_to包含对方id的说明
          补充t.reference, t.belongs_to参数说明
          修改destory_all, delete_all描述(原来的写反了)
          增加了delete, delete_all, destroy, destroy_all的sql执行情况说明
更新: 2019/02/12  补充t.decimal的说明: [ (高精度小数)]
更新: 2019/02/13  补充foreign_key追踪的primary_key必须设有Index
更新: 2019/02/21  增加(补充)validation相关内容
          删除多余的行
         更新部分连接(迁移到博客园前的链接改成博客园的新链接) (更改处: model-生成)
         增加待完成事项
更新: 2019/02/22 对validation进行补充和修正
          补充了设置enum后的注意点
更新: 2019/04/02 补充了has_one, has_many的source选项
更新: 2019/04/05 补充了text类型不能设为index
更新: 2019/04/16 补充validates的uniqueness的scope可以设置多个(用数组)
更新: 2020/02/17 补充migration的bigint
更新: 2021/04/20 补充joins, left_outer_joins, includes的区别

待完成事项: TODO

 
模型(model)
 模型(model)
 生成  rails generate model name field:type [...] [options]
 P48
# TODO: options
类型首字母不大写
 app

例子: 

rails g model questionnaire question:string 

veryAgree:boolean agree:boolean

disagree:boolean veryDisagree:boolean


 

迁移文件的生成

和模型一起生成 rails generate model name field:type [...] [options]
 单独生成 rails generate migration name [field:type ...] [options]

例: rails generate migration AddBirthToAuthors birth: date

     生成的文件名 20180216002328_add_birth_to_authrs.rb

     文件内容

#20180216002328_add_birth_to_authrs.rb

class AddBirthToAuthors < ActiveRecord:: Migration[5.0] 
  def change 
    add_ column :authors, :birth, :date 
  end 
end

 

 

 自带属性  
 概要  键名
 主键(自动生成)  id
 记录(record)的生成时间   created_at 
 记录(record)的更新时间  updated_at
 rails命令行  命令行测试模块(model)
 启动  rails console opts
 指定运行环境  rails console test/development/production
 默认development
 不保留变更  -s
 --sandbox 
   
   
   
 新建  test = modelTest.new(hp: 1200, mp:9999, ad: 12345)
 保存  test.save
 获取最后一个数据  last = ModelTest.last
 退出命令行  quit
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 查看履历   rails db:migrate:status
------------------------------------------------------------------------------
Rails5 <wbr>Model <wbr>Document
------------------------------------------------------------------------------
 生成数据库  rake db:create:all
 设定数据库  rails db:migrate
 db:migrate
 
 运行到指定的版本处(VERSION=...)
 不指定就是用最新版,把所有没迁移的全部迁移了
例: rails db:migrate VERSION=20161205000859
 db:rollback  回滚指定步
例: rails db:rollback STEP=5
 db:migrate:redo  回滚指定步并且重新迁移
例: rails db:migrate:redo STEP=5
 db:migrate:reset  先删除数据库,然后重新生成并且迁移最新版本
例: rails db:migrate:reset 
删除production环境下的数据库
DISABLE_DATABASE_ENVIRONMENT_CHECK=1
windows下删除development下数据貌似也要p312
# TODO:
 选项  
 RAILS_ENV  设置测试用数据库环境: production, development, test
 默认 RAILS_ENV=development
 VERBOSE  是否在命令行输出迁移的过程
 默认true
 VERBOSE=false
   
   
   
 
生成并读取数据库   rails db:setup
相当于
         rails db:create:all
         rails db:migrate
         rails db:seed 或者 rails db:fixtures:load
 schema来构筑数据库
schema文件位置  位置/app/db/schema.rb
 意义  自动获取最新的数据库表格信息
 和迁移文件一样可能会有无法展现的对象(object)
 放弃现在的数据库,
 用schema来构筑最新的数据库
 rails db:reset
windows环境下加DISABLE_DATABASE_ENVIROMENT=1

注意: 只是重构,测试数据要另外读取
   
   
   
   
   
   
   
 seed  初始数据来初始化(也可以用fixture)
 位置  db/seeds.rb
 可能需要自己新建
 形式  ruby脚本
 内容  只需要写生成/保存数据的代码

 如Sample.new(...)
    
 运行  rails db:seed
   
   
   
fixture  测试数据来初始化(也可以用seed)
 位置  test/fixtures/
 表格名.rb
 形式  yaml脚本
 注: 缩进只能用手打两个空格
 内容  标签:
    域名: 数值
    ...
 
 外部键间接写法
 仅yml文件内可用
 模型名: 标签
 缩进  手打的2个空格
   
   
   
   
   
   
   
   
 运行  rails db:fixtures:load
 指定读取文件
 位置: /test/fixtures/...
 FIXTURES=sample1, samples2, ...
 默认: 读取全部
 指定测试环境  RAILS_ENV=prodution/test/development
 默认: development
   
   
   
 读取  rails db:fixtures:load (FIXTURES=samples)
 初始化  rails db:reset (DISABLE_DATABASE_ENVIROMENT_CHECK=1)
 括号内内容是删除production模式的数据库
 windows下development模式好像也要
 删除  rails db:drop:all  (DISABLE_DATABASE_ENVIROMENT_CHECK=1)
 括号内内容是删除production模式的数据库
 windows下development模式好像也要
服务器客户端   
启动  rails db
 或 rails dbconsole
 表格一览  .tables
 确认特定表格结构  .schema samples
 表格内容一览  SELECT * FROM samples;
 关闭客户端  .quit
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
基本的数据检索
 主键(key)搜索  Class.find(keys)
  返回主键所对应的数据(一对一)
 keys  主键的值
 可以用数组指定一串
   
   
   
   
 指定键搜索  Class.find_by(key, value [,...])
 搜索指定的键对应的值
 可以指定多个键来追加限制 
 返回找到的第一个
 搜索不存在则创建  Class.find_or_create_by(同上)
   
   
复杂条件下的数据检索
 设定基本条件式

 Class.where(exp)

 ● joins/includes等关联搜索的参数写法

    where(关联表格名: {关联处的搜索条件} )

cars = cars.includes(:equipment_spec).where(equipment_specs: { power_window: true })


 返回找到的全部(非数组?)

 exp  条件式
 哈希表形式
 哈希表多种形式  
 多个键  a: v1, b: v2, ... 
 键内范围  a: v1..v2
或者a: (v1..v2)
 键内指定多个元素  a: [v1, v2, v3, ...]
   
   
   
   
   
   
   
   
   
   
 
 用占位符生成条件式

 Class.where(exp [, value, ...])
 占位符: ?或者符号(symbol) :sample
 注意: 一定要用占位符,别把输入的字符直接展开进去。SQL可能会爆炸的233

 不等于是not, 只用于不是nil

http://railsdoc.com/references/where

 例  'israre = ? AND def >= ?', false, 1000 
  'israre = :sample1 AND def >= :sample2', sample1: false, sample2: 1000
   Page.where("title not ?", nil)
   
   
   
   
   
   
   
 where的否定  Class.where.not(...)
 参数和where一样
 or  Class.where(...).or(Class.where(...))

 ModelTest.where('ap  <= ?', 1000).or(ModelTest.where('def > :def', :def => 4000))
 排序  Class.where(...).order(sort)
 
 参数
 sort  排序式
 例 :test => :asc
      test: :asc
 默认 :asc, 可省略
 格式  属性: 顺序
 :asc  升序
 :desc  降序 
   
   
   
   
   
   
   
   
   
   
 重排  Class.where(...).order(sort).reorder(sort)
 写法和order一样
 作用是覆盖前面的order
 如果只是想清空前面的order,指定nil
 指定读取的列

 Class.where(...).select(cols)

 默认获取所有的列, 用这个方法可以指定具体要获得的列

 cols  指定的列
 :hp, :mp,....
select中可以使用SQL函数

 设置列名称

 AS

 
AVG(sample)  AS avg_price 

 呼出列

 Class.avg_price

 

 select中使用SQL函数  例: Book.select('AVG(sample) AS avg_sample')
用AS设定名称
   
   
 去除重复  Class.where(...).distinct(flag)
 Class.select(...).distinct(flag)
 flag true 去除重复 
false 保留重复
默认true
   
   
   
   
 获取特定范围  limit/offset
 和order一起用才有现实意义
 limit(rows)
 offset(off)
 rows  最多获取的行数
 off   开始获取的位置(从0开始)
   
   
   
   
   
   
   
   
ModelTest.where('hp >= ? AND mp >= ? AND ad >= ?', 1000, 200, 1500).offset(1).limit(1)
 获取开头/结尾数据  Class.first
 Class.last
(也可以用limit(0))
不能惰性读取,必须放在方法链最后
 分组  Class.where(...).group(key)
 可以指定多个
 :a, :b, :c, ...
 进一步提取信息   Class. group (key).have(exp)
 exp写法参照p206
# TODO:
 @having = ModelTest.all.group(:israre).having('hp >= ?', 0)\
 select中使用SQL函数  例: Book.select('AVG(sample) AS avg_sample')
 用AS设定名称
 去除条件  Class.where(...).select(...)unscope(...).unscope(...)
 注意:unscoped是删除之前的所以条件
 例
ModelTest.where('def <= :def AND israre = :israre', :def => 2000, :israre => false).select(:mp, :mdf).unscope(:select).unscope(where:)
 注意: 如果where里面用最简单的哈希定义,则unscope可以直接删除where的哈希值
 ModelTest.where(israre: false).unscope(where: :israre)
 允许的参数 :where, :select, :group, :order, :lock, :limit, :offset, :joins, :includes, :from, :readonly, :having
 
 返回空对象  Class.none
 Class.where(...)....none
注意: null是空,Class.none这类是空对象(NullObject),可以呼出each等而不出错
   
   
   
   
   
   
获取数据的其他方法
 以数组形式取出列  Class.where(...).pluck(column [,...])
 例:ModelTest.all.pluck(:israre, :mp)
 确认指定的数据是否存在  Class.where(...).exists?
 自定义模型搜索方法
 命名空间
(Named Scope)
 scope :name, ->{ ... }
 scope :rare, -> { where('israre = :israre', israre: true) }
位置: /app/models/...
调用: sample = Class.where(...).scope-name
 设置默认方法  default_scope { ... }
位置: /app/models/...
 例: default_scope { order_with_hp }
      scope :order_with_hp, ->{ order(:hp) }
 计算结果类
 获取结果的行数(数量)   Class.where(...).count
 Class.where(...).size
 Class.where(...).length
推荐用size,基本没有错
 计算类  
 平均值  average(col)
 最大值  minimum(col)
 最小值  maximum(col)
 合计值  sum(col)
   
 一般和group连用  ModelTest.all.group(:israre).average(:hp)
 注意  返回的是哈希表{group的值: 统计值 [, ...]}
   
   
   
 直接使用SQL命令  一般都用query method, 尽量不要直接用SQL命令
 find_by_sql(sql)
 sql   '....', val1 [, val2 ...]
   
   
   
   
   
   
   
记录(record)的登陆,更新,删除
 基础
 新建  Class.new/build
 Class.new(...) Class.build(...)
 哈希表形式指定 {:hp => 54321, :mp => 12345, israre: false}
 @new = ModelTest.new({:hp => 54321, :mp => 12345, israre: false})
Rails5 <wbr>Model <wbr>Document
 新建+保存  Class.create
 登陆(保存)   Class#save
 返回true/false
 注: @sample.save!失败返回例外(用于transaction)
 例: @sample.save
 更新  @sample.update(...)
 返回true/false
 哈希表形式指定 {:hp => 54321, :mp => 12345, israre: false}
 用于已经存在的记录(record)
 删除   @sample.delete

 delete(keys) 单纯删除(直接执行SQL, 不经过Active Recode)
 destroy(keys)  先选择后删除, 新手还没理解delete的时候全用destroy就行

例子: sample.delete
        Class.delete(id)
注: 用对象呼出时候不用指定id
进一步的操作
 全部更新  Class.where(...).update_all(updates)
 返回改动的行数
 删除

  

 delete(keys) 

 单纯删除(并返回被删的)

 Member.first.delete

Member Load (0.3ms)  SELECT  `members`.* FROM `members` ORDER BY `members`.`id` ASC LIMIT 1
  SQL (0.6ms)  DELETE FROM `members` WHERE `members`.`id` = 207

 

 destroy(keys)

 先选择后删除(并返回被删的)

 rails相关处理必须用这个才有(transaction, callback)

 Member.first.destroy

Member Load (0.4ms)  SELECT  `members`.* FROM `members` ORDER BY `members`.`id` ASC LIMIT 1
   (0.2ms)  BEGIN
  SQL (0.3ms)  DELETE FROM `members` WHERE `members`.`id` = 206
   (0.4ms)  COMMIT

 


  

例子: sample.delete
        Class.delete(id)
注: 用对象呼出时候不用指定id

 全部删除

  

 delete_all 

 直接一次性删除 

 Member.delete_all

SQL (0.2ms)  DELETE FROM `members`

 

 destroy_all

 先选择然后一个一个删除(并return 被删除的)

 Member.destroy_all

Member Load (0.5ms)  SELECT `members`.* FROM `members`
   (0.2ms)  BEGIN
  SQL (0.2ms)  DELETE FROM `members` WHERE `members`.`id` = 101
   (1.3ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.2ms)  DELETE FROM `members` WHERE `members`.`id` = 102
   (0.4ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  DELETE FROM `members` WHERE `members`.`id` = 103
   (0.4ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  DELETE FROM `members` WHERE `members`.`id` = 104
   (0.4ms)  COMMIT
   (0.2ms)  BEGIN
  SQL (0.3ms)  DELETE FROM `members` WHERE `members`.`id` = 105
   (0.4ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  DELETE FROM `members` WHERE `members`.`id` = 106
   (0.4ms)  COMMIT
   (0.1ms)  BEGIN
  SQL (0.3ms)  DELETE FROM `members` WHERE `members`.`id` = 107

 

 

 transaction
 事务
 def transaction
    Class.transaction do
       ....
       raise ...
       ....
       raise ...
       ...
    end
    rescue => e
       ...
 end

用发出异常来终止transaction
也可以用模块对象来呼出transaction

经常用@sample.save!  失败返回例外
 事务隔离等级  用:isolation标签指定
 :read_uncommitted
 :read_committed
 :repeatable_read
 :serializable
   
   
   
   
 同时运行的管理  
 乐观锁  
 模块要追加列  追加lock_version:intefer
 用命令行生成时候追加
 已有的表格里也可以增加
 在迁移文件中设置默认值  原来:t.intefer :lock_version
 更改成:t.intefer :lock_version, default: 0
 迁移  rails db:migrate
 设置/改变成隐藏输入框  《%= f.hidden_field :lock_version》
如果有显示lock_version的,要删掉
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
 定义枚举的的域  用与设置数字与符号的对应
 设置过程  以status:integer为例
 设置默认值(非必须)
 位置:db/migrate/
 
t.integer :status, default 0, null: false

 

 设置枚举
 位置:app/models/

 用数组的话值是index(0开始)

enum status: [:draft, :published, :deleted] // 指定值用hash

 

enum status: {draft: 0, published: 1, deleted: 2}

 

   
   
   
   
   
   
   
   
 获取enum的值  @sample = @sample.find_by(...)
 @sample.status
 设置值  @sample.status = 1
 @sample.status = :published
 @sample.published!
注: 设定没有定义好的值会出错, ArgumentError
 列举值作搜索范围  @sample = Sample.published
 @sample = Sample.published.where(...)
 注意:

 设置enum以后validates的是字符串, 不是integer

 

   
   
   
   
   
  其他更新类方法
 P245  暂略
 # TODO:
   
实现验证功能(validation)
 ActiveModel可用的validation

 通用参数:

 注: 可以直接设置成全部适用, 如  validates :id, allow_blank: true, length: { is: 10 }, uniqueness: true   

      或对特地验证适用, 如 validates :id, length: {allow_blank: true, is: 10}, uniqueness: true 

 allow_nil  nil时跳过validation
 allow_blank  nil和empty时跳过validation
 message  修改报错信息
 on

 validation的触发时刻

 create  新建时
 update  更新时
 save  create+update

 默认=:save

 例: acceptance一般只要新建时同意条款

 if

 条件成立时进行validation

 参数: 

 字符串

 

validates :sample, presence: { if: "sample.method" }

 

 作为代码运行

 Proc式  直接运行,  参数是当前的instance
 symbol  symbol对应的方法

 

 unless  条件不成立时进行validation

 with_options:

with_options(on: :create, if: 'true') do |oc|
  oc.validates :name, presence: true
  oc.validates :user_id, presence: true
end

 

 

 ActiveModel可用的validation:

 acceptance

 是否对checkbox打钩

 参数:

 accept

 必须与指定的值一致

 默认=1

   

 不符合时的信息:

must be accepted

 注:

 ● 不一定需要model有此列(自动生成同名假想列)

 

 confirmation

 

 待测text_field必须与_confirmation完全一致(如注册时的密码等)

 参数: 无

 不符合时的信息: 

doesn't match confirmation

  ● 验证所用列不需要model有此列(自动生成加后缀_confirmation的假想列)

  exclusion  

 值是否不在指定数组/范围里

 参数:

 in  指定数组/范围
   

 不符合时的信息: 

is reserved

 

 inclusion 

 值是否在指定数组/范围里

 参数:

 in  指定数组/范围
   

 不符合时的信息: 

is not included in the list

 

 format

 是否符合正规表达式

 参数:

 with  正规表现
   

 不符合时的信息: 

is invalid

 

 length   

 检验字符串的长度

 参数:

 minimum  最小长度
 maximum  最大长度
 in  长度范围(Range)
 tokenizer  字符串分割方式(lamda式)
 is  指定长度
 too_long  不符合maximum时的错误信息
 too_short  不符合minimum时的错误信息
 wrong_length   不符合is时的错误信息

 不符合时的信息: 

is too short(minimum is xxx characters)等

 

 numericality

 检查数值大小/类型

 参数:

 only_integer  只能是整数
 greater_than  必须大于
 greater_than_or_equal_to  必须大于等于
 euqal_to  必须等于
 less_than  必须小于
 less_than_or_equal_to  必须小于等于
 odd  必须是奇数
 even  必须是偶数

 不符合时的信息: 

is not a number 等

 

 presence

 值非null

 参数: 无

 不符合时的信息: 

can't be blank

 

 absence 

 值必须为null

 参数: 无

 不符合时的信息: 

must be blank

 

 uniqueness 

 值必须唯一(unique)

 参数:

 scope

 决定unique的其他列

# 只需要此列值唯一
validates :column1, uniqueness: true
# 和一个列绑定
validates :column2, uniqueness: {allow_blank: true, scope: :a}
# 和多个列绑定
validates :column3, uniqueness: {allow_blank: true, scope: [:a, :b]}

 

 设定了以后则, unique是指两列的组合(a, b)唯一

 case_sensitive

 是否区分大小写

 默认=true 

 不符合时的信息: 

has already been taken

 

 

 声明validate  
validates field [, ...] name: params [, ...]

 参数含义:

 filed  需要检验的field名(可以多个)
 name  validate类型
 params  相关validate类型的参数(没有就指定true)

 

 validate触发时机

 

 触发时机
 create, create!
 save, save!
 update, update!
 不触发的method 
 increment! , decrement!
 increment_counter, decrement_counter
 toggle!
 touch
 update_all
 update_attribute
 update_counters
 update_column, update_columns
 save(validate: false)
 手动触发

 

 valid?  是否符合validation
 invalid?  是否不符合validation

 

 

 errors

 errors (ActiveModel::Errors类)

 前提: 设定了validates, 不然不会有任何错误

 

 是否有错  
sample.errors.any?

 

 获取错误数量  
sample.errors.count
sample.errors.size

 

 获取完整信息(错误信息的文本数组)  
sample.errors.full_messages
sample.errors.to_a

 

 获取特定列的错误  
sample.errors[:column_name]

 

sample.errors.details[:column_name]

 

 手动添加错误  
sample.errors.add(:column_name, 'error_message')
sample.errors[:column_name] << 'error_message'

 

 清除错误

 

sample.errors.clear

 

 注: 只是清除errors里的,实际错误并没有消失

 

   

 

 自定义validator1

 直接呼出方法

validates :method_name [, ...]

 

 自定义validator2

 设置

# config/application.rb

# ...
module Core
  class Application < Rails::Application
    # ...

    # add custom validators path
    config.autoload_paths += Dir["#{config.root}/app/models/validators"]
    config.enable_dependency_loading = true

    # ...
 end
end

创建文件 

# app/models/validators/sample_validator.rb
class SampleValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    puts "UUID custom validate fired, options: #{options}" # 获取options的方法
    record.errors[attribute.to_sym] << "test" # 错误通过此方法发出
  end
end

使用

# sample.rb
# ...
class Sample < ApplicationRecord
  # ...
  validates :user_id, sample: {ko: "ok", sample1: 1}
  # ...
end

# 运行 rails c
a = Sample.new
a.valid? # => false
# UUID custom validate fired, options: {:ko=>"ok", :sample1=>1}

 

 

 定义不与数据库关联的model  # TODO: 需要的时候完成此项 2019/02/21
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
用关联(association)处理复杂表格
 命名规则  
 外部键  sample_id
 注: 主键默认为id
 中间表
 (过渡表)
 用_按字母顺序连接
 modelTest_viewTest
   
   
   
   
   
   
   
   
 参照的设置  belongs_to assoc_id [,opt]
 例: belongs_to :modeltest1
 一对一关系
 参数  
 assoc_id  被关联的模块名
 单数
 opt  
 位置

 写在被参照的一方(即belongs_to的一方含有对方id)

 含义: sample_id属于...

   
   
   
   
   
   
   
   
 一对多  has_many assoc_id [,opt]
 参数  
 assoc_id  被关联的模块名
 复数
 opt  
 位置  写在被参照的一方
 由此被参照方可以获取参照方的数据
   
   
   
   
   
   
   
   
 一对一  has_one assoc_id [,opt]
 参数  
 assoc_id  被关联的模块名
 单数
 opt  
 位置  写在被参照的一方
 由此被参照方可以获取参照方的数据
   
   
   
   
   
   
   
   
 多对多
 m:n
 has_and_belongs_to_many assoc_id [, opt]
 参数  
 assoc_id  被关联的模块名
 单数
 opt  
 步骤   
 创建中间表
 (过渡表)
 用_按字母顺序连接
 modelTest_viewTest
 关联双方定义处
 定义关联
 has_and_belongs_to many
   
   
   
   
   
   
   
   
   
   
   
 多对多2
 m:n
 a---b---c这种情况下,直接关联a---c
 has_many assoc_id, through: middle_id [, opt]  

 b定义处
   belongs_to :a
   belongs_to :c
 a定义处
   has_many :b
   has_many :c through: :b
 c定义处
   has_many :b
   has_many :a through: :b
 由关联自动定义的方法  p282~283
 # TODO:
 略
 关联可用的opt

 

 

 opt  belongs_to  has_one  has_many  has_and_belongs_to_many  

# TODO: Supply [选项的意义]

 as  X  O  O  O  多态(自己的名字)
 association_foreign_key  X  X   X  O

 m:n关联下的外部键

 例: Book里的author_id

 autosave  O  O  O  O  父模型保存/删除时自动保存
 class_name  O  O  O  O  关联模型的类型 
 counter_cache  O  X  O  X  获取数量时是否使用cache
 dependent  O  O  O  X

 删除模型时关联的模型也删除

 

:nullify

 默认, 什么都不做

 foreign_id 变为nil

:destroy

 关联的model被删除时, 则呼出destory来删除

 

:delete

 关联的model被删除时, 则呼出delete来删除

 ● has_many时呼出的是delete_all

 ● 直接执行SQL, 所以不会对自身关联的其他model有影响

 

   

 

 foreign_key  O  O  O  O  使用的外部键名称
 join_table  X  X  X  O  使用的结合表格名称
 optional  O  X  X  X  不检查关联的对象是否存在
 primary_key  O   X  O  X  关联时使用的主键列的名称
 polymorphic  O  X  X  X

 多态关系的有效化?

# TODO: Supply [选项的意义]

 readonly  X  X  X  O  设置关联的对象只可读
 required  O  X  X  X  检查关联对象是否存在
 touch  O  X  X  X  保存的时候关联的对象的create_at/updated_at也更新
 source  X  O  O  X

 对于 has_many:through , 指定源头(即本model的名称)

 只有在指定的关联名称不是对方model名(即使用class_name时)才需要

 through  X  O  O  X  
 validate  O  O  O  O  设置保存的时候是否检查关联的对象
           
           
           

 

 与相关model结合

 joins(exp) 

 前提是已经建立association

 ● 参数是关联名(如has_one则为单数)

 ● 搜索时候套上的symbol是表格名

 ● 2021/04/20 注: 是inner_join

 

cars = cars.includes(:equipment_spec).where(equipment_specs: request)

 

 

 

 exp

 结合条件

 

A. 关联名(Symbol)

 

Car.joins(:equipment_spec).select('cars.*, equipment_specs.*')

 

 B.关联名1: 关联名2

 关联名1和关联名2也关联

EquipmentSpec.joins(car: :maker)
.select('equipment_specs.*, cars.id AS car_id, makers.name AS maker_name')

 

C. 字符串

 SQL

  用于INNER JOIN以外的结合(LEFT JOIN/ RIGHT JOIN)
   

 

 

 

必须要带上select,

不然虽然结合了但只获取了当前的model

EquipmentSpec.joins(:car).select('equipment_specs.*, cars.*')

 

 用join也能达到association的效果

 与相关model结合

 (左外部结合)

 Rails 5.0

 left_outer_joins(exp)

 alias: left_joins(exp)

 结合是左外部结合 LEFT OUTER JOIN

 写法和上面一样, 但不能用C写法

 ● 参数是关联名(如has_one则为单数)

 ● 搜索时候套上的symbol是表格名

 和相关model一起获取

 includes(exp)

写法一样,但不能用C写法

 结合是左外部结合 LEFT OUTER JOIN 2021/04/20注

 ● 不是结合,是多个select, 把所有把都拿出来

 ● 参数是关联名(如has_one则为单数)

 ● 搜索时候套上的symbol是表格名

   
回调(call back)
   用例:注册时候每天的空白自动生成
          注册或者更新时候自动发邮件
          注册更新时保存为履历等
 回调函数
 主要方法  
 各时间点所对应函数
 新建,更新,删除  create/create!, destroy/destroy!, destroy_all
 increment!, decrement!, save/save!, toggle!,
 update/update!, update_attribute, valid?
红色: 之前跳过的
 after_find  搜索数据库后
 all,first, find, find_by, find_by_sql
 
 after_initialize  new来生成或者读取数据库后
 回调函数
 创建  更新  删除  运行时间
 before_validation      验证处理前
 after_validation      验证处理后
 before_save      保存前
 around_save      保存前后
 before_create  before_update  before_destroy  新建/更新/删除前 
 around_create  around_update  around_destroy  新建/更新/删除前后
 after_create  after_update  after_destroy   新建/更新/删除后
 after_save      保存后
 after_commit      commit后?
 after_rollback      回调后
       
       
       
       
       
       
       
       
       
   
   
迁移文件
 位置  app/db/migrate/
 生成migration文件  本页最上面找
 构造  
 schame处理  change方法内部
 迁移文件的版本信息  
class CreateEquipmentCars < ActiveRecord::Migration[5.0]
  #...
end
ActiveRecord::Migration[5.0]部分
用后面的Migration[版本]来控制
   
   
   
   
   
   
   
   
 逻辑方法

 

 判断是否存在某列  has_attribute?
   
   
   
   
   
   

 

 主要方法

 tname: table name

 frname: 外部table name

 fname: field name

 type: field的数据类型

 opt: field的option

 i_opt: index option

 t_opt:table option

 fr_opt: foreign key option

 

 create_table

 change_table

 
create_table tname [,toption] do |t|
   t.type fname [, opt, ...]
   ...
end
 create_table, change_table内部可用方法搜[create_table,change_table代码块内可用的方法]
 tname  表格名

 toption

 表格选项

 选项
 
 id  是否自动生成主键
 默认: true
 primary_key  主键的名称
 默认: id 
 改变默认主值:   set_prime_key '...'
 位置:  模型类
 Model Class
 temporary  是否作为暂时的表格
 默认: false
 force   创建前是否删除已有的表格
 默认: false
 options  其他的表格选项
 暂时不管
 p301
# TODO:
   
   
 追加列  add_column(tname, fname, type [, opt])
 追加索引  add_index(tname, fname [, i_opt])
 追加外部键

 add_foreign_key(tname, frname [, fr_opt])

 tname: 表格名

 frname: 参照的表格名

 注: foreign_key追踪的primary_key(默认xxx_id),必须设有index

 

 追加
 create_at
 update_at
 add_timestamps(tname)
 改变已有列  change_column(tname, fname, type [, opt])
 是否允许该列可以有null  change_column_null(tname, fname, null)
 null: true为不允许无效值
         false允许
 改变默认值  change_column_default(tname, fname, default)
 改变表格

 change_table(tname) 

 create_table, change_table内部可用方法搜[create_table,change_table代码块内可用的方法]

 确认指定列是否存在  column_exists?(tname, fname [, type [,opt]])
 创建表格  create_table(tname [,t_opt])
 最上面那个就是
 创建中间表格  create_join_tabble(tname1, tname2 [, t_opt])
 删除已有表格  drop_table(tname [, opt])
 确认索引是否存在  index_exists?(tname, fname [i_opt])
 删除一个列  remove_column(tname, fname [, type, opt])
 删除多个列  remove_columns(tname, fname [,...])
 删除已有的索引  remove_index(tname [, i_opt])
 删除外部键  remove_foreign_key(tname, frname)
 重命名列  rename_column(tname, old, new)
 重命名索引  rename_index(tname, old, new)
 重命名表格  rename_table(tname, new)
 执行sql指令  execute(sql)

 t.string等的

 opt

 limit  列的位数
 字符: 字符长度
 数字: 数字的字符长度(包含一切元素,如小数点)
 defalut  默认值
 null  是否允许null值
 默认true
 precision  数值的总位数
 123.123  六位
 scale  小数点以下的位数
 polymorphic  belong_to关联所用的列
 index  是否生成索引 
 comment  列的说明 
 after  指定在哪一列的后面
 t_opt
 table option
 
 id  是否自动生成主键
 默认: true
 primary_key  主键的名称
 默认: id 
 改变默认主值:   set_prime_key '...'
 位置:  模型类
 Model Class
 temporary  是否作为暂时的表格
 默认: false
 force   创建前是否删除已有的表格
 默认: false
 options

 其他的表格选项
 暂时不管
 p301
# TODO:

 

 i_opt

 index opt

 
 unique  是否保证每一个索引独一无二
 name  索引名字 
 length  索引包含的列的长度
 length: {col1: 10, col2: 20}
 好处: 加快运行速度
 fr_opt

  

 collum

 外部key的名字

 默认: 参照的model_id 

 

 primary_key

 参照model的主key

 默认: id

 name  ??
 on_delete

 参照的model删除时的动作

 默认:restrict

 :nullify  设置成null
 :cascade  更新
 :restrict

 报错

 错误信息: 违反外部key制约

 

 on_update

 参照的model更新时的动作

 同上

 

 create_table,

 change_table

 代码块内可用的方法

 create_table,change_table代码块内可用的方法

 例:
 

change_table tname do |t|
    ...
end

 

 ● 可用的方法

 t.index

 例: t.index

 相当于不在change_table代码块下的add_index

 t.change  相当于不在change_table代码块下的change_column
 t.change_default   相当于不在change_table代码块下的change_column_default
 t.rename  相当于不在change_table代码块下的rename_column
 t.remove  相当于不在change_table代码块下的remove_column
 t.remove_references  相当于不在change_table代码块下的remove_foreign_key
 t.remove_index  相当于不在change_table代码块下的remove_index
 t.remove_timestamps  相当于不在change_table代码块下的remove_timestamps
   

 

 

 ● 可用的列定义

t.数据类型 :列名, opt

 可利用的数据类型和对应关系

 迁移(fname)

 写在迁移文件里

 例: t.string :str

 

 SQLite  Ruby
 integer  INTEGER 

 Fixnum
 (就是Ruby里的普通整数)

 4byte

 bigint  BIGINT  8byte
 decimal  DECIMAL

 BigDecimal

 (高精度小数)

 float  FLOAT  Float
 string  VARCHAR(255)  String

 text

注: 不能设为index

因为长度太长

 TEXT  String
 binary  BLOB  String
 date  DATE  Date
 datetime  DATETIME  Time
 timestamp  DATETIME   Time
 time  TIME  Time
 boolean  BOOLEAN  TrueClass/FalseClass
 特殊的    
 timestamps

  自动创建created_at和updated_at

  列生成与更新的时候自动设定

 references

 相当于

 belongs_to

 外部键

 例

t.references :book #生成book_id,相当于 t.belongs_to :book

 注: 参数是参照名, 可以不是table名。实际rails使用的都是单数来呼出

 选项(()内为默认值): index: (true), polymorphic: (false)

     
     

 

选项opt

 limit  列的位数
 字符: 字符长度
 数字: 数字的字符长度(包含一切元素,如小数点)
 defalut  默认值
 null  是否允许null值
 默认true
 precision  数值的总位数
 123.123  六位
 scale  小数点以下的位数
 polymorphic  belong_to关联所用的列
 index  是否生成索引 
 comment  列的说明 
 after  指定在哪一列的后面
 模型处改变迁移文件数据类型  attribute(name, type [,default: value]) 
 迁移文件的生成  和模型一起生成 rails generate model name field:type [...] [options]
 单独生成 rails generate migration name [field:type ...] [options]
 删除或者增加列  生成迁移文件时候命名
 rails g model AddXxxxTo表格名
 增加  AddXxxxTo表格名
 删除  RemoveXxxxFrom表格名
   rails generate migration AddBirthToAuthors birth: date
   
   
   
   
   
   
   
 升级与回滚

 

 原则  基本上所有方法都写在change里面
 可以自动被回滚的方法

 

 add_column  
 add_index  
 add_reference  
 add_timestamps  
 change_table  呼出change/change_default/remove的不能自动回滚
 create_table  
 create_join_table  
 remove_timestamps  
 rename_column  
 rename_index  
 rename_reference  
 rename_table  

 

 无法回滚时的方法

 

 增加回滚的信息

 remove_column/drop_table

 

remove_column

 在后面写上被删除的列的详细信息即可

remove_column(tname, fname [, type, opt])

 注意: remove_columns无法追加详细信息

 

 drop_table  写法和create_table一样

 

 升降级处理分开写

 reversible

 

reversible do |dir| # dir: 管理回滚的对象
    dir.up do
        ... # 升级时的处理
    end

    dir.down do
    ... # 降级时的处理
    end
end

 

 写在单独的up/down方法里  
def up
    ... # 升级时的处理
end

def down
    ... # 降级时的处理
end

 

 

   
   
   

 

 schema来构筑数据库  
 schema文件位置  位置/app/db/schema.rb
 意义  自动获取最新的数据库表格信息
 和迁移文件一样可能会有无法展现的对象(object)
 放弃现在的数据库,
 用schema来构筑最新的数据库
 rails db:reset
windows环境下加DISABLE_DATABASE_ENVIROMENT=1

注意: 只是重构,测试数据要另外读取
   
   
   
   
   
   
   
 数据库初始化
 迁移和初始化一起  rails db:setup 
相当于
         rails db:create:all
         rails db:migrate
         rails db:seed 或者 rails db:fixtures:load
 seed file  初始数据来初始化(也可以用fixture)
 位置  db/seed.rb
 可能需要自己新建
 形式  ruby脚本
 内容  只需要写生成/保存数据的代码

 如Sample.new(...)
    
 运行  rails db:seed
   
   
   
   
   
   
   
 fixture  测试数据来初始化(也可以用seed)
 位置  test/fixtures/
 表格名.rb
 形式  yaml脚本
 注: 缩进只能用手打两个空格
 内容  标签:
    域名: 数值
    ...
 
 外部键间接写法
 仅yml文件内可用
 模型名: 标签
 缩进  手打的2个空格
   
   
   
   
   
   
   
   
 运行  rails db:fixtures:load
 指定读取文件
 位置: /test/fixtures/...
 FIXTURES=sample1, samples2, ...
 默认: 读取全部
 指定测试环境  RAILS_ENV=prodution/test/development
 默认: development
   
   
   
   
   
   
   
   
   
 Data/Time相关的有用的方法
 yesterday  昨天
 tomorrow  明天
 prev_xxxx  前年/月/周(year,month,week)
 next_xxxx  下年/月/周(year,month,week)
 beginning_of_xxxx  年/季/月/周的开始一天(year, quarter, month, day)
 end_of_xxxx  年/季/月/周的最后一条(year, quarter, month, day)
   
 n.xxx.ago
 Numeric
 n个年/月/日/时/分/秒以前
years, months, days, hours, minutes, seconds
也可以用单数
 n.xxx.from_now
 Numeric
 n个年/月/日/时/分/秒以后
years, months, days, hours, minutes, seconds
也可以用单数
   

 

posted @ 2017-06-09 17:38  懒虫哥哥  阅读(1066)  评论(0编辑  收藏  举报