python中bottle使用实例代码

模仿学习同事的代码来写的,主要是搞懂python中如何来组织包,如何调用包,如何读取配置文件,连接数据库,设置路由,路由分组。(注:使用的是python3.6)

整体目录设计如下:

根据调用层级从上往下来说:

首先项目根目录下的main.py是整个程序的入口,主要作用启动http服务器,调用分组路由。

main.py

import bottle
from confg.conf import CONF
from api.user import User

db_url = CONF.db.url

default_app = bottle.default_app()

#相当于分组路由
default_app.mount("/user", User(db_url, "").app)

app = default_app

if __name__ == '__main__':
    bottle.run(app=app,
               host="localhost",
               port="8000")

接着是controller层,就是api目录。api目录包括service文件夹和api下的文件。(注:一般来说controller层,service层是同级的,本项目其实api下的非service文件都是属于controller层,所以还是同一层的,因为要遵守调用顺序,不然可能会发生循环调用)。

/api/user.py文件

import logging

from bottle import request
#db数据库引擎
from common.base import DB
#调用service层
from api.service.user import UserService

logger = logging.getLogger("arview")

class User(DB, UserService):

    def __init__(self, *args, **kwargs):
        print(">>> User init begin")
        logging.debug('>>> User init begin')
        super(User, self).__init__(*args, **kwargs)
        self.dispatch()
        logger.debug('>>> User init end')

    def create(self, db=None):
        create_body = request.json
        create_data = self.create_user(create_body, db)
        return create_data

    def delete(self, db=None):
        delete_body = request.json
        delete_data = self.delete_user(delete_body, db)
        return delete_data

    def list(self, db=None):

        list_data = self.list_user(db)
        return list_data
    #相当于分组路由
    def dispatch(self):
        self.app.route('/listUser', method='post')(self.list)
        self.app.route('/createUser', method='post')(self.create)
        self.app.route('/deleteUser', method='post')(self.delete)

/service/user.py

import time
#model层
from db.models.user import UserModel

class UserService(object):
    def list_user(self, db):
        user_info_list = db.query(UserModel).all()
        for item in user_info_list:
            print(item.username)
        return user_info_list

    def create_user(self, create_body, db):
        user_model = UserModel(
            username=create_body.get("username"),
            password=create_body.get("password"),
            role=create_body.get("role"),
            create_time=time.time()
        )
        db.add(user_model)
        db.commit()
        return "success"

    def delete_user(self, delete_body, db):
        db.query(UserModel).filter(UserModel.id == (delete_body["id"])).delete()
        db.commit()
        return delete_body

然后是dao层也就是数据库操作层(但是明显虽然有dao层但是数据库操作的逻辑已经在service层里了)

最后是读取配置文件和创建数据库引擎。

读取配置文件使用的包是oslo_config。

conf.py

# coding:utf8
# from __future__ import print_function
from oslo_config import cfg

DEFAULT_ARVIEW_DB_NAME = 'ginblog'
DEFAULT_ARVIEW_DB_USER = 'root'
DEFAULT_ARVIEW_DB_USER_PASSWORD = '33demysql'
DEFAULT_ARVIEW_DB_HOST = '81.68.179.136'
DEFAULT_ARVIEW_DB_PORT = 3306
DEFAULT_ARVIEW_DB_URL_TEMPLATE = 'mysql+mysqlconnector://{}:{}@' \
                                 '{}:{}/{}?charset=utf8'
DEFAULT_ARVIEW_DB_URL = DEFAULT_ARVIEW_DB_URL_TEMPLATE.format(
    DEFAULT_ARVIEW_DB_USER,
    DEFAULT_ARVIEW_DB_USER_PASSWORD,
    DEFAULT_ARVIEW_DB_HOST,
    DEFAULT_ARVIEW_DB_PORT,
    DEFAULT_ARVIEW_DB_NAME)

# 声明参数选项
opt_group = cfg.OptGroup('keystone_authtoken')
mysql_opt_group = cfg.OptGroup('db')

