Tôi đã làm điều gì đó tương tự gần nhất với điểm 1, nhưng thay vì sử dụng phần mềm trung gian để đặt kết nối mặc định, các bộ định tuyến cơ sở dữ liệu Django được sử dụng. Điều này cho phép logic ứng dụng sử dụng một số cơ sở dữ liệu nếu được yêu cầu cho mỗi yêu cầu. Tùy thuộc vào logic ứng dụng để chọn một cơ sở dữ liệu phù hợp cho mọi truy vấn và đây là nhược điểm lớn của phương pháp này.
Với thiết lập này, tất cả cơ sở dữ liệu được liệt kê trong settings.DATABASES
, bao gồm cả cơ sở dữ liệu có thể được chia sẻ giữa các khách hàng. Mỗi mô hình dành cho khách hàng cụ thể được đặt trong ứng dụng Django có nhãn ứng dụng cụ thể.
ví dụ. Lớp sau định nghĩa một mô hình tồn tại trong tất cả cơ sở dữ liệu khách hàng.
class MyModel(Model):
....
class Meta:
app_label = 'customer_records'
managed = False
Một bộ định tuyến cơ sở dữ liệu được đặt trong settings.DATABASE_ROUTERS
chuỗi để định tuyến yêu cầu cơ sở dữ liệu bằng app_label
, một cái gì đó như thế này (không phải là một ví dụ đầy đủ):
class AppLabelRouter(object):
def get_customer_db(self, model):
# Route models belonging to 'myapp' to the 'shared_db' database, irrespective
# of customer.
if model._meta.app_label == 'myapp':
return 'shared_db'
if model._meta.app_label == 'customer_records':
customer_db = thread_local_data.current_customer_db()
if customer_db is not None:
return customer_db
raise Exception("No customer database selected")
return None
def db_for_read(self, model, **hints):
return self.get_customer_db(model, **hints)
def db_for_write(self, model, **hints):
return self.get_customer_db(model, **hints)
Phần đặc biệt của bộ định tuyến này là thread_local_data.current_customer_db()
gọi điện. Trước khi bộ định tuyến được sử dụng, người gọi / ứng dụng phải thiết lập db của khách hàng hiện tại trong thread_local_data
. Trình quản lý ngữ cảnh Python có thể được sử dụng cho mục đích này để đẩy / bật cơ sở dữ liệu khách hàng hiện tại.
Với tất cả những điều này được định cấu hình, mã ứng dụng sau đó trông giống như thế này, trong đó UseCustomerDatabase
là trình quản lý ngữ cảnh để đẩy / đưa tên cơ sở dữ liệu khách hàng hiện tại vào thread_local_data
để thread_local_data.current_customer_db()
sẽ trả về tên cơ sở dữ liệu chính xác khi bộ định tuyến cuối cùng bị truy cập:
class MyView(DetailView):
def get_object(self):
db_name = determine_customer_db_to_use(self.request)
with UseCustomerDatabase(db_name):
return MyModel.object.get(pk=1)
Đây là một thiết lập khá phức tạp. Nó hoạt động, nhưng tôi sẽ cố gắng tóm tắt những gì tôi thấy là ưu điểm và nhược điểm:
Ưu điểm
- Lựa chọn cơ sở dữ liệu là linh hoạt. Nó cho phép nhiều cơ sở dữ liệu được sử dụng trong một truy vấn, cả cơ sở dữ liệu dành riêng cho khách hàng và cơ sở dữ liệu chia sẻ đều có thể được sử dụng trong một yêu cầu.
- Lựa chọn cơ sở dữ liệu là rõ ràng (không chắc chắn nếu đây là một lợi thế hay bất lợi). Nếu bạn cố gắng chạy một truy vấn truy cập vào cơ sở dữ liệu khách hàng nhưng ứng dụng chưa chọn một truy vấn, một ngoại lệ sẽ xảy ra cho thấy lỗi lập trình.
- Việc sử dụng bộ định tuyến cơ sở dữ liệu cho phép các cơ sở dữ liệu khác nhau tồn tại trên các máy chủ khác nhau, thay vì dựa vào
USE db;
tuyên bố đoán rằng tất cả cơ sở dữ liệu đều có thể truy cập được thông qua một kết nối duy nhất.
Nhược điểm
- Việc thiết lập rất phức tạp và có khá nhiều lớp liên quan để làm cho nó hoạt động.
- Nhu cầu và việc sử dụng dữ liệu cục bộ của chuỗi là không rõ ràng.
- Chế độ xem có nhiều mã lựa chọn cơ sở dữ liệu. Điều này có thể được tóm tắt bằng cách sử dụng các khung nhìn dựa trên lớp để tự động chọn cơ sở dữ liệu dựa trên các tham số yêu cầu giống như cách phần mềm trung gian sẽ chọn cơ sở dữ liệu mặc định.
- Trình quản lý ngữ cảnh để chọn cơ sở dữ liệu phải được bao bọc xung quanh bộ truy vấn theo cách mà trình quản lý ngữ cảnh vẫn hoạt động khi truy vấn được đánh giá.
Đề xuất
Nếu bạn muốn truy cập cơ sở dữ liệu linh hoạt, tôi khuyên bạn nên sử dụng các bộ định tuyến cơ sở dữ liệu của Django. Sử dụng Middleware hoặc một Mixin dạng xem tự động thiết lập cơ sở dữ liệu mặc định để sử dụng cho kết nối dựa trên các tham số yêu cầu. Bạn có thể phải dùng đến luồng dữ liệu cục bộ để lưu trữ cơ sở dữ liệu mặc định để sử dụng để khi bộ định tuyến bị tấn công, nó sẽ biết cơ sở dữ liệu nào cần định tuyến đến. Điều này cho phép Django sử dụng các kết nối liên tục hiện có của nó với cơ sở dữ liệu (có thể nằm trên các máy chủ khác nhau nếu muốn) và chọn cơ sở dữ liệu để sử dụng dựa trên định tuyến được thiết lập trong yêu cầu.
Cách tiếp cận này cũng có ưu điểm là cơ sở dữ liệu cho truy vấn có thể được ghi đè nếu cần bằng cách sử dụng QuerySet using()
chức năng chọn cơ sở dữ liệu khác với cơ sở dữ liệu mặc định.