后台扩展组件
xadmin
安装
pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
注册app
INSTALLED_APPS = [
# ...
# xadmin主体模块
'xadmin',
# 渲染表格模块
'crispy_forms',
# 为模型通过版本控制,可以回滚数据
'reversion',
]
xadmin:需要自己的数据库模型类,完成数据库迁移
python manage.py makemigrations
python manage.py migrate
设置主路由替换掉admin:主urls.py
# xadmin的依赖
import xadmin
xadmin.autodiscover()
# xversion模块自动注册需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()
urlpatterns = [
# ...
path(r'xadmin/', xadmin.site.urls),
]
完成xadmin全局配置:新建adminx.py
# home/adminx.py
# xadmin全局配置
import xadmin
from xadmin import views
class GlobalSettings(object):
"""xadmin的全局配置"""
site_title = "Genral_zy" # 设置站点标题
site_footer = "xxx有限公司" # 设置站点的页脚
menu_style = "accordion" # 设置菜单折叠
xadmin.site.register(views.CommAdminView, GlobalSettings)
在adminx.py中注册model:adminx.py
from . import models
# 注册
xadmin.site.register(models.Banner)
报错处理
Traceback (most recent call last):
File "manage.py", line 22, in
execute_from_command_line(sys.argv)
File "F:\envs\luffy\lib\site-packages\django\core\management_init_.py", line 381, in execute_from_command_line
utility.execute()
File "F:\envs\luffy\lib\site-packages\django\core\management_init_.py", line 357, in execute
django.setup()
File "F:\envs\luffy\lib\site-packages\django_init_.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "F:\envs\luffy\lib\site-packages\django\apps\registry.py", line 122, in populate
app_config.ready()
File "F:\envs\luffy\lib\site-packages\xadmin\apps.py", line 14, in ready
self.module.autodiscover()
File "F:\envs\luffy\lib\site-packages\xadmin_init_.py", line 49, in autodiscover
register_builtin_plugins(site)
File "F:\envs\luffy\lib\site-packages\xadmin\plugins_init_.py", line 41, in register_builtin_plugins
[import_module('xadmin.plugins.%s' % plugin) for plugin in PLUGINS if plugin not in exclude_plugins]
File "F:\envs\luffy\lib\site-packages\xadmin\plugins_init_.py", line 41, in
[import_module('xadmin.plugins.%s' % plugin) for plugin in PLUGINS if plugin not in exclude_plugins]
File "c:\users\ncg\appdata\local\programs\python\python36\lib\importlib_init_.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "", line 978, in _gcd_import
File "", line 961, in _find_and_load
File "", line 950, in _find_and_load_unlocked
File "", line 655, in _load_unlocked
File "", line 678, in exec_module
File "", line 205, in _call_with_frames_removed
File "F:\envs\luffy\lib\site-packages\xadmin\plugins\importexport.py", line 48, in
from import_export.admin import DEFAULT_FORMATS, SKIP_ADMIN_LOG, TMP_STORAGE_CLASS
ImportError: cannot import name 'SKIP_ADMIN_LOG'
解决:
把 48行复制一行然后注释掉,在49行里 去掉 SKIP_ADMIN_LOG, TMP_STORAGE_CLASS,换成 ImportMixin
为什么要换成这个是因为 ImportMixin 这个类里写了 skip_admin_log 和 tmp_storage_class 方法
simpleUi
在所有的Django后台美化插件中,SimpleUI处于第一阵营,非常符合国人的审美观。
安装
pip install django-simpleui
注册app
一定要把simpleui放到admin前面。
INSTALLED_APPS = [
'simpleui',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
simpleui页面
相比xadmin,simpleui还是很美观的。
常用配置
换Logo和管理后台名字
SIMPLEUI_LOGO = '你的log地址'
# admin.py
from django.contrib import admin
admin.site.site_header = 'xx管理后台' # 设置header
admin.site.site_title = 'xx管理后台' # 设置title
admin.site.index_title = 'xx管理后台'
关闭右侧广告
SIMPLEUI_HOME_INFO = False
SIMPLEUI_ANALYSIS = False
自定义首页
# 隐藏首页的快捷操作和最近动作
SIMPLEUI_HOME_QUICK = False
SIMPLEUI_HOME_ACTION = False
# 修改左侧菜单首页设置
SIMPLEUI_HOME_PAGE = 'https://www.baidu.com' # 指向页面(十有八九失败)
SIMPLEUI_HOME_TITLE = '百度欢迎你!' # 首页标题
SIMPLEUI_HOME_ICON = 'fa fa-code' # 首页图标
# 设置右上角Home图标跳转链接,会以另外一个窗口打开
SIMPLEUI_INDEX = 'https://www.baidu.com'
任务队列组件celery
celery
- celery是一个处理大量消息的分布式系统,是一个专注于实时处理的异步队列。
- celery可以执行异步任务,延迟任务,定时任务
- celery架构:消息中间件(broker)+任务执行单元(worker)+任务执行结果存储。
- 消息中间件:可以用redis或专门的消息队列。
- worker:从broker中获取任务信息并执行任务,结果丢个backend。
- backend:存储任务执行结果。
安装
pip install celery ==4.4.6
目录
启动方法
- windows:(需要安装eventlet)
celery worker -A server -l info -P eventlet
- linux:
celey worker -A server -l info
参数说明:-A 包含celery的模块,-l 日志级别
使用
# celery.py
from celery import Celery
broker = 'redis://127.0.0.1:6379/1'
backend = 'redis://127.0.0.1:6379/2'
app = Celery(__name__, broker=broker, backend=backend, include=['server.tasks'])
异步任务
# tasks.py
from .celery import app
# 定义任务
@app.task
def add(x, y):
return x + y
# run.py (可以在任意处执行,包括某个项目内部)
from server.celery import app
from server.tasks import add
from celery.result import AsyncResult
# tid: the id of task
tid = add.delay(1, 2)
print(type(tid))
res = AsyncResult(str(tid), app=app)
if res.successful():
print(res.get())
>>> <class 'celery.result.AsyncResult'>
>>> 3
延迟任务
# run.py
from server.celery import app
from server.tasks import add
from celery.result import AsyncResult
from datetime import datetime, timedelta
eta = datetime.utcnow() + timedelta(seconds=10)
tid = add.apply_async(args=(200, 50), eta=eta)
print(type(tid))
res = AsyncResult(str(tid), app=app)
if res.successful():
print(res.get())
===
10s后可以在redis中看到结果
>>> {"status": "SUCCESS", "result": 250, "traceback": null, "children": [], "date_done": "2022-02-16T02:11:20.155155", "task_id": "b82399bf-d476-47dd-8a8e-0d024600275b"}
定时任务
# celery.py
需要增加定时任务配置
# 统一时间
app.conf.timezone = 'Asia/Shanghai'
# 禁用utc
app.conf.enable_utc = False
# 配置定时任务
app.conf.beat_schedule = {
'add-task': {
# 利用反射取到add任务
'task': 'server.tasks.add',
# 三秒提交一次
'schedule': timedelta(seconds=3),
# 任务参数
'args': (200, 50)
}
}
启动beat:!!再开一个终端,不要关闭worker打工人。
celery beat -A server -l info
每三秒提交一次任务:
redis中的结果:
关于定时任务
from celery.schedule import crontab
'schedule':crontab(hour=8,day_of_week =1 )每周一早上八点提交一次任务
可以定义如:每周五晚上23点将redis的数据存储到mysql中的任务......
django使用
-
由于django 和 celery是独立的进程,所以需要导入django 环境
from celery import Celery import django import os os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffyapi.settings.dev') django.setup() broker = 'redis://127.0.0.1:6379/1' backend = 'redis://127.0.0.1:6379/2' app = Celery(__name__, broker=broker, backend=backend, include=['celery_task.home_task'])
-
定义异步任务
@app.task def banner_update(): from home import serializer from home import models from django.conf import settings queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('display_order')[ :settings.BANNER_COUNT] banner_ser = serializer.BannerModelSerializer(instance=queryset, many=True) for banner in banner_ser: banner['img'] = 'http://127.0.0.1:8000' + banner['img'] cache.set('banner_list', banner_ser.data) return True
-
执行任务
class BannerView(GenericViewSet, ListModelMixin): # 限定只展示三条 queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('orders')[ :settings.BANNER_COUNT] serializer_class = serializer.BannerModelSerializer def list(self, request, *args, **kwargs): banner_list = cache.get('banner_list') if not banner_list: # 缓存中没有 banner_update.dalay() response = super().list(request, *args, **kwargs) cache.set('banner_list', response.data) return Response(data=response.data) return Response(data=banner_list)
支付宝支付+腾讯短信+腾讯对象存储封装代码
支付
settings.py
import os
APPID = "your appid"
# 拼接路径
with open(os.path.join(os.path.dirname(__file__), 'keys', 'app_private')) as f:
APP_PRIVATE_KEY_STRING = f.read()
with open(os.path.join(os.path.dirname(__file__), 'keys', 'alipay_public')) as f:
ALIPAY_PUBLIC_KEY_STRING = f.read()
SIGN_TYPE = "RSA2", # RSA or RSA2
DEBUG = True, # False by default
# 沙盒接口
GATEWAY = 'https://openapi.alipaydev.com/gateway.do'
# 设置回调通知地址(POST)
# ALIPAY_NOTIFY_URL = 'http://127.0.0.1:8000/result'
# # 设置回调通知地址(GET)
# ALIPAY_RETURN_URL = 'http://127.0.0.1:8000/result'
pay.py
from alipay.aop.api.AlipayClientConfig import AlipayClientConfig
from alipay.aop.api.DefaultAlipayClient import DefaultAlipayClient
from alipay.aop.api.domain.AlipayTradePagePayModel import AlipayTradePagePayModel
from alipay.aop.api.request.AlipayTradePagePayRequest import AlipayTradePagePayRequest
from libs.ali_pay import settings
from alipay.aop.api.util.SignatureUtils import verify_with_rsa
def get_alipay_url(out_trade_no: str, total_amount: float, subject, body):
"""
获取支付宝支付url
:param out_trade_no: 商品id
:param total_amount: 商品总价格
:param subject: 交易名
:param body: 交易描述
:return: 交易url
"""
"""
设置配置,包括支付宝网关地址、app_id、应用私钥、支付宝公钥等,其他配置值可以查看AlipayClientConfig的定义。
"""
alipay_client_config = AlipayClientConfig()
alipay_client_config.server_url = settings.GATEWAY
alipay_client_config.app_id = settings.APPID
alipay_client_config.app_private_key = settings.APP_PRIVATE_KEY_STRING
alipay_client_config.alipay_public_key = settings.ALIPAY_PUBLIC_KEY_STRING
# 客户端对象
client = DefaultAlipayClient(alipay_client_config=alipay_client_config)
# 对照接口文档,构造请求对象
model = AlipayTradePagePayModel()
# 商品编号
model.out_trade_no = out_trade_no
# 总价格
model.total_amount = total_amount
# 商品名
model.subject = subject
# 商品描述
model.body = body
# 签约
model.product_code = "FAST_INSTANT_TRADE_PAY"
# 创建请求对象
request = AlipayTradePagePayRequest(biz_model=model)
# 得到构造的请求,如果http_method是GET,则是一个带完成请求参数的url,如果http_method是POST,则是一段HTML表单片段
request.notify_url = settings.ALIPAY_NOTIFY_URL # 设置回调通知地址(POST)
request.return_url = settings.ALIPAY_RETURN_URL # 设置回调通知地址(GET)
response = client.page_execute(request, http_method='GET')
return response
def check_pay(params): # 定义检查支付结果的函数
sign = params.pop('sign', None) # 取出签名
params.pop('sign_type') # 取出签名类型
params = sorted(params.items(), key=lambda e: e[0], reverse=False) # 取出字典元素按key的字母升序排序形成列表
message = "&".join(u"{}={}".format(k, v) for k, v in params).encode() # 将列表转为二进制参数字符串
try:
status = verify_with_rsa(settings.ALIPAY_PUBLIC_KEY_STRING.encode('utf-8').decode('utf-8'), message,
sign)
# 验证签名并获取结果
return status # 返回验证结果
except: # 如果验证失败,返回假值。
return False
腾讯短信
conf.py
# 短信应用 SDK AppID
appid =
# 短信应用 SDK AppKey
appkey = ""
# 带有超时时间的模板
template_id =
# 签名
sms_sign = ""
secret_id = ''
secret_key = ''
region = ''
# 对象存储
CORS_CONFIG = {
'CORSRule': [
{
'AllowedOrigin': "*",
'AllowedMethod': ['GET', 'POST', "PUT", 'DELETE', 'HEAD'],
'AllowedHeader': '*',
'ExposeHeader': '*',
'MaxAgeSeconds': 500
}
]
}
cos.py
from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from libs.tencent import conf
from sts.sts import Sts
from json import dumps
region = 'ap-nanjing'
config = CosConfig(Region=conf.region, SecretId=conf.secret_id, SecretKey=conf.secret_key)
client = CosS3Client(config)
def upload_file(bucket, key, body):
response = client.upload_file_from_buffer(
Bucket=bucket,
Key=key,
Body=body
)
return response
def create_bucket(bucket):
client.create_bucket(Bucket=bucket, ACL='public-read')
client.put_bucket_cors(Bucket=bucket, CORSConfiguration=conf.CORS_CONFIG)
def delete_file(bucket, key):
return client.delete_object(Bucket=bucket, Key=key)
def delete_files(bucket, keys):
objects = {
"Quiet": "true",
"Object": keys
}
return client.delete_objects(Bucket=bucket, Delete=objects)
def credentials(bucket):
credentials_config = {
'url': 'https://sts.tencentcloudapi.com/',
# 域名,非必须,默认为 sts.tencentcloudapi.com
'domain': 'sts.tencentcloudapi.com',
# 临时密钥有效时长,单位是秒
'duration_seconds': 300,
'secret_id': conf.secret_id,
# 固定密钥
'secret_key': conf.secret_key,
# 设置网络代理
# 'proxy': {
# 'http': 'xx',
# 'https': 'xx'
# },
# 换成你的 bucket
'bucket': bucket,
# 换成 bucket 所在地区
'region': conf.region,
# 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径
# 例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
'allow_prefix': '*',
# 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
'allow_actions': [
# 简单上传
'name/cos:PutObject',
'name/cos:PostObject',
# 分片上传
# 'name/cos:InitiateMultipartUpload',
# 'name/cos:ListMultipartUploads',
# 'name/cos:ListParts',
# 'name/cos:UploadPart',
# 'name/cos:CompleteMultipartUpload'
],
}
sts = Sts(credentials_config)
response = sts.get_credential()
return dict(response)
def auth(bucket, key):
return client.head_object(Bucket=bucket, Key=key)
def delete_bucket(bucket):
contents = client.list_objects(Bucket=bucket).get('Contents')
while contents:
objects = {
"Quiet": "true",
'Object': [{'Key': item['Key']} for item in contents]
}
client.delete_objects(Bucket=bucket, Delete=objects)
contents = client.list_objects(Bucket=bucket).get('Contents')
# 找碎片删除
uploads = client.list_multipart_uploads(Bucket=bucket).get('Upload')
while uploads:
for item in uploads:
client.abort_multipart_upload(Bucket=bucket, Key=item['Key'], UploadId=item['UploadId'])
uploads = client.list_multipart_uploads(Bucket=bucket).get('Upload')
client.delete_bucket(Bucket=bucket)
send.py
from qcloudsms_py import SmsSingleSender
from libs.tencent import conf
def send_message(phone, code):
ssender = SmsSingleSender(conf.appid, conf.appkey)
params = [code, '2']
result = ssender.send_with_param(86, phone, conf.template_id, params, sign=conf.sms_sign, extend="", ext="")
if not result.get('result'):
return True
return False
支付后端处理样例
# 同步通知会在支付完成后,跳转到回调通知地址页面,并以GET方式传递参数。
# 异步通知会在支付完成后,向回调通知地址发出POST请求,同时传递参数字典。
@csrf_exempt
def pay_result(request): # 定义处理回调通知的函数
if request.method == 'GET':
params = request.GET.dict() # 获取参数字典
out_trade_no = params.get('out_trade_no')
if check_pay(params): # 调用检查支付结果的函数
'''
此处编写支付成功后的业务逻辑
'''
return render(request, 'pay_success.html', {'out_trade_no': out_trade_no})
else:
'''
此处编写支付失败后的业务逻辑
'''
return render(request, 'pay_failed.html', {'out_trade_no': out_trade_no})
if request.method == 'POST':
params = request.POST.dict() # 获取参数字典
if check_pay(params): # 调用检查支付结果的函数
'''
此处编写支付成功后的业务逻辑
订单表加数据,用户权限升级
'''
body = params.get('body').split('|')
trans_obj = models.Transaction.objects.filter(user_id=int(body[0]), order=body[-1]).first()
trans_obj.status = 2
trans_obj.save()
return HttpResponse('success') # 返回成功信息到支付宝服务器,必须返回success
django多数据库
关系数据库
定义你的数据库(官方文档删减)
-
在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')