auth_opts = [
    cfg.StrOpt('memcached_servers',
               default='localhost:11211',
               choices=("localhost:11211", "0.0.0.0:11211"),
               help=('localhost local', '0.0.0.0 So listen')
               ),

    cfg.StrOpt('signing_dir',
               default='/var/cache/cinder',
               choices=("/var/cache/cinder", "/var/cache/cinder"),
               ),
]

# mysql
mysql_opts = [
    cfg.StrOpt('url', default=DEFAULT_ARVIEW_DB_URL),
    cfg.StrOpt('Db', default='3mysql'),
    cfg.StrOpt('DbHost', default='381.68.179.136'),
    cfg.StrOpt('DbPort', default='33306'),
    cfg.StrOpt('DbUser', default='3DbUser'),
    cfg.StrOpt('DbPassWord', default='3DbPassWord'),
    cfg.StrOpt('DbName', default='3DbName'),
    cfg.BoolOpt('create', default=False),
    cfg.BoolOpt('commit', default=True),
    cfg.BoolOpt('echo', default=True, help='是否显示回显'),
    cfg.BoolOpt('echo_pool', default=False, help='数据库连接池是否记录 checkouts/checkins操作'),
    cfg.IntOpt('pool_size', default=1000, help='数据库连接池中保持打开的连接数量'),
    cfg.IntOpt('pool_recycle', default=600, help='数据库连接池在连接被创建多久(单位秒)以后回收连接')
]

token_opts = [
    cfg.StrOpt('project_domain_name'),
    cfg.StrOpt('project_name'),
]

CINDER_OPTS = (auth_opts +
               token_opts)
MYSQLCINDER_OPTS = (mysql_opts)

# 注册参数选项
CONF = cfg.CONF
# 注册组
CONF.register_group(opt_group)
CONF.register_group(mysql_opt_group)

# 将各个选项注册进组里
CONF.register_opts(CINDER_OPTS, group=opt_group)
CONF.register_opts(MYSQLCINDER_OPTS, group=mysql_opt_group)

if __name__ == "__main__":
    # 要读取哪个配置文件
    CONF(default_config_files=['cinder.conf'])
    print('mysql Db配置组为%s' % (CONF.db.Db))
    print('mysql DbHost%s' % (CONF.db.DbHost))
    print('mysql DbPort配置组为%s' % (CONF.db.DbPort))
    print('mysql DbUser%s' % (CONF.db.DbUser))

配置文件cinder.conf

[db]
Db = mysql
DbHost = 81.68.179.136
DbPort = 3306
DbUser = root
DbPassWord = 33demysql
DbName = ginblog
create = false
commit = true
echo = false
echo_pool = false
pool_size = 1000
pool_recycle =600

它的使用方法是,先声明参数选项就是(相当于声明组)

mysql_opt_group = cfg.OptGroup('db'),

然后声明组内的选项,

mysql_opts = [
    cfg.StrOpt('url', default=DEFAULT_ARVIEW_DB_URL),
    cfg.StrOpt('Db', default='3mysql'),
    cfg.StrOpt('DbHost', default='381.68.179.136'),
    cfg.StrOpt('DbPort', default='33306'),
    cfg.StrOpt('DbUser', default='3DbUser'),
    cfg.StrOpt('DbPassWord', default='3DbPassWord'),
    cfg.StrOpt('DbName', default='3DbName'),
    cfg.BoolOpt('create', default=False),
    cfg.BoolOpt('commit', default=True),
    cfg.BoolOpt('echo', default=True, help='是否显示回显'),
    cfg.BoolOpt('echo_pool', default=False, help='数据库连接池是否记录 checkouts/checkins操作'),
    cfg.IntOpt('pool_size', default=1000, help='数据库连接池中保持打开的连接数量'),
    cfg.IntOpt('pool_recycle', default=600, help='数据库连接池在连接被创建多久(单位秒)以后回收连接')
]

拼接选项

MYSQLCINDER_OPTS = (mysql_opts)

接着注册组,

CONF.register_group(mysql_opt_group)

最后将选项注册进组。

