服务器启动后Django多个动态数据库 [英] Django multiple dynamic databases after server startup

查看:125
本文介绍了服务器启动后Django多个动态数据库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是django的新手,并尝试找出对我而言在django中动态使用多个数据库的最佳解决方案。
我知道django可以使用settings.py文件中注册的多个数据库,但就我而言,我有一个主数据库(sqlite)作为我的存储库,我创建了所有模型,其余的api视图集

I'm new to django and try to figure out what would be the best solution for me to use dynamically multiple databases in django. I know django is able to work with multiple databases registered in the settings.py file but In my case I have one main database (sqlite) which acts as my repository, I created all the models, the rest api viewsets for this one.

用户可以通过输入连接信息来选择连接到Oracle数据库,然后我需要从该数据库收集数据并将其插入到我的存储库中。用户可以通过该应用程序注册多个Oracle数据库。我想知道是否应该使用纯cx_Oracle类来处理来自Django的那些连接,还是应该在settings.py中注册它们?

The user can choose to connect to an Oracle database by entering the connection information and then I will need to gather data from this database and insert it to my repository. The user can register multiple Oracle databases through the app. I'm wondering if I should use a pure cx_Oracle class to handle those connection from django or if I should register them in the settings.py?

前端中的每个视图都映射到特定的数据库,我将需要在它们之间切换上下文,如果使用cx_Oracle类,如何将请求路由到右侧后端中的类实例?

Each view in the frontend maps to a specific database and I will need to switch context between them, If I use a cx_Oracle class how can I route the requests to the right class instance in the backend?

任何帮助或见识都会受到赞赏,我在互联网上找不到与我的用例匹配的东西。

Any help or insight would be appreciated, I didn't find anything matching my use case on the internet.

推荐答案

在注释中找到-此处是一篇文章,描述了如何通过动态选择数据库来设置服务器实例,因此其作者是应该获得所有荣誉的人。重述基本方法:

As found out in the comments - here is an article describing how to set up a server instance with selecting databases on the fly, so its author is the one that should get all credits. Restating the basic approach:


  1. 创建代表数据库连接的模型类:

  1. Create a model class representing the db connection:

