Rails Active Record Nested Attributes
Bài đăng này đã không được cập nhật trong 5 năm
1. Giới thiệu:
Nested Attributes
cho phép bạn lưu (create hoặc update) associated record vào database thông qua parent record.- Theo mặc định thì
Nested Attributes
bị disable, để enable chức năng này ta gọi hàmaccepts_nested_attributes_for
trong model class của parent record.
2. Nested Attributes cho quan hệ one-to-one (has_one association):
- Ta chạy migration để tạo 2 model
Member
vàAtatar
như sau:
rails g model member name:string
rails g model avatar url:string member:references
- Khi đó ta có 2 model
Member
vàAvatar
có quan hệ như sau:
class Member < ApplicationRecord
has_one :avatar
accepts_nested_attributes_for :avatar
end
class Avatar < ApplicationRecord
belongs_to :member
end
- Thêm
accepts_nested_attributes_for :avatar
vàomemeber.rb
để enableNested Attributes
cho modelMember
vàAvatar
. Nested Attributes
sẽ thêm mộtAttribute Writer
làavatar_attributes
vào modelMember
.- Khi đó ta có thể
create
,update
hoặcdelete
avatar thông qua memeber của avatar đó, sử dụngavatar_attributes
.
a. Tạo Member và Avatar:
- Ta có thể tạo đồng thời member và avatar của member đó như sau:
member = Member.create name: "ThachThao",
avatar_attributes: {
url: "http://i.pravatar.cc/100?img=1"
}
member.id # 1
member.name # ThachThao
member.avatar.id # 1
member.avatar.url # http://i.pravatar.cc/100?img=1
- Đối với trường hợp
has_one association
,avatar_attributes
nhận giá trị là mộthash_attributes
của modelAvatar
. - Giả sử model
Avatar
có thêm các attributes khác nhưfile_type
,file_size
ta có thể tạo member và avatar như sau
member = Member.create name: "ThachThao",
avatar_attributes: {
url: "http://i.pravatar.cc/100?img=1",
file_type: "png",
file_size: 5
}
b. Update Member và Avatar:
- Ta có thể update đồng thời member và avatar của member đó như sau:
member.update_attributes name: "MaiNgo",
avatar_attributes: {
id: 1,
url: "http://i.pravatar.cc/100?img=2"
}
member.id # 1
member.name # MaiNgo
member.avatar.id # 1
member.avatar.url # http://i.pravatar.cc/100?img=2
- Trong trường hợp mặc định, ta phải truyền
id
của avatar như một tham số vàoavatar_attributes
, khi không truyền tham sốid
, việc update member và avatar gặp lỗi như sau
member.update_attributes name: "Mai Ngo", avatar_attributes: {url: "http://i.pravatar.cc/100?img=2"}
(0.2ms) begin transaction
(0.1ms) rollback transaction
Traceback (most recent call last):
ActiveRecord::RecordNotSaved (Failed to remove the existing associated avatar. The record failed to save after its foreign key was set to nil.)
-
Nguyên nhân là đối với trường hợp mặc định
has_one association
, tùy thuộc vào các tham số truyền vàoavatar_attributes
có tham sốid
hay không mà thực hiện việc update hay tạo avatar cho user. -
Khi các tham số truyền vào
avatar_attributes
có tham sốid
thì thực hiện update avatar cho member. -
Khi các tham số truyền vào
avatar_attributes
không có tham sốid
thì thực hiện tạo avatar cho member . -
Nêu bạn muốn thực hiện update cho avatar mà không cần truyền tham số
id
, thêm optionupdate_only: true
(mặc định làupdate_only: false
) vào hàmaccepts_nested_attributes_for :avatar
.
class Member < ActiveRecord::Base
has_one :avatar
accepts_nested_attributes_for :avatar, update_only: true
end
- Thực hiện update avatar của member mà không cần truyền tham số
id
vàoavatar_attributes
.
member.update_attributes name: "KathyNguyen",
avatar_attributes: {
url: "http://i.pravatar.cc/100?img=3"
}
member.id # 1
member.name # KathyNguyen
member.avatar.id # 1
member.avatar.url # http://i.pravatar.cc/100?img=3
c. Xóa Avatar:
- Theo mặc định, bạn chỉ có thể tạo and update attributes của avatar thông qua member.
- Nếu bạn muốn xóa avatar thông qua
hash_attributes
, bạn phải thêm optionallow_destroy: true
(mặc định làallow_destroy: false
) vào hàmaccepts_nested_attributes :avatar
.
class Member < ActiveRecord::Base
has_one :avatar
accepts_nested_attributes_for :avatar, allow_destroy: true
end
- Khi đó để xóa avatar, ta truyền tham số
_destroy
với giá trị tương đươngtrue
vàoavatar_attributes
.
member.update_attributes avatar_attributes: {_destroy: true}
2. Nested Attributes cho quan hệ one-to-many (has_many association):
- Ta chạy migration để tạo model
Post
như sau:
rails g model post content:text member:references
- Khi đó ta có 2 model
Member
vàPost
có quan hệ như sau:
class Member < ApplicationRecord
has_many :posts
accepts_nested_attributes_for :posts
end
class Post < ApplicationRecord
belongs_to :member
end
- Thêm
accepts_nested_attributes_for :posts
vàomemeber.rb
để enableNested Attributes
cho modelMember
vàPost
. Nested Attributes
sẽ thêm mộtAttribute Writer
làposts_attributes
vào modelMember
.- Khi đó ta có thể
create
,update
hoặcdelete
posts thông qua memeber của posts đó, sử dụngposts_attributes
.
a. Tạo Member và Posts:
- Ta có thể tạo đồng thời member và posts của member đó như sau:
member = Member.create name: "ThachThao",
posts_attributes: [{
content: "Friday Post"
}, {
content: "Saturday Post"
}]
member.id # 1
member.name # ThachThao
member.posts.count # 2
member.posts.first.content # Friday Post
member.posts.second.content # Saturday Post
- Đối với trường hợp
has_many association
,posts_attributes
nhận giá trị là một array củahash_attributes
của modelPost
.
b. Update Member và Posts:
- Ta có thể update đồng thời member và avatar của member đó như sau:
member.update_attributes name: "MaiNgo",
posts_attributes: [{
id: 1,
content: "Sunday Post"
}, {
id: 2,
content: "Monday Post"
}, {
content: "Tuesday Post"
}]
member.id # 1
member.name # MaiNgo
member.posts.count # 3
member.posts.first.content # Sunday Post
member.posts.second.content # Monday Post
member.posts.third.content # Tuesday Post
- Để update posts của member, ta truyền tham số
id
cùng với các attributes khác vàohash_attributes
, trong trường hợp không truyền tham sốid
vàohas_attributes
thì bài post mới được tạo ra.
c. Xóa Posts:
- Theo mặc định, bạn chỉ có thể tạo and update attributes của posts thông qua member.
- Nếu bạn muốn xóa posts thông qua
hash_attributes
, bạn phải thêm optionallow_destroy: true
(mặc định làallow_destroy: false
) vào hàmaccepts_nested_attributes :posts
.
class Member < ActiveRecord::Base
has_many :posts
accepts_nested_attributes_for :posts, allow_destroy: true
end
- Khi đó để xóa posts, ta truyền tham số
id
cùng với tham số_destroy
giá trị tương đươcngtrue
vàohash_attributes
member.update_attributes posts_attributes: [{
id: 1,
_destroy: true
}, {
id: 2,
_destroy: true
}]
member.id # 1
member.posts.count # 1
member.posts.first.content # Tuesday Post
All rights reserved