Простой пример cgi-скрипта подключающего шаблон Jinja
Довжина списку словників, та обробка його обробка:
Copy {% if EQU_TYPES_LIST|length > 0 %}
Copy {%- for dict in EQU_TYPES_LIST %}
< div class = "col py-3" >
< h5 class = "card-title" >{{ dict.typ_mark }}</ h5 >
< p >{{ dict.typ_name }}</ p >
</ div >
{% endfor -%}
Install
Copy Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1" %}
❗ На сервере, где установлено окружение (напр. python3.7), необходимо добавить опцию --user
:
Copy which pip3.7
/usr/local/bin/pip3.7
Copy pip3.7 install Jinja2"
Copy Could not install packages due to an EnvironmentError: [Errno 13] Permission denied...
Consider using the `--user` option or check the permissions." %}
Copy pip3.7 install --user Jinja2"
Copy Installing collected packages: MarkupSafe, Jinja2
Successfully installed Jinja2-2.11.2 MarkupSafe-1.1.1" %}
⚠️ Экспериментальная поддержка Python 3 :nbsp: Jinja 2.7 обеспечивает экспериментальную поддержку Python >= 3.3. Это означает, что все unittests передают новую версию, но там могут быть небольшие ошибки, и поведение может быть непоследовательным. Если вы заметили какие-либо ошибки, сообщите об этом в трекер Jinja.
Почему Jinja2?
http://python2or3.blogspot.com/2015/08/python.html
Если по вопросу "с какого фреймворка начинать в Питоне" еще есть плюрализм (от "Flask - он легковесный, наращиваешь сам и понимаешь как все работает" до "Django - в нем все есть, а чего нет, быстро доустанавливаешь"), то с шаблонизаторами все упростилось.
До недавнего времени шаблонизаторов было 3-4: Jinja, Mako, Genshi и, конечно, Django Templates.
Собственно, они никуда не делись, Genshi и сейчас используется по умолчанию в Turbogears 2, а Mako - в Pylons/Pyramid.
Но и Turbogears, и Django и Pyramid с недавнего времени поддерживают шаблонизатор, ставший популярным благодаря Flask - Jinja 2.
Ну и, наконец, посмтрим на подборку генераторов статических сайтов (http://www.staticgen.com/): 11 из 17, в том числе самый популярный Pelican (http://docs.getpelican.com/) опираются на Jinja2.
Итого - вопрос с выбором шаблонизатора Python на сегодня не стоит.
Пример использования Jinja2
https://lectureswww.readthedocs.io/6.www.sync/2.codding/3.templates/jinja2.html
Hello name!
Copy # -*- coding: utf-8 -*-
from jinja2 import Template
template = Template ( 'Hello ![pic]( name )!' )
print (template. render (name = u 'Вася' ))
Hello Вася!
Комментарии
Copy { # Это кусок кода, который временно не нужен, но удалять жалко
{ % for user in users % }
...
{ % endfor % }
#}
Операторы
См.также https://ru.wikipedia.org/wiki/Оператор_(программирование)
Copy from jinja2 import Template
text = '{% for item in range(5) %}Hello ![pic]( name )! {% endfor %}'
template = Template (text)
print (template. render (name = u 'Вася' ))
Hello Вася! Hello Вася! Hello Вася! Hello Вася! Hello Вася!
Модули
Copy from jinja2 import Template
template = Template ( "{% set a, b, c = 'foo', 'фуу', 'föö' %}" )
m = template . module
print (m.a)
print (m.b)
print (m.c)
foo
фуу
föö
Макросы
Copy from jinja2 import Template
template = Template ( '{% macro foo() %}42{% endmacro %}23' )
m = template . module
print (m)
print (m. foo ())
23
42
Чтение из файла
Copy <! DOCTYPE html >
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
</ head >
< body >
{% for item in range(5) %}
Hello ![pic]( name )!
{% endfor %}
</ body >
</ html >
jinja2/3.loader/0.file.py
— чтение шаблона из файла
Copy from jinja2 import Template
html = open ( 'foopkg/templates/0.hello.html' ). read ()
template = Template (html)
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
Copy <! DOCTYPE html >
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" />
</ head >
< body >
Hello Петя!
Hello Петя!
Hello Петя!
Hello Петя!
Hello Петя!
</ body >
</ html >
Окружение (Environment)
См.также
http://jinja.pocoo.org/docs/dev/api/#loaders
http://jinja.pocoo.org/docs/dev/api/#jinja2.Environment
Настройки
Загрузчики шаблонов (Loaders)
FileSystemLoader
Copy # -*- coding: utf-8 -*-
from jinja2 import Environment , FileSystemLoader
env = Environment (loader = FileSystemLoader ( 'foopkg/templates' ))
template = env . get_template ( '0.hello.html' )
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
PackageLoader
Copy from jinja2 import Environment , DictLoader
html = '''<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
{% for item in range(5) %}
Hello ![pic]( name )!
{% endfor %}
</body>
</html>
'''
env = Environment (loader = DictLoader ({ 'index.html' : html}))
template = env . get_template ( 'index.html' )
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
DictLoader
Copy # -*- coding: utf-8 -*-
from jinja2 import Environment , DictLoader
html = '''<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<div data-gb-custom-block data-tag="for" data-0='5' data-1='5' data-2='5' data-3='5' data-4='5' data-5='5' data-6='5' data-7='5' data-8='5' data-9='5' data-10='5' data-11='5' data-12='5' data-13='5' data-14='5'>
Hello ![pic]( name )!
</div>
</body>
</html>
'''
env = Environment (loader = DictLoader ({ 'index.html' : html}))
template = env . get_template ( 'index.html' )
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
FunctionLoader
Copy from jinja2 import Environment , FunctionLoader
html = '''<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
{% for item in range(5) %}
Hello ![pic]( name )!
{% endfor %}
</body>
</html>
'''
def myloader ( name ):
if name ### 'index.html':
return html
env = Environment (loader = FunctionLoader (myloader))
template = env . get_template ( 'index.html' )
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
PrefixLoader
Copy from jinja2 import Environment , FunctionLoader , PackageLoader , PrefixLoader
html = '''<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
{% for item in range(5) %}
Hello ![pic]( name )!
{% endfor %}
</body>
</html>
'''
def myloader ( name ):
if name ### 'index.html':
return html
env = Environment (loader = PrefixLoader ({
'foo' : FunctionLoader (myloader),
'bar' : PackageLoader ( 'foopkg' , 'templates' )
}))
template1 = env . get_template ( 'foo/index.html' )
template2 = env . get_template ( 'bar/0.hello.html' )
print (template1. render (name = u 'Петя' ))
print (template2. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
ChoiceLoader
Copy from jinja2 import Environment , FunctionLoader , PackageLoader , ChoiceLoader
html = '''<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
{% for item in range(5) %}
Hello ![pic]( name )!
{% endfor %}
</body>
</html>
'''
def myloader ( name ):
if name ### 'index.html':
return html
env = Environment (loader = ChoiceLoader ({
FunctionLoader (myloader),
PackageLoader ( 'foopkg' , 'templates' )
}))
template1 = env . get_template ( 'index.html' )
template2 = env . get_template ( '0.hello.html' )
print (template1. render (name = u 'Петя' ))
print (template2. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
ModuleLoader
Copy # -*- coding: utf-8 -*-
from jinja2 import Environment , FileSystemLoader , ModuleLoader
# Compile template
Environment (loader = FileSystemLoader ( 'foopkg/templates' ))\
. compile_templates ( "foopkg/compiled/foopkg.zip" ,
py_compile = True ) # pyc generate, only for python2
# Environment
env = Environment (loader = ModuleLoader ( "foopkg/compiled/foopkg.zip" ))
template = env . get_template ( '0.hello.html' )
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
BaseLoader
Copy # -*- coding: utf-8 -*-
from os . path import exists , getmtime , join
from jinja2 import BaseLoader , Environment , TemplateNotFound
class FoopkgLoader ( BaseLoader ):
def __init__ ( self , path = "foopkg/templates" ):
self . path = path
def get_source ( self , environment , template ):
path = join (self.path, template)
if not exists (path):
raise TemplateNotFound (template)
mtime = getmtime (path)
with open (path) as f :
source = f . read ()
return source , path , lambda : mtime ### getmtime(path)
# Environment
env = Environment (loader = FoopkgLoader ())
template = env . get_template ( '0.hello.html' )
print (template. render (name = u 'Петя' ))
Результат рендеринга шаблона jinja2/3.loader/foopkg/templates/0.hello.html
Шаблон конфига Nginx
Copy server {
listen ![pic] ( SRC_SERVER_PUB_IP ): 80 ;
servern_name ![pic] ( FQDN }} www.{{ FQDN )
location / {
proxy_pass http: // ![pic]( SRC_SERVER_LOCAL_IP ): 80 / ;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X - Real - IP $remote_addr;
proxy_set_header X - Forwarded - For $proxy_add_x_forwarded_for;
}
}
Copy #!/usr/bin/env python
from jinja2 import Environment , FileSystemLoader
env = Environment (loader = FileSystemLoader ( '.' ))
template = env . get_template ( 'nginx_proxy_conf.tpl' )
data = {
"SRC_SERVER_PUB_IP" : "192.168.0.100" ,
"SRC_SERVER_LOCAL_IP" : "10.0.3.100" ,
"FQDN" : "example.com"
}
conf = template . render ( ** data)
print (conf)
open ( "proxy.nginx.conf" , "w" ). write (conf)
Copy server {
listen 192.168 . 0 . 100 : 80 ;
servern_name example . com www . example . com
location / {
proxy_pass http : // 10.0 . 3 . 100 : 80 / ;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X - Real - IP $remote_addr;
proxy_set_header X - Forwarded - For $proxy_add_x_forwarded_for;
}
}
Copy <! DOCTYPE html >
< html lang = "en" >
< head >
< div data-gb-custom-block data-tag = "block" >
< link rel = "stylesheet" href = "style.css" />
< title >
< div data-gb-custom-block data-tag = "block" ></ div > - My Webpage</ title >
< meta charset = 'utf-8' >
</ div >
</ head >
< body >
< div id = "content" >
< div data-gb-custom-block data-tag = "block" ></ div ></ div >
< div id = "footer" >
< div data-gb-custom-block data-tag = "block" >
© Copyright 2008 by < a href = "http://domain.invalid/" >you</ a >.
</ div >
</ div >
</ body >
</ html >
Copy
< div data - gb - custom - block data - tag = "extends" data - 0 = 'base.html' ></ div >
< div data - gb - custom - block data - tag = "block" > Index </ div >
< div data - gb - custom - block data - tag = "block" >
![pic] ( super () )
< style type = "text/css" >
. important { color : #336699; }
</ style >
</ div >
< div data - gb - custom - block data - tag= "block" >
< h1 > Index </ h1 >
< p class = "important" >
Welcome ![pic] ( name ) to my awesome homepage .
</ p >
</ div >
Copy # -*- coding: utf-8 -*-
from jinja2 import Environment , FileSystemLoader
env = Environment (loader = FileSystemLoader ( '.' ))
template = env . get_template ( 'index.html' )
print (template. render (name = u 'Петя' ))
< !DOCTYPE html >
< html lang = "en" >
< head >
< link rel = "stylesheet" href = "style.css" />
< title > Index — My Webpage </ title >
< meta charset = 'utf-8' >
< style type = "text/css" >
. important { color : #336699; }
</ style >
</ head >
< body >
< div id = "content" >
< h1 > Index </ h1 >
< p class = "important" >
Welcome Петя to my awesome homepage .
</ p >
</ div >
< div id = "footer" >
& copy; Copyright 2008 by < a href= "http://domain.invalid/" > you </ a > .
</ div >
</ body >
</ html >
Блог
templates/base.html
— базовый шаблон.
Copy < !DOCTYPE html >
< html lang = "en" >
< head >
< div data - gb - custom - block data - tag = "block" >
< link rel = "stylesheet" href = "style.css" />
< title >
< div data - gb - custom - block data - tag = "block" ></ div > - My Blog </ title >
< meta charset = 'utf-8' >
</ div >
</ head >
< body >
< div id = "content" >
< div data - gb - custom - block data - tag = "block" ></ div ></ div >
< br />
< br />
< br />
< div id = "footer" >
< div data - gb - custom - block data - tag = "block" >
& copy; Copyright 2015 by < a href = "http://domain.invalid/" > you </ a > .
</ div >
</ div >
</ body >
</ html >
Главная страница templates/index.html
наследуется от templates/base.html
Copy
< div data - gb - custom - block data - tag = "extends" data - 0 = 'base.html' ></ div >
< div data - gb - custom - block data - tag = "block" > Index </ div >
< div data - gb - custom - block data - tag = "block" >
< h1 > Simple Blog </ h1 >
< a href = "/article/add" > Add article </ a >
< br />
< br />
< div data - gb - custom - block data - tag = "for" >
![pic] ( article.id }} - ( < a href= "/article/ {{ article.id )/delete" > delete </ a > |
< a href= "/article/![pic]( article.id )/edit" > edit </ a > )
< a href = "/article/![pic]( article.id }} " > {{ article.title ) </ a >< br />
</ div >
</ div >
templates/create.html
наследуется от базового шаблона.
Copy
< div data - gb - custom - block data - tag = "extends" data - 0 = 'base.html' ></ div >
< div data - gb - custom - block data - tag = "block" > Create </ div >
< div data - gb - custom - block data - tag = "block" >
< h1 > Simple Blog -> EDIT </ h1 >
< form action = "" method = "POST" >
Title : < br >
< input type = "text" name = "title" value = "![pic]( article.title )" >< br >
Content : < br >
< textarea name = "content" > ![pic] ( article.content ) </ textarea >< br >< br >
< input type = "submit" value = "Submit" >
</ form >
</ div >
templates/read.html
наследуется от базового шаблона.
Copy
< div data - gb - custom - block data - tag = "extends" data - 0 = 'base.html' ></ div >
< div data - gb - custom - block data - tag = "block" > Index </ div >
< div data - gb - custom - block data - tag = "block" >
< h1 >< a href = "/" > Simple Blog </ a > -> READ </ h1 >
< h2 > ![pic] ( article.title ) </ h2 >
![pic] ( article.content )
</ div >
views.py
— окружение Jinja2.
Copy from models import ARTICLES
from jinja2 import Environment , FileSystemLoader
env = Environment (loader = FileSystemLoader ( 'templates' ))
class BaseBlog ( object ):
def __init__ ( self , environ , start_response ):
self . environ = environ
self . start = start_response
class BaseArticle ( BaseBlog ):
def __init__ ( self , * args ):
super (BaseArticle, self). __init__ ( * args)
article_id = self . environ [ 'wsgiorg.routing_args' ] [ 1 ][ 'id' ]
(self . index ,
self . article) = next (((i, art) for i, art in enumerate (ARTICLES)
if art[ 'id' ] ### int(article_id)),
( None , None ))
class BlogIndex (BaseBlog):
def __iter__ (self):
self. start ( '200 OK' , [( 'Content-Type' , 'text/html' )])
yield str . encode (
env. get_template ( 'index.html' ). render (articles = ARTICLES)
)
class BlogCreate (BaseBlog):
def __iter__ (self):
if self.environ[ 'REQUEST_METHOD' ]. upper () ### 'POST':
from urllib.parse import parse_qs
values = parse_qs (self.environ[ 'wsgi.input' ]. read ())
max_id = max ([art[ 'id' ] for art in ARTICLES])
ARTICLES. append (
{ 'id' : max_id + 1 ,
'title' : values[b 'title' ]. pop (). decode (),
'content' : values[b 'content' ]. pop (). decode ()
}
)
self. start ( '302 Found' ,
[( 'Content-Type' , 'text/html' ),
( 'Location' , '/' )])
return
self. start ( '200 OK' , [( 'Content-Type' , 'text/html' )])
yield str . encode (
env. get_template ( 'create.html' ). render (article = None )
)
class BlogRead (BaseArticle):
def __iter__ (self):
if not self.article:
self. start ( '404 Not Found' , [( 'content-type' , 'text/plain' )])
yield b 'not found'
return
self. start ( '200 OK' , [( 'Content-Type' , 'text/html' )])
yield str . encode (
env. get_template ( 'read.html' ). render (article = self.article)
)
class BlogUpdate (BaseArticle):
def __iter__ (self):
if self.environ[ 'REQUEST_METHOD' ]. upper () ### 'POST':
from urllib.parse import parse_qs
values = parse_qs (self.environ[ 'wsgi.input' ]. read ())
self.article[ 'title' ] = values[b 'title' ]. pop (). decode ()
self.article[ 'content' ] = values[b 'content' ]. pop (). decode ()
self. start ( '302 Found' ,
[( 'Content-Type' , 'text/html' ),
( 'Location' , '/' )])
return
self. start ( '200 OK' , [( 'Content-Type' , 'text/html' )])
yield str . encode (
env. get_template ( 'create.html' ). render (article = self.article)
)
class BlogDelete (BaseArticle):
def __iter__ (self):
self. start ( '302 Found' , # '301 Moved Permanently',
[( 'Content-Type' , 'text/html' ),
( 'Location' , '/' )])
ARTICLES. pop (self.index)
yield b ''
Links
https://eax.me/python-jinja/Работа с HTML-шаблонами в Python при помощи Jinja