关系数据库
定义你的数据库(官方文档删减)
-
在Django中使用多个数据库的第一步是告诉Django 你将要使用的数据库服务器。这通过使用DATABASES 设置完成。该设置映射数据库别名到一个数据库连接设置的字典,这是整个Django 中引用一个数据库的方式。
-
你可以为数据库选择任何别名。然而,default这个别名具有特殊的含义。当没有选择其它数据库时,Django 使用具有default 别名的数据库。
-
下面是settings.py的一个示例片段,它定义两个数据库 —— 一个默认的PostgreSQL 数据库和一个叫做users的MySQL 数据库:
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'users': {
'NAME': 'user_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
- Django 要求default 数据库必须定义,但是如果不会用到,其参数字典可以保留为空。
- 若要这样做,你必须为你的所有的应用的模型建立DATABASE_ROUTERS,包括正在使用的contrib 中的应用和第三方应用,以使得不会有查询被路由到默认的数据库。
- 下面是settings.py 的一个示例片段,它定义两个非默认的数据库,其中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'
}
}
- 如果你试图访问没有在DATABASES 设置中定义的数据库,Django 将抛出一个django.db.utils.ConnectionDoesNotExist异常。
同步你的数据库
migrate 管理命令一次操作一个数据库。默认情况下,它在default 数据库上操作,但是通过提供一个–database 参数,你可以告诉migrate 同步一个不同的数据库。
$ ./manage.py migrate
$ ./manage.py migrate --database=users
如果你不想每个应用都被同步到同一台数据库上,你可以定义一个数据库路由。
数据库路由
数据库路由是一个类,它提供4个方法:
- db_for_read(model, **hints)¶
建议model类型的对象的读操作应该使用的数据库。
如果一个数据库操作能够提供其它额外的信息可以帮助选择一个数据库,它将在hints字典中提供。合法的hints 的详细信息在下文给出。
如果没有建议,则返回None。
- db_for_write(model, **hints)¶
建议Model 类型的对象的写操作应该使用的数据库。
如果一个数据库操作能够提供其它额外的信息可以帮助选择一个数据库,它将在hints字典中提供。 合法的hints 的详细信息在下文给出。
如果没有建议,则返回None。
-
allow_relation(obj1, obj2, **hints)¶
如果obj1 和obj2 之间应该允许关联则返回True,如果应该防止关联则返回False,如果路由无法判断则返回None。这是纯粹的验证操作,外键和多对多操作使用它来决定两个对象之间是否应该允许一个关联。 -
allow_migrate(db, app_label, model_name=None, **hints)¶
定义迁移操作是否允许在别名为db的数据库上运行。如果操作应该运行则返回True ,如果不应该运行则返回False,如果路由无法判断则返回None。
位置参数app_label 是正在迁移的应用的标签。
大部分迁移操作设置model_name的值为正在迁移的模型的model.meta.model_name(模型的__name_ 的小写)。对于RunPython和RunSQL 操作它的值为None,除非这两个操作使用hint 提供它。
hints 用于某些操作来传递额外的信息给路由。
当设置了model_name时,hints 通常通过键’model’包含该模型的类。注意,它可能是一个历史模型,因此不会有自定的属性、方法或管理器。你应该只依赖_meta。
这个方法还可以用来决定一个给定数据库上某个模型的可用性。
注意,如果这个方法返回False,迁移将默默地不会在模型上做任何操作。这可能导致你应用某些操作之后出现损坏的外键、表多余或者缺失。
路由不必提供所有这些方法 —— 它可以省略一个或多个。如果某个方法缺失,在做相应的检查时Django 将忽略该路由。
例子
- 这个配置将有几个数据库:一个用于auth 应用,所有其它应用使用一个具有两个读replica 的 primary/replica。下面是表示这些数据库的设置:
DATABASES = {
'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',
},
}
- 首先,我们需要一个路由,它知道发送auth 应用的查询到auth_db:
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=None, **hints):
"""
Make sure the auth app only appears in the 'auth_db'
database.
"""
if app_label == 'auth':
return db == 'auth_db'
return None
- 我们还需要一个路由,它发送所有其它应用的查询到primary/replica 配置,并随机选择一个replica 来读取:
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=None, **hints):
"""
All non-auth models end up in this pool.
"""
return True
- 最后,在设置文件中,我们添加如下内容:
DATABASE_ROUTERS = ['path.to.AuthRouter', 'path.to.PrimaryReplicaRouter']
手动选择一个数据库
- 为QuerySet手动选择一个数据库¶
你可以在QuerySet“链”的任意节点上为QuerySet选择数据库 。只需要在QuerySet上调用using()就可以让QuerySet使用一个指定的数据库。
using() 接收单个参数:你的查询想要运行的数据库的别名。例如:
>>> # 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()
为save() 选择一个数据库¶
对Model.save()使用using 关键字来指定数据应该保存在哪个数据库。
例如,若要保存一个对象到legacy_users 数据库,你应该使用:
>>> my_object.save(using='legacy_users')
选择一个数据库用于删除表单¶
默认情况下,删除一个已存在对象的调用将在与获取对象时使用的相同数据库上执行:
>>> u = User.objects.using('legacy_users').get(username='fred')
>>> u.delete() # will delete from the `legacy_users` database
或
>>> user_obj.save(using='new_users')
>>> user_obj.delete(using='legacy_users')
Django 的管理站点中使用多数据库¶
Django 的管理站点没有对多数据库的任何显式的支持。如果你给数据库上某个模型提供的管理站点不想通过你的路由链指定,你将需要编写自定义的ModelAdmin类用来将管理站点导向一个特殊的数据库。
ModelAdmin 对象具有5个方法,它们需要定制以支持多数据库:
class MultiDBModelAdmin(admin.ModelAdmin):
# A handy constant for the name of the alternate database.
using = 'other'
def save_model(self, request, obj, form, change):
# Tell Django to save objects to the 'other' database.
obj.save(using=self.using)
def delete_model(self, request, obj):
# Tell Django to delete objects from the 'other' database
obj.delete(using=self.using)
def get_queryset(self, request):
# Tell Django to look for objects on the 'other' database.
return super(MultiDBModelAdmin, self).get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
# Tell Django to populate ForeignKey widgets using a query
# on the 'other' database.
return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request=request, using=self.using, **kwargs)
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
# Tell Django to populate ManyToMany widgets using a query
# on the 'other' database.
return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request=request, using=self.using, **kwargs)
redis
利用单例
- 建立一个redis集群池,做成单例
# pool
# __init__.py
from .redis_pools import master,slave1,slave2
# redis_pools.py
import redis
master=redis.ConnectionPool(host='127.0.0.1',port=6379)
slave1=redis.ConnectionPool(host='127.0.0.1',port=6380)
slave2=redis.ConnectionPool(host='127.0.0.1',port=6381)
- 使用
from pool import master,salve1,slave2
import redis
writer=redis.Redis(connection_pool=master)
reader1=redis.Redis(connection_pool=slave1)
reader2=redis.Redis(connection_pool=slave2)
writer.set('username','general_zy',60)
reader1.get('username')
reader2.get('username')
利用django-redis
- 配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
}
},
"user": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6380",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 100}
}
}
}
- 使用1
from django_redis import get_redis_connection
conn=get_redis_connection('default')
user=get_redis_connection('default')
conn.set('username','general_zy')
user.get('username')
- 使用2
from django.core.cache import cache
cache.set('username','general_zy')
cache.get('username')