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实现获取网站PR及百度权重
Jan 21 Python
python开发之list操作实例分析
Feb 22 Python
python下os模块强大的重命名方法renames详解
Mar 07 Python
python实现将汉字保存成文本的方法
Nov 16 Python
python虚拟环境迁移方法
Jan 03 Python
Python3模拟登录操作实例分析
Mar 12 Python
python实现美团订单推送到测试环境,提供便利操作示例
Aug 09 Python
Django实现auth模块下的登录注册与注销功能
Oct 10 Python
在pytorch 中计算精度、回归率、F1 score等指标的实例
Jan 18 Python
Python日期格式和字符串格式相互转换的方法
Feb 18 Python
在 Python 中使用 7zip 备份文件的操作
Dec 11 Python
Python趣味挑战之实现简易版音乐播放器
May 28 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面向对象分析设计的61条军规小结
2010/07/17 PHP
PHP用SAX解析XML的实现代码与问题分析
2011/08/22 PHP
CI框架中site_url()和base_url()的区别
2015/01/07 PHP
Laravel实现用户注册和登录
2015/01/23 PHP
php正则表达式验证(邮件地址、Url地址、电话号码、邮政编码)
2016/03/14 PHP
PHP实现163邮箱自动发送邮件
2016/03/29 PHP
详解PHP中websocket的使用方法
2016/09/15 PHP
PHP实现超简单的SSL加密解密、验证及签名的方法示例
2017/08/28 PHP
php源码的使用方法讲解
2019/09/26 PHP
Js实现手机发送验证码时按钮延迟操作
2014/06/20 Javascript
AngularJS语法详解
2015/01/23 Javascript
jquery验证邮箱格式并显示提交按钮
2015/11/07 Javascript
javascript数组去重小结
2016/03/07 Javascript
jQuery防止重复绑定事件的解决方法
2016/05/14 Javascript
AngularJS表单和输入验证实例
2016/11/02 Javascript
Vue.js中用v-bind绑定class的注意事项
2016/12/13 Javascript
Bootstrap3多级下拉菜单
2017/02/24 Javascript
jquery 校验中国身份证号码实例详解
2017/04/11 jQuery
微信小程序实现表单校验功能
2020/03/30 Javascript
Vue-router结合transition实现app前进后退动画切换效果的实例
2017/10/11 Javascript
vue+elementUI实现表单和图片上传及验证功能示例
2019/05/14 Javascript
[43:48]Ti4正赛第一天 VG vs NEWBEE 2
2014/07/19 DOTA
python类型强制转换long to int的代码
2013/02/10 Python
python实现定时播放mp3
2015/03/29 Python
Python实现读取并保存文件的类
2017/05/11 Python
Python微信库:itchat的用法详解
2017/08/14 Python
python微信跳一跳系列之棋子定位颜色识别
2018/02/26 Python
python调用虹软2.0第三版的具体使用
2019/02/22 Python
在css3中background-clip属性与background-origin属性的用法介绍
2012/11/13 HTML / CSS
英国国家美术馆商店:National Gallery
2019/05/01 全球购物
瑞士图书网站:Weltbild.ch
2019/09/17 全球购物
社区活动总结报告
2014/05/05 职场文书
校园环保标语
2014/06/13 职场文书
公务员个人考察材料
2014/12/23 职场文书
2015年治庸问责工作总结
2015/07/27 职场文书
python如何正确使用yield
2021/05/21 Python