Cherrypy和Jinja2入门

lif*_*ter 11 frameworks cherrypy jinja2 python-2.7

这是我第一次深入研究python中的web开发.我唯一的另一个经验是PHP,我之前从未使用过框架,所以我发现这非常令人生畏和困惑.

我有兴趣学习CherryPy/Jinja2为我的NAS制作ZFS监视器.我已经阅读了关于CherryPy/Jinja2的文档的基础知识,但我发现样本是脱节的而且过于简单化,我真的不明白如何使这两个东西"优雅地聚集在一起".

我有一些问题:

  1. 有没有一个简单的教程显示你如何使CherryPy和Jinja2很好地协同工作?我要么发现过于简单的样本,比如CherryPy/Jinja2文档中的样本,要么是复杂的样本.(例如:https: //github.com/jovanbrakus/cherrypy-example).

  2. 是否有标准化或"预期"的方式为CherryPy创建Web应用程序?(例子:我的目录结构应该是什么样的?有没有办法声明静态的东西;它甚至是必要的吗?)

  3. 有没有人为此推荐文献或在线文档是最好的资源?

ljs*_*dev 30

恭喜选择Python,我相信你会像我一样学会爱它.

关于CherryPy,我不是专家,但几天前也和你在同一条船上,我同意这些教程的部分内容有点脱节.

为了集成Jinja2,就像在他们的doc页面中一样,HTML的片段应该被指定为模板文件,因此保存在路径/templates/index.html中.他们还使用了模板代码示例和控制器示例中不匹配的变量.

以下是使用CherryPy和Jinja2的简单hello世界的完整工作样本

/main.py:

import cherrypy
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('templates'))

class Root:
    @cherrypy.expose
    def index(self):
        tmpl = env.get_template('index.html')
        return tmpl.render(salutation='Hello', target='World')

cherrypy.config.update({'server.socket_host': '127.0.0.1',
                         'server.socket_port': 8080,
                        })

cherrypy.quickstart(Root())
Run Code Online (Sandbox Code Playgroud)

/templates/index.html:

<h1>{{ salutation }} {{ target }}</h1>
Run Code Online (Sandbox Code Playgroud)

然后在shell /命令提示符下,使用以下命令提供应用程序:

python main.py
Run Code Online (Sandbox Code Playgroud)

在您的浏览器中,您应该能够看到它 http://localhost:8080

这有助于您将Jinja2模板连接到您的CherryPy应用程序.CherryPy确实是一个轻量级且非常灵活的框架,您可以选择许多不同的方式来构建代码和文件结构.

  • 我也很难找到一个好的项目结构,这个帖子和包含的链接有助于澄清一些事情.https://groups.google.com/forum/?fromgroups#!topic/cherrypy-users/L7YXZD_55ec (2认同)

saa*_*aaj 12

应用结构

首先是关于项目的标准目录结构.没有,因为CherryPy没有强制要求,也没有告诉你使用什么数据层,表单验证或模板引擎.这完全取决于您和您的要求.当然,这是一个很大的灵活性,因为它会给初学者带来一些困惑.这是接近真实单词应用程序目录结构的样子.

.           — Python virtual environment
??? website — cherryd to add this to sys.path, -P switch
    ??? application
    ?   ??? controller.py — request routing, model use
    ?   ??? model.py      — data access, domain logic
    ?   ??? view          — template
    ?   ?   ??? layout
    ?   ?   ??? page
    ?   ?   ??? part
    ?   ??? __init__.py — application bootstrap
    ??? public
    ?   ??? resource — static
    ?       ??? css
    ?       ??? image
    ?       ??? js
    ??? config.py — configuration, environments
    ??? serve.py  — bootstrap call, cherryd to import this, -i switch
Run Code Online (Sandbox Code Playgroud)

然后站在虚拟环境的根目录中,您通常会执行以下操作以在开发环境中启动CherryPy.cherryd是CherryPy建议运行应用程序的方法.

. bin/activate
cherryd -i serve -P website
Run Code Online (Sandbox Code Playgroud)

