创建: 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

待完成事项: 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

------------------------------------------------------------------------------


------------------------------------------------------------------------------

 生成数据库  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})
 新建+保存  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是表格名

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)

结合是左外部结合 LEFT OUTER JOIN

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

● 参数是关联名(如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
也可以用单数
   

最新文章

  1. .Net Core[译文]
  2. 封装的ajax请求
  3. HTML5之Canvas时钟(网页效果--每日一更)
  4. 餐厅点餐APP总结
  5. MYSQL转换JSON
  6. devDependencies和dependencies的区别
  7. PHP读书笔记(1)-PHP语法结构与变量
  8. python 发送邮件例子
  9. [置顶] android之存储篇_SQLite数据库_让你彻底学会SQLite的使用
  10. SICP 练习 1.3
  11. 【转载】Android Studio jar、so、library项目依赖,原文链接http://zhengxiaopeng.com/2014/12/13/Android-Studio-jar、so、library项目依赖/
  12. linux技术框架
  13. ThinkPHP中的volist标签中使用eq标签出错
  14. js---疑点代码段解析
  15. DOM知识梳理
  16. Java进阶(四十六)简述ArrayList、Vector与LinkedList的异同点
  17. retry重试常见场景及实现
  18. django 初始命令
  19. Stylus的使用
  20. Android 一条竖线或横线、画边框

热门文章

  1. prometheus监控mysql
  2. 1002. A+B for Polynomials (25) (浮点数判0)
  3. 使用mysql-proxy 快速实现mysql 集群 读写分离
  4. css3 &amp; background &amp; background-image
  5. bootstrap删除模态框弹出并询问是否删除【通用删除模态框】
  6. iOS点击cell时,控件背景色消失的解决方法
  7. org.springframework.amqp.AmqpIOException: java.net.UnknownHostException: guest解决
  8. Ubuntu 16.04安装WinRAR/7-Zip(基于CrossOver)
  9. Ubuntu 16.04下减小/释放/清理VirtualBox虚拟硬盘文件的大小
  10. MyBatis实体属性与表的字段不对应的解决方案