Struttura dell'applicazione
Prima sulla struttura di directory standard di un progetto. Non ce n'è, poiché CherryPy non lo impone, né ti dice quale livello dati, convalida moduli o motore modello utilizzare. Dipende tutto da te e dalle tue esigenze.E naturalmente visto che questa è una grande flessibilità in quanto provoca confusione ai principianti. Ecco come può apparire una struttura di directory dell'applicazione simile alla parola reale.
. — 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
poi in piedi nella radice di virtual environment di solito non come segue per avviare CherryPy in ambiente di sviluppo. cherryd
è il modo suggerito da CherryPy di eseguire un'applicazione.
. bin/activate
cherryd -i serve -P website
Templating
Ora diamo un'occhiata più vicina alla directory dei modelli e che cosa può sembrare.
.
├── layout
│ └── main.html
├── page
│ ├── index
│ │ └── index.html
│ ├── news
│ │ ├── list.html
│ │ └── show.html
│ ├── user
│ │ └── profile.html
│ └── error.html
└── part
└── menu.html
di sfruttare bella caratteristica di Jinja2 di template inheritance, qui ci sono i layout che definiscono la struttura di una pagina, gli slot che possono essere riempiti in una pagina particolare. Potresti avere un layout per un sito Web e un layout per le notifiche via email. C'è anche una directory per una parte, lo snippet riutilizzabile usato su diverse pagine. Ora vediamo il codice che corrisponde alla struttura sopra.
Ho reso disponibile la seguente anche come a runnable che è più facile da navigare i file, è possibile eseguire e giocare con esso. I percorsi iniziano con .
come nell'albero della prima sezione.
sito/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')
}
}
sito/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()
sito/applicazione/__ init__.py
parte notevole: ecco un Strumento CherryPy che aiuta ad evitare lo schema relativo al renderin g modelli. Hai solo bisogno di restituire un dict
dal gestore di pagine CherryPy con i dati per il modello. Seguendo il principio di convenzione sulla configurazione, lo strumento quando non fornito con il nome del modello utilizzerà classname/methodname.html
ad es. user/profile.html
. Per sovrascrivere il modello predefinito è possibile utilizzare @cherrypy.tools.template(name = 'other/name')
. Si noti inoltre che lo strumento espone un metodo automatico, in modo da non avete bisogno di aggiungere @cherrypy.expose
in cima
# -*- 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)
sito/applicazione/controller.py
Come si può vedere con l'utilizzo dello strumento i gestori di pagine sembrano piuttosto puliti e saranno coerenti con altri strumenti, ad es 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()
In questa demo app ho usato blueprint file css, per dimostrare come statica lavori di movimentazione di risorse. Mettilo nel website/application/public/resource/css/blueprint.css
. Il resto è meno interessante, solo i modelli Jinja2 per completezza.
sito/applicazione/view/layout/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>
sito/applicazione/view/page/index/index.html
{% extends 'layout/main.html' %}
{% block content %}
<div class='span-18 last'>
<p>Root page</p>
</div>
{% endblock %}
sito/applicazione/view/page/News/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 %}
sito/applicazione/view/page/News/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 %}
sito web/applicazione/vista/pagina/utente/profilo.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 %}
sito/applicazione/view/page/error.html
Si tratta di un 404-pagina.
{% extends 'layout/main.html' %}
{% block content %}
<h1>Error has happened</h1>
{% endblock %}
sito/applicazione/view/parte/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>
Riferimenti
codice sopra va a stretto contatto con la sezione di back-end qooxdoo-website-skeleton. Per l'implementazione completa di Debain di tale applicazione, cherrypy-webapp-skeleton potrebbe essere utile.
Ho avuto problemi a trovare una buona struttura del progetto e questo post e i collegamenti inclusi hanno aiutato a chiarire un po 'le cose. https://groups.google.com/forum/?fromgroups#!topic/cherrypy-users/L7YXZD_55ec –
Bello! questa introduzione è abbastanza per me per iniziare per CherryPy e Mako. –
Voto positivo, ma per favore aiuto, perché il mio problema continua con Nginx http://stackoverflow.com/questions/23359095/how-to-put-cherrypy-wsgi-behind-nginx. – Alex