模板

现在让我们看一下模板目录及其外观.

.
??? layout
?   ??? main.html
??? page
?   ??? index
?   ?   ??? index.html
?   ??? news
?   ?   ??? list.html
?   ?   ??? show.html
?   ??? user
?   ?   ??? profile.html
?   ??? error.html     
??? part
    ??? menu.html
Run Code Online (Sandbox Code Playgroud)

为了利用Jinja2的模板继承功能,这里有定义页面结构的布局,可以在特定页面中填充的插槽.您可能拥有网站布局和电子邮件通知布局.还有一个部件目录,可在不同页面使用的可重复使用的代码段.现在让我们看一下与上面结构相对应的代码.

我已经将以下内容作为runnable提供,它更容易导航文件,您可以运行和使用它.路径从.第一部分的树开始.

网站/ config.py

# -*- coding: utf-8 -*-


import os


path   = os.path.abspath(os.path.dirname(__file__))
config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8,

    'engine.autoreload.on' : False,

    'tools.trailing_slash.on' : False
  },
  '/resource' : {
    'tools.staticdir.on'  : True,
    'tools.staticdir.dir' : os.path.join(path, 'public', 'resource')
  }
}
Run Code Online (Sandbox Code Playgroud)

网站/ serve.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-


from application import bootstrap


bootstrap()


# debugging purpose, e.g. run with PyDev debugger
if __name__ == '__main__':
  import cherrypy
  cherrypy.engine.signals.subscribe()
  cherrypy.engine.start()
  cherrypy.engine.block()
Run Code Online (Sandbox Code Playgroud)

网站/应用/ __ init__.py

这里值得注意的部分是CherryPy工具,它有助于避免与渲染模板相关的样板.您只需dict要从CherryPy页面处理程序返回一个包含模板数据的a.遵循约定优于配置原则,未提供模板名称的工具将使用classname/methodname.html例如user/profile.html.要覆盖您可以使用的默认模板@cherrypy.tools.template(name = 'other/name').另请注意,该工具会自动公开方法,因此您无需@cherrypy.expose在顶部附加

# -*- coding: utf-8 -*-


import os
import types

import cherrypy
import jinja2

import config


class TemplateTool(cherrypy.Tool):

  _engine = None
  '''Jinja environment instance'''


  def __init__(self):
    viewLoader   = jinja2.FileSystemLoader(os.path.join(config.path, 'application', 'view'))
    self._engine = jinja2.Environment(loader = viewLoader)

    cherrypy.Tool.__init__(self, 'before_handler', self.render)

  def __call__(self, *args, **kwargs):
    if args and isinstance(args[0], (types.FunctionType, types.MethodType)):
      # @template
      args[0].exposed = True
      return cherrypy.Tool.__call__(self, **kwargs)(args[0])
    else:
      # @template()
      def wrap(f):
        f.exposed = True
        return cherrypy.Tool.__call__(self, *args, **kwargs)(f)
      return wrap

  def render(self, name = None):
    cherrypy.request.config['template'] = name

    handler = cherrypy.serving.request.handler
    def wrap(*args, **kwargs):
      return self._render(handler, *args, **kwargs)
    cherrypy.serving.request.handler = wrap

  def _render(self, handler, *args, **kwargs):
    template = cherrypy.request.config['template']
    if not template:
      parts = []
      if hasattr(handler.callable, '__self__'):
        parts.append(handler.callable.__self__.__class__.__name__.lower())
      if hasattr(handler.callable, '__name__'):
        parts.append(handler.callable.__name__.lower())
      template = '/'.join(parts)

    data     = handler(*args, **kwargs) or {}
    renderer = self._engine.get_template('page/{0}.html'.format(template))

    return renderer.render(**data) if template and isinstance(data, dict) else data


def bootstrap():
  cherrypy.tools.template = TemplateTool()

  cherrypy.config.update(config.config)

  import controller

  cherrypy.config.update({'error_page.default': controller.errorPage})
  cherrypy.tree.mount(controller.Index(), '/', config.config)
