Django syncdb trying to insert duplicate rows when multiple databases are specified
-
06-06-2021 - |
質問
I'm in the process of configuring my Django app for multiple databases, and as a stop-gap measure, I'm using the following configuration in my settings.py:
READ_DATABASE = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'thedatabase',
'USER': 'read_only',
'PASSWORD': '',
'OPTIONS': {
'init_command': 'SET storage_engine=INNODB'
}
}
WRITE_DATABASE = {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'thedatabase',
'USER': 'root',
'PASSWORD': '',
'OPTIONS': {
'init_command': 'SET storage_engine=INNODB'
}
}
DATABASES = {'default': WRITE_DATABASE,
'read': READ_DATABASE}
These point to the same database, in order to emulate a master-slave pair. The read_only
user, as you might guess, only has read permissions.
My database router looks like this:
from django.conf import settings
class MasterSlaveRouter(object):
def db_for_read(self, model, **hints):
return 'read'
def db_for_write(self, model, **hints):
return 'default'
def allow_relation(self, db1, db2, **hints):
return True
def allow_syncdb(self, db, model):
return db in ('default',)
When running syncdb, it now crashes immediately after setting up the superuser, with the error:
django.db.utils.IntegrityError: (1062, "Duplicate entry 'auth-permission' for key 'app_label'")
The offending SQL that's failing to execute is:
INSERT INTO `django_content_type` (`name`, `app_label`, `model`) VALUES (permission, auth, permission)
This error only occurs when the second read database is sepcified in my settings.py
, when only a default
database is present, the syncdb command completes successfully.
Any suggestions what could be causing this?
Edit: Django version is 1.2.3
解決
After a few hours of tinkering, I've deduced that the reason this was occurring is because some Django models (in this case django.contrib.contenttype.models.ContentType
, but also django.contrib.site.models.Site
) have their own inbuilt caching mechanism.
Since I have set up two different connections to the same database, this was causing a problem since the contenttype
cache for the read-only connection was not being invalidated by a write to a ContentType
model on the write connection.
The solution to this was to change my db_for_read
method to something like this:
def db_for_read(self, model, **hints): if model._meta.app_label in ('contenttypes', 'sites'): return 'write' else: return 'read'