С Django multidb довольно легко написать маршрутизатор, который запускает инфраструктуру master/slave. Но можно ли написать маршрутизатор, который записывает в несколько баз данных? Мой вариант использования — это набор проектов, работающих в одном домене. Чтобы избавить пользователей от регистрации/входа на каждый сайт, я хотел бы синхронизировать таблицы contrib.auth
и contrib.sessions
. Возможно ли это с Django multidb или мне следует изучить функции репликации системы баз данных (в моем случае MySQL)?
Django multidb: запись в несколько баз данных
Ответы (4)
Сейчас я работаю над схемой шардинга Django.
Я посмотрел на роутер Django, но решил накатить свой.
некоторые мысли по вашей проблеме:
- Одна из идей состоит в том, чтобы использовать одну базу данных, а затем копировать соответствующие данные с помощью сигналов Django при пост-сохранении.
что-то типа--
import settings.databases as dbs_list
def post_save_function(UserModel):
for db in dbs_list:
UserModel.save(using=db,force_insert=True)
сохранение пользовательских объектов (по крайней мере, в модели с одной БД), по-видимому, сохраняет данные сеанса, аутентификации и т. д. под капотом с помощью различных магических действий, происходящих внутри django.contrib, поэтому есть шанс, что вам не придется заходить и выяснять имена и типы всех этих таблиц базы данных.
в поддержку возможности этой работы, клянусь, я недавно где-то читал (вероятно, в одном из сообщений в блоге Алекса Гейнора), что если объект имеет внешний ключ, Django попытается использовать ту же БД, в которой живет объект (по очевидным причинам в в свете того, как обычно работает Django).
- другая идея:
из примера на странице Django multiDB, на которую вы ссылались, мне интересно, будет ли работать что-то вроде следующего:
их пример кода:
def allow_syncdb(self, db, model):
"Explicitly put all models on all databases."
return True
возможная модификация:
def allow_syncdb (я, БД, модель):
if isinstance(model,User):
return True
elif isinstance(model,Session):
return True
else:
''' something appropriate --
whatever your sharding schema is for other objects '''
взглянув на него еще раз, этот код, вероятно, был бы более полезен в качестве функции "db_for_write". Но ты получил идею.
нет никаких сомнений в том, что вам придется добавить другие типы моделей, чтобы сделать эту работу (все материалы аутентификации, которые обширны).
удачи! надеюсь, что это полезно в некотором роде.
мне интересны ваши выводы и комментарии!
jb
я думаю, вам будет лучше внедрить службу SSO или OAuth
но если вы хотите синхронизировать пользователей вашей таблицы между двумя базами данных, и если вы используете свою собственную UserModel, вы можете сделать что-то вроде этого
class MyUser(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, unique=True)
def save(self, ...): # ALL the signature
super(MyUser, self).save(using='database_1')
super(MyUser, self).save(using='database_2')
вы также можете использовать такой декоратор, как этот, вы также можете использовать его для синхронизации других таблиц:
def save_multi_db(model_class):
def save_wrapper(save_func):
def new_save(self, *args, **kws):
super(model_class, self).save(using='database_1')
super(model_class, self).save(using='database_1')
return new_save
func = getattr(model_class, 'save')
setattr(model_class, 'save', save_wrapper(func))
return save_wrapper
# and use it like this:
@save_multi_db
class MyUser(models.Model):
....
Надеюсь, это поможет :)
contrib.auth
и contrib.sessions
). Если это вообще возможно, я бы хотел не связываться с самим Django.
- person Benjamin Wohlwend; 28.10.2010
Во-первых, я думаю, что вам нужна больше структура единого входа, например в этом посте
Я попробовал ответ mouad, но не смог заставить работать декоратор классов... И мне кажется, что это решение не позволяет иметь пользовательские save() в моделях.
Более подходящий для моих нужд, я определил собственный универсальный класс и просто переопределил функцию save().
class MultiDbModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
for dbname in settings.DATABASES:
super(MultiDbModel, self).save(using=dbname)
А потом:
class MyObject(MultiDbModel):
[...]
def save(self, *args, **kwargs):
[custom save]
super(MyObject, self).save(args, kwargs)
Вот мой multidb_model.py, который, кажется, также обрабатывает внешние ключи. Обновит его, если есть ошибки, но как только вы наследуете этот класс в соответствии с ответом Stepahne, это будет обрабатывать сохранение внешнего ключа.
import logging
from django.db import models
DEFAULT_DB = 'default' # main postgres host
MIRROR_COPY_DB = 'pg_mirror' # a copy original db, i.e. if you want to move your data and keep it in sync
class MultiPgDbModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwarg):
super(MultiPgDbModel, self).save(using=DEFAULT_DB)
try:
fields = [field for field in self._meta.fields if field.get_internal_type() == 'ForeignKey']
for field in fields:
getattr(self, field.name).save(using=MIRROR_COPY_DB)
super(MultiPgDbModel, self).save(using=MIRROR_COPY_DB)
except Exception as e:
logging.exception(f"MultiPgDbModel.save unexpected error when saving to pg_mirror, object id "
f"{self.pk} of model {self._meta.model}. {e}")