Направих нещо подобно, което е най-близо до точка 1, но вместо да използвам междинен софтуер за задаване на връзка по подразбиране, се използват Django рутери за база данни. Това позволява на логиката на приложението да използва определен брой бази данни, ако е необходимо за всяка заявка. От логиката на приложението зависи да избере подходяща база данни за всяка заявка и това е големият недостатък на този подход.
С тази настройка всички бази данни са изброени в settings.DATABASES
, включително бази данни, които могат да се споделят между клиентите. Всеки модел, който е специфичен за клиента, се поставя в приложение на Django, което има специфичен етикет на приложението.
напр. Следният клас дефинира модел, който съществува във всички клиентски бази данни.
class MyModel(Model):
....
class Meta:
app_label = 'customer_records'
managed = False
Рутер за база данни се поставя в settings.DATABASE_ROUTERS
верига за маршрутизиране на заявка за база данни от app_label
, нещо подобно (не е пълен пример):
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)
Специалната част за този рутер е thread_local_data.current_customer_db()
обадете се. Преди да се използва рутерът, обаждащият се/приложението трябва да е настроил текущата клиентска база данни в thread_local_data
. За тази цел може да се използва контекстен мениджър на Python за натискане/изкарване на текуща клиентска база данни.
След като всичко това е конфигурирано, кодът на приложението изглежда така, където UseCustomerDatabase
е контекстен мениджър за натискане/изкарване на текущо име на клиентска база данни в thread_local_data
така че thread_local_data.current_customer_db()
ще върне правилното име на базата данни, когато рутерът бъде ударен в крайна сметка:
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)
Това вече е доста сложна настройка. Работи, но ще се опитам да обобщя какво виждам като предимства и недостатъци:
Предимства
- Изборът на база данни е гъвкав. Той позволява използването на множество бази данни в една заявка, както специфични за клиента, така и споделени бази данни могат да се използват в заявка.
- Изборът на база данни е изричен (не съм сигурен дали това е предимство или недостатък). Ако се опитате да изпълните заявка, която удря клиентска база данни, но приложението не е избрало такава, ще възникне изключение, което показва програмна грешка.
- Използването на рутер за база данни позволява различни бази данни да съществуват на различни хостове, вместо да разчитат на
USE db;
изявление, което предполага, че всички бази данни са достъпни чрез една връзка.
Недостатъци
- Настройването му е сложно и има няколко слоя, за да го накара да функционира.
- Необходимостта и използването на локални данни на нишката е неясна.
- Изгледите са осеяни с код за избор на база данни. Това може да се абстрахира с помощта на базирани на клас изгледи за автоматично избиране на база данни въз основа на параметрите на заявката по същия начин, както междинният софтуер би избрал база данни по подразбиране.
- Мениджърът на контекста за избор на база данни трябва да бъде обвит около набор от заявки по такъв начин, че мениджърът на контекста да е все още активен, когато заявката се оценява.
Предложения
Ако искате гъвкав достъп до база данни, бих предложил да използвате рутерите на базата данни на Django. Използвайте Middleware или изглед Mixin, който автоматично настройва база данни по подразбиране, която да се използва за връзката въз основа на параметрите на заявката. Може да се наложи да прибягвате до локални данни за нишки, за да съхраните базата данни по подразбиране, която да използвате, така че когато рутерът бъде ударен, той да знае към коя база данни да се насочи. Това позволява на Django да използва съществуващите си постоянни връзки към база данни (която може да се намира на различни хостове, ако желае) и избира базата данни за използване въз основа на маршрутизирането, зададено в заявката.
Този подход също има предимството, че базата данни за заявка може да бъде отменена, ако е необходимо, като се използва QuerySet using()
функция за избор на база данни, различна от тази по подразбиране.