Rails Model
https://guides.rubyonrails.org/
第一次写于 : 2020年3月22日
忙碌了一段时间,这几天根据官方文档和API手册,学习了一些Rails中对数据库操作的内容;
也就是Active Record
;
文章中大部分内容就是对文档的翻译和抽取; 同时,也加入了一些个人的理解;
翻译过程,有些内容懒得翻译了;
好的框架都是相似的,不好的框架各不相同;
学习过spring,JPA 等框架;
发现除了写法上有差异,但是总体上类似;
ORM框架啊,都是对数据库操作的封装;
这里,就不得不提, 学好SQL和数据库原理对使用ORM框架是非常有帮助的;
当然,这两者也是相辅相成的;
知道原理后,ORM就是对这些操作的封装.使用起来会更加得心应手;
Active Record 基础
命名约定
模型Class 大驼峰单数
rails会自动生成 复数的名称的table
模式约定
模型Class 大驼峰单数
创建 Active Record 模型
默认会添加三个字段
- id
- created_at
- updated_at
rails g model User username:string
命令输入完成后,会在 db/migrate
目录下生成一个迁移文件
完善文件后 执行迁移命令
rails db:migrate
之后,我们就可以在 db/schema.rb
文件中看到我们声明的model
记住: 这个文件只能看,不要手动修改
;
覆盖命名约定:self.table_name
取消默认命名;使用自定义的命名
class Product < ApplicationRecord
self.table_name = "my_products"
end
CRUD
在官网上有详细的介绍
https://guides.rubyonrails.org/active_record_basics.html
- 创建
user = User.create(name: "David", occupation: "Code Artist")
# 只有当执行save方法时; 才会把数据保存到数据库中
user = User.new
user.name = "David"
user.occupation = "Code Artist"
user.save
- 读取
user = User.first
user = User.find_by_id(1)
- 更新
user = User.first
user.update(username: "hello")
#
user.name = 'Dave'
user.save
User.update_all "max_login_attempts = 3, must_change_password = 'true'"
- 删除
user = User.find_by(name: 'David')
user.destroy
# find and delete all users named David
User.destroy_by(name: 'David')
# delete all users
User.destroy_all
事务
事务的开启
尽管,事务是被每一个Active Record
类调用的;
但是,由于一个事务属于一个数据库连接,而非一个class;
因此,在一个class中,写不用的model调用也是可以的;
另外 save
和 destroy
方法,自动被事务包裹
我们传入一个 block ; 在这个block中的操作都会被事务包裹;
分布式事务, 不在
Active Record
支持范围;
Account.transaction do
balance.save!
account.save!
end
balance.transaction do
balance.save!
account.save!
end
事务中的异常处理
记住,如果一个事务block中抛出的异常,会在触发ROLLBACK后传播到上级;
注意要捕获这些问题;
我们不要在事务的block中 捕获ActiveRecord::StatementInvalid
;
如果捕获了,可能会导致整个事务的block被废弃;
# Suppose that we have a Number model with a unique column called 'i'.
Number.transaction do
Number.create(i: 0)
begin
# This will raise a unique constraint error...
Number.create(i: 0)
rescue ActiveRecord::StatementInvalid
# ...which we ignore.
end
# On PostgreSQL, the transaction is now unusable. The following
# statement will cause a PostgreSQL error, even though the unique
# constraint is no longer violated:
Number.create(i: 1)
# => "PG::Error: ERROR: current transaction is aborted, commands
# ignored until end of transaction block"
end
内置事务
内置事务只有MS-SQL支持,Active Record 只是尽可能的模拟内置事务;
下面操作的结果可能会令你大吃一惊;
User.transaction do
User.create(username: 'Kotori')
User.transaction do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
这样,会创建两个对象 Kotori 和 Nemu;
尽管,抛出了异常,但是并没有让事务回滚;
在事务block中发生的异常,父级block看不到;导致了无法回滚;
下面, User.transaction(requires_new: true)
设置需要新的事务块
可以正常回滚;
但是,实际上很多数据库是不支持内置事务的,这只是模拟;
User.transaction do
User.create(username: 'Kotori')
User.transaction(requires_new: true) do
User.create(username: 'Nemu')
raise ActiveRecord::Rollback
end
end
Active Record 迁移
命令
If the migration name is of the form “AddColumnToTable” or “RemoveColumnFromTable” and
is followed by a list of column names and types then a migration containing the appropriate
add_column and remove_column statements will be created.
创建独立的迁移
rails generate migration AddPartNumberToProducts
生成的迁移文件会有一个timestamp 前缀;
每次迁移都会在数据库中生成一条记录;
防止数据操作不一致;导致数据库修改错乱;
执行迁移命令
rails db:migrate
模型生成器:命令
rails g model User
创建数据表
一般都是通过创建model 来生成迁移文件
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :username
t.string :password
t.numeric :gender
t.string :avatar_url
t.string :email
t.timestamps
end
end
end
修改字段
添加字段
rails generate migration AddPartNumberToProducts part_number:string
change_column command is irreversible.
改变column是不可逆的
class AddTestToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :test, :string
remove_column :users,:test
# change_column :users,:gender,:text
add_index :users,:username
change_column_null :products, :name, false
change_column_default :products, :approved, from: true, to: false
end
end
字段修饰符(重要)
字段修改符 可以用在修改或者创建column时
- limit Sets the maximum size of the string/text/binary/integer fields.
- precision Defines the precision for the decimal fields, representing the total number of digits in the number.
- scale Defines the scale for the de