Tạo ứng dụng Rails với database có sẵn

Trong bài viết lần này, mình sẽ giới thiệu với các bạn cách tạo một Rails app với một database có sẵn. Điều này rất có ích trong một số trường hợp như: bỗng nhiên bạn muốn thay đổi ứng dụng của mình được viết bằng Rails trong khi trước đó đang viết trên 1 nền tảng khác, hoặc trong trường hợp thiết kế một ứng dựng đa nền tảng sử dụng chung một database có sẵn.

Bắt đầu nào

Giả sử bạn có 2 bảng dữ liệu là NguoiDung và BaiViet như sau:

Nó được việt hóa hoàn toàn và không tuân thủ theo quy tắc đặt tên của Rails 😨 Tới đây bạn bắt đầu manh nha suy nghĩ thôi tạo lại DB từ đầu cho dễ chơi dễ trúng thưởng. Nhưng mà khoan, như thế thì mất hết dữ liệu à. Thôi thử làm tiếp xem sao

Tạo mới Rails app

Lại thấy easy rồi 😄 Ở đây DB của mình sử dụng là MySQL nên sẽ thêm gem 'mysql2' vào và bundle install để cài đặt một số gem cần thiết Thủ tục cuối cùng là config DB, tìm đến file config/database.yml và cấu hình đến database của bạn

development:
  adapter: mysql2
  encoding: utf8
  database: test_db
  pool: 5
  username: *****
  password: *****
  host: 127.0.0.1
  port: 3306

Kéo data về với bản làng

Mọi thứ dường như đã sẵn sàng, công việc cuối cùng là làm thế nào để Rails ActiveRecord có thể tương tác với DB để nó làm việc mai mối còn chúng ta chỉ việc code Ruby và không cần quan tâm tới sự đời. Theo cách bình thường, bạn sẽ tạo các file migration, chạy lệnh migrate để generate ra schema, tạo model và code. Nhưng mà khoan, chúng ta có DB có sẵn mà nhỉ Rails nó có lệnh dump để tạo ra schema kết nối với database đã config

rails db:schema:dump

Với database lúc nãy, mình sẽ có file schema.rb như sau:

Tạo model

Bây giờ hãy tạo 2 model đại diện cho 2 cái bảng trong DB mà mình vừa dump về nhé. Ồ, table tiếng việt à, thế thì model cũng để tiếng việt luôn cho nó khác người 😄

models/nguoi_dung.rb
class NguoiDung < ApplicationRecord
end
models/bai_viet.rb
class BaiViet < ApplicationRecord
end

Thử rails c xem lấy được dữ liệu không nào

2.4.0 :001 > NguoiDung.first
   (0.3ms)  SET NAMES utf8,  @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'),  @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
  NguoiDung Load (0.5ms)  SELECT  `nguoi_dungs`.* FROM `nguoi_dungs` ORDER BY `nguoi_dungs`.`id` ASC LIMIT 1
ActiveRecord::StatementInvalid: Mysql2::Error: Table 'test_db.nguoi_dungs' doesn't exist: SELECT  `nguoi_dungs`.* FROM `nguoi_dungs` ORDER BY `nguoi_dungs`.`id` ASC LIMIT 1
	from (irb):1
2.4.0 :002 > 

Như các bạn đã thấy, theo quy tắc của rails thì model NguoiDung nó sẽ select đến bảng nguoi_dungs, mã tất nhiên trong DB của chúng ta chỉ có bảng NguoiDung chứ làm gì có nguoi_dungs để nó truy vấn. Vậy thì hơi mệt một chút, trót lấy DB không theo quy tắc của Rails thì phải mất công ngồi config thêm xí nữa. Hãy khai báo cho Rails biết model của bạn đang đại diện cho bảng nào trong CSDL

class NguoiDung < ApplicationRecord
  self.table_name = "NguoiDung"
end

Kết quả:

2.4.0 :005 > NguoiDung.first
  NguoiDung Load (0.2ms)  SELECT  `NguoiDung`.* FROM `NguoiDung` ORDER BY `NguoiDung`.`MaNguoiDung` ASC LIMIT 1
 => #<NguoiDung MaNguoiDung: 1, TenNguoiDung: "Da xua"> 
2.4.0 :006 >

Cùng config thêm mấy cái quan hệ nào

models/nguoi_dung.rb
class NguoiDung < ApplicationRecord
  self.table_name = "NguoiDung"

  has_many :bai_viet, class_name: BaiViet.name, foreign_key: "MaNguoiDung"
end
models/bai_viet.rb
class BaiViet < ApplicationRecord
  self.table_name = "BaiViet"

  belongs_to :nguoi_dung, class_name: NguoiDung.name, foreign_key: "MaNguoiDung"
end

rails c ra và kết quả là

2.4.0 :022 > NguoiDung.first.bai_viet
  NguoiDung Load (0.2ms)  SELECT  `NguoiDung`.* FROM `NguoiDung` ORDER BY `NguoiDung`.`MaNguoiDung` ASC LIMIT 1
  BaiViet Load (0.3ms)  SELECT  `BaiViet`.* FROM `BaiViet` WHERE `BaiViet`.`MaNguoiDung` = 1 LIMIT 11
 => #<ActiveRecord::Associations::CollectionProxy [#<BaiViet MaBaiViet: 1, TieuDe: "15gg", NoiDung: "Hasagi", MaNguoiDung: 1>, #<BaiViet MaBaiViet: 2, TieuDe: "20gg", NoiDung: "Death is like the wind, always by my site", MaNguoiDung: 1>]> 
2.4.0 :023 > BaiViet.first.nguoi_dung
  BaiViet Load (0.7ms)  SELECT  `BaiViet`.* FROM `BaiViet` ORDER BY `BaiViet`.`MaBaiViet` ASC LIMIT 1
  NguoiDung Load (0.6ms)  SELECT  `NguoiDung`.* FROM `NguoiDung` WHERE `NguoiDung`.`MaNguoiDung` = 1 LIMIT 1
 => #<NguoiDung MaNguoiDung: 1, TenNguoiDung: "Da xua"> 
2.4.0 :024 > 

Chúc các bạn thành công!