python-2.7 - async - jinja2 get_template
Comenzando con Cherrypy y Jinja2 (2)
Esta es la primera vez que profundizo en el desarrollo web en Python. Mi única otra experiencia es PHP, y nunca usé un framework antes, así que estoy encontrando esto muy intimidante y confuso.
Estoy interesado en aprender CherryPy / Jinja2 para hacer un monitor ZFS para mi NAS. He leído los fundamentos de los documentos sobre CherryPy / Jinja2 pero me parece que las muestras son inconexas y demasiado simplistas, realmente no entiendo cómo hacer que estas 2 cosas "se unan" con gracia.
Algunas preguntas que tengo:
¿Hay algún tutorial simple que demuestre cómo logras que CherryPy y Jinja2 funcionen juntos muy bien? Estoy encontrando muestras que son demasiado simples, como las muestras en los documentos CherryPy / Jinja2, o de manera compleja. (ejemplo: https://github.com/jovanbrakus/cherrypy-example ).
¿Existe una forma estandarizada o "esperada" para crear aplicaciones web para CherryPy? (Ejemplo: ¿Cómo debería ser la estructura de mi directorio? ¿Hay alguna manera de declarar cosas estáticas, incluso es necesario?)
¿Alguien ha recomendado literatura para esto o la documentación en línea es el mejor recurso?
Felicitaciones por elegir Python, estoy seguro de que aprenderás a amarlo como yo.
Con respecto a CherryPy, no soy un experto, pero también estuve en el mismo barco que tú hace unos días y estoy de acuerdo en que los tutoriales están un poco dislocados por partes.
Para integrar Jinja2, como en su página de documento , el fragmento de HTML debería haber especificado que es el archivo de plantilla y, como tal, guardado en la ruta /templates/index.html. También usaron variables que no coincidían en la muestra del código de la plantilla y la muestra del controlador.
El siguiente es, en cambio, una muestra completa de trabajo de un mundo hello simple utilizando CherryPy y Jinja2
/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())
/templates/index.html:
<h1>{{ salutation }} {{ target }}</h1>
Luego, en el intérprete de comandos / shell, preséntela usando:
python main.py
Y en su navegador debería poder verlo en http://localhost:8080
Es de esperar que te ayude a conectar plantillas Jinja2 a tu aplicación CherryPy. CherryPy es realmente un marco ligero y muy flexible, donde puedes elegir diferentes formas de estructurar tu código y estructuras de archivos.
Estructura de la aplicación
Primero sobre la estructura de directorio estándar de un proyecto. No hay ninguno, ya que CherryPy no lo ordena, ni le dice qué capa de datos, validación de formulario o motor de plantilla usar. Todo depende de usted y sus requisitos. Y, por supuesto, esta es una gran flexibilidad, ya que causa cierta confusión a los principiantes. Así es como se ve una estructura de directorio de aplicación cercana a la palabra real.
. — 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
Luego, pararte en la raíz del entorno virtual , generalmente haces lo siguiente para iniciar CherryPy en el entorno de desarrollo. cherryd
es la manera sugerida por CherryPy de ejecutar una aplicación.
. bin/activate
cherryd -i serve -P website
Templating
Ahora veamos más de cerca el directorio de plantillas y cómo puede verse.
.
├── layout
│ └── main.html
├── page
│ ├── index
│ │ └── index.html
│ ├── news
│ │ ├── list.html
│ │ └── show.html
│ ├── user
│ │ └── profile.html
│ └── error.html
└── part
└── menu.html
Para aprovechar la característica de Jinja2 de la herencia de plantillas , aquí hay diseños que definen la estructura de una página, los espacios que se pueden llenar en una página en particular. Puede tener el diseño de un sitio web y el diseño de las notificaciones por correo electrónico. También hay un directorio para una parte, fragmento reutilizable utilizado en diferentes páginas. Ahora veamos el código que corresponde a la estructura de arriba.
También he hecho que lo siguiente esté disponible como un ejecutable que es más fácil de navegar por los archivos, puede ejecutarlo y jugar con él. Los caminos comienzan con .
como en el árbol de la primera sección.
sitio web / 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'')
}
}
sitio web / 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()
sitio web / aplicación / __ init__.py
La parte notable aquí es una herramienta CherryPy que ayuda a evitar la repetición de plantillas relacionadas. Solo necesita devolver un dict
del manejador de página CherryPy con datos para la plantilla. Siguiendo el principio de convención sobre configuración, la herramienta cuando no se proporciona el nombre de la plantilla usará classname/methodname.html
por ejemplo, user/profile.html
. Para anular la plantilla predeterminada, puede usar @cherrypy.tools.template(name = ''other/name'')
. También tenga en cuenta que la herramienta expone un método de forma automática, por lo que no es necesario agregar @cherrypy.expose
en la parte superior
# -*- 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)
sitio web / aplicación / controller.py
Como puede ver con el uso de la herramienta, los manejadores de páginas parecen bastante limpios y serán consistentes con otras herramientas, por ejemplo, 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()
En esta aplicación de demostración utilicé el archivo blueprint css, para demostrar cómo funciona el manejo de recursos estáticos. Ponlo en el website/application/public/resource/css/blueprint.css
. El resto es menos interesante, solo las plantillas Jinja2 para completar.
sitio web / application / 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>
sitio web / application / view / page / index / index.html
{% extends ''layout/main.html'' %}
{% block content %}
<div class=''span-18 last''>
<p>Root page</p>
</div>
{% endblock %}
sitio web / aplicación / vista / página / noticias / lista.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 %}
sitio web / aplicación / vista / página / noticias / 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 %}
sitio web / aplicación / vista / página / usuario / perfil.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 %}
sitio web / aplicación / vista / página / error.html
Es una página de 404.
{% extends ''layout/main.html'' %}
{% block content %}
<h1>Error has happened</h1>
{% endblock %}
sitio web / application / view / part / 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>
Referencias
El código anterior se relaciona estrechamente con la sección de backend de qooxdoo-website-skeleton . Para el despliegue completo de Debain de tal aplicación, el esqueleto de cereza-webapp puede ser útil.