Sanic教程: 5.数据库使用

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
云数据库 Tair(兼容Redis),内存型 2GB
简介: Sanic教程: 5.数据库使用

介绍中说的很明白, Sanic 是一个可以使用 async/await 语法编写项目的异步非阻塞框架,既然是异步框架,那么在使用过程中用到的第三方包也最好是异步的,比如http请求,最好就使用 aihttp而非 requests,对于数据库的连接,也是同样如此,下面我将用代码的形式来说明下如何在Sanic中连接数据库。


操作Mysql


对于mysql数据库的异步操作,我只在一些脚本中用过,用的是aiomysql,其中官方文档中讲得很清楚,也支持结合 sqlalchemy编写 ORM,然后aiomysql提供了自己编写的异步引擎。

from aiomysql.sa import create_engine
# 这个才是关键

下面我编写一个具体的例子来用异步语句操作下数据库,首先建立如下目录:

aio_mysql
├── demo.py
├── model.py
└── requirements.txt

建立表:

create database test_mysql;
CREATE TABLE user
(
 id        INT AUTO_INCREMENT
   PRIMARY KEY,
 user_name VARCHAR(16) NOT NULL,
 pwd       VARCHAR(32) NOT NULL,
 real_name VARCHAR(6)  NOT NULL
);

一切准备就绪,下面编写代码:

# script: model.py
import sqlalchemy as sa
metadata = sa.MetaData()
user = sa.Table(
   'user',
   metadata,
   sa.Column('id', sa.Integer, autoincrement=True, primary_key=True),
   sa.Column('user_name', sa.String(16), nullable=False),
   sa.Column('pwd', sa.String(32), nullable=False),
   sa.Column('real_name', sa.String(6), nullable=False),
)
# script: demo.py
import asyncio
from aiomysql.sa import create_engine
from model import user,metadata
async def go(loop):
   """
   aiomysql项目地址:https://github.com/aio-libs/aiomysql
   :param loop:
   :return:
   """
   engine = await create_engine(user='root', db='test_mysql',
                                host='127.0.0.1', password='123456', loop=loop)
   async with engine.acquire() as conn:
       await conn.execute(user.insert().values(user_name='user_name01', pwd='123456', real_name='real_name01'))
       await conn.execute('commit')
       async for row in conn.execute(user.select()):
           print(row.user_name, row.pwd)
   engine.close()
   await engine.wait_closed()
loop = asyncio.get_event_loop()
loop.run_until_complete(go(loop))

运行 python demo.py,会看到如下输出:

user_name01 123456

很简单吧,具体示例见aio_mysql,如果你比较喜欢类似SQLAlchemy的操作方式,这里推荐一个异步ORM,gino。


操作MongoDB


我业余写的一个项目,基本用的就是 MongoDB来储存数据,对于异步操作 MongoDB,目前Python主要用的是motor,使用起来依旧很简单,但是结合具体功能,就有不同的需求,最后就会形成各种各样的连接方案,这里我主要分享下自己是如何使用的,目录如下所示:

aio_mongo
├── demo.py
└── requirements.txt

MongoDB是一个基于分布式文件存储的数据库,它介于关系数据库和非关系数据库之间,所以它使用起来也是比较灵活的,打开 demo.py

#!/usr/bin/env python
import os
from functools import wraps
from motor.motor_asyncio import AsyncIOMotorClient
MONGODB = dict(
   MONGO_HOST=os.getenv('MONGO_HOST', ""),
   MONGO_PORT=os.getenv('MONGO_PORT', 27017),
   MONGO_USERNAME=os.getenv('MONGO_USERNAME', ""),
   MONGO_PASSWORD=os.getenv('MONGO_PASSWORD', ""),
   DATABASE='test_mongodb',
)
class MotorBaseOld:
   """
   默认实现了一个db只创建一次,缺点是更换集合麻烦
   """
   _db = None
   MONGODB = MONGODB
   def client(self, db):
       # motor
       self.motor_uri = 'mongodb://{account}{host}:{port}/{database}'.format(
           account='{username}:{password}@'.format(
               username=self.MONGODB['MONGO_USERNAME'],
               password=self.MONGODB['MONGO_PASSWORD']) if self.MONGODB['MONGO_USERNAME'] else '',
           host=self.MONGODB['MONGO_HOST'] if self.MONGODB['MONGO_HOST'] else 'localhost',
           port=self.MONGODB['MONGO_PORT'] if self.MONGODB['MONGO_PORT'] else 27017,
           database=db)
       return AsyncIOMotorClient(self.motor_uri)
   @property
   def db(self):
       if self._db is None:
           self._db = self.client(self.MONGODB['DATABASE'])[self.MONGODB['DATABASE']]
       return self._db