Run Code Online (Sandbox Code Playgroud)

网站/应用/ controller.py

正如您所看到的那样,使用工具页面处理程序看起来相当干净,并且与其他工具保持一致,例如json_out.

# -*- coding: utf-8 -*-


import datetime

import cherrypy


class Index:

  news = None
  user = None


  def __init__(self):
    self.news = News()
    self.user = User()

  @cherrypy.tools.template
  def index(self):
    pass

  @cherrypy.expose
  def broken(self):
    raise RuntimeError('Pretend something has broken')


class User:

  @cherrypy.tools.template
  def profile(self):
    pass


class News:

  _list = [
    {'id': 0, 'date': datetime.datetime(2014, 11, 16), 'title': 'Bar', 'text': 'Lorem ipsum'},
    {'id': 1, 'date': datetime.datetime(2014, 11, 17), 'title': 'Foo', 'text': 'Ipsum lorem'}
  ]


  @cherrypy.tools.template
  def list(self):
    return {'list': self._list}

  @cherrypy.tools.template
  def show(self, id):
    return {'item': self._list[int(id)]}


def errorPage(status, message, **kwargs):
  return cherrypy.tools.template._engine.get_template('page/error.html').render()
Run Code Online (Sandbox Code Playgroud)

在这个演示应用程序中,我使用了blueprint css文件来演示静态资源处理的工作原理.把它放进去website/application/public/resource/css/blueprint.css.剩下的就不那么有趣了,只是Jinja2模板的完整性.

网站/应用/视图/布局/ main.html中

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv='content-type' content='text/html; charset=utf-8' />
    <title>CherryPy Application Demo</title>
    <link rel='stylesheet' media='screen' href='/resource/css/blueprint.css' />
  </head>
  <body>
    <div class='container'>
      <div class='header span-24'>
        {% include 'part/menu.html' %}
      </div>
      <div class='span-24'>{% block content %}{% endblock %}</div>      
    </div>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

网站/应用/视图/页/指数/ index.html的

{% extends 'layout/main.html' %}
{% block content %}
  <div class='span-18 last'>
      <p>Root page</p>      
  </div>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

网站/应用/视图/页/新闻/ list.html

{% extends 'layout/main.html' %}
{% block content %}
  <div class='span-20 last prepend-top'>
    <h1>News</h1>
    <ul>
    {% for item in list %}
      <li><a href='/news/show/{{ item.id }}'>{{ item.title }}</a> ({{ item.date }})</li>
    {% endfor %}
    </ul>
  </div>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

网站/应用/视图/页/新闻/ show.html

{% extends 'layout/main.html' %}
{% block content %}
  <div class='span-20 last prepend-top'>
    <h2>{{ item.title }}</h2>
    <div class='span-5 last'>{{ item.date }}</div>
    <div class='span-19 last'>{{ item.text }}</div>
  </div>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

网站/应用/视图/页/用户/ profile.html

{% extends 'layout/main.html' %}
{% block content %}
  <div class='span-18'>
    <table>
      <tr><td>First name:</td><td>John</td></tr>
      <tr><td>Last name:</td><td>Doe</td></tr>
    <table>
  </div>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

网站/应用/视图/页/的error.html

这是一个404页.

{% extends 'layout/main.html' %}
{% block content %}
  <h1>Error has happened</h1>
{% endblock %}
Run Code Online (Sandbox Code Playgroud)

网站/应用/视图/零件/ menu.html

<div class='span-4 prepend-top'>
  <h2><a href='/'>Website</a></h2>
</div>
<div class='span-20 prepend-top last'>
    <ul>
      <li><a href='/news/list'>News</a></li>
      <li><a href='/user/profile'>Profile</a></li>
      <li><a href='/broken'>Broken</a></li>
    </ul>
</div>
Run Code Online (Sandbox Code Playgroud)

参考

上面的代码与qooxdoo-website-skeleton的后端部分密切相关.对于这种应用程序的完整Debain部署,cherrypy-webapp-skeleton可能很有用.