Cherrypy 在 _cp_dispatch/popargs 中使用插件
Cherrypy use plugin in _cp_dispatch/popargs
我将 cherrypy 与 sqlalchemy 结合使用,以构建无头(仅 CLI 客户端)restful 服务器。
我使用以下 receipe 将 sqlalchemy 绑定到 cherrypy 引擎:https://bitbucket.org/Lawouach/cherrypy-recipes/src/c8290261eefb/web/database/sql_alchemy/
收据略有修改,以便在不存在的情况下构建数据库。
服务器公开了几个 uri,例如客户端、文章、商店...
- GET /clients 获取客户端列表
- GET /clients/1 获取客户端 ID #1
- GET /clients/foo 获取名为 foo
的客户端
- PUT /clients/1 更新客户端#1
- DELETE /client/foo 删除名为 foo 的客户端
...
我打算使用装饰器 popargs 和 _cp_dispatch 以便提前将我的资源名称转换为它们的 ID处理。我使用 cherrypy.dispatch.MethodDispatcher 作为调度程序(所以我可以编写 GET/POST/PUT/DELETE 方法)
我可以通过任何 GET/POST/PUT/DELETE 方法访问该插件,但我无法通过 _cp_dispatch.
访问它
知道如何在进入 GET/POST/PUT/DELETE 方法之前将资源名称转换为它们的 ID 吗?
这是我的问题的重现者
$ tree
.
├── __init__.py
├── models.py
├── my.db
├── root.py
├── saplugin.py
├── saplugin.py.original
└── satool.py
我的 sqlalchemy 模型(我有几个,重现问题只需要一个)
$ cat models.py
# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
这里是服务器的主脚本
$ cat root.py
# -*- coding: utf-8 -*-
import cherrypy
from models import User
from saplugin import SAEnginePlugin
from satool import SATool
def name_to_id(param, table):
if not param.isdigit():
param = cherrypy.request.db.query(table.id).\
filter(table.name == param).one()
return param
class Root(object):
exposed = True
def __init__(self):
self.users = Users()
def GET(self):
# Get the SQLAlchemy session associated
# with this request.
# It'll be released once the request
# processing terminates
return "Hello World"
class Users(object):
exposed = True
@cherrypy.popargs('user_id')
def GET(self, user_id=None, **kwargs):
user_id = name_to_id(user_id, User)
return "I'm resource %s" % user_id
if __name__ == '__main__':
# Register the SQLAlchemy plugin
SAEnginePlugin(cherrypy.engine).subscribe()
# Register the SQLAlchemy tool
cherrypy.tools.db = SATool()
cherrypy.quickstart(Root(), '', {'/': {'tools.db.on': True,
"request.dispatch": cherrypy.dispatch.MethodDispatcher()}})
这是应用于 sqlalchemy 插件 receipe 的修改
$ diff -up saplugin.py.original saplugin.py
--- saplugin.py.original 2015-06-15 18:14:45.469706863 +0200
+++ saplugin.py 2015-06-15 18:14:37.042741785 +0200
@@ -3,6 +3,7 @@ import cherrypy
from cherrypy.process import wspbus, plugins
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
+from models import User, Base
__all__ = ['SAEnginePlugin']
@@ -26,7 +27,16 @@ class SAEnginePlugin(plugins.SimplePlugi
self.sa_engine = create_engine('sqlite:///my.db', echo=False)
self.bus.subscribe("bind-session", self.bind)
self.bus.subscribe("commit-session", self.commit)
-
+ # Creating the database
+ self.bus.log('Creating database')
+ self.bus.subscribe("create-all", self.create_all)
+ try:
+ self.create_all()
+ except Exception as err:
+ logging.error("Can't start")
+ logging.error(err.message)
+ sys.exit(1)
+
def stop(self):
self.bus.log('Stopping down DB access')
self.bus.unsubscribe("bind-session", self.bind)
@@ -59,4 +69,8 @@ class SAEnginePlugin(plugins.SimplePlugi
raise
finally:
self.session.remove()
-
+
+ def create_all(self):
+ """ create database structure """
+ self.bus.log('Creating database')
+ Base.metadata.create_all(self.sa_engine)
这是服务器的日志,当我在 GET 方法的开头转换名称时。
$ python root.py
[15/Jun/2015:18:16:23] ENGINE Listening for SIGHUP.
[15/Jun/2015:18:16:23] ENGINE Listening for SIGTERM.
[15/Jun/2015:18:16:23] ENGINE Listening for SIGUSR1.
[15/Jun/2015:18:16:23] ENGINE Bus STARTING
[15/Jun/2015:18:16:23] ENGINE Starting up DB access
[15/Jun/2015:18:16:23] ENGINE Creating database
[15/Jun/2015:18:16:23] ENGINE Creating database
[15/Jun/2015:18:16:23] ENGINE Started monitor thread 'Autoreloader'.
[15/Jun/2015:18:16:23] ENGINE Started monitor thread '_TimeoutMonitor'.
[15/Jun/2015:18:16:23] ENGINE Serving on 127.0.0.1:8080
[15/Jun/2015:18:16:23] ENGINE Bus STARTED
127.0.0.1 - - [15/Jun/2015:18:16:26] "GET /users/1 HTTP/1.1" 200 14 "" "curl/7.29.0"
127.0.0.1 - - [15/Jun/2015:18:16:28] "GET /users/foo HTTP/1.1" 200 14 "" "curl/7.29.0"
这里是查询之前的日志
$ curl 127.0.0.1:8080/users/1; echo
I'm resource 1
$ curl 127.0.0.1:8080/users/foo; echo
I'm resource 1
这里是数据库的内容
$ sqlite3 my.db
sqlite> .schema
CREATE TABLE users (
id INTEGER NOT NULL,
name VARCHAR,
PRIMARY KEY (id)
);
sqlite> select * from users;
1|foo
2|bar
我不能同时使用装饰器 popargs 和 _cp_dispatch 方法。
我还没有找到如何使用装饰器处理我的 url 段。
当我尝试只使用 _cp_dispatch 方法时,我遇到了以下错误:
File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 628, in respond
self.get_resource(path_info)
File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 744, in get_resource
dispatch(path)
File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 423, in __call__
resource, vpath = self.find_handler(path_info)
File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 311, in find_handler
subnode = dispatch(vpath=iternames)
File "root.py", line 49, in _cp_dispatch
vpath[0] = name_to_id(vpath[0], Users)
File "root.py", line 10, in name_to_id
param = cherrypy.request.db.query(table.id).\
File "/usr/lib/python2.7/site-packages/cherrypy/__init__.py", line 208, in __getattr__
return getattr(child, name)
AttributeError: 'Request' object has no attribute 'db'
这是应用于用户的修改 class:
class Users(object):
# [....]
def _cp_dispatch(self, vpath):
if len(vpath) > 1:
raise
vpath[0] = name_to_id(vpath[0], Users)
return vpath
我正在使用以下版本(我正在 Centos 7 环境中工作,无法使用 pip 中的那些进行更改):
$ rpm -qa | egrep "cherrypy|sqlalchemy"
python-cherrypy-3.2.2-4.el7.noarch
python-sqlalchemy-0.9.7-3.el7.x86_64
非常感谢您的帮助!!!!
中描述了此问题的解决方案
我已将以下工具添加到根脚本中:
class UserManager(cherrypy.Tool):
def __init__(self):
cherrypy.Tool.__init__(self, 'before_handler',
self.load, priority=10)
def load(self, **kwargs):
req = cherrypy.request
# let's assume we have a db session
# attached to the request somehow
db = req.db
# retrieve the user id and remove it
# from the request parameters
if 'user' in req.params:
user = req.params.pop('user')
if not user.isdigit():
req.params['user'] = db.query(User).\
filter(User.name == user).one()
else:
req.params['user'] = db.query(User).\
filter(User.id == user).one()
cherrypy.tools.user = UserManager()
并重写用户 class,如下所示:
@cherrypy.popargs("user")
class Users(object):
exposed = True
@cherrypy.tools.user()
def GET(self, user=None, **kwargs):
if user:
return "I'm resource %s" % user.id
else:
return "All users"
装饰器 @cherrypy.popargs("user") 告诉 cherrypy 获取 url 中的第一段并将其存储到键 "user"。
装饰器 @cherrypy.tools.user() 告诉 cherrypy 在进入 GET 方法之前调用工具用户。
在 UserManager class 的 load 方法中,我将用户 ID 或名称转换为 sqlalchemy 模型。
我将 cherrypy 与 sqlalchemy 结合使用,以构建无头(仅 CLI 客户端)restful 服务器。
我使用以下 receipe 将 sqlalchemy 绑定到 cherrypy 引擎:https://bitbucket.org/Lawouach/cherrypy-recipes/src/c8290261eefb/web/database/sql_alchemy/ 收据略有修改,以便在不存在的情况下构建数据库。
服务器公开了几个 uri,例如客户端、文章、商店...
- GET /clients 获取客户端列表
- GET /clients/1 获取客户端 ID #1
- GET /clients/foo 获取名为 foo 的客户端
- PUT /clients/1 更新客户端#1
- DELETE /client/foo 删除名为 foo 的客户端 ...
我打算使用装饰器 popargs 和 _cp_dispatch 以便提前将我的资源名称转换为它们的 ID处理。我使用 cherrypy.dispatch.MethodDispatcher 作为调度程序(所以我可以编写 GET/POST/PUT/DELETE 方法)
我可以通过任何 GET/POST/PUT/DELETE 方法访问该插件,但我无法通过 _cp_dispatch.
访问它知道如何在进入 GET/POST/PUT/DELETE 方法之前将资源名称转换为它们的 ID 吗?
这是我的问题的重现者
$ tree
.
├── __init__.py
├── models.py
├── my.db
├── root.py
├── saplugin.py
├── saplugin.py.original
└── satool.py
我的 sqlalchemy 模型(我有几个,重现问题只需要一个)
$ cat models.py
# -*- coding: utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
这里是服务器的主脚本
$ cat root.py
# -*- coding: utf-8 -*-
import cherrypy
from models import User
from saplugin import SAEnginePlugin
from satool import SATool
def name_to_id(param, table):
if not param.isdigit():
param = cherrypy.request.db.query(table.id).\
filter(table.name == param).one()
return param
class Root(object):
exposed = True
def __init__(self):
self.users = Users()
def GET(self):
# Get the SQLAlchemy session associated
# with this request.
# It'll be released once the request
# processing terminates
return "Hello World"
class Users(object):
exposed = True
@cherrypy.popargs('user_id')
def GET(self, user_id=None, **kwargs):
user_id = name_to_id(user_id, User)
return "I'm resource %s" % user_id
if __name__ == '__main__':
# Register the SQLAlchemy plugin
SAEnginePlugin(cherrypy.engine).subscribe()
# Register the SQLAlchemy tool
cherrypy.tools.db = SATool()
cherrypy.quickstart(Root(), '', {'/': {'tools.db.on': True,
"request.dispatch": cherrypy.dispatch.MethodDispatcher()}})
这是应用于 sqlalchemy 插件 receipe 的修改
$ diff -up saplugin.py.original saplugin.py
--- saplugin.py.original 2015-06-15 18:14:45.469706863 +0200
+++ saplugin.py 2015-06-15 18:14:37.042741785 +0200
@@ -3,6 +3,7 @@ import cherrypy
from cherrypy.process import wspbus, plugins
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
+from models import User, Base
__all__ = ['SAEnginePlugin']
@@ -26,7 +27,16 @@ class SAEnginePlugin(plugins.SimplePlugi
self.sa_engine = create_engine('sqlite:///my.db', echo=False)
self.bus.subscribe("bind-session", self.bind)
self.bus.subscribe("commit-session", self.commit)
-
+ # Creating the database
+ self.bus.log('Creating database')
+ self.bus.subscribe("create-all", self.create_all)
+ try:
+ self.create_all()
+ except Exception as err:
+ logging.error("Can't start")
+ logging.error(err.message)
+ sys.exit(1)
+
def stop(self):
self.bus.log('Stopping down DB access')
self.bus.unsubscribe("bind-session", self.bind)
@@ -59,4 +69,8 @@ class SAEnginePlugin(plugins.SimplePlugi
raise
finally:
self.session.remove()
-
+
+ def create_all(self):
+ """ create database structure """
+ self.bus.log('Creating database')
+ Base.metadata.create_all(self.sa_engine)
这是服务器的日志,当我在 GET 方法的开头转换名称时。
$ python root.py
[15/Jun/2015:18:16:23] ENGINE Listening for SIGHUP.
[15/Jun/2015:18:16:23] ENGINE Listening for SIGTERM.
[15/Jun/2015:18:16:23] ENGINE Listening for SIGUSR1.
[15/Jun/2015:18:16:23] ENGINE Bus STARTING
[15/Jun/2015:18:16:23] ENGINE Starting up DB access
[15/Jun/2015:18:16:23] ENGINE Creating database
[15/Jun/2015:18:16:23] ENGINE Creating database
[15/Jun/2015:18:16:23] ENGINE Started monitor thread 'Autoreloader'.
[15/Jun/2015:18:16:23] ENGINE Started monitor thread '_TimeoutMonitor'.
[15/Jun/2015:18:16:23] ENGINE Serving on 127.0.0.1:8080
[15/Jun/2015:18:16:23] ENGINE Bus STARTED
127.0.0.1 - - [15/Jun/2015:18:16:26] "GET /users/1 HTTP/1.1" 200 14 "" "curl/7.29.0"
127.0.0.1 - - [15/Jun/2015:18:16:28] "GET /users/foo HTTP/1.1" 200 14 "" "curl/7.29.0"
这里是查询之前的日志
$ curl 127.0.0.1:8080/users/1; echo
I'm resource 1
$ curl 127.0.0.1:8080/users/foo; echo
I'm resource 1
这里是数据库的内容
$ sqlite3 my.db
sqlite> .schema
CREATE TABLE users (
id INTEGER NOT NULL,
name VARCHAR,
PRIMARY KEY (id)
);
sqlite> select * from users;
1|foo
2|bar
我不能同时使用装饰器 popargs 和 _cp_dispatch 方法。 我还没有找到如何使用装饰器处理我的 url 段。 当我尝试只使用 _cp_dispatch 方法时,我遇到了以下错误:
File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 628, in respond
self.get_resource(path_info)
File "/usr/lib/python2.7/site-packages/cherrypy/_cprequest.py", line 744, in get_resource
dispatch(path)
File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 423, in __call__
resource, vpath = self.find_handler(path_info)
File "/usr/lib/python2.7/site-packages/cherrypy/_cpdispatch.py", line 311, in find_handler
subnode = dispatch(vpath=iternames)
File "root.py", line 49, in _cp_dispatch
vpath[0] = name_to_id(vpath[0], Users)
File "root.py", line 10, in name_to_id
param = cherrypy.request.db.query(table.id).\
File "/usr/lib/python2.7/site-packages/cherrypy/__init__.py", line 208, in __getattr__
return getattr(child, name)
AttributeError: 'Request' object has no attribute 'db'
这是应用于用户的修改 class:
class Users(object):
# [....]
def _cp_dispatch(self, vpath):
if len(vpath) > 1:
raise
vpath[0] = name_to_id(vpath[0], Users)
return vpath
我正在使用以下版本(我正在 Centos 7 环境中工作,无法使用 pip 中的那些进行更改): $ rpm -qa | egrep "cherrypy|sqlalchemy" python-cherrypy-3.2.2-4.el7.noarch python-sqlalchemy-0.9.7-3.el7.x86_64
非常感谢您的帮助!!!!
我已将以下工具添加到根脚本中:
class UserManager(cherrypy.Tool):
def __init__(self):
cherrypy.Tool.__init__(self, 'before_handler',
self.load, priority=10)
def load(self, **kwargs):
req = cherrypy.request
# let's assume we have a db session
# attached to the request somehow
db = req.db
# retrieve the user id and remove it
# from the request parameters
if 'user' in req.params:
user = req.params.pop('user')
if not user.isdigit():
req.params['user'] = db.query(User).\
filter(User.name == user).one()
else:
req.params['user'] = db.query(User).\
filter(User.id == user).one()
cherrypy.tools.user = UserManager()
并重写用户 class,如下所示:
@cherrypy.popargs("user")
class Users(object):
exposed = True
@cherrypy.tools.user()
def GET(self, user=None, **kwargs):
if user:
return "I'm resource %s" % user.id
else:
return "All users"
装饰器 @cherrypy.popargs("user") 告诉 cherrypy 获取 url 中的第一段并将其存储到键 "user"。 装饰器 @cherrypy.tools.user() 告诉 cherrypy 在进入 GET 方法之前调用工具用户。 在 UserManager class 的 load 方法中,我将用户 ID 或名称转换为 sqlalchemy 模型。