CONF.register_opts(MYSQLCINDER_OPTS, group=mysql_opt_group)

当然最重要的注册参数选项,我的理解就是暴露句柄。

# 注册参数选项
CONF = cfg.CONF

然后创建数据库引擎

common/utils/sqlalchemy_util.py

import logging
from json import loads as json_loads

from sqlalchemy.engine import create_engine
from sqlalchemy.pool import QueuePool
from confg import CONF

SQLALCHEMY_ENGINE_CONTAINER = {}

logger = logging.getLogger("arview")

def json_deserializer(s, **kw):
    if isinstance(s, bytes):
        return json_loads(s.decode('utf-8'), **kw)

    else:
        return json_loads(s, **kw)

def get_sqlalchemy_engine(db_url):
    if db_url not in SQLALCHEMY_ENGINE_CONTAINER:
        engine = create_engine(db_url, echo=CONF.db.echo,
                               # pool_pre_ping如果值为True,那么每次从连接池中拿连接的时候,都会向数据库发送一个类似
                               # select 1的测试查询语句来判断服务器是否正常运行。当该连接出现disconnect的情况时,
                               # 该连接连同pool中的其它连接都会被回收
                               pool_pre_ping=True,
                               echo_pool=CONF.db.echo_pool,
                               pool_size=CONF.db.pool_size,
                               pool_recycle=CONF.db.pool_recycle,
                               json_deserializer=json_deserializer,
                               poolclass=QueuePool)
        logger.info('Create sqlalchemy engine %s', engine)
        SQLALCHEMY_ENGINE_CONTAINER[db_url] = engine

    return SQLALCHEMY_ENGINE_CONTAINER[db_url]

这里引用配置文件的数据,直接引入CONF

from confg import CONF

然后使用

CONF.db.echo_pool

创建句柄,

与我之前使用的方法不同的是,这里的数据库引擎不需要在使用的地方引入了,会在main里注册路由分组时,通过plugin插件自动将数据库引擎导入。这也是我有点搞不懂的地方,虽然更方便,但是不知道就很难知道了,问了同事才知道是怎么回事。

bottle源码

def install(self, plugin):
    ''' Add a plugin to the list of plugins and prepare it for being
        applied to all routes of this application. A plugin may be a simple
        decorator or an object that implements the :class:`Plugin` API.
    '''

plugin就是相当与golang的中间件,不过作用范围是全部路由。

这里创建数据库句柄并使用是一个比较绕的过程。总体思路:

1.写一个bottle plugin,创建数据库句柄,然后install安装这个plugin。就可以在所有的路由中自动引入这个插件(就是不用在包里在导入db句柄了,bottle会自动导入)。

/common/base.py 创建plugin并安装

import logging
from bottle import Bottle
from confg.conf import CONF
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from db.models.base import Base as ApiModelBase
from common.utils.sqlalchemy_util import get_sqlalchemy_engine
from bottle_sqlalchemy import SQLAlchemyPlugin

logger = logging.getLogger("arview")
base = ApiModelBase  # sqlalchemy orm base class

class Plugins:
    SQLALCHEMY_PLUGIN = None  # sqlalchemy plugin, global only one instance
    APSCHEDULER_PLUGIN = None  # apsechduler plugin. global only one instance

class Base(object):
    def __init__(self, *args, **kwargs):
        logger.debug('>>>> Base init begin')
        self.app = Bottle()
        # self.app.install(SwaggerPlugin(self._type))

        logger.debug('>>>> Base init end')

class DB(Base):
    def __init__(self, db_url, create=None, commit=None, *args, **kwargs):
        print('db_url:', db_url)
        super(DB, self).__init__(*args, **kwargs)

        if create is None:
            create = CONF.db.create
        if commit is None:
            commit = CONF.db.commit

        if Plugins.SQLALCHEMY_PLUGIN is None:
            Plugins.SQLALCHEMY_PLUGIN = _create_sqlalchemy_plugin(db_url, create=create, commit=commit)
        self.app.install(Plugins.SQLALCHEMY_PLUGIN)
        logger.debug("Install plugin: sqlalchemy.")
        # if CONF.api.enable_request_interval_plugin:
        #     self.app.install(RequestTimeIntervalPlugin())
        logger.debug('>>>> DB init end')

