zoukankan      html  css  js  c++  java
  • Django使用多数据库

    有些项目可能涉及到使用多个数据库的情况,方法很简单。

    1.在settings中设定DATABASE

    比如要使用两个数据库:

    DATABASES = {
        'default': {
            'NAME': 'app_data',
            'ENGINE': 'django.db.backends.postgresql',
            'USER': 'postgres_user',
            'PASSWORD': 's3krit'
        },
        'users': {
            'NAME': 'user_data',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_user',
            'PASSWORD': 'priv4te'
        }
    }
    

    这样就确定了2个数据库,别名一个为default,一个为user。数据库的别名可以任意确定。

    default的别名比较特殊,一个Model在路由中没有特别选择时,默认使用default数据库。

    当然,default也可以设置为空:

    DATABASES = {
        'default': {},
        'users': {
            'NAME': 'user_data',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_user',
            'PASSWORD': 'superS3cret'
        },
        'customers': {
            'NAME': 'customer_data',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_cust',
            'PASSWORD': 'veryPriv@ate'
        }
    }
    

    这样,因为没有了默认的数据库,就需要为所有的Model,包括使用的第三方库中的Model做好数据库路由选择。

    2.为需要做出数据库选择的Model规定app_label

    class MyUser(models.Model):
        ...
    
        class Meta:
            app_label = 'users'
    

    3.写Database Routers

    Database Router用来确定一个Model使用哪一个数据库,主要定义以下四个方法:

    db_for_read(model**hints)

    规定model使用哪一个数据库读取。

    db_for_write(model**hints)

    规定model使用哪一个数据库写入。

    allow_relation(obj1obj2**hints)

    确定obj1和obj2之间是否可以产生关联, 主要用于foreign key和 many to many操作。

    allow_migrate(dbapp_labelmodel_name=None**hints)

    确定migrate操作是否可以在别名为db的数据库上运行。

    一个完整的例子:

    数据库设定:

    DATABASES = {
        'default': {},
        'auth_db': {
            'NAME': 'auth_db',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_user',
            'PASSWORD': 'swordfish',
        },
        'primary': {
            'NAME': 'primary',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_user',
            'PASSWORD': 'spam',
        },
        'replica1': {
            'NAME': 'replica1',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_user',
            'PASSWORD': 'eggs',
        },
        'replica2': {
            'NAME': 'replica2',
            'ENGINE': 'django.db.backends.mysql',
            'USER': 'mysql_user',
            'PASSWORD': 'bacon',
        },
    }
    

    如果想要达到如下效果:

    app_label为auth的Model读写都在auth_db中完成,其余的Model写入在primary中完成,读取随机在replica1和replica2中完成。

    auth:

    class AuthRouter(object):
        """
        A router to control all database operations on models in the
        auth application.
        """
        def db_for_read(self, model, **hints):
            """
            Attempts to read auth models go to auth_db.
            """
            if model._meta.app_label == 'auth':
                return 'auth_db'
            return None
    
        def db_for_write(self, model, **hints):
            """
            Attempts to write auth models go to auth_db.
            """
            if model._meta.app_label == 'auth':
                return 'auth_db'
            return None
    
        def allow_relation(self, obj1, obj2, **hints):
            """
            Allow relations if a model in the auth app is involved.
            """
            if obj1._meta.app_label == 'auth' or 
               obj2._meta.app_label == 'auth':
               return True
            return None
    
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            """
            Make sure the auth app only appears in the 'auth_db'
            database.
            """
            if app_label == 'auth':
                return db == 'auth_db'
            return None
    

    这样app_label为auth的Model读写都在auth_db中完成,允许有关联,migrate只在auth_db数据库中可以运行。

    其余的:

    import random
    
    class PrimaryReplicaRouter(object):
        def db_for_read(self, model, **hints):
            """
            Reads go to a randomly-chosen replica.
            """
            return random.choice(['replica1', 'replica2'])
    
        def db_for_write(self, model, **hints):
            """
            Writes always go to primary.
            """
            return 'primary'
    
        def allow_relation(self, obj1, obj2, **hints):
            """
            Relations between objects are allowed if both objects are
            in the primary/replica pool.
            """
            db_list = ('primary', 'replica1', 'replica2')
            if obj1._state.db in db_list and obj2._state.db in db_list:
                return True
            return None
    
        def allow_migrate(self, db, app_label, model_name=None, **hints):
            """
            All non-auth models end up in this pool.
            """
            return True
    

    这样读取在随机在replica1和replica2中完成,写入使用primary。

    最后在settings中设定:

    DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']
    

    就可以了。

    进行migrate操作时:

    $ ./manage.py migrate
    $ ./manage.py migrate --database=users
    

    migrate操作默认对default数据库进行操作,要对其它数据库进行操作,可以使用--database选项,后面为数据库的别名。

    与此相应的,dbshell,dumpdata,loaddata命令都有--database选项。

    也可以手动的选择路由:

    查询

    >>> # This will run on the 'default' database.
    >>> Author.objects.all()
    
    >>> # So will this.
    >>> Author.objects.using('default').all()
    
    >>> # This will run on the 'other' database.
    >>> Author.objects.using('other').all()
    

    保存

    >>> my_object.save(using='legacy_users')
    

    移动

    >>> p = Person(name='Fred')
    >>> p.save(using='first')  # (statement 1)
    >>> p.save(using='second') # (statement 2)
    

    以上的代码会产生问题,当p在first数据库中第一次保存时,会默认生成一个主键,这样使用second数据库保存时,p已经有了主键,这个主键如果未被使用不会产生问题,但如果先前被使用了,就会覆盖原先的数据。

    有两个解决方法;

    1.保存前清除主键:

    >>> p = Person(name='Fred')
    >>> p.save(using='first')
    >>> p.pk = None # Clear the primary key.
    >>> p.save(using='second') # Write a completely new object.
    

    2.使用force_insert

    >>> p = Person(name='Fred')
    >>> p.save(using='first')
    >>> p.save(using='second', force_insert=True)
    

    删除

    从哪个数据库取得的对象,从哪删除

    >>> u = User.objects.using('legacy_users').get(username='fred')
    >>> u.delete() # will delete from the `legacy_users` database
    

    如果想把一个对象从legacy_users数据库转移到new_users数据库:

    >>> user_obj.save(using='new_users')
    >>> user_obj.delete(using='legacy_users')
    
  • 相关阅读:
    eclipse中的Invalid text string (xxx).
    在jsp文件中出现Unknown tag (c:out)
    eclipse 界面复原
    ecilpse 纠错插件
    Multiple annotations found at this line:- The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path
    Port 8080 required by Tomcat v9.0 Server at localhost is already in use. The server may already be running in another process, or a system process may be using the port.
    调用第三方https接口
    调用第三方http接口
    创建带值枚举
    spring整合redis之Redis配置文件
  • 原文地址:https://www.cnblogs.com/linxiyue/p/7485392.html
Copyright © 2011-2022 走看看