我最开始,使用的是这种方式来连接 MongoDB,上面代码保证了集合中的db被_db维护,保证只会创建一次,如果你项目中不会随意更改集合的话,也没什么大问题,如果不是,我推荐使用下面这样的连接方式,可以自由地更换集合与db:

def singleton(cls):
   """
   用装饰器实现的实例 不明白装饰器可见附录 装饰器:https://github.com/howie6879/Sanic-For-Pythoneer/blob/master/docs/part2/%E9%99%84%E5%BD%95%EF%BC%9A%E5%85%B3%E4%BA%8E%E8%A3%85%E9%A5%B0%E5%99%A8.md
   :param cls: cls
   :return: instance
   """
   _instances = {}
   @wraps(cls)
   def instance(*args, **kw):
       if cls not in _instances:
           _instances[cls] = cls(*args, **kw)
       return _instances[cls]
   return instance
@singleton
class MotorBase:
   """
   更改mongodb连接方式 单例模式下支持多库操作
   About motor's doc: https://github.com/mongodb/motor
   """
   _db = {}
   _collection = {}
   MONGODB = MONGODB
   def __init__(self):
       self.motor_uri = ''
   def client(self, db):
       # motor
       self.motor_uri = 'mongodb://{account}{host}:{port}/{database}'.format(
           account='{username}:{password}@'.format(
               username=self.MONGODB['MONGO_USERNAME'],
               password=self.MONGODB['MONGO_PASSWORD']) if self.MONGODB['MONGO_USERNAME'] else '',
           host=self.MONGODB['MONGO_HOST'] if self.MONGODB['MONGO_HOST'] else 'localhost',
           port=self.MONGODB['MONGO_PORT'] if self.MONGODB['MONGO_PORT'] else 27017,
           database=db)
       return AsyncIOMotorClient(self.motor_uri)
   def get_db(self, db=MONGODB['DATABASE']):
       """
       获取一个db实例
       :param db: database name
       :return: the motor db instance
       """
       if db not in self._db:
           self._db[db] = self.client(db)[db]
       return self._db[db]
   def get_collection(self, db_name, collection):
       """
       获取一个集合实例
       :param db_name: database name
       :param collection: collection name
       :return: the motor collection instance
       """
       collection_key = db_name + collection
       if collection_key not in self._collection:
           self._collection[collection_key] = self.get_db(db_name)[collection]
       return self._collection[collection_key]

为了避免重复创建MotorBase实例,可以实现一个单例模式来保证资源的有效利用,具体代码以及运行demo见aio_mongo


操作Redis


对于Redis的异步操作,我选用的是 asyncio_redis,你大可不必非要使用这个,或许其他的库实现地更好,我只是用这个举个例子,建立如下目录:

aio_redis
├── demo.py
└── requirements.txt

建立一个redis连接池:

#!/usr/bin/env python
import os
import asyncio_redis
REDIS_DICT = dict(
   IS_CACHE=True,
   REDIS_ENDPOINT=os.getenv('REDIS_ENDPOINT', "localhost"),
   REDIS_PORT=os.getenv('REDIS_PORT', 6379),
   REDIS_PASSWORD=os.getenv('REDIS_PASSWORD', None),
   DB=0,
   POOLSIZE=10,
)
class RedisSession:
   """
   建立redis连接池
   """
   _pool = None
   async def get_redis_pool(self):
       if not self._pool:
           self._pool = await asyncio_redis.Pool.create(
               host=str(REDIS_DICT.get('REDIS_ENDPOINT', "localhost")), port=int(REDIS_DICT.get('REDIS_PORT', 6379)),
               poolsize=int(REDIS_DICT.get('POOLSIZE', 10)), password=REDIS_DICT.get('REDIS_PASSWORD', None),
               db=REDIS_DICT.get('DB', None)
           )
       return self._pool

具体见aio_redis,使用起来很简单,不做多叙述。


说明