class CommonBase(object):
    def __init__(self):
        self._db = None

    @property
    def db(self):
        if not self._db:
            DBURL = "mysql+mysqlconnector://{}:{}@{}:{}/{}?charset=utf8".format(CONF.mysql.DbUser,
                                                                                CONF.mysql.DbPassWord,
                                                                                CONF.mysql.DbHost,
                                                                                CONF.mysql.DbPort,
                                                                                CONF.mysql.DbName)
            engine = create_engine(DBURL, echo=False)
            self._db = sessionmaker()(bind=engine)
        return self._db

    @db.deleter
    def db(self):
        if self._db:
            self._db.commit()
            self._db.close()
            self._db = None

def _create_sqlalchemy_plugin(db_url, create, commit):
    """
    创建sqlalchemy插件
    :param db_url:
    :param echo:
    :param create:
    :param commit:
    :return:
    """

    logger.debug('>>>> create sqlalchemy plugin begin')
    engine = get_sqlalchemy_engine(db_url)
    plugin = SQLAlchemyPlugin(engine, metadata=ApiModelBase.metadata, create=create, commit=commit, use_kwargs=True)
    logger.debug('>>>> create sqlalchemy plugin %s' % plugin)
    return plugin

最后使用

/api/user.py

import logging

from bottle import request

from common.base import DB
from api.service.user import UserService

logger = logging.getLogger("arview")

class User(DB, UserService):

    def __init__(self, *args, **kwargs):
        print(">>> User init begin")
        logging.debug('>>> User init begin')
        super(User, self).__init__(*args, **kwargs)
        self.dispatch()
        logger.debug('>>> User init end')

    def create(self, db=None):
        create_body = request.json
        create_data = self.create_user(create_body, db)
        return create_data

    def delete(self, db=None):
        delete_body = request.json
        delete_data = self.delete_user(delete_body, db)
        return delete_data

    def list(self, db=None):

        list_data = self.list_user(db)
        return list_data

    def dispatch(self):
        self.app.route('/listUser', method='post')(self.list)
        self.app.route('/createUser', method='post')(self.create)
        self.app.route('/deleteUser', method='post')(self.delete)

这里的db就不需要导入了,可以直接使用。

db层
主要是模型层 /db/model/user.py

from sqlalchemy import Column, String, Enum, TIMESTAMP, Boolean, Integer, BIGINT, DATETIME

from db.models.base import Base

class UserModel(Base):
    __tablename__ = "user"
    id = Column("id", BIGINT, primary_key=True, comment="用户id")
    created_at = Column("created_at", DATETIME, comment="创建时间")
    updated_at = Column("updated_at", DATETIME, comment="更新时间")
    deleted_at = Column("deleted_at", DATETIME, comment="删除时间")
    username = Column("username", String(20), comment="用户名")
    password = Column("password", String(500), comment="密码")
    role = Column("role", BIGINT, comment="角色")

    def __init__(self, id, created_at, updated_at, deleted_at, username, password, role):
        self.id = id
        self.created_at = created_at
        self.updated_at = updated_at
        self.deleted_at = deleted_at
        self.username = username
        self.password = password
        self.role = role

/db/model/base.py

from datetime import datetime

from sqlalchemy import Column, TIMESTAMP
from sqlalchemy.ext.declarative import declarative_base

# sqlalchemy orm base class
Base = declarative_base()

class TimestampMixin(object):
    """为ORM提供时间戳基类"""

    created_at = Column('created_at', TIMESTAMP(True), default=datetime.now,
                        comment=u"创建时间")
    updated_at = Column('updated_at', TIMESTAMP(True), default=datetime.now,
                        onupdate=datetime.now, comment=u"更新时间")

