Pecan入门小结

原创
2017/10/25 11:26
阅读数 6.9K

安装

pip install pecan

创建项目

$ pecan create pecan_tutorial
$ tree pecan_tutorial
pecan_tutorial
├── config.py
├── MANIFEST.in
├── pecan_tutorial
│   ├── app.py
│   ├── controllers
│   │   ├── __init__.py
│   │   ├── __init__.pyc
│   │   ├── root.py
│   │   └── root.pyc
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── model
│   │   ├── __init__.py
│   │   └── __init__.pyc
│   ├── templates
│   │   ├── error.html
│   │   ├── index.html
│   │   └── layout.html
│   └── tests
│       ├── config.py
│       ├── __init__.py
│       ├── test_functional.py
│       ├── test_units.py
│       └── test_units.pyc
├── public
│   ├── css
│   │   └── style.css
│   └── images
│       └── logo.png
├── setup.cfg
└── setup.py
  1. 通过项目结构可以看出pecan遵循MVC模式,在项目根目录下有:
  • model
  • controller
  • tempaltes
  1. pecan通过根目录下配置文件config.py进行项目的配置, 最重要的是定义了root controller,也就是应用程序的入口。
    # Server Specific Configurations
    server = {
        'port': '8080',
        'host': '0.0.0.0'
    }
    
    # Pecan Application Configurations
    app = {
        'root': 'pecan_tutorial.controllers.root.RootController',
        'modules': ['pecan_tutorial'],
        'static_root': '%(confdir)s/public',
        'template_path': '%(confdir)s/pecan_tutorial/templates',
        'debug': True,
        'errors': {
            404: '/error/404',
            '__force_dict__': True
        }
    }
    
    logging = {
        'root': {'level': 'INFO', 'handlers': ['console']},
        'loggers': {
            'pecan_tutorial': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False},
            'pecan': {'level': 'DEBUG', 'handlers': ['console'], 'propagate': False},
            'py.warnings': {'handlers': ['console']},
            '__force_dict__': True
        },
        'handlers': {
            'console': {
                'level': 'DEBUG',
                'class': 'logging.StreamHandler',
                'formatter': 'color'
            }
        },
        'formatters': {
            'simple': {
                'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]'
                           '[%(threadName)s] %(message)s')
            },
            'color': {
                '()': 'pecan.log.ColorFormatter',
                'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]'
                           '[%(threadName)s] %(message)s'),
            '__force_dict__': True
            }
        }
    }
    
  2. 静态文件通常保存在项目根目录下的public目录下
  3. WSGI Application一般是在根目录下同名包中app.py模块中设置。其实setup_app函数中参数config就是2中说明的config.py导入到python环境中的表示
    from pecan import make_app
    from pecan_tutorial import model
    
    
    def setup_app(config):
        model.init_model()
        app_conf = dict(config.app)
    
        return make_app(
            app_conf.pop('root'),
            logging=getattr(config, 'logging', {}),
            **app_conf
        )
    
  4. 应用程序的入口就是pecan_tutorial.controllers.root.RootController,这里面定义了业务逻辑。
    from pecan import expose, redirect
    from webob.exc import status_map
    
    
    class RootController(object):
    
        @expose(generic=True, template='index.html')
        def index(self):
            return dict()
    
        @index.when(method='POST')
        def index_post(self, q):
            redirect('https://pecan.readthedocs.io/en/latest/search.html?q=%s' % q)
    
        @expose('error.html')
        def error(self, status):
            try:
                status = int(status)
            except ValueError:  # pragma: no cover
                status = 500
            message = getattr(status_map.get(status), 'explanation', '')
            return dict(status=status, message=message)
    
  5. expose装饰器将会使得被装饰的方法,与/路径进行绑定,所有使用HTTP GET方法访问/时都是由index方法处理。

运行项目

# 将当前项目添加到PYTHONPATH中
$ python setup.py develop

$ pecan serve config.py

控制器和路由实例

from pecan import expose

class BooksController(object):
    @expose()
    def index(self):
        return "Welcome to book section."

    @expose()
    def bestsellers(self):
        return "We have 5 books in the top 10."

class CatalogController(object):
    @expose()
    def index(self):
        return "Welcome to the catalog."

    books = BooksController()

class RootController(object):
    @expose()
    def index(self):
        return "Welcome to store.example.com!"

    @expose()
    def hours(self):
        return "Open 24/7 on the web."

    catalog = CatalogController()

pecan_tutorial项目为例,如果pecan_tutorial.controllers.root模块中内容如上的话,则:

  • 访问/时,会由RootController类的index方法进行响应
  • 访问/hours或者/hours/时,会由RootController类的hours方法进行响应
  • 访问/catalog时,首先会通过RootController找到其catalog属性,也就是一个CatalogController实例,然后寻找CatalogController类的index方法进行响应
  • 访问/catalog/books时,首先会通过RootController找到其catalog属性,也就是一个CatalogController实例,然后寻找CatalogController类的books属性,也就是一个BookController实例,使用其index方法进行响应
  • 访问/catalog/books/bestsellers时,首先会通过RootController找到其catalog属性,也就是一个CatalogController实例,然后寻找CatalogController类的books属性,也就是一个BookController实例,使用其bestseller方法进行响应

