2016-08-03 254 views
3

假设这是应该使用两个MySQL数据库Django应用程序:Django和只读数据库连接

  • default - 用于存储
  • support由模型AB(读写访问)所表示的数据 - 导入的车型CD表示的数据(只读访问)

support数据库是外部应用程序的一部分,并且不能修改为

由于Django的应用程序使用内置的ORM的模型AB我想它应该使用模型CD非常相同的ORM,即使它们映射到外部数据库表(support。)

为了实现我所定义的模型CD如下:

from django.db import models 


class ExternalModel(models.Model): 
    class Meta: 
     managed = False 
     abstract = True 


class ModelC(ExternalModel): 
    some_field = models.TextField(db_column='some_field') 

    class Meta(ExternalModel.Meta): 
     db_table = 'some_table_c' 


class ModelD(ExternalModel): 
    some_other_field = models.TextField(db_column='some_other_field') 

    class Meta(ExternalModel.Meta): 
     db_table = 'some_table_d' 

然后我定义的数据库糅之三:

from myapp.myapp.models import ExternalModel 


class DatabaseRouter(object): 
    def db_for_read(self, model, **hints): 
     if issubclass(model, ExternalModel): 
      return 'support' 

     return 'default' 

    def db_for_write(self, model, **hints): 
     if issubclass(model, ExternalModel): 
      return None 

     return 'default' 

    def allow_relation(self, obj1, obj2, **hints): 
     return (isinstance(obj1, ExternalModel) == isinstance(obj2, ExternalModel)) 

    def allow_migrate(self, db, app_label, model_name=None, **hints): 
     return (db == 'default') 

最后调整settings.py

# (...) 

DATABASES = { 
    'default': { 
     'ENGINE': 'django.db.backends.mysql', 
     'OPTIONS': { 
      'read_default_file': os.path.join(BASE_DIR, 'resources', 'default.cnf'), 
     }, 
    }, 
    'support': { 
     'ENGINE': 'django.db.backends.mysql', 
     'OPTIONS': { 
      'read_default_file': os.path.join(BASE_DIR, 'resources', 'support.cnf'), 
     }, 
    }, 
} 

DATABASE_ROUTERS = ['myapp.database_router.DatabaseRouter'] 

# (...) 

support.confsupport数据库已分配只读权限指定的用户。

但是当我运行python manage.py makemigrations失败与下面的输出:

Traceback (most recent call last): 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/utils.py", line 62, in execute 
    return self.cursor.execute(sql) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/mysql/base.py", line 112, in execute 
    return self.cursor.execute(query, args) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 226, in execute 
    self.errorhandler(self, exc, value) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler 
    raise errorvalue 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 217, in execute 
    res = self._query(query) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 378, in _query 
    rowcount = self._do_query(q) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 341, in _do_query 
    db.query(q) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/connections.py", line 280, in query 
    _mysql.connection.query(self, query) 
_mysql_exceptions.OperationalError: (1142, "CREATE command denied to user 'somedbuser'@'somehost' for table 'django_migrations'") 

The above exception was the direct cause of the following exception: 

Traceback (most recent call last): 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/migrations/recorder.py", line 57, in ensure_schema 
    editor.create_model(self.Migration) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/base/schema.py", line 295, in create_model 
    self.execute(sql, params or None) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/base/schema.py", line 112, in execute 
    cursor.execute(sql, params) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/utils.py", line 79, in execute 
    return super(CursorDebugWrapper, self).execute(sql, params) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute 
    return self.cursor.execute(sql, params) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/utils.py", line 94, in __exit__ 
    six.reraise(dj_exc_type, dj_exc_value, traceback) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/utils/six.py", line 685, in reraise 
    raise value.with_traceback(tb) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/utils.py", line 62, in execute 
    return self.cursor.execute(sql) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/backends/mysql/base.py", line 112, in execute 
    return self.cursor.execute(query, args) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 226, in execute 
    self.errorhandler(self, exc, value) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler 
    raise errorvalue 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 217, in execute 
    res = self._query(query) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 378, in _query 
    rowcount = self._do_query(q) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/cursors.py", line 341, in _do_query 
    db.query(q) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/MySQLdb/connections.py", line 280, in query 
    _mysql.connection.query(self, query) 