class Database(models.Model):
    name = models.CharField(max_length=256, unique=True)
    config = JSONField()


  • 添加 label 属性以区分db连接实体。在这里,它需要在django设置中设置字符串 DYNAMIC_DATABASES_PREFIX DYNAMIC_DATABASES_SEPARATOR ,但也可以将其硬编码为一些常量:

  • Add a label property to distinct the db connection entities. Here, it requires to set strings DYNAMIC_DATABASES_PREFIX and DYNAMIC_DATABASES_SEPARATOR in the django settings, but could also be hardcoded as some constants:

    class Database(models.Model):
        ...
        @property
        def label(self):
            # We want to be able to identify the dynamic databases and apps
            # So we prepend their names with a common string
            prefix = getattr(settings, 'DYNAMIC_DATABASES_PREFIX', 'DYNAMIC_DATABASE')
            separator = getattr(settings, 'DYNAMIC_DATABASES_SEPARATOR', '_')
            return '{}{}{}'.format(prefix, separator, self.pk)
    


  • 添加一种方法,用于将db连接添加到Django的db连接中/从db连接中删除db连接(精妙的部分正在为每个数据库连接放置一个虚拟应用程序-这样,我们可以拥有具有重复表名的不同数据库):

  • Add a method for adding the db connection to/removing the db connection from django's db connections (the nifty part is putting a dummy app for each db connection - this way we can have different databases with duplicate table names):

    class Database(models.Model):
        ...
        def register(self):
            # label for the database connection and dummy app
            label = self.label
            # Do we have this database registered yet
            if label not in connections._databases:
                # Register the database
                connections._databases[label] = self.config
                # Break the cached version of the database dict so it'll find our new database
                del connections.databases
            # Have we registered our fake app that'll hold the models for this database
            if label not in apps.app_configs:
                # We create our own AppConfig class, because the Django one needs a path to the module that is the app.
                # Our dummy app obviously doesn't have a path
                AppConfig2 = type('AppConfig'.encode('utf8'),(AppConfig,),
                                  {'path': '/tmp/{}'.format(label)})
                app_config = AppConfig2(label, label)
                # Manually register the app with the running Django instance
                apps.app_configs[label] = app_config
                apps.app_configs[label].models = {}
    
        def unregister(self):
            label = self.label
            if label in apps.app_configs:
                del apps.app_configs[label]
            if label in apps.all_models:
                del apps.all_models[label]
            if label in connections._databases:
                del connections._databases[label]
                del connections.databases
    


  • 通过连接添加连接查找该名称还将注册与正在运行的django实例的连接,从而使其可操作:

  • Add a connection lookup by connection name that also registers the connection to running django instance, making it operational:

    class Database(models.Model):
        ...
        def get_model(self, table_name):
            # Ensure the database connect and it's dummy app are registered
            self.register()
            label = self.label
            model_name = table_name.lower().replace('_', '')
    
            # Is the model already registered with the dummy app?
            if model_name not in apps.all_models[label]:
                # Use the "inspectdb" management command to get the structure of the table for us.
                file_obj = StringIO()
                Command(stdout=file_obj).handle(database=label, table_name_filter=lambda t: t == table_name)
                model_definition = file_obj.getvalue()
                file_obj.close()
    
                # Make sure that we found the table and have a model definition
                loc = model_definition.find('(models.Model):')
                if loc != -1:
                    # Ensure that the Model has a primary key.
                    # Django doesn't support multiple column primary keys,
                    # So we have to add a primary key if the inspect command didn't
                    if model_definition.find('primary_key', loc) == -1:
                        loc = model_definition.find('(', loc + 14)
                        model_definition = '{}primary_key=True, {}'.format(model_definition[:loc + 1], model_definition[loc + 1:])
                    # Ensure that the model specifies what app_label it belongs to
                    loc = model_definition.find('db_table = \'{}\''.format(table_name))
                    if loc != -1:
                        model_definition = '{}app_label = \'{}\'\n        {}'.format(model_definition[:loc], label, model_definition[loc:])
    
                    # Register the model with Django. Sad day when we use 'exec'
                    exec(model_definition, globals(), locals())
                    # Update the list of models that the app
                    # has to match what Django now has for this app
                    apps.app_configs[label].models = apps.all_models[label]
                else:
                    logger.info('Could not find table: %s %s', label, table_name)
            else:
                logger.info('Already added dynamic model: %s %s', label, table_name)
    
            # If we have the connection, app and model. Return the model class
            if (label in connections._databases and label in apps.all_models and model_name in apps.all_models[label]):
                return apps.get_model(label, model_name)
    


  • 使用提到的配置字符串选择数据库来创建自定义数据库路由:

  • Create custom db routing, using the mentioned config strings for db selection:

    class DynamicDatabasesRouter(object):
        label_prefix = '{}{}'.format(
            getattr(settings, 'DYNAMIC_DATABASES_PREFIX', 'DYNAMIC_DATABASE'),
            getattr(settings, 'DYNAMIC_DATABASES_SEPARATOR', '_')
        )
    
        def db_for_read(self, model, **hints):
            if model._meta.app_label.startswith(self.label_prefix):
                # We know that our app_label matches the database connection's name
                return model._meta.app_label
            return None
    
        def db_for_write(self, model, **hints):
            if model._meta.app_label.startswith(self.label_prefix):
                # We know that our app_label matches the database connection's name
                return model._meta.app_label
            return None
    
        def allow_relation(self, obj1, obj2, **hints):
            return None
    
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            return None
    


  • 在设置中注册路由器:

  • Register the router in settings:

    DATABASE_ROUTERS = ['myapp.routing.DynamicDatabasesRouter']
    


  • (可选)使模型可在管理网站(如果使用它):

  • (Optional) make the model modifiable in the admin site if you use it:

    def config(conn):
        return json.dumps(conn.config)
    config.short_description = 'Config'
    
    class DatabaseAdmin(admin.ModelAdmin):
        list_display = ('name', config)
    
    admin.site.register(Database, DatabaseAdmin)
    


  • 视图中的示例用法:

    class HomeView(TemplateView):
        template_name = 'home.html'
    
        def get_context_data(self):
            context = super(HomeView, self).get_context_data()
    
            # We can pick which dynamic database connection we want based on a GET parameter
            db = Database.objects.get(pk=self.request.GET.get('env', 1))
            # Pass the database instance to the template so we can display it.
            context['db'] = db
    
            # Get a model class for a table in our dynamic database.
            # Lets pretend there's a table called 'author'
            Author = db.get_model('author')
            authors = Author.objects.all().order_by('name')
            # Send the author instances to the template for iterating over.
            context['authors'] = authors
    
            return context
    

    这篇关于服务器启动后Django多个动态数据库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