到此这篇关于python bottle使用实例的文章就介绍到这了,更多相关python bottle使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Python使用Bottle来提供一个简单的web服务

    介绍 今天有个不正经的需求,就是要快速做一个restful api的性能测试,要求测试在海量作业数据的情况下客户端分页获取所有作业的性能.因为只是一个小的的测试工作,所以就想到了Bottle框架作为Web服务器,这里就简单说说怎样使用Bottle框架. 安装 pip install bottle 启动服务 运行下面的python脚本即可启动一个Web服务. from bottle import route, run, request @route('/hello') def hello(): r

  • Python的Bottle框架的一些使用技巧介绍

    之前对bottle做过不少的介绍,也写过一些文章来说明bottle的缺点,最近发现其实之前有些地方说的不太公平,所以趁此机会也来更正一下. bottle是支持类似flask url_for的语法的,具体使用方法在下文介绍     bottle的request.query之类的参数默认是str类型,也是有原因的,比如我在给google做代理的时候,编码就不一定是utf8的,如果强制转化utf8就会报错     之前的bug也得到了修正,比如mount('/x',app)之后,/x/和/x都可以访问

  • 在Python的Bottle框架中使用微信API的示例

    微信这个东西估计宅男没几个不熟悉的吧,微信经过这么两年多的发展终于向开放平台跨出了友好的一步.蛋疼的以为微信会出一个详细的api等接口,兴奋不已的去申请了微信公共平台,然后开始找各种api的位置-- 花费了近一个小时,依然没找到-- 最后动用Google大杀器,终于找到了这么个链接.我了个去的,没比这还简单的api文档了吧. 最让人无法理解的是:居然没有本地开发环境支持,每次都要放在生产环境去调试. 最让人欣慰的是:就那么俩方法,生产环境调试几次也就完事了. Python(bottle)版代码如

  • 使用Python的Bottle框架写一个简单的服务接口的示例

    是不是有这么一个场景,对外提供一堆数据或者是要返回给用户一个结果.但是不想把内部的一些数据和逻辑暴露给对方...简单点来说,就是想以服务的方式对外提供一个接口.对于这种有很多处理方式,RPC,搭建一个web服务啥的....但是这些毕竟都太重量级了,操作起来很麻烦.我这里给出的一种非常easy的方式来处理.使用bottle解决问题. 需求: 检查一个zookeeper服务中的某些节点是否存在,如果存在返回OK,不存在则给出不存的节点信息.要求返回的信息是和pyunit的结果信息一致. 实现环境:

  • Python轻量级web框架bottle使用方法解析

    Bottle是一个轻量级的Web框架,此框架只由一个 bottle.py 文件构成,不依赖任何第三方模块. #!/usr/bin/env python # -*- coding:utf-8 -*- from bottle import template, Bottle app = Bottle() @app.route('/say') def index(): return "Hello World" # return template('<b>Hello {{name}}

  • python中bottle使用实例代码

    模仿学习同事的代码来写的,主要是搞懂python中如何来组织包,如何调用包,如何读取配置文件,连接数据库,设置路由,路由分组.(注:使用的是python3.6) 整体目录设计如下: 根据调用层级从上往下来说: 首先项目根目录下的main.py是整个程序的入口,主要作用启动http服务器,调用分组路由. main.py import bottle from confg.conf import CONF from api.user import User db_url = CONF.db.url d

  • python的re正则表达式实例代码

    本文研究的主要是python的re正则表达式的相关内容,具体如下. 概念:正则表达式(通项公式)是用来简洁表达一组字符串的表达式.优势是简洁,一行胜千言. 应用:字符串匹配. 实例代码: CODEC = 'UTF-8' #encoding:utf-8 import re p=re.compile("ab") str = "abfffa" #match必须匹配首字母 if p.match(str): print p.match(str).group() #match必

  • Python编程求质数实例代码

    本文研究的主要是Python编程求质数实例,选取了几个数进行了测试,具体如下. 定义:质数又称素数.一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数:否则称为合数. 我们知道自然数(除了0和1以外)都可以写成几个质数相乘再乘以一的格式,所以我们可以用以个数去试一试看看它能否将小于它的质数整除. 首先我们创建一个空的list,然后我们知道2是最小的质数,于是我们把2添加进这个空白的list,之后我们开始循环,第一个数从3开始,用3除以小于3的质数,没有小于它的质数能被它整除,

  • python模块之paramiko实例代码

    本文研究的主要是python模块之paramiko的相关用法,具体实现代码如下,一起来看看. paramiko模块提供了ssh及sft进行远程登录服务器执行命令和上传下载文件的功能.这是一个第三方的软件包,使用之前需要安装. 1 基于用户名和密码的 sshclient 方式登录 # 建立一个sshclient对象 ssh = paramiko.SSHClient() # 允许将信任的主机自动加入到host_allow 列表,此方法必须放在connect方法的前面 ssh.set_missing_

  • 简单的python协同过滤程序实例代码

    本文研究的主要是python协同过滤程序的相关内容,具体介绍如下. 关于协同过滤的一个最经典的例子就是看电影,有时候不知道哪一部电影是我们喜欢的或者评分比较高的,那么通常的做法就是问问周围的朋友,看看最近有什么好的电影推荐.在问的时候,都习惯于问跟自己口味差不多的朋友,这就是协同过滤的核心思想. 这个程序完全是为了应付大数据分析与计算的课程作业所写的一个小程序,先上程序,一共55行.不在意细节的话,55行的程序已经表现出了协同过滤的特性了.就是对每一个用户找4个最接近的用户,然后进行推荐,在选择

  • python实现Adapter模式实例代码

    本文研究的主要是python实现Adapter模式的相关内容,具体实现代码如下. Adapter模式有两种实现方式一种是类方式. #理解 #就是电源适配器的原理吧,将本来不兼容的接口类能够工作 #这个是类实现方式 #例子 #假如一个插座类输出脚是3脚的,而台灯需要的是两脚插座,现在就需要一个Adapter实现适配插座 #Adaptee class socket(object): def Trigle(self): print 'power supply' #target class tableL

  • python实现Decorator模式实例代码

    本文研究的主要是python实现Decorator模式,具体介绍如下. 一般来说,装饰器是一个函数,接受一个函数(或者类)作为参数,返回值也是也是一个函数(或者类).首先来看一个简单的例子: # -*- coding: utf-8 -*- def log_cost_time(func): def wrapped(*args, **kwargs): import time begin = time.time() try: return func(*args, **kwargs) finally:

  • Python下载网络小说实例代码

    看网络小说一般会攒上一波,然后导入Kindle里面去看,但是攒的多了,机械的Ctrl+C和Ctrl+V实在是OUT,所以就出现了此文. 其实Python我也是小白,用它的目的主要是它强大文本处理能力和网络支持,以及许多好用的库,不需要自己造轮子.而且真心比C方便啊(真是用了才知道) 分析要获取的网页 我要获取的主要是3个东西: 文章的标题.<div id="title">正文 第一章 北灵院</div> 文章正文内容.<div id="conte

  • Python之reload流程实例代码解析

    本文研究的主要是Python之reload流程的相关内容,具体如下. 在Python中,reload() 用于重新载入之前载入的模块. reload() 函数语法: reload(module) Python中 import 只执行一次,后续的 import 仅仅在 sys.modules 中查找是否存在对应的模块对象,而对于源文件进行修改后想要立即重新导入该文件而不想整体重新执行程序时, reload 就在该处派上用途了.在实际中,测试代码修改结果,或者对于不能停止的服务需要动态改变运行行为

  • Python中协程用法代码详解

    本文研究的主要是python中协程的相关问题,具体介绍如下. Num01–>协程的定义 协程,又称微线程,纤程.英文名Coroutine. 首先我们得知道协程是啥?协程其实可以认为是比线程更小的执行单元. 为啥说他是一个执行单元,因为他自带CPU上下文.这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程. 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的. Num02–>协程和线程的差异 那么这个过程看起来和线程差不多.其实不然, 线程切换从系统层面远不止保存和恢复 CP

随机推荐