如果你使用其它类型的数据库,其实使用方式也是类似。 本章代码地址,见demo05

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
存储 SQL 关系型数据库
【入门级教程】MySQL:从零开始的数据库之旅
本教程面向零基础用户,采用通俗易懂的语言和丰富的示例,帮助你快速掌握MySQL的基础知识和操作技巧。内容涵盖SQL语言基础(SELECT、INSERT、UPDATE、DELETE等常用语句)、使用索引提高查询效率、存储过程等。适合学生、开发者及数据库爱好者。
42 0
【入门级教程】MySQL:从零开始的数据库之旅
|
1月前
|
tengine 关系型数据库 MySQL
Tengine、Nginx安装MySQL数据库命令教程
本指南详细介绍了在Linux系统上安装与配置MySQL数据库的步骤。首先通过下载并安装MySQL社区版本,接着启动MySQL服务,使用`systemctl start mysqld.service`命令。若启动失败,可尝试使用`sudo /etc/init.d/mysqld start`。利用`systemctl status mysqld.service`检查MySQL的服务状态,确保其处于运行中。通过日志文件获取初始密码,使用该密码登录数据库,并按要求更改初始密码以增强安全性。随后创建一个名为`tengine`的数据库,最后验证数据库创建是否成功以及完成整个设置流程。
|
2月前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的在线考试系统(含教程&源码&数据库数据)
本文介绍了一个基于Spring Boot和Vue.js实现的在线考试系统。随着在线教育的发展,在线考试系统的重要性日益凸显。该系统不仅能提高教学效率,减轻教师负担,还为学生提供了灵活便捷的考试方式。技术栈包括Spring Boot、Vue.js、Element-UI等,支持多种角色登录,具备考试管理、题库管理、成绩查询等功能。系统采用前后端分离架构,具备高性能和扩展性,未来可进一步优化并引入AI技术提升智能化水平。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的在线考试系统(含教程&源码&数据库数据)
|
2月前
|
Java 关系型数据库 MySQL
毕设项目&课程设计&毕设项目:springboot+jsp实现的房屋租租赁系统(含教程&源码&数据库数据)
本文介绍了一款基于Spring Boot和JSP技术的房屋租赁系统,旨在通过自动化和信息化手段提升房屋管理效率,优化租户体验。系统采用JDK 1.8、Maven 3.6、MySQL 8.0、JSP、Layui和Spring Boot 2.0等技术栈,实现了高效的房源管理和便捷的租户服务。通过该系统,房东可以轻松管理房源,租户可以快速找到合适的住所,双方都能享受数字化带来的便利。未来,系统将持续优化升级,提供更多完善的服务。
毕设项目&课程设计&毕设项目:springboot+jsp实现的房屋租租赁系统(含教程&源码&数据库数据)
|
1月前
|
SQL NoSQL MongoDB
一款基于分布式文件存储的数据库MongoDB的介绍及基本使用教程
一款基于分布式文件存储的数据库MongoDB的介绍及基本使用教程
46 0
|
1月前
|
存储 NoSQL API
.NET NoSQL 嵌入式数据库 LiteDB 使用教程
.NET NoSQL 嵌入式数据库 LiteDB 使用教程~
|
3月前
|
SQL 存储 小程序
【教程】navicat配合HTTP通道远程连接SQLite数据库
本文介绍了如何通过 Navicat Premium 工具配合 n_tunnel_sqlite.php 和 HTTP 通道远程连接服务器上的 SQLite 数据库。SQLite 是一种自给自足的、无服务器的 SQL 数据库引擎,由于其端口未对外开放,直接使用 Navicat 进行远程连接不可行。文章详细记录了使用 HTTP 通道实现远程连接的过程,包括定位本地 `ntunnel_sqlite.php` 文件,将其上传至服务器,并通过 Navicat 配置 HTTP 通道连接 SQLite 数据库的具体步骤。
176 0
【教程】navicat配合HTTP通道远程连接SQLite数据库
|
3月前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的选课管理系统(含教程&源码&数据库数据)
本文介绍了一个基于Spring Boot和Vue.js技术栈的高校选课管理系统的设计与实现。该系统采用前后端分离架构,旨在提高选课效率、优化资源分配及提升用户体验。技术栈包括:后端Spring Boot 2.0、前端Vue 2.0、数据库MySQL 8.0、开发环境JDK 1.8和Maven 3.6等。系统功能覆盖登录、学生信息管理、选课管理、成绩查询等多个方面,并针对学生、教师和管理员提供了不同的操作界面。系统采用了响应式设计,支持多设备访问,并通过Element UI增强了界面的友好性和交互性。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的选课管理系统(含教程&源码&数据库数据)
|
3月前
|
JavaScript Java 关系型数据库
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的考试管理系统(含教程&源码&数据库数据)
在数字化时代背景下,本文详细介绍了如何使用Spring Boot框架结合Vue.js技术栈,实现一个前后端分离的考试管理系统。该系统旨在提升考试管理效率,优化用户体验,确保数据安全及可维护性。技术选型包括:Spring Boot 2.0、Vue.js 2.0、Node.js 12.14.0、MySQL 8.0、Element-UI等。系统功能涵盖登录注册、学员考试(包括查看试卷、答题、成绩查询等)、管理员功能(题库管理、试题管理、试卷管理、系统设置等)。
毕设项目&课程设计&毕设项目:基于springboot+vue实现的前后端分离的考试管理系统(含教程&源码&数据库数据)
|
3月前
|
SQL Shell API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
下一篇
无影云桌面