Một vài vấn đề về Active Record Associations
Trong Rails, association là một kết nối giữa hai Active Record models. Chúng ta sử dụng association giữa hai models để làm các hàm, các biểu thức, phép tính trong code trở lên đơn giản hơn. 1. Các loại quan hệ Rails support 6 loại quan hệ: belongs_to has_one has_many has_many :through ...
Trong Rails, association là một kết nối giữa hai Active Record models. Chúng ta sử dụng association giữa hai models để làm các hàm, các biểu thức, phép tính trong code trở lên đơn giản hơn.
1. Các loại quan hệ
Rails support 6 loại quan hệ:
- belongs_to
- has_one
- has_many
- has_many :through
- has_one :through
- has_and_belongs_to_many
1.1 belongs_to
belongs_to là quan hệ một - một giữa 2 model, sao cho mỗi cá thể của model này thuộc về 1 cá thể của model kia. Ví dụ chúng ta có 2 model là Author và Book. Mỗi book thuộc về 1 author. Trong model Book ta sẽ khai báo:
class Book < ApplicationRecord belongs_to :author end
! Chú ý khi khai báo belongs_to phải sử dụng từ số ít.
1.2 has_one
has_one cũng là quan hệ một - một, nhưng về ngữ nghĩa sẽ ngược với belongs_to về model sở hữu và bị sở hữu. Ví dụ 1 supplier có 1 account:
class Supplier < ApplicationRecord has_one :account end
1.3 has_many
has_many là quan hệ một - nhiều với model. Nó thường đi kèm với quan hệ belongs_to ở phía model kia. Nó chỉ ra 1 cá thể của model này có thể không có hoặc có nhiều cá thể của model kia. Ví dụ 1 author có thể có nhiều book.
class Author < ApplicationRecord has_many :books end
! Chú ý khi khai báo has_many cần sử dụng từ dạng số nhiều.
1.4 has_many :through
has_many :through thường xuyên được sử dụng để tạo quan hệ nhiều - nhiều với model khác. Nó cho phép 1 cá thể của model này liên kết với nhiều cá thể của model khác thông qua một model thứ 3. Ví dụ trong một buổi khám sức khỏe tổng quát. Mỗi bệnh nhân (patient) sẽ lần lượt xếp hàng để đợi đến những lượt khám (appointment) để gặp các bác sĩ (physician). Như vậy mỗi lượt khám, 1 bệnh nhân sẽ gặp 1 bác sĩ. Như vậy cả buổi khám, 1 bênh nhân sẽ gặp nhiều bác sĩ và 1 bác sĩ sẽ khám cho nhiều bệnh nhân.
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
has_many :through cũng có thể dùng để tạo quan hệ has_many lồng nhau. Ví dụ mỗi document có nhiều section, mỗi section lại có nhiều paragraph.
class Document < ApplicationRecord has_many :sections has_many :paragraphs, through: :sections end class Section < ApplicationRecord belongs_to :document has_many :paragraphs end class Paragraph < ApplicationRecord belongs_to :section end
1.5 has_one :through
Đây là quan hệ một - một. Nó chỉ ra 1 cá thể của model này liên kết với 1 cá thể của model khác thông qua một model thứ 3. Ví dụ:
class Supplier < ApplicationRecord has_one :account has_one :account_history, through: :account end class Account < ApplicationRecord belongs_to :supplier has_one :account_history end class AccountHistory < ApplicationRecord belongs_to :account end
1.6 has_and_belongs_to_many
has_and_belongs_to_many là quan hệ nhiều - nhiều. Ví dụ:
class Assembly < ApplicationRecord has_and_belongs_to_many :parts end class Part < ApplicationRecord has_and_belongs_to_many :assemblies end
! Tuy không cần khai báo model phụ của 2 model này những vẫn cần tạo assemblies_parts.
2. Lựa chọn giữa belongs_to và has_one
Khi xây dựng quan hệ một - một ta cần thêm belongs_to vào một model và has_one vào một model. Vậy làm cách nào ta biết thêm cái nào vào đâu. Nếu đã có sẵn cơ sở dữ liệu thì ta sẽ dựa vào khóa ngoài (foreign key), nó nằm ở bảng nào thì model đó sẽ đặt quan hệ belongs_to. Tuy nhiêu khi cần xây dựng cơ sở dữ liệu thì ta cần dự vào ý nghĩa thực tế để xác định. Ví dụ với 2 thứ là Người và Tên. Theo đúng ý nghĩa thì mỗi người có một (has_one) tên, và mỗi tên thuộc về (belongs_to) một người.
3. Lựa chọn giữa has_many :through và has_and_belongs_to_many
Đây đều là quan hệ nhiều nhiều.
Với has_and_belongs_to_many
class Assembly < ApplicationRecord has_and_belongs_to_many :parts end class Part < ApplicationRecord has_and_belongs_to_many :assemblies end
Với has_many :through
class Assembly < ApplicationRecord has_many :manifests has_many :parts, through: :manifests end class Manifest < ApplicationRecord belongs_to :assembly belongs_to :part end class Part < ApplicationRecord has_many :manifests has_many :assemblies, through: :manifests end
Để lựa chọn, đơn giản nhất là dựa vào ứng dụng của model quan hệ thứ 3. Nếu nó chỉ đơn thuần để liên kết 2 model chính và không còn tác dụng gì khác thì ta nên dùng has_and_belongs_to_many, còn nếu ta cần sử dụng nó vào nhiều mục đích khác thì nên dùng has_many :through
4. Controlling Caching
Tất cả các association methods đều được xây dựng xung quang bộ nhớ đệm, cái mà sẽ giữ kết quả của lần query gần nhất cho những tính toán tương tự về sau. Cache có thể chia sẽ giữa các hàm trong controller. Ví dụ:
author.books # lấy ra books từ trong cơ sở dữ liệu author.books.size # sử dụng lại books đã lưu cache author.books.empty? # sử dụng lại books đã lưu cache
Nếu bán muốn reload cache vì có thể dữ liệu đã bị thay đổi ở chỗ nào đó thì bạn có thể dùng hàm reload.
author.books # lấy ra books từ trong cơ sở dữ liệu author.books.size # sử dụng lại books đã lưu cache author.books.reload.empty? # loại bỏ cache đã lưu books và lấy ra lại từ cơ sở dữ liệu