Pecan路由

  • pecan采用的是对象分发(object-dispatch)将HTTP请求映射到控制器,然后到控制器定义的方法
  • 如果访问的地址以/结尾的话,pecan会寻找最后找到的控制器下面定义的index方法进行响应;如果最后一个控制器已经是某个控制器的方法的话,则使用最后的控制器响应。

Pecan中路由涉及以下相关概念:

  • expose
  • _lookup
  • _default
  • _route
  • route

expose

pecan默认采用expose进行路由的绑定,将需要路由相应控制器类的方法都经过expose装饰器装饰,则pecan可以请http请求路径找到对应的处理方法。不同使用方法会有不同的效果:

  • @expose(), 被装饰的方法需要返回一个字符串,表示HTML响应的body
  • @expose(html_template_name),被装饰的方法返回一个字典,字典的key可以在html模板中使用${key}的方式引用
  • @expose(route='some-path'),被装饰方法响应/some-path请求
  • @expose(generic=True),实现一个请求路径,根据请求方法进行覆盖
from pecan import expose


class RootController(object):

    # HTTP GET /
    @expose(generic=True, template='json')
    def index(self):
        return dict()

    # HTTP POST /
    @index.when(method='POST', template='json')
    def index_POST(self, **kw):
        uuid = create_something()
        return dict(uuid=uuid)
  • expose方法可以叠加使用。叠加使用后一个hello方法可以响应三种格式的请求(application/json, text/plain, text/html)
from pecan import expose

class RootController(object):
    @expose('json')
    @expose('text_template.mako', content_type='text/plain')
    @expose('html_template.mako')
    def hello(self):
        return {'msg': 'Hello!'}

_lookup

The _lookup() special method provides a way to process a portion of a URL, and then return a new controller object to route to for the remainder. _lookup方法是最后尝试的方法,只有没有控制器可以响应请求的URL,而且最后找到的控制器没有定义_default方法时,采用_lookup

from pecan import expose, abort
from somelib import get_student_by_name

class StudentController(object):
    def __init__(self, student):
        self.student = student

    @expose()
    def name(self):
        return self.student.name

class RootController(object):
    @expose()
    def _lookup(self, primary_key, *remainder):
        student = get_student_by_primary_key(primary_key)
        if student:
            return StudentController(student), remainder
        else:
            abort(404)

An HTTP GET request to /8/name would return the name of the student where primary_key == 8.

_default

The _default() method is called as a last resort when no other controller methods match the URL via standard object-dispatch.

from pecan import expose

class RootController(object):
    @expose()
    def english(self):
        return 'hello'

    @expose()
    def french(self):
        return 'bonjour'

    @expose()
    def _default(self):
        return 'I cannot say hello in that language'
$ curl http://localhost:8080/english
hello

$ curl http://localhost:8080/french
bonjour

$ curl http://localhost:8080/another
I cannot say hello in that language

_route

The _route() method allows a controller to completely override the routing mechanism of Pecan. Pecan itself uses the _route() method to implement its RestController. If you want to design an alternative routing system on top of Pecan, defining a base controller class that defines a _route() method will enable you to have total control.

route

  • 可以通过route方法实现路由和控制器的绑定
  • expose的作用是使得被装饰的函数可以被外部访问,route的作用是可以将url请求path全部或者部分与经过expose的函数进行绑定
class ChildController(object):

    @pecan.expose()
    def child(self):
        return dict()

class RootController(object):
    @pecan.expose()
    def some_path(self):
        return dict()

pecan.route('some-path', RootController.some_path)
pecan.route(RootController, 'child-path', ChildController())

Arguments

In Pecan, HTTP GET and POST variables that are not consumed during the routing process can be passed onto the controller method as arguments.

from pecan import expose

class RootController(object):
    @expose()
    def index(self, arg):
        return arg
    
    @expose()
    def args(self, *args):
        return ','.join(args)

    @expose()
    def kwargs(self, **kwargs):
        return str(kwargs)
1. query string to arguments
$ curl http://localhost:8080/?arg=foo
foo
$ curl http://localhost:8080/kwargs?a=1&b=2&c=3
{u'a': u'1', u'c': u'3', u'b': u'2'}

2. remaining path to arguments
$ curl http://localhost:8080/args/one/two/three
one,two,three

3. POST body to arguments
$ curl -X POST "http://localhost:8080/" -H "Content-Type: application/x-www-form-urlencoded" -d "arg=foo"
foo
$ curl -X POST "http://localhost:8080/kwargs" -H "Content-Type: application/x-www-form-urlencoded" -d "name=foo;age=18"
{u'name': u'foo', u'age': u'18'}

可以发现,不管是GET还是POST方法,当处理方法中定义的是位置参数时,query string或者是POST body中要传递的参数需要与处理方法中定义的位置参数名称相同才可以,否则会报错。

Pecan Request/Response

For every HTTP request, Pecan maintains a thread-local reference to the request and response object, pecan.request and pecan.response.

展开阅读全文
打赏
1
1 收藏
分享
加载中
请问,controller里面怎么调用到model里面定义的方法
2020/03/18 14:19
回复
举报
alazyer博主
很久不搞Python了。 如果是models中提供的辅助方法。类等(包级别),那么可以通过在controllers中导入models包,然后直接models.func或者models.Class来引用 如果是实例的方法的话,在controller里面获取到实例后,直接instance.method调用就行
2020/03/30 11:55
回复
举报
更多评论
打赏
2 评论
1 收藏
1
分享
返回顶部
顶部