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实现从字典中删除元素的方法
May 04 Python
Python中字符串的常见操作技巧总结
Jul 28 Python
requests和lxml实现爬虫的方法
Jun 11 Python
python3+PyQt5实现支持多线程的页面索引器应用程序
Apr 20 Python
python opencv实现运动检测
Jul 10 Python
详解Django中间件执行顺序
Jul 16 Python
Python实现非正太分布的异常值检测方式
Dec 09 Python
python字符串替换re.sub()实例解析
Feb 09 Python
Python paramiko 模块浅谈与SSH主要功能模拟解析
Feb 29 Python
Python TestSuite生成测试报告过程解析
Jul 23 Python
Python基于Webhook实现github自动化部署
Nov 28 Python
Python装饰器的练习题
Nov 23 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模拟HTTP认证
2006/10/09 PHP
PHP自动生成表单代码分享
2015/06/19 PHP
php blowfish加密解密算法
2016/07/02 PHP
asp.net和php的区别点总结
2019/10/10 PHP
DHTML Slide Show script图片轮换
2008/03/03 Javascript
JQuery浮动DIV提示信息并自动隐藏的代码
2010/08/29 Javascript
网易JS面试题与Javascript词法作用域说明
2010/11/09 Javascript
50个比较实用jQuery代码段
2011/09/18 Javascript
jQuery学习笔记 操作jQuery对象 文档处理
2012/09/19 Javascript
js中window.open打开一个新的页面
2014/08/10 Javascript
jQuery实现的图片分组切换焦点图插件
2015/01/06 Javascript
jquery基础知识第一讲之认识jquery
2016/03/17 Javascript
Html中 IFrame的用法及注意点
2016/12/22 Javascript
Webpack实现按需打包Lodash的几种方法详解
2017/05/08 Javascript
轻松理解vue的双向数据绑定问题
2017/10/30 Javascript
微信小程序使用audio组件播放音乐功能示例【附源码下载】
2017/12/08 Javascript
细说webpack源码之compile流程-rules参数处理技巧(2)
2017/12/26 Javascript
vue实现element-ui对话框可拖拽功能
2018/08/17 Javascript
基于axios 解决跨域cookie丢失的问题
2018/09/26 Javascript
JavaScript简单实现的仿微博留言功能示例
2019/01/17 Javascript
vue created钩子函数与mounted钩子函数的用法区别
2020/11/05 Javascript
Javascript实现关闭广告效果
2021/01/29 Javascript
Python使用django获取用户IP地址的方法
2015/05/11 Python
Python中的with...as用法介绍
2015/05/28 Python
Python使用ftplib实现简易FTP客户端的方法
2015/06/03 Python
Python 实现数据库更新脚本的生成方法
2017/07/09 Python
使用apiDoc实现python接口文档编写
2019/11/19 Python
Django通用类视图实现忘记密码重置密码功能示例
2019/12/17 Python
基于python实现文件加密功能
2020/01/06 Python
Python hashlib和hmac模块使用方法解析
2020/12/08 Python
python+opencv实现车道线检测
2021/02/19 Python
详解px单位html5响应式方案
2018/03/08 HTML / CSS
英语专业大学生求职简历的自我评价
2013/10/18 职场文书
幼儿园小班教学反思
2014/02/02 职场文书
2015年全国保险公众宣传日活动方案
2015/05/06 职场文书
SQL解决未能删除约束问题drop constraint
2022/05/30 SQL Server