12/08/2018, 17:11

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
0