重学rails: association 关联 basic
表与表关系 basic
关联
- 了解各种类型的关联。
- 声明 Active Record 模型之间的关联。
- 为您的模型选择正确的关联类型。
- 使用单表继承。
- 设置和使用委托类型。
过与 VS 现在
操作 | 没有关联 | 有关联 |
---|---|---|
创建 Migratoin |
|
|
Model |
|
|
删除操作 |
|
|
我的准备
rails g model Author name:string
rails g model Book title:string published_at:date author:references
六种关联
Rails 支持六种类型的关联,每种类型都有特定的用例。
belongs_to
- 声明模型的每个实例都“属于”另一个模型的一个实例
- 例如,如果您的应用程序包含作者和书籍,并且每本书都可以分配给一个作者
class Book < ApplicationRecord
belongs_to :author
end
optional: true 的含义
这确保了即使 optional: true允许author_id为 NULL,当它不为 NULL 时,它仍然必须引用作者表中的有效记录。
class Book < ApplicationRecord
belongs_to :author, optional: true
end
添加的方法belongs_to
class Book < ApplicationRecord
belongs_to :author
end
class Author < ApplicationRecord
has_many :books
validates :name, presence: true
end
该模型的实例Book
将具有以下方法:
author
author=
build_author
create_author
create_author!
reload_author
reset_author
author_changed?
author_previously_changed?
初始化新的has_one或belongs_to关联时,必须使用build_前缀来构建关联,而不是 association.build用于has_many或 has_and_belongs_to_many关联的方法。要创建关联,请使用create_前缀。
对上面那句话的理解:参考这里
aric: 其实还不是特别理解这一句话。
在 Rails 中,当处理 `has_one` 或 `belongs_to` 关联时,使用 `build_` 前缀来构建关联对象,意味着创建一个新的、尚未保存到数据库的关联对象。例如,如果一个 `User` 模型 `has_one :profile`,你可以使用 `build_profile` 来构建一个 `Profile` 对象,但这个对象还没有保存到数据库中。
### `build_` 和 `association.build` 的区别
- `build_` 前缀(例如 `build_profile`)主要用于 `has_one` 和 `belongs_to` 关联。
- 对于 `has_many` 或 `has_and_belongs_to_many` 关联,你会使用 `association.build` 方法。例如,`user.posts.build` 会为 `has_many :posts` 关联创建一个新帖子,但同样不会保存到数据库。
#### 示例:
```ruby
# has_one or belongs_to
user = User.find(1)
profile = user.build_profile(name: "John Doe") # 构建 profile,但不保存到数据库
# has_many
post = user.posts.build(title: "New Post") # 构建 post,但不保存到数据库
```
### 使用 `create_` 前缀创建关联
当你希望在创建关联的同时立即保存到数据库时,可以使用 `create_` 前缀。
- 对于 `has_one` 或 `belongs_to` 关联,使用 `create_profile`,它不仅会构建 `Profile`,还会将其保存。
- 对于 `has_many` 关联,使用 `association.create`,如 `user.posts.create`,同样会立即创建并保存关联对象。
#### 示例:
```ruby
# has_one or belongs_to
profile = user.create_profile(name: "John Doe") # 创建并保存 profile 到数据库
# has_many
post = user.posts.create(title: "New Post") # 创建并保存 post 到数据库
```
### 总结:
- `build_` 前缀用于 `has_one` 和 `belongs_to` 关联,只构建但不保存对象。
- `association.build` 用于 `has_many` 和 `has_and_belongs_to_many` 关联,也只构建对象。
- `create_` 前缀用于所有关联类型,同时构建并保存对象到数据库。
检查关联变更
不要
model.association_changed?
与 混淆model.association.changed?
。前者检查关联是否已被新记录替换,而后者跟踪关联属性的变化。
@book.author # => #<Author author_number: 123, author_name: "John Doe">
@book.author_changed? # => false
@book.author_previously_changed? # => false
@book.author = Author.second # => #<Author author_number: 456, author_name: "Jane Smith">
@book.author_changed? # => true
@book.save!
@book.author_changed? # => false
@book.author_previously_changed? # => true
创建实例
- 创建 book: 单数 create_author/build_author
- 创建 author: author.books.create 添加book到 author 管理;其实就是 push/add
# 使用 build_author/create_author
book = Book.create(title: 'Harry')
book.build_author(name: 'aric')
book.save
# 使用 create 添加 books
author = Author.find(1)
# 错误信息
author = Author.new
author.save
author.errors.full_messages
has_one: 一对一
关联
has_one
表示另一个模型引用了此模型。可以通过此关联获取该模型。例如,如果应用程序中的每个供应商只有一个帐户,则可以像这样声明供应商模型:
belongs_to: 都含有 xx_id 关联的
class Supplier < ApplicationRecord
has_one :account
end
class Account < ApplicationRecord
validates :terms, presence: true
belongs_to :supplier
end
delete vs destroy
delete: 只会删除,但会跳过
callback
destroy: 删除,并执行正常的
callback
has_many :through 多对多
关联has_many :through
通常用于与另一个模型建立多对多关系。此关联表示声明模型可以通过第三个模型与另一个模型的零个或多个实例匹配。
例如,假设有一家医疗机构,患者可以预约看医生。相关关联声明可能如下所示:
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
回调
关联回调与普通回调类似,但它们是由与 Active Record 对象关联的集合的生命周期中的事件触发的。有四种可用的关联回调:
before_add
after_add
before_remove
after_remove