django.db.utils.OperationalError: (1142, "CREATE command denied to user 'somedbuser'@'somehost' for table 'django_migrations'") 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): 
    File "manage.py", line 22, in <module> 
    execute_from_command_line(sys.argv) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line 
    utility.execute() 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/core/management/__init__.py", line 359, in execute 
    self.fetch_command(subcommand).run_from_argv(self.argv) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/core/management/base.py", line 305, in run_from_argv 
    self.execute(*args, **cmd_options) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/core/management/base.py", line 356, in execute 
    output = self.handle(*args, **options) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/core/management/commands/makemigrations.py", line 100, in handle 
    loader.check_consistent_history(connection) 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/migrations/loader.py", line 276, in check_consistent_history 
    applied = recorder.applied_migrations() 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/migrations/recorder.py", line 65, in applied_migrations 
    self.ensure_schema() 
    File "/Users/username/Development/stuff/myapp/lib/python3.5/site-packages/django/db/migrations/recorder.py", line 59, in ensure_schema 
    raise MigrationSchemaMissing("Unable to create the django_migrations table (%s)" % exc) 
django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the django_migrations table ((1142, "CREATE command denied to user 'somedbuser'@'somehost' for table 'django_migrations'")) 

看来,Django的尝试在只读数据库support创建django_migrations表不过。

是否有任何干净的方法来防止迁移机制尝试呢?或者,我必须雇用另一个ORM库来对support数据库进行只读访问吗?

+0

为了记录显式访问 - 我还没有找到任何解决方法,决定使用'peewee'并在没有Django的情况下进行只读访问。 – Konowau

回答

1

有同样的问题。 Django试图在所有数据库中创建'django_migrations'表。 即使没有与只读DB 关联的模型,并且所有路由器都指向不同的DB,也会发生这种情况。

我也结束了使用peewee。

2

我遇到了同样的问题(使用Django 1.11),这个问题在我的谷歌搜索结果的顶部。

您的初始解决方案只缺少一个关键部分。你需要告诉Django'C'和'D'正在使用哪些数据库模型。什么工作对我来说:

class ExternalModel(models.Model): 
    class Meta: 
     managed = False 
     abstract = True  
     app_label = 'support' 

然后告诉你的数据库路由器如何在遇到app_label在allow_migrate()段的行为:

def allow_migrate(self, db, app_label, model_name=None, **hints): 
     if app_label == 'support': 
      return False 
     return (db == 'default') 

我不知道那是最正确的解决方案在Django团队的眼中,但效果是allow_migrate()返回False,以使用该app_label属性值定义的任何模型。 (或至少通过模型代码示例说明了ORM如何将'db'的值传递给allow_migrate()),但是'app_label'和'app_label'管理'的属性,你可以得到它的工作*。

*在我的情况下,默认值是postgres,只读数据库是通过cx_Oracle的Oracle 12。

1

似乎在Django 1.10.1的时间框架内,Tim Graham(主Django维护者)接受了一个修补程序,该修补程序抑制了这个特定的异常,但稍后又取消了修补程序,主要采用以下方法来解决此问题并使用Django ORM支持只读数据库。

  1. Django documentation on routers描述我已附加的路由下面的示例路由器基于模型的元的“应用”标志一个不同 数据库定义一个数据库路由器。

  2. 在你的路由器的allow_migrations方法中,对于任何db参数 对应于一个只读数据库返回False。这可以防止模型表的迁移,而不管它们将被路由到何处。

  3. 接下来的一部分有点奇怪,但橡胶碰到了路, 实际上回答了原来的问题。要保留制作迁移从 试图在您的只读 数据库中创建django_migrations表,数据库流量不应路由。在示例 路由器中,意思是'read_only'是DATABASE_APPS_MAPPING中的而不是

  4. 所以,相反,只读数据库与 “使用”(例如MyReadOnlyModel.objects.using( 'READ_ONLY')。所有()

Django database apps router