Django多数据库的实现过程详解


Posted in Python onAugust 01, 2019

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

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(obj1, obj2, **hints)

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

allow_migrate(db, app_label, model_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')

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python translator使用实例
Sep 06 Python
Python中用于去除空格的三个函数的使用小结
Apr 07 Python
python一键升级所有pip package的方法
Jan 16 Python
Python开发的HTTP库requests详解
Aug 29 Python
windows下Virtualenvwrapper安装教程
Dec 13 Python
Python使用xlwt模块操作Excel的方法详解
Mar 27 Python
pandas将DataFrame的列变成行索引的方法
Apr 10 Python
使用Python脚本zabbix自定义key监控oracle连接状态
Aug 28 Python
python脚本监控logstash进程并邮件告警实例
Apr 28 Python
Python生成器next方法和send方法区别详解
May 30 Python
详解tf.device()指定tensorflow运行的GPU或CPU设备实现
Feb 20 Python
Python可视化神器pyecharts之绘制地理图表练习
Jul 07 Python
Python解决pip install时出现的Could not fetch URL问题
Aug 01 #Python
numpy.meshgrid()理解(小结)
Aug 01 #Python
Python-接口开发入门解析
Aug 01 #Python
Python列表(list)所有元素的同一操作解析
Aug 01 #Python
详解numpy.meshgrid()方法使用
Aug 01 #Python
解决安装python3.7.4报错Can''t connect to HTTPS URL because the SSL module is not available
Jul 31 #Python
numpy中的meshgrid函数的使用
Jul 31 #Python
You might like
用PHP函数解决SQL injection
2006/10/09 PHP
php disk_free_space 返回目录可用空间
2010/05/10 PHP
PHP gbk环境下json_dencode传送来的汉字
2012/11/13 PHP
解析PHP中数组元素升序、降序以及重新排序的函数
2013/06/20 PHP
Smarty变量调节器失效的解决办法
2014/08/20 PHP
php之curl设置超时实例
2014/11/03 PHP
php+mysql结合Ajax实现点赞功能完整实例
2015/01/30 PHP
php实现简易聊天室应用代码
2015/09/23 PHP
ThinkPHP设置禁止百度等搜索引擎转码(简单实用)
2016/02/15 PHP
php简单计算年龄的方法(周岁与虚岁)
2016/12/06 PHP
php类自动装载、链式操作、魔术方法实现代码
2017/07/23 PHP
PHP lcfirst()函数定义与用法
2019/03/08 PHP
ThinkPHP框架整合微信支付之Native 扫码支付模式二图文详解
2019/04/09 PHP
ajax无刷新动态调用股票信息(改良版)
2008/11/01 Javascript
使用js判断当前时区TimeZone是否是夏令时
2014/02/23 Javascript
js实现键盘操作实现div的移动或改变的原理及代码
2014/06/23 Javascript
JavaScript数值转换的三种方式总结
2014/07/31 Javascript
JavaScript编程中容易出BUG的几点小知识
2015/01/31 Javascript
jQuery中slidedown与slideup方法用法示例
2016/09/16 Javascript
微信小程序实战之运维小项目
2017/01/17 Javascript
基于vue 实现token验证的实例代码
2017/12/14 Javascript
electron demo项目npm install安装失败的解决方法
2018/02/06 Javascript
详解React+Koa实现服务端渲染(SSR)
2018/05/23 Javascript
Vue.js的复用组件开发流程完整记录
2018/11/29 Javascript
js实现无缝滚动双图切换效果
2019/07/09 Javascript
vue中使用vue-print.js实现多页打印
2020/03/05 Javascript
[28:05]完美世界DOTA2联赛循环赛Inki vs DeMonsTer 第一场 10月30日
2020/10/31 DOTA
Python中的列表生成式与生成器学习教程
2016/03/13 Python
一键搞定python连接mysql驱动有关问题(windows版本)
2016/04/23 Python
Python多线程threading和multiprocessing模块实例解析
2018/01/29 Python
Python安装lz4-0.10.1遇到的坑
2018/05/20 Python
用Python获取摄像头并实时控制人脸的实现示例
2019/07/11 Python
python numpy数组中的复制知识解析
2020/02/03 Python
元旦红领巾广播稿
2014/02/19 职场文书
房屋鉴定委托书范本
2014/09/23 职场文书
社区环境卫生倡议书
2015/04/29 职场文书