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选择排序算法的实现代码
Nov 21 Python
Python爬取Coursera课程资源的详细过程
Nov 04 Python
Python 列表排序方法reverse、sort、sorted详解
Jan 22 Python
python微信跳一跳游戏辅助代码解析
Jan 29 Python
Scrapy基于selenium结合爬取淘宝的实例讲解
Jun 13 Python
将Dataframe数据转化为ndarry数据的方法
Jun 28 Python
使用Python画股票的K线图的方法步骤
Jun 28 Python
python笔记_将循环内容在一行输出的方法
Aug 08 Python
python爬虫 Pyppeteer使用方法解析
Sep 28 Python
tensorflow 只恢复部分模型参数的实例
Jan 06 Python
浅析python字符串前加r、f、u、l 的区别
Jan 24 Python
总结Pyinstaller打包的高级用法
Jun 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中定义数组常量(array常量)的方法
2014/11/17 PHP
PHP实现单文件、多个单文件、多文件上传函数的封装示例
2019/09/02 PHP
javascript 常用方法总结
2009/06/03 Javascript
JQuery 将元素显示在屏幕的中央的代码
2010/02/27 Javascript
javascript 数组学习资料收集
2010/04/11 Javascript
JQuery切换显示的效果实例代码
2013/02/27 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 图片库
2015/01/09 Javascript
基于Jquery实现仿百度百科右侧导航代码附源码下载
2015/11/27 Javascript
HTML5游戏引擎LTweenLite实现的超帅动画效果(附demo源码下载)
2016/01/26 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(三)
2016/09/14 Javascript
BootStrap Table 获取同行不同列元素的方法
2016/12/19 Javascript
BootStrap Fileinput初始化时的一些参数
2016/12/30 Javascript
老生常谈jquery中detach()和remove()的区别
2017/03/02 Javascript
JS简单判断字符在另一个字符串中出现次数的2种常用方法
2017/04/20 Javascript
jQuery extend()详解及简单实例
2017/05/06 jQuery
Vue项目webpack打包部署到服务器的实例详解
2017/07/17 Javascript
基于Vue实现可以拖拽的树形表格实例详解
2018/10/18 Javascript
Vue动态组件与异步组件实例详解
2019/02/23 Javascript
如何使用 JavaScript 操作浏览器历史记录 API
2020/11/24 Javascript
解决vue下载后台传过来的乱码流的问题
2020/12/05 Vue.js
[01:08:09]DOTA2上海特级锦标赛主赛事日 - 1 胜者组第一轮#1Liquid VS Alliance第二局
2016/03/02 DOTA
python避免死锁方法实例分析
2015/06/04 Python
Python文件右键找不到IDLE打开项解决办法
2015/06/08 Python
python实现简单点对点(p2p)聊天
2017/09/13 Python
python测试mysql写入性能完整实例
2018/01/18 Python
python3 读写文件换行符的方法
2018/04/09 Python
详解tensorflow载入数据的三种方式
2018/04/24 Python
一款纯css3实现简单的checkbox复选框和radio单选框
2014/11/05 HTML / CSS
加拿大快时尚零售商:Ardene
2018/02/14 全球购物
行政监察建议书
2014/05/19 职场文书
个人求职信范文
2014/05/24 职场文书
元旦晚会活动总结
2014/07/09 职场文书
交通事故和解协议书
2015/01/27 职场文书
房屋质量投诉书
2015/07/02 职场文书
2019广播稿怎么写
2019/04/17 职场文书