Instalações diversas do Python, pip e venv


Onde o Python está instalado

Muitas vezes precisamos saber em que diretório está nossa instalação do python. Representaremos por $ o prompt do terminal do sistema, e comentários por #. O sinal [...] indica linhas omitidas.

# Primeiro verificamos se o python está instalado
$ python --version
  Python 3.9.13
# Essa versão pode ser inicializada
$ python
  Python 3.9.13 (main, Aug 25 2022, 23:26:10) 
  [GCC 11.2.0] :: Anaconda, Inc. on linux
  Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
  ['', '/home/usuario/.anaconda3/lib/python39.zip', '/home/usuario/.anaconda3/lib/python3.9', '/home/usuario/.anaconda3/lib/python3.9/lib-dynload', '/home/usuario/.anaconda3/lib/python3.9/site-packages']
>>> exit()

O terminal interativo do python pode ser abandonado com exit(), quit() ou CTRL-D.

Não há problema em ter mais de uma versão instalada. Para ter um controle de qual versão estamos usando, e quais os módulos foram instalados para um projeto, devemos usar um ambiente virtual, como o venv.

Vemos que o Python 3.9.13, instalado com o Anaconda (usado para disparar o Jupyter Notebook) teve prioridade na chamada. Aproveitamos para importar o módulo sys e verificar os caminhos válidos para essa instalação.

No entanto, tenho outra versão, mais recente, do Python instalada. Versões diferentes podem ser usadas para abrir um terminal de comando de linha, como acima, ou para carregar um ambiente virtual, por exemplo. Temos, portanto, que encontrar quais são as versões instaladas.

Existem algumas maneiras de descobrir onde o Python está instalado, tipicamente com os comandos which, whereis e find.

which exibe o diretório de instalação do executável Python que está no PATH,
whereis exibe o diretórios de instalação do código fonte, binários e páginas man,
find procura no sistema onde estão os arquivos *.py.

Por exemplo:

$ which python
  /home/guilherme/.anaconda3/bin/python
$ whereis python
  python: /usr/bin/python /home/guilherme/.anaconda3/bin/python /usr/share/man/man1/python.1.gz
$ find ~/Projetos -name *.py
# uma lista de arquivos com extensão py é exibida.

No último comando usamos find ~/Projetos -name *.py para encontrar arquivos com extensão .py partindo do diretório ~/Projetos, lembrando que ~ significa a pasta do usuário, /home/usuario. O comando find é muito mais geral que o exemplo mostrado e pode ser usado de muitas formas diferentes. Nenhum desses comandos são específicos para o python.

O comando whereis python mostra que tenho outra versão instalada, além do python da Anaconda, em /usr/bin/python . Ela pode ser executada diretamente do terminal:

$ /usr/bin/python
  Python 3.11.0 (main, Oct 24 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-2)] on linux
  Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
  ['', '/usr/lib64/python311.zip', '/usr/lib64/python3.11', '/usr/lib64/python3.11/lib-dynload', '/usr/lib64/python3.11/site-packages', '/usr/lib/python3.11/site-packages']

Vimos assim que há uma versão mais recente do python instalada, o python3.11. Como uma ilustração do uso de versões diferentes do python vamos estender a discussão para a instalação e uso de módulos instalados.

Módulos em diferentes versões

Vimos acima que, no caso de meu computador o comando python está associado ao python3.9.3 da anaconda (porque é o caminho que ele encontra primeiro no PATH (exibido abaixo). Essa instalação já contém o pip. No entanto não há pip instalado para a versão do python3.11.

# exeminando o PATH
$ echo $PATH
  /home/usuario/.anaconda3/share/rubygems/bin:/home/usuario/.anaconda3/share/rubygems/bin:
  /home/usuario/.anaconda3/bin:/home/usuario/.anaconda3/condabin:/home/usuario/.local/bin:
  /home/usuario:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin

# pip está instalado no python3.9
$ python -m pip --version
pip 22.2.2 from /home/guilherme/.anaconda3/lib/python3.9/site-packages/pip (python 3.9)

# pip não está instalado no python3.11
$ python3.11 -m pip --version
/usr/bin/python3.11: No module named pip

O pip não está instalado para o python3.11. Para essa instalação vamos usar o ensurepip, descrito no artigo sobre ambientes virtuais.

$ python3.11 -m ensurepip
  [...]
  Successfully installed pip-22.2.2

# para verificar a instalação
$ python3.11 -m pip --version
  pip 22.2.2 from /home/usuario/.local/lib/python3.11/site-packages/pip (python 3.11)

# caso um upgrade do pip seja necessário
$ python3.11 -m ensurepip --upgrade

Vemos assim que essa instalação do pip está em diretório diferente da instalação do anaconda. Para instalar módulos fazemos:

# para instalar módulos no ambiente do python3.9
$ python -m pip install <modulo>
# ou    
$ pip install <modulo>
 
# para instalar módulos no ambiente do python3.11
$ python3.11 -m pip install <modulo>


Observe que essa diferenciação entre os nomes python e python3.11 ocorre no meu computador e pode ser diferente dependendo da forma de instalação usada. Um computador pode ter várias outras versões do python instaladas, como o python2. Da mesma forma a criação de um ambiente virtual depende da versão do python que se quer usar.

Por default o Python é instalado no Linux no diretório /usr/local/bin/ e no Windows em C:\Python39 ou C:\Users\YourUser\AppData\Local\Programs\Python\PythonX onde X é o número de versão. O Anaconda é instalado em diretório definido pelo usuário.

Usando o pip

Para instalar um módulo no python3.11:

$ python3.11 -m pip install django

Para verificar quais os módulos instalados usamos:

$ python3.11 -m pip freeze
  [..]
  beautifulsoup4==4.11.0
  Django==4.1.3
  Markdown==3.4.1
  Pillow==9.2.0
  [..]

Os módulos instalados junto com o Anaconda são outros:

$ python -m pip freeze
  [..]
  anaconda-client==1.11.0
  anaconda-navigator==2.3.2
  [..]

Em ambos os casos várias linhas foram omitidas.
Também podemos ver os módulos instalados de dentro de uma sessão do python.

$ python3.11
  Python 3.11.0 (main, Oct 24 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-2)] on linux
>>> help("modules")
  Please wait a moment while I gather a list of all available modules...
  /usr/lib/python3.11/site-packages/_distutils_hack/__init__.py:33: UserWarning: Setuptools is replacing distutils.
  warnings.warn("Setuptools is replacing distutils.")
  PIL                     bisect              glob                       re
  PyQt5               blivet               gnome_abrt           readline
  [..]

# para obter ajuda específica sobre um módulo digitamos help("nome_modulo")
>>> help("random")

Módulos em ambientes virtuais

Para criar um ambiente virtual em uma versão específica do python informamos qual versão deve ser usada. Por ex., para criar um ambiente virtual em ~/Projetos/.venv usamos:

$ python3.11 -m venv ~/Projetos/.venv

# para ativar esse ambiente
$ source ~/Projetos/.venv/bin/activate
# o prompt muda para refletir esse estado
(.venv) $ 

# para executar o python nesse ambiente
$ cd ~/Projetos/.venv
(.venv) (~/Projetos/.venv)$ python
  Python 3.11.0 

Como visto, toda a operação feita dentro do ambiente se refere à versão do python que instalou o ambiente. Nesse ambiente a biblioteca pandas não está instalada. Para instalar essa biblioteca saimos do python e usamos pip:

>>> import pandas
  [..]
  ModuleNotFoundError: No module named 'pandas'
# saimos do python
>>> exit()

# instalamos o módulo pandas
(.venv) (~/Projetos/.venv)$ python -m pip install pandas
  Collecting pandas
  [..]
  Successfully installed numpy-1.23.5 pandas-1.5.2 python-dateutil-2.8.2 pytz-2022.6 six-1.16.0

# o comando acima sugere um upgrade do pip
  [notice] A new release of pip available: 22.2.2 -> 22.3.1
  [notice] To update, run: pip install --upgrade pip

(.venv) (~/Projetos/.venv)$ pip install --upgrade pip
  [..]
  Successfully installed pip-22.3.1

O uso do pip acima, após a instalação da biblioteca pandas, informa a existência de um upgrade e fornece o comando para fazer essa atualização.

Bibliografia

Sites

todos acessados em dezembro de 2022.

Entidades Html

Entidades HTML são códigos usados para a exibição de símbolos que não estão diretamente acessíveis pelo teclado, dentro de uma página de HTML (páginas da WEB). Eles podem representar caracteres técnicos, matemáticos, símbolos de moedas, sinais gráficos e muitos outros. A tabela lista os códigos html hexadecimal e decimal. Por exemplo, a página:

<html>
<body>
<h1>Exibindo html </h1>
<p>Símbolo de um naipe espada de baralho: &clubs; - &clubsuit; - &♣; - &#9827;.</p>
<p>Símbolo matemático da integral de contorno: &conint; - &oint; - &#x0222e; - &#8750;.
</body>
</html>

seria exibida da seguinte forma (dependendo, é claro, do browser e das instruções css da página):

Exibindo html

Símbolo de um naipe espada de baralho: ♣ – ♣ – ♣ – ♣.
Símbolo matemático, integral de contorno: ∮ – ∮ – ∮ – ∮.

Nem todos os browsers podem exibir corretamente todos os símbolos. Um código na primeira coluna da tabela representa uma falha de exibição. Passe o mouse sobre os caracteres da 1ª coluna para ampliar.

Caracteres comuns: Pontuação
Caracter HTML HEX DEC Descrição
&quot; &#x00022; &#34; aspas
, &comma; &#x0002c; &#44; vírgula
. &period; &#x0002e; &#46; ponto final
: &colon; &#x0003a; &#58; dois pontos
; &semi; &#x0003b; &#59; ponto e vírgula
? &quest; &#x0003f; &#63; ponto de interrogação
¿ &iquest; &#x000bf; &#191; ponto de interrogação invertido
! &excl; &#x00021; &#33; ponto de exclamação
¡ &iexcl; &#x000a1; &#161; ponto de exclamação invertido
Barras e Aspas
Caracter HTML HEX DEC Descrição
&hyphen; &dash; &#x02010; &#8208; hífen
&ndash; &#x02013; &#8211; en dash
&mdash; &#x02014; &#8212; travessão, metade de emdash
&horbar; &#x02015; &#8213; barra horizontal
| &verbar; &vert; &#x02016; &#8214; barra vertical
/ &sol; &#x0002f; &#47; barra, travessa ou tranca
\ &bsol; &#x0005c; &#92; barra reversa
_ &lowbar; &#x0005f; &#95; linha baixa
¦ &brvbar; &#x000a6; &#166; barra quebrada
&lsquo; &opencurlyquote; &#x02018; &#8216; aspas simples esquerda
&rsquo; &rsquor; &closecurlyquote; &#x02019; &#8217; aspas simples direitas
&lsquor; &sbquo; &#x0201a; &#8218; aspas baixas simples
&ldquo; &opencurlydoublequote; &#x0201c; &#8220; aspas duplas esquerdas
&rdquo; &rdquor; &closecurlydoublequote; &#x0201d; &#8221; aspas duplas direitas
&ldquor; &bdquo; &#x0201e; &#8222; aspas baixas duplas
« &laquo; &#x000ab; &#171; aspa dupla angular esquerda
» &raquo; &#x000bb; &#187; aspa dupla angular direita
Sinais Tipográficos
Caracter HTML HEX DEC Descrição
§ &sect; &#x000a7; &#167; seção (parágrafo)
&para; &#x000b6; &#182; marca de parágrafo, alínea
&blank; &#x02423; &#9251; caixa aberta
Abreviaturas
Caracter HTML HEX DEC Descrição
# &num; &#x00023; &#35; número
$ &dollar; &#x00024; &#36; cifrão
% &percnt; &#x00025; &#37; porcentagem
@ &commat; &#x00040; &#64; comercial em (arroba)
® &reg; &#x000ae; &#174; marca registrada
° &deg; &#x000b0; &#176; grau
¢ &cent; &#x000a2; &#162; 1 centésimo
¤ &curren; &#x000a4; &#164; moeda
&euro; &#x020ac; &#8364; sinal do euro
£ &pound; &#x000a3; &#163; libra
¥ &yen; &#x000a5; &#165; iene
& &amp; &#x00026; &#38; e comercial
* &ast; &midast; &#x0002a; &#42; asterisco
¬ &not; &#x000ac; &#172; negação (not)
­­ &shy; &#x000ad; &#173; hífen suave
¯ &macr; &strns; &#x000af; &#175; mácron
· &middot; &#x000b7; &#183; ponto central
Espaçamento e controles
Caracter HTML HEX DEC Descrição
tab &tab; &#x00009; &#9; caracter de tabulação
newline &newline; &#x0000a; &#10; alimentação de linha (lf)
&nbsp; &nonbreakingspace; &#x000a0; &#160; espaço sem quebra | |
&ensp; &#x02002; &#8194; espaço | |
&emsp; &#x02003; &#8195; espaço | |
&emsp13; &#x02004; &#8196; espaço 1 terço | |
&emsp14; &#x02005; &#8197; espaço 1 quarto | |
&numsp; &#x02007; &#8199; espaço de figura | |
&puncsp; &#x02008; &#8200; espaço de pontuação | |
&thinsp; &thinspace; &#x02009; &#8201; espaço pequeno | |
&hairsp; &verythinspace; &#x0200a; &#8202; espaço muito pequeno | |
&zerowidthspace; &negativethickspace; &#x0200b; &#8203; espaço de largura zero |​|
&zwnj; &#x0200c; &#8204; largura zero não grudado |‌|
&zwj; &#x0200d; &#8205; largura zero grudado |‍|
&mediumspace; &#x0205f; &#8287; espaço matemático médio | |
&nobreak; &#x02060; &#8288; juntador de palavras
&invisibletimes; &it; &#x02062; &#8290; multiplicação invisível
&invisiblecomma; &ic; &#x02063; &#8291; vírgula invisível
&lrm; &#x0200e; &#8206; marca da esquerda para a direita
&rlm; &#x0200f; &#8207; marca da direita para a esquerda
Ícones, Símbolos
Caracter HTML HEX DEC Descrição
&starf; &bigstar; &#x02605; &#9733; estrela preta
&star; &#x02606; &#9734; estrela branca
&spades; &spadesuit; &#x02660; &#9824; naipe paus preto
&clubs; &clubsuit; &#x02663; &#9827; naipe espada preto
&hearts; &heartsuit; &#x02665; &#9829; naipe copa preto
&diams; &diamondsuit; &#x02666; &#9830; naipe ouro preto
&loz; &#9674; &#x25CA; losango branco
&sung; &#x0266a; &#9834; oitava nota
&flat; &#x0266d; &#9837; bemol musical
&natur; &natural; &#x0266e; &#9838; natural de música
&sharp; &#x0266f; &#9839; sharp, sustenido músical
&check; &checkmark; &#x02713; &#10003; marca de verificação
&cross; &#x02717; &#10007; seleção x
&malt; &maltese; &#x02720; &#10016; cruz maltesa
&sext; &#x02736; &#10038; estrela preta de seis pontas
&lozf; &blacklozenge; &#x029eb; &#10731; losango preto
© &copy; &#x000a9; &#169; direitos autorais
&os; &circleds; &#x024c8; &#9416; S maiúsculo circulado
&phone; &#x0260e; &#9742; telefone preto
&female; &#x02640; &#9792; signo feminino
&male; &#x02642; &#9794; signo masculino
&elinters; &#x023e7; &#9191; intersecção elétrica
Símbolos Matemáticos: Operadores
Caracter HTML HEX DEC Descrição
+ &plus; &#x0002b; &#43; soma, mais
± &plusmn; &pm; &#x000b1; &#177; mais ou menos
× &times; &#x000d7; &#215; multiplicação
÷ &divide; &div; &#x000f7; &#247; divisão
= &equals; &#x0003d; &#61; igual
< &lt; &#x0003c; &#60; menor que
> &gt; &#x0003e; &#62; maior que
&minus; &#x02212; &#8722; menos (subtração)
&mnplus; &mp; &minusplus; &#x02213; &#8723; menos ou mais
&plusdo; &dotplus; &#x02214; &#8724; ponto mais
&radic; &sqrt; &#x0221a; &#8730; raiz quadrada
&setmn; &setminus; &ssetmn; &#x02216; &#8726; menos de conjunto
&lowast; &#x02217; &#8727; operador asterisco
&compfn; &smallcircle; &#x02218; &#8728; operador anel
&oplus; &circleplus; &#x02295; &#8853; mais circulado
&ominus; &circleminus; &#x02296; &#8854; menos circulado
&otimes; &circletimes; &#x02297; &#8855; vezes circulado
&osol; &#x02298; &#8856; divisão circulada
&odot; &circledot; &#x02299; &#8857; ponto circulado
&ocir; &circledcirc; &#x0229a; &#8858; operador de anel circular
&oast; &circledast; &#x0229b; &#8859; asterisco circulado
&odash; &circleddash; &#x0229d; &#8861; traço circulado
&plusb; &boxplus; &#x0229e; &#8862; mais quadrado
&minusb; &boxminus; &#x0229f; &#8863; menos quadrado
&timesb; &boxtimes; &#x022a0; &#8864; vezes quadrado
&sdotb; &dotsquare; &#x022a1; &#8865; ponto quadrado
&xodot; &bigodot; &#x02a00; &#10752; operador ponto circulado
&xoplus; &bigoplus; &#x02a01; &#10753; mais círculo
&xotime; &bigotimes; &#x02a02; &#10754; vezes circulado
&xuplus; &biguplus; &#x02a04; &#10756; operador de união com mais
&xsqcup; &bigsqcup; &#x02a06; &#10758; operador de união quadrado
&forall; &#x02200; &#8704; para todos
&comp; &complement; &#x02201; &#8705; complemento
&part; &partiald; &#x02202; &#8706; diferencial parcial
&exist; &exists; &#x02203; &#8707; existe
&nexist; &nexists; &#x02204; &#8708; não existe
&empty; &emptyset; &emptyv; &#x02205; &#8709; conjunto vazio
&nabla; &del; &#x02207; &#8711; nabla
&isin; &isinv; &element; &in; &#x02208; &#8712; elemento de
&notin; &notelement; &notinva; &#x02209; &#8713; não é elemento de
&niv; &reverseelement; &ni; &suchthat; &#x0220b; &#8715; contém como membro
&notni; &notniva; &notreverseelement; &#x0220c; &#8716; não contém como membro
&prod; &product; &#x0220f; &#8719; produtório
&coprod; &coproduct; &#x02210; &#8720; coprodutório
&sum; &#x02211; &#8721; somatório
Operações e Relações em Conjuntos
Caracter HTML HEX DEC Descrição
&uplus; &unionplus; &#x0228e; &#8846; união de conjuntos
&xcap; &intersection; &bigcap; &#x022c2; &#8898; interseção de conjuntos
&xcup; &union; &bigcup; &#x022c3; &#8899; união de conjuntos
&sub; &subset; &#x02282; &#8834; subconjunto de
&sup; &supset; &superset; &#x02283; &#8835; superconjunto de
&nsub; &#x02284; &#8836; não é um subconjunto de
&nsup; &#x02285; &#8837; não um superconjunto de
&sube; &subsetequal; &subseteq; &#x02286; &#8838; subconjunto de ou igual a
&supe; &supseteq; &supersetequal; &#x02287; &#8839; superconjunto de ou igual a
&nsube; &nsubseteq; &notsubsetequal; &#x02288; &#8840; nem um subconjunto de nem igual a
&nsupe; &nsupseteq; &notsupersetequal; &#x02289; &#8841; nem um superconjunto de nem igual a
&subne; &subsetneq; &#x0228a; &#8842; subconjunto de com não igual a
&supne; &supsetneq; &#x0228b; &#8843; superconjunto de com não igual a
&cupdot; &#x0228d; &#8845; multiplicação de conjuntos
Caracteres Especiais
Caracter HTML HEX DEC Descrição
ı &imath; &inodot; &#x00131; &#305; i sem ponto (mat.)
ȷ &jmath; &#x00237; &#567; j sem ponto (mat)
ƒ &fnof; &#x00192; &#402; f cortado
¹ &sup1; &#x000b9; &#185; um sobrescrito
&iinfin; &#x029dc; &#10716; infinito incompleto
&infintie; &#x029dd; &#10717; ligadura sobre infinito
&nvinfin; &#x029de; &#10718; infinito negado com barra vertical
µ &micro; &#x000b5; &#181; micro
Frações
Caracter HTML HEX DEC Descrição
¼ &frac14; &#x000bc; &#188; fração um quarto
½ &frac12; &half; &#x000bd; &#189; fração metade
¾ &frac34; &#x000be; &#190; fração três quartos
&frac13; &#x02153; &#8531; fração um terço
&frac23; &#x02154; &#8532; fração dois terços
&frac15; &#x02155; &#8533; fração um quinto
&frac25; &#x02156; &#8534; fração dois quintos
&frac35; &#x02157; &#8535; fração três quintos
&frac45; &#x02158; &#8536; fração quatro quintos
&frac16; &#x02159; &#8537; fração um sexto
&frac56; &#x0215a; &#8538; fração cinco sextos
&frac18; &#x0215b; &#8539; fração um oitavo
&frac38; &#x0215c; &#8540; fração três oitavos
&frac58; &#x0215d; &#8541; fração cinco oitavos
&frac78; &#x0215e; &#8542; fração sete oitavos
Sobrescritos
Caracter HTML HEX DEC Descrição
² &sup2; &#x000b2; &#178; dois sobrescrito
³ &sup3; &#x000b3; &#179; três sobrescrito
ª &ordf; &#x000aa; &#170; ordinal feminino
º &ordm; &#x000ba; &#186; ordinal masculino
Delimitadores
Caracter HTML HEX DEC Descrição
( &lpar; &#x00028; &#40; parêntese esquerdo
) &rpar; &#x00029; &#41; parêntese direito
[ &lsqb; &lbrack; &#x0005b; &#91; colchete esquerdo
] &rsqb; &rbrack; &#x0005d; &#93; colchete direito
{ &lcub; &lbrace; &#x0007b; &#123; chave esquerda
} &rcub; &rbrace; &#x0007d; &#125; chave direita
| &verbar; &vert; &#x0007c; &#124; linha vertical
&lang; &leftanglebracket; &langle; &#x027e8; &#10216; colchete angular esquerdo
&rang; &rightanglebracket; &rangle; &#x027e9; &#10217; colchete angular direito
&lang; &#x027ea; &#10218; colchete angular esquerdo duplo
&rang; &#x027eb; &#10219; colchete angular direito duplo
&verticalseparator; &#x02758; &#10072; barra vertical leve
Delimitadores Decorativos
Caracter HTML HEX DEC Descrição
&lbbrk; &#x02772; &#10098; chave ornamental esquerda
&rbbrk; &#x02773; &#10099; chave ornamental direita
&lobrk; &leftdoublebracket; &#x027e6; &#10214; colchete branco esquerdo
&robrk; &rightdoublebracket; &#x027e7; &#10215; colchete vazado direito
&loang; &#x027ec; &#10220; colchete vazado esquerdo
&roang; &#x027ed; &#10221; colchete vazado direito
&ufisht; &#x0297e; &#10622; rabo de peixe
⥿ &dfisht; &#x0297f; &#10623; rabo de peixe invertido
&lopar; &#x02985; &#10629; parêntese vazado esquerdo
&ropar; &#x02986; &#10630; parêntese vazado direito
&lbrke; &#x0298b; &#10635; colchete esquerdo com barra inferior
&rbrke; &#x0298c; &#10636; colchete direito com barra inferior
&lbrkslu; &#x0298d; &#10637; colchete esquerdo com marca no canto superior
&rbrksld; &#x0298e; &#10638; colchete direito com marca no canto inferior
&lbrksld; &#x0298f; &#10639; colchete esquerdo com marca no canto inferior
&rbrkslu; &#x02990; &#10640; colchete direito com marca no canto superior
&langd; &#x02991; &#10641; colchete angular esquerdo com ponto
&rangd; &#x02992; &#10642; colchete angular direito com ponto
&lparlt; &#x02993; &#10643; arco esquerdo menor que colchete
&rpargt; &#x02994; &#10644; arco direito maior que colchete
&gtlpar; &#x02995; &#10645; arco esquerdo duplo maior que colchete
&ltrpar; &#x02996; &#10646; arco direito duplo menor que colchete
Integrais
Caracter HTML HEX DEC Descrição
&int; &integral; &#x0222b; &#8747; integral
&int; &#x0222c; &#8748; integral dupla
&tint; &iiint; &#x0222d; &#8749; integral triplo
&conint; &oint; &contourintegral; &#x0222e; &#8750; integral de contorno
&conint; &doublecontourintegral; &#x0222f; &#8751; integral de superfície
&cwint; &#x02231; &#8753; integral no sentido horário
&awint; &#x02a11; &#10769; integração anti-horário
&cwconint; &#x02232; &#8754; integral de contorno no sentido horário
&awconint; &#x02233; &#8755; integral de contorno anti-horário
&qint; &iiiint; &#x02a0c; &#10764; operador quádruplo integral
&fpartint; &#x02a0d; &#10765; parte finita integral
&cirfnint; &#x02a10; &#10768; função de circulação
&rppolint; &#x02a12; &#10770; integral de caminho quadrado em torno do pólo
&scpolint; &#x02a13; &#10771; integral de caminho semicircular em torno do pólo
&npolint; &#x02a14; &#10772; integral de linha excluindo pólo
&pointint; &#x02a15; &#10773; integral circular em torno do polo
&quatint; &#x02a16; &#10774; operador integral de quatérnio
&intlarhk; &#x02a17; &#10775; integral com seta esquerda com gancho
Setas
Caracter HTML HEX DEC Descrição
&larr; &leftarrow; &leftarrow; &slarr; &#x02190; &#8592; seta esquerda
&uarr; &uparrow; &uparrow; &shortuparrow; &#x02191; &#8593; seta para cima
&rarr; &rightarrow; &rightarrow; &srarr; &#x02192; &#8594; seta direita
&darr; &downarrow; &downarrow; &shortdownarrow; &#x02193; &#8595; seta para baixo
&harr; &leftrightarrow; &leftrightarrow; &#x02194; &#8596; seta esquerda-direita
&nlarr; &#x0219a; &#8602; seta esquerda cortada
&nrarr; &nrightarrow; &#x0219b; &#8603; seta direita cortada
&rarrw; &rightsquigarrow; &#x0219d; &#8605; seta ondulada para a direita
&larr; &twoheadleftarrow; &#x0219e; &#8606; seta de duas pontas esquerda
&uarr; &#x0219f; &#8607; seta de duas pontas para cima
&rarr; &twoheadrightarrow; &#x021a0; &#8608; seta de duas pontas para a direita
&darr; &#x021a1; &#8609; seta de duas pontas para baixo
&cudarrr; &#x02935; &#10549; seta para a direita e curvando para baixo
&larrhk; &hookleftarrow; &#x021a9; &#8617; seta esquerda com gancho
&rarrhk; &hookrightarrow; &#x021aa; &#8618; seta direita com gancho
&varr; &updownarrow; &updownarrow; &#x02195; &#8597; seta para cima e para baixo
&nwarr; &nwarrow; &#x02196; &#8598; seta noroeste
&nearr; &nearrow; &#x02197; &#8599; seta nordeste
&searr; &searrow; &#x02198; &#8600; seta sudeste
&swarr; &swarrow; &#x02199; &#8601; seta sudoeste
&larrtl; &leftarrowtail; &#x021a2; &#8610; seta esquerda com cauda
&rarrtl; &rightarrowtail; &#x021a3; &#8611; seta direita com cauda
&leftteearrow; &mapstoleft; &#x021a4; &#8612; seta esquerda barra
&upteearrow; &mapstoup; &#x021a5; &#8613; seta para cima, barra
&map; &rightteearrow; &mapsto; &#x021a6; &#8614; seta direita, barra
&ruledelayed; &#x029f4; &#10740; dois pontos, seta direita; rule-delayed
&larrlp; &looparrowleft; &#x021ab; &#8619; seta esquerda com laço
&rarrlp; &looparrowright; &#x021ac; &#8620; seta direita com laço
&harrw; &leftrightsquigarrow; &#x021ad; &#8621; seta onda esquerda direita
&nharr; &nleftrightarrow; &#x021ae; &#8622; seta traço esquerda direita
&lsh; &#x021b0; &#8624; seta acima, ponta à esquerda
&rsh; &#x021b1; &#8625; seta acima, ponta à direita
&ldsh; &#x021b2; &#8626; seta abaixo, ponta à esquerda
&rdsh; &#x021b3; &#8627; seta abaixo, ponta à direita
&crarr; &#x021b5; &#8629; seta para baixo com canto esquerdo
&cularr; &curvearrowleft; &#x021b6; &#8630; seta semicírculo superior anti-horária
&curarr; &curvearrowright; &#x021b7; &#8631; seta de semicírculo superior horária
&olarr; &circlearrowleft; &#x021ba; &#8634; seta circular aberta anti-horária
&orarr; &circlearrowright; &#x021bb; &#8635; seta circular aberta horária
&lharu; &leftvector; &leftharpoonup; &#x021bc; &#8636; arpão esquerda, farpa para cima
&lhard; &downleftvector; &#x021bd; &#8637; arpão esquerda, farpa para baixo
&uharr; &rightupvector; &#x021be; &#8638; arpão para cima, farpa para a direita
&uharl; &leftupvector; &#x021bf; &#8639; arpão para cima, farpa para a esquerda
&rharu; &rightvector; &#x021c0; &#8640; arpão direita, farpa para cima
&rhard; &downrightvector; &#x021c1; &#8641; arpão direita, farpa para baixo
&dharr; &rightdownvector; &#x021c2; &#8642; arpão para baixo, farpa para a direita
&dharl; &leftdownvector; &#x021c3; &#8643; arpão para baixo, farpa para a esquerda
&rlarr; &rightleftarrows; &#x021c4; &#8644; seta direita sobre seta esquerda
&udarr; &uparrowdownarrow; &#x021c5; &#8645; seta para cima para a esquerda da seta para baixo
&lrarr; &leftrightarrows; &#x021c6; &#8646; seta esquerda sobre seta direita
&llarr; &leftleftarrows; &#x021c7; &#8647; duas setas, esquerda
&uuarr; &upuparrows; &#x021c8; &#8648; duas setas para cima
&rrarr; &rightrightarrows; &#x021c9; &#8649; duas setas para a direita
&ddarr; &downdownarrows; &#x021ca; &#8650; duas setas para baixo
&lrhar; &reverseequilibrium; &leftrightharpoons; &#x021cb; &#8651; arpão esquerda sobre o arpão direita
&rlhar; &rightleftharpoons; &equilibrium; &#x021cc; &#8652; arpão da direita sobre arpão da esquerda
&nlarr; &nleftarrow; &#x021cd; &#8653; seta dupla para a esquerda cortada
&nharr; &nleftrightarrow; &#x021ce; &#8654; seta dupla esquerda direita cortada
&nrarr; &nrightarrow; &#x021cf; &#8655; seta dupla para a direita com traço
&larr; &leftarrow; &doubleleftarrow; &#x021d0; &#8656; seta dupla para a esquerda
&uarr; &uparrow; &doubleuparrow; &#x021d1; &#8657; seta dupla para cima
&rarr; &rightarrow; &implies; &doublerightarrow; &#x021d2; &#8658; seta dupla para a direita
&darr; &downarrow; &doubledownarrow; &#x021d3; &#8659; seta dupla para baixo
&harr; &leftrightarrow; &doubleleftrightarrow; &iff; &#x021d4; &#8660; seta dupla esquerda direita
&laarr; &lleftarrow; &#x021da; &#8666; seta tripla para a esquerda
&raarr; &rrightarrow; &#x021db; &#8667; seta tripla para a direita
&zigrarr; &#x021dd; &#8669; seta zigzag para a direita
&larrb; &leftarrowbar; &#x021e4; &#8676; seta esquerda para barra
&rarrb; &rightarrowbar; &#x021e5; &#8677; seta direita para barra
&duarr; &downarrowuparrow; &#x021f5; &#8693; seta para baixo para a esquerda da seta para cima
&loarr; &#x021fd; &#8701; seta de cabeça aberta para a esquerda
&roarr; &#x021fe; &#8702; seta de cabeça aberta para a direita
&hoarr; &#x021ff; &#8703; seta esquerda direita aberta
&xlarr; &longleftarrow; &longleftarrow; &#x027f5; &#10229; seta longa para a esquerda
&xlarr; &longleftarrow; &doublelongleftarrow; &#x027f8; &#10232; seta dupla longa para a esquerda
&xrarr; &longrightarrow; &longrightarrow; &#x027f6; &#10230; seta longa para a direita
&xharr; &longleftrightarrow; &longleftrightarrow; &#x027f7; &#10231; seta longa esquerda direita
&xrarr; &longrightarrow; &doublelongrightarrow; &#x027f9; &#10233; seta dupla longa para a direita
&xharr; &longleftrightarrow; &#x027fa; &#10234; seta dupla longa esquerda direita
&xmap; &longmapsto; &#x027fc; &#10236; seta longa para a direita da barra
&dzigrarr; &#x027ff; &#10239; seta longa para a direita
&nvlarr; &#x02902; &#10498; seta dupla para a esquerda com traço vertical
&nvrarr; &#x02903; &#10499; seta dupla para a direita com traço vertical
&nvharr; &#x02904; &#10500; seta dupla esquerda direita com traço vertical
&map; &#x02905; &#10501; seta de duas pontas para a direita da barra
&lbarr; &#x0290c; &#10508; seta esquerda com traço duplo
&rbarr; &bkarow; &#x0290d; &#10509; seta direita com traço duplo
&lbarr; &#x0290e; &#10510; seta de traço triplo para a esquerda
&rbarr; &dbkarow; &#x0290f; &#10511; seta de traço triplo para a direita
&rbarr; &drbkarow; &#x02910; &#10512; seta de duas pontas para a direita com traço triplo
&ddotrahd; &#x02911; &#10513; seta direita com haste pontilhada
&uparrowbar; &#x02912; &#10514; seta para cima para barra
&downarrowbar; &#x02913; &#10515; seta para baixo para barra
&rarrtl; &#x02916; &#10518; seta de duas pontas para a direita com cauda
&latail; &#x02919; &#10521; cauda de seta esquerda
&ratail; &#x0291a; &#10522; cauda de seta direita
&latail; &#x0291b; &#10523; cauda de seta dupla para a esquerda
&ratail; &#x0291c; &#10524; cauda de seta dupla para a direita
&larrfs; &#x0291d; &#10525; seta esquerda para diamante preto
&rarrfs; &#x0291e; &#10526; seta direita para diamante preto
&larrbfs; &#x0291f; &#10527; seta esquerda da barra ao diamante preto
&rarrbfs; &#x02920; &#10528; seta direita da barra para o diamante preto
&nwarhk; &#x02923; &#10531; seta noroeste com gancho
&nearhk; &#x02924; &#10532; flecha nordeste com gancho
&searhk; &hksearow; &#x02925; &#10533; seta sudeste com gancho
&swarhk; &hkswarow; &#x02926; &#10534; seta sudoeste com gancho
&nwnear; &#x02927; &#10535; seta noroeste e seta nordeste
&nesear; &toea; &#x02928; &#10536; seta nordeste e seta sudeste
&seswar; &tosa; &#x02929; &#10537; seta sudeste e seta sudoeste
&swnwar; &#x0292a; &#10538; seta sudoeste e seta noroeste
&rarrc; &#x02933; &#10547; seta ondulada para a direita
&ldca; &#x02936; &#10550; seta para baixo e curvando para a esquerda
&rdca; &#x02937; &#10551; seta para baixo e curvando para a direita
&cudarrl; &#x02938; &#10552; arco do lado direito seta no sentido horário
&larrpl; &#x02939; &#10553; arco do lado esquerdo no sentido anti-horário
&curarrm; &#x0293c; &#10556; arco superior seta no sentido horário com menos
&cularrp; &#x0293d; &#10557; seta do arco superior no sentido anti-horário com mais
&rarrpl; &#x02945; &#10565; seta direita com mais abaixo
&harrcir; &#x02948; &#10568; seta esquerda direita através de círculo
&uarrocir; &#x02949; &#10569; seta de duas pontas para cima do círculo
&lurdshar; &#x0294a; &#10570; farpa esquerda para cima direita farpa para baixo arpão
&ldrushar; &#x0294b; &#10571; farpa esquerda para baixo direita farpa para cima arpão
&leftrightvector; &#x0294e; &#10574; farpa esquerda para cima direita farpa para arpão
&rightupdownvector; &#x0294f; &#10575; para cima farpa direita para baixo farpa arpão direito
&downleftrightvector; &#x02950; &#10576; farpa esquerda para baixo direita farpa para baixo arpão
&leftupdownvector; &#x02951; &#10577; cima farpa esquerda baixo farpa esquerda arpão
&leftvectorbar; &#x02952; &#10578; arpão esquerda com farpa até a barra
&rightvectorbar; &#x02953; &#10579; arpão direita com farpa até a barra
&rightupvectorbar; &#x02954; &#10580; arpão para cima com farpa direita na barra
&rightdownvectorbar; &#x02955; &#10581; arpão para baixo com farpa à direita da barra
&downleftvectorbar; &#x02956; &#10582; arpão esquerda com farpa até a barra
&downrightvectorbar; &#x02957; &#10583; arpão direita com farpa até a barra
&leftupvectorbar; &#x02958; &#10584; arpão para cima com farpa da esquerda para a barra
&leftdownvectorbar; &#x02959; &#10585; arpão para baixo com farpa da esquerda para a barra
&leftteevector; &#x0295a; &#10586; arpão esquerda com farpa para cima da barra
&rightteevector; &#x0295b; &#10587; arpão direita com farpa para cima da barra
&rightupteevector; &#x0295c; &#10588; arpão para cima com farpa direto da barra
&rightdownteevector; &#x0295d; &#10589; arpão para baixo com farpa direto da barra
&downleftteevector; &#x0295e; &#10590; arpão esquerda com farpa para baixo da barra
&downrightteevector; &#x0295f; &#10591; arpão direita com farpa para baixo da barra
&leftupteevector; &#x02960; &#10592; arpão para cima com farpa esquerda da barra
&leftdownteevector; &#x02961; &#10593; arpão para baixo com farpa esquerda da barra
&lhar; &#x02962; &#10594; arpão esquerda com farpa para cima acima do arpão esquerda com farpa para baixo
&uhar; &#x02963; &#10595; arpão para cima com farpa esquerda ao lado arpão para cima com farpa direita
&rhar; &#x02964; &#10596; arpão direita com farpa para cima acima para arpão direita com farpa para baixo
&dhar; &#x02965; &#10597; arpão para baixo com farpa esquerda ao lado arpão para baixo com farpa direita
&luruhar; &#x02966; &#10598; arpão esquerda com farpa para cima acima do arpão direita com farpa para cima
&ldrdhar; &#x02967; &#10599; arpão esquerda com farpa para baixo acima do arpão direita com farpa para baixo
&ruluhar; &#x02968; &#10600; arpão direita com farpa para cima acima do arpão esquerda com farpa para cima
&rdldhar; &#x02969; &#10601; arpão direita com farpa para baixo acima do arpão esquerda com farpa para baixo
&lharul; &#x0296a; &#10602; arpão esquerda com farpa acima do longo traço
&llhard; &#x0296b; &#10603; arpão esquerda com farpa abaixo do longo traço
&rharul; &#x0296c; &#10604; arpão direita com farpa acima do longo traço
&lrhard; &#x0296d; &#10605; arpão direita com farpa abaixo do longo traço
&udhar; &upequilibrium; &#x0296e; &#10606; arpão p/ cima com farpa esquerda ao lado arpão para baixo com farpa direita
&duhar; &reverseupequilibrium; &#x0296f; &#10607; arpão para baixo com farpa esquerda ao lado arpão para cima com farpa direita
&roundimplies; &#x02970; &#10608; seta dupla direita com cabeça arredondada
&erarr; &#x02971; &#10609; igual acima da seta direita
&simrarr; &#x02972; &#10610; operador til acima da seta direita
&larrsim; &#x02973; &#10611; seta esquerda acima do operador til
&rarrsim; &#x02974; &#10612; seta direita acima do operador til
&rarrap; &#x02975; &#10613; seta direita acima quase igual a
&ltlarr; &#x02976; &#10614; seta esquerda menor que acima
&gtrarr; &#x02978; &#10616; maior do que acima da seta direita
&subrarr; &#x02979; &#10617; subconjunto acima da seta direita
&suplarr; &#x0297b; &#10619; superconjunto acima da seta esquerda
&lfisht; &#x0297c; &#10620; cauda de peixe esquerda
&rfisht; &#x0297d; &#10621; cauda de peixe direita
Caracteres Gráficos: Desenho
Caracter HTML HEX DEC Descrição
&boxh; &horizontalline; &#x02500; &#9472; parte de caixa: linha horizontal
&boxv; &#x02502; &#9474; parte de caixa: linha vertical
&boxdr; &#x0250c; &#9484; parte de caixa: canto superior esquerdo
&boxdl; &#x02510; &#9488; desenho da caixa canto superior direito
&boxur; &#x02514; &#9492; parte de caixa: canto inferior esquerdo
&boxul; &#x02518; &#9496; parte de caixa: canto inferior direito
&boxvr; &#x0251c; &#9500; parte de caixa: linha central direita
&boxvl; &#x02524; &#9508; parte de caixa: linha central esquerda
&boxhd; &#x0252c; &#9516; parte de caixa: linha para baixo e horizontal
&boxhu; &#x02534; &#9524; parte de caixa: iluminados e horizontais
&boxvh; &#x0253c; &#9532; parte de caixa: de linha vertical e horizontal
&boxh; &#x02550; &#9552; parte de caixa: dupla horizontal
&boxv; &#x02551; &#9553; parte de caixa: dupla vertical
&boxdr; &#x02552; &#9554; parte de caixa: para baixo simples e duplo direito
&boxdr; &#x02553; &#9555; parte de caixa: para baixo duplo e único direito
&boxdr; &#x02554; &#9556; os desenhos da caixa dobram para baixo e para a direita
&boxdl; &#x02555; &#9557; parte de caixa: para baixo simples e duplo esquerdo
&boxdl; &#x02556; &#9558; parte de caixa: para baixo duplo e único esquerdo
&boxdl; &#x02557; &#9559; desenhos da caixa dobrar para baixo e para a esquerda
&boxur; &#x02558; &#9560; parte de caixa: s simples e duplas à direita
&boxur; &#x02559; &#9561; caixa de desenhos dupla e direita simples
&boxur; &#x0255a; &#9562; os desenhos da caixa dobram e correm
&boxul; &#x0255b; &#9563; parte de caixa: para cima simples e duplo esquerdo
&boxul; &#x0255c; &#9564; parte de caixa: duplo e esquerdo simples
&boxul; &#x0255d; &#9565; os desenhos da caixa dobram e saem
&boxvr; &#x0255e; &#9566; parte de caixa: vertical simples e duplo direito
&boxvr; &#x0255f; &#9567; parte de caixa: vertical duplo e direito simples
&boxvr; &#x02560; &#9568; parte de caixa: dupla vertical e direita
&boxvl; &#x02561; &#9569; parte de caixa: vertical simples e duplo esquerdo
&boxvl; &#x02562; &#9570; parte de caixa: dupla vertical e esquerda simples
&boxvl; &#x02563; &#9571; parte de caixa: duplo vertical e esquerdo
&boxhd; &#x02564; &#9572; parte de caixa: para baixo simples e duplo horizontal
&boxhd; &#x02565; &#9573; parte de caixa: para baixo duplo e horizontal simples
&boxhd; &#x02566; &#9574; parte de caixa: dobrar para baixo e horizontal
&boxhu; &#x02567; &#9575; parte de caixa: para cima simples e duplo horizontal
&boxhu; &#x02568; &#9576; parte de caixa: duplo e horizontal simples
&boxhu; &#x02569; &#9577; parte de caixa: dobrados e horizontais
&boxvh; &#x0256a; &#9578; parte de caixa: vertical simples e horizontal duplo
&boxvh; &#x0256b; &#9579; parte de caixa: dupla vertical e simples horizontal
&boxvh; &#x0256c; &#9580; parte de caixa: duplo vertical e horizontal
&uhblk; &#x02580; &#9600; meio bloco superior
&lhblk; &#x02584; &#9604; meio bloco inferior
&block; &#x02588; &#9608; bloco completo
&blk14; &#x02591; &#9617; sombra clara
&blk12; &#x02592; &#9618; tom médio
&blk34; &#x02593; &#9619; sombra escura
Figuras Geométricas
Caracter HTML HEX DEC Descrição
&squ; &square; &square; &#x025a1; &#9633; quadrado branco
&squf; &squarf; &blacksquare; &filledverysmallsquare; &#x025aa; &#9642; quadrado pequeno preto
&emptyverysmallsquare; &#x025ab; &#9643; quadrado pequeno branco
&rect; &#x025ad; &#9645; retângulo branco
&marker; &#x025ae; &#9646; retângulo preto vertical
&fltns; &#x025b1; &#9649; paralelogramo branco
&xutri; &bigtriangleup; &#x025b3; &#9651; triângulo branco para cima
&utrif; &blacktriangle; &#x025b4; &#9652; triângulo pequeno preto para cima
&utri; &triangle; &#x025b5; &#9653; triângulo pequeno branco para cima
&rtrif; &blacktriangleright; &#x025b8; &#9656; triângulo pequeno preto para a direita
&rtri; &triangleright; &#x025b9; &#9657; triângulo pequeno branco para a direita
&xdtri; &bigtriangledown; &#x025bd; &#9661; triângulo branco para baixo
&dtrif; &blacktriangledown; &#x025be; &#9662; triângulo pequeno preto para baixo
&dtri; &triangledown; &#x025bf; &#9663; ponto baixo branco
&ltrif; &blacktriangleleft; &#x025c2; &#9666; triângulo pequeno preto para a esquerda
&ltri; &triangleleft; &#x025c3; &#9667; triângulo pequeno branco para a esquerda
&loz; &lozenge; &#x025ca; &#9674; pastilha
&cir; &#x025cb; &#9675; círculo branco
&tridot; &#x025ec; &#9708; triângulo branco para cima com ponto
&xcirc; &bigcirc; &#x025ef; &#9711; grande círculo
&ultri; &#x025f8; &#9720; triângulo superior esquerdo
&urtri; &#x025f9; &#9721; triângulo superior direito
&lltri; &#x025fa; &#9722; triângulo inferior esquerdo
&emptysmallsquare; &#x025fb; &#9723; quadrado médio branco
&filledsmallsquare; &#x025fc; &#9724; quadrado médio preto
Acentos
Caracter HTML HEX DEC Descrição
´ &acute; &diacriticalacute; &#x000b4; &#180; acento agudo
^ &hat; &#x0005e; &#94; acento circunflexo
ˆ &circ; &#x002c6; &#710; acento circunflexo
` &grave; &diacriticalgrave; &#x00060; &#96; acento grave
˘ &breve; &#x002d8; &#728; breve
¸ &cedil; &cedilla; &#x000b8; &#184; cedilha
¨ &dot; &die; &uml; &#x000a8; &#168; trema
&apos; &#x00027; &#39; apóstrofe
˜ &tilde; &#x002dc; &#732; til pequeno
˙ &dot; &#x002d9; &#729; ponto acima
ˇ &caron; &#x002c7; &#711; caron
˚ &ring; &#x002da; &#730; anel acima
˛ &ogon; &#x002db; &#731; ogonek
˝ &dblac; &#x002dd; &#733; acento agudo duplo
_ &UnderBar; &#x00332; &#818; traço subescrito
Caracteres Acentuados
Caracter HTML HEX DEC Descrição
À &Agrave; &#x000c0; &#192; A grave
Á &Aacute; &#x000c1; &#193; A agudo
 &Acirc; &#x000c2; &#194; A circunflexo
à &Atilde; &#x000c3; &#195; A til
Ä &Auml; &#x000c4; &#196; A trema
Å &Aring; &#x000c5; &#197; A anel
Æ &Aelig; &#x000c6; &#198; Ae
à &agrave; &#x000e0; &#224; a grave
á &aacute; &#x000e1; &#225; a agudo
â &acirc; &#x000e2; &#226; a circunflexo
ã &atilde; &#x000e3; &#227; a til
ä &auml; &#x000e4; &#228; a trema
å &aring; &#x000e5; &#229; a anel acima
æ &aelig; &#x000e6; &#230; ae
Ā &Amacr; &#x00100; &#256; A mácron
ā &amacr; &#x00101; &#257; a mácron
Ă &Abreve; &#x00102; &#258; A breve
ă &abreve; &#x00103; &#259; a breve
Ą &Aogon; &#x00104; &#260; A ogonek
ą &aogon; &#x00105; &#261; a ogonek
Ć &Cacute; &#x00106; &#262; C com agudo
ć &cacute; &#x00107; &#263; c com agudo
Ĉ &Ccirc; &#x00108; &#264; C com circunflexo
ĉ &ccirc; &#x00109; &#265; c com circunflexo
Ċ &Cdot; &#x0010a; &#266; C com ponto acima
ċ &cdot; &#x0010b; &#267; c com ponto acima
č &ccaron; &#x0010c; &#268; maiúscula c com caron
č &ccaron; &#x0010d; &#269; minúscula c com caron
ç &ccedil; &#x000c7; &#199; c com cedilha
Ď &Dcaron; &#x0010e; &#270; D caron
ď &dcaron; &#x0010f; &#271; d caron
Đ &Dstrok; &#x00110; &#272; D com traço
đ &dstrok; &#x00111; &#273; d com traço
È &Egrave; &#x000e8; &#232; E grave
è &egrave; &#x000c8; &#200; e com grave
é &eacute; &#x000c9; &#201; e com agudo
é &eacute; &#x000e9; &#233; e agudo
ê &ecirc; &#x000ea; &#234; e circunflexo
ê &ecirc; &#x000ca; &#202; e com circunflexo
Ë &Euml; &#x000cb; &#203; E com trema
ë &euml; &#x000eb; &#235; e trema
Ē &Emacr; &#x00112; &#274; E mácron
ē &emacr; &#x00113; &#275; e mácron
Ė &Edot; &#x00116; &#278; E ponto
ė &edot; &#x00117; &#279; e ponto
Ę &Eogon; &#x00118; &#280; E com ogonek
ę &eogon; &#x00119; &#281; e com ogonek
Ě &Ecaron; &#x0011a; &#282; E caron
ě &ecaron; &#x0011b; &#283; e caron
ð &eth; &#x000f0; &#240; eth minúsculo
Ð &Eth; &#x000d0; &#208; eth maiúsculo
Ĝ &Gcirc; &#x0011c; &#284; G circunflexo
ĝ &gcirc; &#x0011d; &#285; g circunflexo
Ğ &Gbreve; &#x0011e; &#286; G breve
ğ &gbreve; &#x0011f; &#287; g breve
Ġ &Gdot; &#x00120; &#288; G com ponto acima
ġ &gdot; &#x00121; &#289; g com ponto acima
ǵ &gacute; &#x001f5; &#501; g com agudo
Ĥ &Hcirc; &#x00124; &#292; H circunflexo
ĥ &hcirc; &#x00125; &#293; h circunflexo
Ħ &Hstrok; &#x00126; &#294; H com traço
ħ &hstrok; &#x00127; &#295; h com traço
ì &igrave; &#x000ec; &#236; i grave
í &iacute; &#x000ed; &#237; i agudo
î &icirc; &#x000ee; &#238; i circunflexo
ï &iuml; &#x000ef; &#239; i trema
Ĩ &itilde; &#x00129; &#297; I til
ĩ &itilde; &#x00128; &#296; i til
Ī &Imacr; &#x0012a; &#298; I com mácron
ī &imacr; &#x0012b; &#299; i com mácron
Į &Iogon; &#x0012e; &#302; I com ogonek
į &iogon; &#x0012f; &#303; i com ogonek
İ &Idot; &#x00130; &#304; I com ponto
IJ &IJlig; &#x00132; &#306; ligadura IJ
ij &ijlig; &#x00133; &#307; ligadura ij
Ĵ &Jcirc; &#x00134; &#308; J circunflexo
ĵ &jcirc; &#x00135; &#309; j circunflexo
Ķ &Kcedil; &#x00136; &#310; K com cedilha
ķ &kcedil; &#x00137; &#311; k com cedilha
ĸ &kgreen; &#x00138; &#312; kra minúsculo
Ĺ &Lacute; &#x00139; &#313; L agudo
ĺ &lacute; &#x0013a; &#314; l agudo
Ļ &Lcedil; &#x0013b; &#315; L com cedilha
ļ &lcedil; &#x0013c; &#316; l com cedilha
Ľ &Lcaron; &#x0013d; &#317; L com caron
ľ &lcaron; &#x0013e; &#318; l com caron
Ŀ &Lmidot; &#x0013f; &#319; L com ponto
ŀ &lmidot; &#x00140; &#320; l com ponto
Ł &Lstrok; &#x00141; &#321; L com traço
ł &lstrok; &#x00142; &#322; l com traço
Ń &Nacute; &#x00143; &#323; N com agudo
ń &nacute; &#x00144; &#324; n com agudo
Ņ &Ncedil; &#x00145; &#325; N com cedilha
ņ &ncedil; &#x00146; &#326; n com cedilha
Ň &Ncaron; &#x00147; &#327; N com caron
ň &ncaron; &#x00148; &#328; n com caron
ʼn &napos; &#x00149; &#329; n precedida de apóstrofo
ñ &ntilde; &#x000d1; &#209; n com til
ò &ograve; &#x000f2; &#242; o grave
ó &oacute; &#x000f3; &#243; o agudo
ô &ocirc; &#x000f4; &#244; o circunflexo
õ &otilde; &#x000f5; &#245; o til
ö &ouml; &#x000f6; &#246; o com trema
ø &oslash; &#x000f8; &#248; o com traço
Ō &Omacr; &#x0014c; &#332; O com macron
ō &omacr; &#x0014d; &#333; o com mácron
Ő &Odblac; &#x00150; &#336; O com duplo agudo
ő &odblac; &#x00151; &#337; o com duplo agudo
Œ &Oelig; &#x00152; &#338; ligadura OE maiúscula
œ &oelig; &#x00153; &#339; ligadura oe minúscula
Ŕ &Racute; &#x00154; &#340; R com agudo
ŕ &racute; &#x00155; &#341; r com agudo
Ŗ &Rcedil; &#x00156; &#342; R com cedilha
ŗ &rcedil; &#x00157; &#343; r com cedilha
Ř &Rcaron; &#x00158; &#344; R com caron
ř &rcaron; &#x00159; &#345; r minúscula com caron
Ś &Sacute; &#x0015a; &#346; S com agudo
ś &sacute; &#x0015b; &#347; s com agudo
Ŝ &Scirc; &#x0015c; &#348; S com circunflexo
ŝ &scirc; &#x0015d; &#349; s com circunflexo
Ş &Scedil; &#x0015e; &#350; S com cedilha
ş &scedil; &#x0015f; &#351; s com cedilha
Š &Scaron; &#x00160; &#352; S com caron
š &scaron; &#x00161; &#353; s com caron
Ţ &Tcedil; &#x00162; &#354; T com cedilha
ţ &tcedil; &#x00163; &#355; t com cedilha
Ť &Tcaron; &#x00164; &#356; T com caron
ť &tcaron; &#x00165; &#357; t com caron
Ŧ &Tstrok; &#x00166; &#358; T com traço
ŧ &tstrok; &#x00167; &#359; t com traço
ù &ugrave; &#x000f9; &#249; u grave
ú &uacute; &#x000fa; &#250; u agudo
û &ucirc; &#x000fb; &#251; u circunflexo
ü &uuml; &#x000fc; &#252; u com trema
Ũ &Utilde; &#x00168; &#360; U com til
ũ &utilde; &#x00169; &#361; u com til
Ū &Umacr; &#x0016a; &#362; U com mácron
ū &umacr; &#x0016b; &#363; u com mácron
Ŭ &Ubreve; &#x0016c; &#364; U breve
ŭ &ubreve; &#x0016d; &#365; u breve
Ů &Uring; &#x0016e; &#366; U com anel
ů &uring; &#x0016f; &#367; u com anel
Ű &Udblac; &#x00170; &#368; U com duplo agudo
ű &udblac; &#x00171; &#369; u com duplo agudo
Ų &Uogon; &#x00172; &#370; U com ogonek
ų &uogon; &#x00173; &#371; u com ogonek
Ŵ &Wcirc; &#x00174; &#372; W com circunflexo
ŵ &wcirc; &#x00175; &#373; w com circunflexo
Ŷ &Ycirc; &#x00176; &#374; Y com circunflexo
ŷ &ycirc; &#x00177; &#375; y com circunflexo
Ÿ &Yuml; &#x00178; &#376; Y com trema
ÿ &yuml; &#x000ff; &#255; y com trema
Ý &Yacute; &#x000dd; &#221; Y com agudo
ý &yacute; &#x000fd; &#253; y com agudo
Ź &Zacute; &#x00179; &#377; Z com agudo
ź &zacute; &#x0017a; &#378; z com agudo
Ż &Zdot; &#x0017b; &#379; Z com ponto acima
ż &zdot; &#x0017c; &#380; z com ponto acima
Ž &Zcaron; &#x0017d; &#381; Z com caron
ž &zcaron; &#x0017e; &#382; z com caron
Ƶ &imped; &#x001b5; &#437; Z cortado
Ligaduras de Carateres Latinos
Caracter HTML HEX DEC Descrição
IJ &IJlig; &#x00132; &#306; ligadura IJ (maiúsculo)
ij &ijlig; &#x00133; &#307; ligadura ij (minúsculo)
Œ &OElig; &#x00152; amp;Œ; ligadura OE maiúscula
œ &oelig; &#x00153; &#339; ligadura oe minúscula
&fflig; &#x0fb00; &#64256; ligadura ff
&filig; &#x0fb01; &#64257; ligadura fi
&fllig; &#x0fb02; &#64258; ligadura fl
&ffilig; &#x0fb03; &#64259; ligadura ffi
&ffllig; &#x0fb04; &#64260; ligadura ffl
Carateres Não Latinos
Caracter HTML HEX DEC Descrição
ŋ &eng; &#x0014b; &#331; minúscula eng
Þ &Thorn; &#x000de; &#222; ‘thorn’ maiúsculo
þ &thorn; &#x000fe; &#254; ‘thorn’ minúsculo
ß &szlig; &#x000df; &#223; ‘eszett’ alemão
Caracteres script
Caracter HTML HEX DEC Descrição
&ascr; &#x1d49c; &#119964; a minúsculo
&bscr; &#x1d49D; &#119965; b
&cscr; &#x1d49e; &#119966; c
&dscr; &#x1d49f; &#119967; d
&escr; &#x1d4a0; &#119968; e
&fscr; &#x1d4a1; &#119969; e
&gscr; &#x1d4a2; &#119970; g
&hscr; &#x1d4a3; &#119971; h
&iscr; &#x1d4a4; &#119972; i
&jscr; &#x1d4a5; &#119973; j
&kscr; &#x1d4a6; &#119974; k
&lscr; &#x1d4a7; &#119975; l
&mscr; &#x1d4a8; &#119976; m
&nscr; &#x1d4a9; &#119977; n
&oscr; &#x1d4aa; &#119978; o
&pscr; &#x1d4ab; &#119979; p
&qscr; &#x1d4ac; &#119980; q
&rscr; &#x1d4ad; &#119981; r
&sscr; &#x1d4ae; &#119982; s
&tscr; &#x1d4af; &#119983; t
&uscr; &#x1d4b0; &#119984; u
&vscr; &#x1d4b1; &#119985; v
&wscr; &#x1d4b2; &#119986; w
&xscr; &#x1d4b3; &#119987; x
&yscr; &#x1d4b4; &#119988; y
&zscr; &#x1d4b5; &#119989; z
Caracter HTML HEX DEC Descrição
&Ascr; &#x1d4b6; &#119989; A maiúsculo
&Bscr; &#x1d4b7; &#119990; B
&Cscr; &#x1d4b8; &#119991; C
&Dscr; &#x1d4b9; &#119992; D
&Escr; &#x1d4ba; &#119993; E
&Fscr; &#x1d4bb; &#119994; F
&Gscr; &#x1d4bc; &#119995; G
&Hscr; &#x1d4bd; &#119996; H
&Iscr; &#x1d4be; &#119997; I
&Jscr; &#x1d4bf; &#119998; J
&Kscr; &#x1d4c0; &#119999; K
&Lscr; &#x1d4c1; &#120000; L
&Mscr; &#x1d4c2; &#120001; M
&Nscr; &#x1d4c3; &#120002; N
&Oscr; &#x1d4c4; &#120003; O
&Pscr; &#x1d4c5; &#120004; P
&Qscr; &#x1d4c6; &#120005; Q
&Rscr; &#x1d4c7; &#120006; R
&Sscr; &#x1d4c8; &#120007; S
&Tscr; &#x1d4c9; &#120008; T
&Uscr; &#x1d4ca; &#120009; U
&Vscr; &#x1d4cb; &#120010; V
&Wscr; &#x1d4cc; &#120011; W
&Xscr; &#x1d4cd; &#120012; X
&Yscr; &#x1d4ce; &#120013; Y
&Zscr; &#x1d4ce; &#120014; Z
Caracteres Fraktur
Caracter HTML HEX DEC Descrição
&afr; &#x1d504; &#120068; a minúsculo
&bfr; &#x1d505; &#120069; b
&cfr; &#x1d506; &#120070; c
&dfr; &#x1d507; &#120071; d
&efr; &#x1d508; &#120072; e
&ffr; &#x1d509; &#120073; f
&gfr; &#x1d50a; &#120074; g
&hfr; &#x1d50b; &#120075; h
&ifr; &#x1d50c; &#120076; i
&jfr; &#x1d50d; &#120077; j
&kfr; &#x1d50e; &#120078; k
&lfr; &#x1d50f; &#120079; l
&mfr; &#x1d510; &#120080; m
&nfr; &#x1d511; &#120081; n
&ofr; &#x1d512; &#120082; o
&pfr; &#x1d513; &#120083; p
&qfr; &#x1d514; &#120084; q
&rfr; &#x1d515; &#120085; r
&sfr; &#x1d516; &#120086; s
&tfr; &#x1d517; &#120087; t
&ufr; &#x1d518; &#120088; u
&vfr; &#x1d519; &#120089; v
&wfr; &#x1d51a; &#120090; w
&xfr; &#x1d51b; &#120091; x
&yfr; &#x1d51c; &#120092; y
&zfr; &#x1d51d; &#120093; z
Caracter HTML HEX DEC Descrição
&Afr; &#x1d51e; &#120094; A maiúsculo
&Bfr; &#x1d51f; &#120095; B
&Cfr; &#x1d520; &#120096; C
&Dfr; &#x1d521; &#120097; D
&Efr; &#x1d522; &#120098; E
&Ffr; &#x1d523; &#120099; F
&Gfr; &#x1d524; &#120100; G
&Hfr; &#x1d525; &#120101; H
&Ifr; &#x1d526; &#120102; I
&Jfr; &#x1d527; &#120103; J
&Kfr; &#x1d528; &#120104; K
&Lfr; &#x1d529; &#120105; L
&Mfr; &#x1d52a; &#120106; M
&Nfr; &#x1d52b; &#120107; N
&Ofr; &#x1d52c; &#120108; O
&Pfr; &#x1d52d; &#120109; P
&Qfr; &#x1d52e; &#120110; Q
&Rfr; &#x1d52f; &#120111; R
&Sfr; &#x1d530; &#120112; S
&Tfr; &#x1d531; &#120113; T
&Ufr; &#x1d532; &#120114; U
&Vfr; &#x1d533; &#120115; V
&Wfr; &#x1d534; &#120116; W
&Xfr; &#x1d535; &#120117; X
&Yfr; &#x1d536; &#120118; Y
&Zfr; &#x1d537; &#120119; Z
Caracteres de traço duplo
Caracter HTML HEX DEC Descrição
&aopf; &#x1d538; &#120120; a minúsculo
&bopf; &#x1d539; &#120121; b
&copf; &#x1d539; &#120122; c
&dopf; &#x1d53b; &#120123; d
&eopf; &#x1d53c; &#120124; e
&fopf; &#x1d53d; &#120125; f
&gopf; &#x1d53e; &#120126; g
&hopf; &#x1d53e; &#120127; h
&iopf; &#x1d540; &#120128; i
&jopf; &#x1d541; &#120129; j
&kopf; &#x1d542; &#120130; k
&lopf; &#x1d543; &#120131; l
&mopf; &#x1d544; &#120132; m
&nopf; &#x1d545; &#120133; n
&oopf; &#x1d546; &#120134; o
&popf; &#x1d547; &#120135; p
&qopf; &#x1d548; &#120136; q
&ropf; &#x1d549; &#120137; r
&sopf; &#x1d54a; &#120138; s
&topf; &#x1d54b; &#120139; t
&uopf; &#x1d54c; &#120140; u
&vopf; &#x1d54d; &#120141; v
&wopf; &#x1d54e; &#120142; w
&xopf; &#x1d54f; &#120143; x
&yopf; &#x1d550; &#120144; y
&zopf; &#x1d551; &#120145; z
Caracter HTML HEX DEC Descrição
&Aopf; &#x1d552; &#120146; A maiúsculo
&Bopf; &#x1d553; &#120147; B
&Copf; &#x1d554; &#120148; C
&Dopf; &#x1d555; &#120149; D
&Eopf; &#x1d556; &#120150; E
&Fopf; &#x1d557; &#120151; F
&Gopf; &#x1d558; &#120152; G
&Hopf; &#x1d559; &#120153; H
&Iopf; &#x1d55a; &#120154; I
&Jopf; &#x1d55b; &#120155; J
&Kopf; &#x1d55c; &#120156; K
&Lopf; &#x1d55d; &#120157; L
&Mopf; &#x1d55e; &#120158; M
&Nopf; &#x1d55f; &#120159; N
&Oopf; &#x1d560; &#120160; O
&Popf; &#x1d561; &#120161; P
&Qopf; &#x1d562; &#120162; Q
&Ropf; &#x1d563; &#120163; R
&Sopf; &#x1d564; &#120164; S
&Topf; &#x1d565; &#120165; T
&Uopf; &#x1d566; &#120166; U
&Vopf; &#x1d567; &#120167; V
&Wopf; &#x1d568; &#120168; W
&Xopf; &#x1d569; &#120169; X
&Yopf; &#x1d56a; &#120170; Y
&Zopf; &#x1d56b; &#120171; Z
Caracteres Gregos
Caracter HTML HEX DEC Descrição
α &alpha; &#x00391; &#913; alfa maiúsculo grego
β &beta; &#x00392; &#914; beta maiúsculo grego
γ &gamma; &#x00393; &#915; gama grega maiúscula
δ &delta; &#x00394; &#916; letra maiúscula grega delta
ε &epsilon; &#x00395; &#917; epsilon com letra maiúscula grega
ζ &zeta; &#x00396; &#918; zeta letra maiúscula grega
η &eta; &#x00397; &#919; letra maiúscula grega eta
θ &theta; &#x00398; &#920; teta maiúscula grega
ι &iota; &#x00399; &#921; letra maiúscula iota
κ &kappa; &#x0039a; &#922; kappa maiúscula
λ &lambda; &#x0039b; &#923; lamda letra maiúscula
μ &mu; &#x0039c; &#924; letra maiúscula mu
ν &nu; &#x0039d; &#925; nu maiúsculo
ξ &xi; &#x0039e; &#926; letra maiúscula xi
ο &omicron; &#x0039f; &#927; omicron maiúsculo
π &pi; &#x003a0; &#928; pi maiúscula
ρ &rho; &#x003a1; &#929; rho maiúsculo
σ &sigma; &#x003a3; &#931; sigma maiúsculo
τ &tau; &#x003a4; &#932; tau maiúsculo
υ &upsilon; &#x003a5; &#933; upsilon maiúsculo
φ &phi; &#x003a6; &#934; letra maiúscula phi
χ &chi; &#x003a7; &#935; maiúsculo chi
ψ &psi; &#x003a8; &#936; letra maiúscula psi
ω &omega; &#x003a9; &#937; ômega letra maiúscula
ς &sigmav; &varsigma; &sigmaf; &#x003c2; &#962; final sigma minúsculo
ϑ &thetav; &vartheta; &thetasym; &#x003d1; &#977; letra minúscula theta
υ ϒ &upsi; &upsih; &#x003d2; &#978; Upsilon com símbolo de gancho
φ &straightphi; &#x003d5; &#981; letra minúscula phi
ϖ &piv; &varpi; &#x003d6; &#982; letra minúscula pi
ϝ &gammad; &#x003dc; &#988; letra digamma
ϰ &kappav; &varkappa; &#x003f0; &#1008; símbolo kappa
ϱ &rhov; &varrho; &#x003f1; &#1009; letra rho
ε &epsi; &straightepsilon; &#x003f5; &#1013; épsilon semilunar
϶ &bepsi; &backepsilon; &#x003f6; &#1014; épsilon semilunar invertido

Letras gregas maiúsculas podem ser obtidas com o primeiro caracter em maiúsculo no codigo html. Por exemplo:

&Lambda; = Λ, &Sigma; = Σ, &Omega; = Ω.

A maioria das letra gregas maiúsculas são parecida com as do alfabeto latino e, por isso, não são muito úteis na simbologia.

Caracteres Cirílicos
Caracter HTML HEX DEC Descrição
ё &iocy; &#x00401; &#1025; cirílico maiúsculo io
ђ &djcy; &#x00402; &#1026; dje cirílico maiúsculo
ѓ &gjcy; &#x00403; &#1027; cirílico maiúsculo gje
є &jukcy; &#x00404; &#1028; cirílico maiúsculo ucraniano, isto é
ѕ &dscy; &#x00405; &#1029; cirílico maiúsculo dze
і &iukcy; &#x00406; &#1030; cirílico maiúsculo bielorrussa-ucraniana i
ї &yicy; &#x00407; &#1031; cirílico maiúsculo yi
ј &jsercy; &#x00408; &#1032; cirílico maiúsculo je
љ &ljcy; &#x00409; &#1033; cirílico maiúsculo lje
њ &njcy; &#x0040a; &#1034; cirílico maiúsculo nje
ћ &tshcy; &#x0040b; &#1035; cirílico maiúsculo tshe
ќ &kjcy; &#x0040c; &#1036; cirílico maiúsculo kje
ў &ubrcy; &#x0040e; &#1038; cirílico maiúsculo curta u
џ &dzcy; &#x0040f; &#1039; cirílico maiúsculo dzhe
а &acy; &#x00410; &#1040; cirílico maiúsculo a
б &bcy; &#x00411; &#1041; cirílico maiúsculo seja
в &vcy; &#x00412; &#1042; cirílico maiúsculo ve
г &gcy; &#x00413; &#1043; cirílico maiúsculo ghe
д &dcy; &#x00414; &#1044; cirílico maiúsculo de
е &iecy; &#x00415; &#1045; cirílico maiúsculo , ou seja
ж &zhcy; &#x00416; &#1046; cirílico maiúsculo zhe
з &zcy; &#x00417; &#1047; cirílico maiúsculo ze
и &icy; &#x00418; &#1048; cirílico maiúsculo i
й &jcy; &#x00419; &#1049; cirílico maiúsculo curto i
к &kcy; &#x0041a; &#1050; cirílico maiúsculo ka
л &lcy; &#x0041b; &#1051; cirílico maiúsculo el
м &mcy; &#x0041c; &#1052; cirílico maiúsculo em
н &ncy; &#x0041d; &#1053; cirílico maiúsculo en
о &ocy; &#x0041e; &#1054; cirílico maiúsculo o
п &pcy; &#x0041f; &#1055; cirílico maiúsculo pe
р &rcy; &#x00420; &#1056; cirílico maiúsculo
с &scy; &#x00421; &#1057; letras maiúsculas cirílicas
т &tcy; &#x00422; &#1058; cirílico maiúsculo te
у &ucy; &#x00423; &#1059; cirílico maiúsculo u
ф &fcy; &#x00424; &#1060; cirílico maiúsculo ef
х &khcy; &#x00425; &#1061; cirílico maiúsculo ha
ц &tscy; &#x00426; &#1062; cirílico maiúsculo tse
ч &chcy; &#x00427; &#1063; cirílico maiúsculo che
ш &shcy; &#x00428; &#1064; cirílico maiúsculo sha
щ &shchcy; &#x00429; &#1065; cirílico maiúsculo shcha
ъ &hardcy; &#x0042a; &#1066; cirílico maiúsculo
ы &ycy; &#x0042b; &#1067; cirílico maiúsculo yeru
ь &softcy; &#x0042c; &#1068; signo suave em cirílico maiúsculo
э &ecy; &#x0042d; &#1069; cirílico maiúsculo e
ю &yucy; &#x0042e; &#1070; cirílico maiúsculo yu
я &yacy; &#x0042f; &#1071; cirílico maiúsculo ya
а &acy; &#x00430; &#1072; cirílico minúsculo a
б &bcy; &#x00431; &#1073; cirílico minúsculo seja
в &vcy; &#x00432; &#1074; cirílico minúsculo ve
г &gcy; &#x00433; &#1075; cirílico minúsculo ghe
д &dcy; &#x00434; &#1076; cirílico minúsculo de
е &iecy; &#x00435; &#1077; cirílico minúsculo , isto é
ж &zhcy; &#x00436; &#1078; cirílico minúsculo zhe
з &zcy; &#x00437; &#1079; letra minúscula ze cirílica
и &icy; &#x00438; &#1080; cirílico minúsculo i
й &jcy; &#x00439; &#1081; cirílico minúsculo curta i
к &kcy; &#x0043a; &#1082; letra minúscula ka cirílica
л &lcy; &#x0043b; &#1083; letra minúscula el cirílica
м &mcy; &#x0043c; &#1084; em minúsculo cirílico
н &ncy; &#x0043d; &#1085; cirílico minúsculo en
о &ocy; &#x0043e; &#1086; minúsculo cirílico o
п &pcy; &#x0043f; &#1087; pe minúsculo cirílico
р &rcy; &#x00440; &#1088; cirílico minúsculo er
с &scy; &#x00441; &#1089; letras minúsculas cirílicas
т &tcy; &#x00442; &#1090; cirílico minúsculo te
у &ucy; &#x00443; &#1091; letra minúscula você em cirílico
ф &fcy; &#x00444; &#1092; cirílico minúsculo ef
х &khcy; &#x00445; &#1093; cirílico minúsculo ha
ц &tscy; &#x00446; &#1094; cirílico minúsculo tse
ч &chcy; &#x00447; &#1095; cirílico minúsculo che
ш &shcy; &#x00448; &#1096; letra minúscula em cirílico sha
щ &shchcy; &#x00449; &#1097; cirílico minúsculo shcha
ъ &hardcy; &#x0044a; &#1098; letra minúscula cirílico duro
ы &ycy; &#x0044b; &#1099; cirílico minúsculo yeru
ь &softcy; &#x0044c; &#1100; signo macio de letra minúscula cirílico
э &ecy; &#x0044d; &#1101; letra minúscula e cirílica
ю &yucy; &#x0044e; &#1102; letra minúscula em cirílico, você
я &yacy; &#x0044f; &#1103; cirílico minúsculo sim
ё &iocy; &#x00451; &#1105; cirílico minúsculo io
ђ &djcy; &#x00452; &#1106; dje cirílico minúsculo
ѓ &gjcy; &#x00453; &#1107; cirílico minúsculo gje
є &jukcy; &#x00454; &#1108; cirílico minúsculo ucraniana, ie
ѕ &dscy; &#x00455; &#1109; cirílico minúsculo dze
і &iukcy; &#x00456; &#1110; cirílico minúsculo bielorrussa-ucraniana i
ї &yicy; &#x00457; &#1111; letra minúscula em cirílico yi
ј &jsercy; &#x00458; &#1112; je minúsculo cirílico
љ &ljcy; &#x00459; &#1113; cirílico minúsculo lje
њ &njcy; &#x0045a; &#1114; nje minúsculo cirílico
ћ &tshcy; &#x0045b; &#1115; cirílico minúsculo tshe
ќ &kjcy; &#x0045c; &#1116; kje minúsculo cirílico
ў &ubrcy; &#x0045e; &#1118; cirílico minúsculo curta u
џ &dzcy; &#x0045f; &#1119; dzhe minúsculo cirílico
Miscelânia de Caracteres
Caracter HTML HEX DEC Descrição
&vzigzag; &#x0299a; &#10650; linha em ziguezague vertical
&vangrt; &#x0299c; &#10652; variante de ângulo reto com quadrado
&angrtvbd; &#x0299d; &#10653; ângulo reto medido com ponto
&ange; &#x029a4; &#10660; ângulo com barra
&range; &#x029a5; &#10661; ângulo invertido com barra inferior
&dwangle; &#x029a6; &#10662; ângulo oblíquo abrindo
&uwangle; &#x029a7; &#10663; abertura de ângulo oblíquo para baixo
&angmsdaa; &#x029a8; &#10664; medida de ângulo, seta nordeste
&angmsdab; &#x029a9; &#10665; medida de ângulo, seta oeste
&angmsdac; &#x029aa; &#10666; medida de ângulo, seta sudeste
&angmsdad; &#x029ab; &#10667; medida de ângulo, seta sudoeste
&angmsdae; &#x029ac; &#10668; medida de ângulo, seta nordeste
&angmsdaf; &#x029ad; &#10669; medida de ângulo, seta oeste
&angmsdag; &#x029ae; &#10670; medida de ângulo, seta sudeste
&angmsdah; &#x029af; &#10671; medida de ângulo, seta nordeste
&bemptyv; &#x029b0; &#10672; conjunto vazio invertido
&demptyv; &#x029b1; &#10673; conjunto vazio com barra superior
&cemptyv; &#x029b2; &#10674; conjunto vazio com círculo acima
&raemptyv; &#x029b3; &#10675; conjunto vazio com a seta direita acima
&laemptyv; &#x029b4; &#10676; conjunto vazio com seta esquerda acima
&ohbar; &#x029b5; &#10677; círculo com barra horizontal
&omid; &#x029b6; &#10678; barra vertical circulada
&opar; &#x029b7; &#10679; circulado paralelo
&operp; &#x029b9; &#10681; circular perpendicular
&olcross; &#x029bb; &#10683; círculo com x sobreposto
&odsold; &#x029bc; &#10684; divisão girado no sentido anti-horário
&olcir; &#x029be; &#10686; bala branca circulada
⦿ &ofcir; &#x029bf; &#10687; bala circulada
&olt; &#x029c0; &#10688; circulou menos que
&ogt; &#x029c1; &#10689; circulado maior que
&cirscir; &#x029c2; &#10690; círculo com círculo menor à direita
&cire; &#x029c3; &#10691; círculo com dois traços horizontais para a direita
&solb; &#x029c4; &#10692; barra diagonal ascendente quadrada
&bsolb; &#x029c5; &#10693; quadrado caindo barra diagonal
&boxbox; &#x029c9; &#10697; dois quadrados unidos
&trisb; &#x029cd; &#10701; triângulo com serifas na parte inferior
&rtriltri; &#x029ce; &#10702; triângulo direito acima do triângulo esquerdo
&lefttrianglebar; &#x029cf; &#10703; triângulo esquerdo ao lado da barra vertical
&righttrianglebar; &#x029d0; &#10704; barra vertical ao lado do triângulo retângulo
∽̱ &race; &#x029da; &#10714; cerca esquerda dupla ondulada
&eparsl; &#x029e3; &#10723; é igual a sinal e paralelo inclinado
&smeparsl; &#x029e4; &#10724; é igual ao sinal e inclinado paralelo ao til acima
&eqvparsl; &#x029e5; &#10725; idêntico e inclinado paralelo
&dsol; &#x029f6; &#10742; barra com traço superior
&pluscir; &#x02a22; &#10786; mais com ponto acima
&plusacir; &#x02a23; &#10787; mais com circunflexo acima
&simplus; &#x02a24; &#10788; mais com til acima
&plusdu; &#x02a25; &#10789; mais com ponto abaixo
&plussim; &#x02a26; &#10790; mais com til abaixo
&plustwo; &#x02a27; &#10791; mais com subscrito dois
&mcomma; &#x02a29; &#10793; menos com vírgula acima
&minusdu; &#x02a2a; &#10794; menos com ponto abaixo
&loplus; &#x02a2d; &#10797; mais no semicírculo esquerdo
&roplus; &#x02a2e; &#10798; mais no semicírculo direito
&cross; &#x02a2f; &#10799; vetor ou produto vetorial
&timesd; &#x02a30; &#10800; multiplicação com ponto acima
&timesbar; &#x02a31; &#10801; multiplicação com barra inferior
&smashp; &#x02a33; &#10803; esmagar produto
&lotimes; &#x02a34; &#10804; multiplicação no semicírculo esquerdo
&rotimes; &#x02a35; &#10805; multiplicação no semicírculo direito
&otimesas; &#x02a36; &#10806; multiplicação circulado com acento circunflexo
&otimes; &#x02a37; &#10807; multiplicação em círculo duplo
&odiv; &#x02a38; &#10808; divisão circulado
&triplus; &#x02a39; &#10809; mais no triângulo
&triminus; &#x02a3a; &#10810; menos no triângulo
&tritime; &#x02a3b; &#10811; multiplicação em triângulo
⨼ ⨼ &iprod; &intprod; &#x02a3c; &#10812; produto interior
⨿ &amalg; &#x02a3f; &#10815; amalgamação ou coproduto
&capdot; &#x02a40; &#10816; intersecção com ponto
&ncup; &#x02a42; &#10818; união com barra superior
&ncap; &#x02a43; &#10819; intersecção com barra superior
&capand; &#x02a44; &#10820; intersecção com lógica e
&cupor; &#x02a45; &#10821; união com lógica ou
&cupcap; &#x02a46; &#10822; união acima da interseção
&capcup; &#x02a47; &#10823; intersecção acima da união
&cupbrcap; &#x02a48; &#10824; união acima da barra acima da interseção
&capbrcup; &#x02a49; &#10825; intersecção acima da barra acima da união
&cupcup; &#x02a4a; &#10826; união ao lado e juntou-se a união
&capcap; &#x02a4b; &#10827; intersecção ao lado e junta com intersecção
&ccups; &#x02a4c; &#10828; união fechada com serifas
&ccaps; &#x02a4d; &#10829; interseção fechada com serifas
&ccupssm; &#x02a50; &#10832; união fechada com serifas e produto esmagado
&and; &#x02a53; &#10835; duplo lógico e
&or; &#x02a54; &#10836; duplo lógico ou
&andand; &#x02a55; &#10837; dois lógicos que se cruzam e
&oror; &#x02a56; &#10838; dois lógicos que se cruzam ou
&orslope; &#x02a57; &#10839; grande inclinado ou
&andslope; &#x02a58; &#10840; inclinado grande e
&andv; &#x02a5a; &#10842; lógico e com haste intermediária
&orv; &#x02a5b; &#10843; lógico ou com haste intermediária
&andd; &#x02a5c; &#10844; lógico e com traço horizontal
&ord; &#x02a5d; &#10845; lógico ou com traço horizontal
&wedbar; &#x02a5f; &#10847; lógico e com barra inferior
&sdote; &#x02a66; &#10854; é igual ao sinal com o ponto abaixo
&simdot; &#x02a6a; &#10858; Operador til com ponto acima
&congdot; &#x02a6d; &#10861; congruente com o ponto acima
&easter; &#x02a6e; &#10862; é igual a asterisco
&apacir; &#x02a6f; &#10863; quase igual a com acento circunflexo
&ape; &#x02a70; &#10864; aproximadamente igual ou igual a
&eplus; &#x02a71; &#10865; igual acima do mais
&pluse; &#x02a72; &#10866; mais acima do igual
&esim; &#x02a73; &#10867; igual ao sinal acima do operador til
&colone; &#x02a74; &#10868; dois pontos duplos iguais
&equal; &#x02a75; &#10869; dois sinais de igual consecutivos
&eddot; &ddotseq; &#x02a77; &#10871; é igual ao sinal com dois pontos acima e dois pontos abaixo
&equivdd; &#x02a78; &#10872; equivalente com quatro pontos acima
&ltcir; &#x02a79; &#10873; menos do que com um círculo dentro
&gtcir; &#x02a7a; &#10874; maior do que com um círculo dentro
&ltquest; &#x02a7b; &#10875; menos do que com ponto de interrogação acima
&gtquest; &#x02a7c; &#10876; maior do que com ponto de interrogação acima
&les; &lessslantequal; &leqslant; &#x02a7d; &#10877; menor ou inclinado igual a
&ges; &greaterslantequal; &geqslant; &#x02a7e; &#10878; maior que ou inclinado igual a
⩿ &lesdot; &#x02a7f; &#10879; menor que ou inclinado igual a com ponto dentro
&gesdot; &#x02a80; &#10880; maior que ou inclinado igual a com ponto dentro
&lesdoto; &#x02a81; &#10881; menor que ou inclinado igual ao ponto acima
&gesdoto; &#x02a82; &#10882; maior que ou inclinado igual ao ponto acima
&lesdotor; &#x02a83; &#10883; menor que ou inclinado igual a com ponto acima à direita
&gesdotol; &#x02a84; &#10884; maior que ou inclinado igual ao ponto acima à esquerda
&lap; &lessapprox; &#x02a85; &#10885; menor ou aproximado
&gap; &gtrapprox; &#x02a86; &#10886; maior que ou aproximado
&lne; &lneq; &#x02a87; &#10887; menor que e linha única não igual a
&gne; &gneq; &#x02a88; &#10888; maior que e linha única diferente de
&lnap; &lnapprox; &#x02a89; &#10889; inferior e não aproximado
&gnap; &gnapprox; &#x02a8a; &#10890; maior que e não aproximado
&leg; &lesseqqgtr; &#x02a8b; &#10891; menor que acima linha dupla igual acima maior que
&gel; &gtreqqless; &#x02a8c; &#10892; maior que acima linha dupla igual acima menor que
&lsime; &#x02a8d; &#10893; menor que acima semelhante ou igual
&gsime; &#x02a8e; &#10894; maior do que acima semelhante ou igual
&lsimg; &#x02a8f; &#10895; menor que acima semelhante acima maior que
&gsiml; &#x02a90; &#10896; maior que acima semelhante acima menor que
&lge; &#x02a91; &#10897; menor que acima maior que acima linha dupla igual
&gle; &#x02a92; &#10898; maior que acima menor que acima linha dupla igual
&lesges; &#x02a93; &#10899; menor que acima inclinado igual acima maior que acima inclinado igual
&gesles; &#x02a94; &#10900; maior que acima inclinado igual acima menor que acima inclinado igual
&els; &eqslantless; &#x02a95; &#10901; inclinado igual ou menor que
&egs; &eqslantgtr; &#x02a96; &#10902; inclinado igual ou maior que
&elsdot; &#x02a97; &#10903; inclinado igual ou menor do que com ponto dentro
&egsdot; &#x02a98; &#10904; inclinado igual ou maior que com ponto dentro
&el; &#x02a99; &#10905; linha dupla igual ou menor que
&eg; &#x02a9a; &#10906; linha dupla igual ou maior que
&siml; &#x02a9d; &#10909; semelhante ou inferior
&simg; &#x02a9e; &#10910; semelhante ou maior que
&simle; &#x02a9f; &#10911; semelhante acima menos-que acima é igual ao sinal
&simge; &#x02aa0; &#10912; semelhante acima maior do que acima é igual ao sinal
&lessless; &#x02aa1; &#10913; duplo aninhado menor que
&greatergreater; &#x02aa2; &#10914; aninhado duplo maior que
&glj; &#x02aa4; &#10916; maior que sobreposição menor que
&gla; &#x02aa5; &#10917; maior que ao lado menor que
&ltcc; &#x02aa6; &#10918; menos que fechado por curva
&gtcc; &#x02aa7; &#10919; maior que fechado por curva
&lescc; &#x02aa8; &#10920; menos que fechado pela curva acima inclinado igual
&gescc; &#x02aa9; &#10921; maior que fechado por curva acima inclinado igual
&smt; &#x02aaa; &#10922; menor que
&lat; &#x02aab; &#10923; maior que
&smte; &#x02aac; &#10924; menor ou igual a
&late; &#x02aad; &#10925; maior ou igual a
&bumpe; &#x02aae; &#10926; é igual ao sinal com irregular acima
&pre; &preceq; &precedesequal; &#x02aaf; &#10927; precede acima do igual de linha única
&sce; &succeq; &succeedsequal; &#x02ab0; &#10928; sucede acima do igual de linha única
&pre; &#x02ab3; &#10931; precede o igual
&sce; &#x02ab4; &#10932; sucede acima do igual
&prne; &precneqq; &#x02ab5; &#10933; precede acima não é igual a
&scne; &succneqq; &#x02ab6; &#10934; sucede acima não igual a
&prap; &precapprox; &#x02ab7; &#10935; precede acima quase igual a
&scap; &succapprox; &#x02ab8; &#10936; sucede acima de quase igual a
&prnap; &precnapprox; &#x02ab9; &#10937; precede acima, não quase igual a
&scnap; &succnapprox; &#x02aba; &#10938; sucede acima, não quase igual a
&pr; &#x02abb; &#10939; o dobro precede
&sc; &#x02abc; &#10940; sucesso duplo
&subdot; &#x02abd; &#10941; subconjunto com ponto
&supdot; &#x02abe; &#10942; superconjunto com ponto
⪿ &subplus; &#x02abf; &#10943; subconjunto com mais abaixo
&supplus; &#x02ac0; &#10944; superconjunto com mais abaixo
&submult; &#x02ac1; &#10945; subconjunto com multiplicação abaixo
&supmult; &#x02ac2; &#10946; superconjunto com multiplicação abaixo
&subedot; &#x02ac3; &#10947; subconjunto de ou igual a com ponto acima
&supedot; &#x02ac4; &#10948; superconjunto de ou igual a com ponto acima
&sube; &subseteqq; &#x02ac5; &#10949; subconjunto de igual acima
&supe; &supseteqq; &#x02ac6; &#10950; superconjunto de igual acima
&subsim; &#x02ac7; &#10951; subconjunto do operador til acima
&supsim; &#x02ac8; &#10952; superconjunto do operador til acima
&subne; &subsetneqq; &#x02acb; &#10955; subconjunto de acima não igual a
&supne; &supsetneqq; &#x02acc; &#10956; superconjunto de acima não igual a
&csub; &#x02acf; &#10959; subconjunto fechado
&csup; &#x02ad0; &#10960; superconjunto fechado
&csube; &#x02ad1; &#10961; subconjunto fechado ou igual a
&csupe; &#x02ad2; &#10962; superconjunto fechado ou igual a
&subsup; &#x02ad3; &#10963; subconjunto acima do superconjunto
&supsub; &#x02ad4; &#10964; superconjunto acima do subconjunto
&subsub; &#x02ad5; &#10965; subconjunto acima do subconjunto
&supsup; &#x02ad6; &#10966; superconjunto acima do superconjunto
&suphsub; &#x02ad7; &#10967; superconjunto ao lado do subconjunto
&supdsub; &#x02ad8; &#10968; superconjunto ao lado e unido por traço com subconjunto
&forkv; &#x02ad9; &#10969; elemento de abertura para baixo
&topfork; &#x02ada; &#10970; forcado com camiseta
&mlcp; &#x02adb; &#10971; intersecção transversal
&dashv; &doublelefttee; &#x02ae4; &#10980; barra vertical dupla catraca esquerda
&vdashl; &#x02ae6; &#10982; traço longo do membro esquerdo da vertical dupla
&barv; &#x02ae7; &#10983; amura curta com barra superior
&vbar; &#x02ae8; &#10984; amura curta com barra inferior
&vbarv; &#x02ae9; &#10985; amura curta acima da amura curta
&vbar; &#x02aeb; &#10987; dobrar a aderência
¬ &not; &#x02aec; &#10988; traço duplo não sinal
&bnot; &#x02aed; &#10989; traço duplo invertido sem sinal
&rnmid; &#x02aee; &#10990; não divide com barra de negação invertida
&cirmid; &#x02aef; &#10991; linha vertical com círculo acima
&midcir; &#x02af0; &#10992; linha vertical com círculo abaixo
&topcir; &#x02af1; &#10993; rumo para baixo com círculo abaixo
&nhpar; &#x02af2; &#10994; paralelo com traço horizontal
&parsim; &#x02af3; &#10995; paralelo com operador til
&parsl; &#x02afd; &#11005; operador barra dupla
Sinais Gráficos
Caracter HTML HEX DEC Descrição
&dagger; &#x02020; &#8224; punhal
&dagger; &ddagger; &#x02021; &#8225; punhal duplo
&bull; &bullet; &#x02022; &#8226; bala
&nldr; &#x02025; &#8229; líder de dois pontos
&hellip; &mldr; &#x02026; &#8230; elipse horizontal
&permil; &#x02030; &#8240; por milhar
&pertenk; &#x02031; &#8241; por dez mil
&prime; &#x02032; &#8242; aspas
&prime; &#x02033; &#8243; aspas dupla
&tprime; &#x02034; &#8244; aspas tripla
&qprime; &#x02057; &#8279; aspas quádruplas
&bprime; &backprime; &#x02035; &#8245; aspas invertida
&lsaquo; &#x02039; &#8249; única aspa angular esquerda
&rsaquo; &#x0203a; &#8250; única aspa angular direita
&oline; &#x0203e; &#8254; overline
&caret; &#x02041; &#8257; ponto de inserção acento circunflexo
&hybull; &#x02043; &#8259; hífen marcador
&frasl; &#x02044; &#8260; barra de fração
&bsemi; &#x0204f; &#8271; ponto e vírgula invertido
&applyfunction; &af; &#x02061; &#8289; aplicação de função
&tdot; &tripledot; &#x020db; &#8411; combinando três pontos acima
&copf; &complexes; &#x02102; &#8450; c maiúsculo duplo
&incare; &#x02105; &#8453; aos cuidados de
&gscr; &#x0210a; &#8458; script g minúsculo
&hamilt; &hilbertspace; &hscr; &#x0210b; &#8459; script h maiúsculo
&hfr; &poincareplane; &#x0210c; &#8460; letra preta h maiúsculo
&quaternions; &hopf; &#x0210d; &#8461; H maiúsculo duplo
&planckh; &#x0210e; &#8462; constante de planck
&planck; &hbar; &plankv; &hslash; &#x0210f; &#8463; Planck cortado
&iscr; &imagline; &#x02110; &#8464; script I maiúsculo
&image; &im; &imagpart; &ifr; &#x02111; &#8465; letra preta i maiúscula
&lscr; &lagran; &laplacetrf; &#x02112; &#8466; script l maiúsculo
&ell; &#x02113; &#8467; roteiro pequeno l
&nopf; &naturals; &#x02115; &#8469; n maiúsculo duplo
&numero; &#x02116; &#8470; sinal numero
&copysr; &#x02117; &#8471; gravação de som copyright
&weierp; &wp; &#x02118; &#8472; script p
&popf; &primes; &#x02119; &#8473; p maiúsculo duplo
&rationals; &qopf; &#x0211a; &#8474; Q maiúsculo duplo
&rscr; &realine; &#x0211b; &#8475; script r maiúsculo
&real; &re; &realpart; &rfr; &#x0211c; &#8476; reais
&reals; &ropf; &#x0211d; &#8477; r maiúsculo duplo
&rx; &#x0211e; &#8478; receita
&trade; &trade; &#x02122; &#8482; marca registrada
&integers; &zopf; &#x02124; &#8484; z maiúsculo duplo
Ω &ohm; &#x02126; &#8486; ohm (resistência)
&mho; &#x02127; &#8487; ohm invertido
&zfr; &zeetrf; &#x02128; &#8488; preta z maiúscula
&iiota; &#x02129; &#8489; minúsculo iota
Å &angst; &#x0212b; &#8491; angstrom
&bernou; &bernoullis; &#x0212c; &#8492; script b maiúsculo
&cfr; &cayleys; &#x0212d; &#8493; letra preta c maiúscula
&escr; &#x0212f; &#8495; script e pequeno
&escr; &expectation; &#x02130; &#8496; script e maiúsculo
&fscr; &fouriertrf; &#x02131; &#8497; script f maiúsculo
&phmmat; &mellintrf; &mscr; &#x02133; &#8499; script m
&order; &orderof; &oscr; &#x02134; &#8500; script pequeno o
ℵ ℵ &alefsym; &aleph; &#x02135; &#8501; símbolo alef
&beth; &#x02136; &#8502; símbolo de aposta
&gimel; &#x02137; &#8503; símbolo gimel
&daleth; &#x02138; &#8504; símbolo dalet
&dD; &#x02145; &#8517; itálico duplo traçado d
&dd; &#x02146; &#8518; itálico duplo traçado pequeno d
&exponentiale; &exponentiale; &ee; &#x02147; &#8519; e pequeno itálico duplo traçado
&imaginaryi; &ii; &#x02148; &#8520; itálico duplo traçado pequeno i
&prop; &propto; &vprop; &#x0221d; &#8733; proporcional a
&infin; &#x0221e; &#8734; infinito
&angrt; &#x0221f; &#8735; ângulo reto
&ang; &#x02220; &#8736; ângulo
&angmsd; &measuredangle; &#x02221; &#8737; ângulo medido
&angsph; &#x02222; &#8738; ângulo esférico
&mid; &smid; &shortmid; &#x02223; &#8739; divide
&nmid; &nsmid; &nshortmid; &#x02224; &#8740; não divide
&par; &parallel; &spar; &#x02225; &#8741; paralelo a
&npar; &nparallel; &#x02226; &#8742; não paralelo a
&and; &wedge; &#x02227; &#8743; lógico e
&or; &vee; &#x02228; &#8744; lógico ou
&cap; &#x02229; &#8745; interseção
&cup; &#x0222a; &#8746; união
&there4; &therefore; &therefore; &#x02234; &#8756; portanto
&becaus; &because; &because; &#x02235; &#8757; porque
&ratio; &#x02236; &#8758; razão
: &colon; &proportion; &#x02237; &#8759; proporção
&minusd; &dotminus; &#x02238; &#8760; ponto menos
&homtht; &#x0223b; &#8763; homotético
&sim; &thksim; &thicksim; &#x0223c; &#8764; operador til
&bsim; &backsim; &#x0223d; &#8765; til invertido
&ac; &mstpos; &#x0223e; &#8766; S invertido
&acd; &#x0223f; &#8767; senoidal
&wreath; &verticaltilde; &wr; &#x02240; &#8768; produto de coroa
&nsim; &nottilde; &#x02241; &#8769; til cortado
&esim; &equaltilde; &eqsim; &#x02242; &#8770; menos til
&sime; &tildeequal; &simeq; &#x02243; &#8771; assintoticamente igual a
&nsime; &nsimeq; &nottildeequal; &#x02244; &#8772; não assintoticamente igual a
&cong; &tildefullequal; &#x02245; &#8773; aproximadamente igual a
&simne; &#x02246; &#8774; aproximadamente, mas não exatamente igual
&ncong; &nottildefullequal; &#x02247; &#8775; nem aproximado nem igual a
&asymp; &ap; &approx; &thkap; &thickapprox; &#x02248; &#8776; quase igual
&nap; &nottildetilde; &napprox; &#x02249; &#8777; não quase igual
&ape; &approxeq; &#x0224a; &#8778; quase igual ou igual
&apid; &#x0224b; &#8779; triplo till
&bcong; &backcong; &#x0224c; &#8780; todos iguais a
&asympeq; &cupcap; &#x0224d; &#8781; equivalente a
&bump; &humpdownhump; &bumpeq; &#x0224e; &#8782; geometricamente equivalente a
&bumpe; &humpequal; &bumpeq; &#x0224f; &#8783; diferença entre
&esdot; &dotequal; &doteq; &#x02250; &#8784; aproxima-se do limite
ė &edot; &doteqdot; &#x02251; &#8785; geometricamente igual a
&colone; &coloneq; &#x02254; &#8788; dois pontos igual
&ecolon; &eqcolon; &#x02255; &#8789; igual dois pontos
&ecir; &eqcirc; &#x02256; &#8790; anel igual a
&cire; &circeq; &#x02257; &#8791; anel igual a
&wedgeq; &#x02259; &#8793; estimativas
&veeeq; &#x0225a; &#8794; equiangular para
&trie; &triangleq; &#x0225c; &#8796; delta igual
&equest; &questeq; &#x0225f; &#8799; igual questionado
&ne; &notequal; &#x02260; &#8800; não é igual a (diferente)
&equiv; &congruent; &#x02261; &#8801; idêntico a
&nequiv; &notcongruent; &#x02262; &#8802; não idêntico a
&le; &leq; &#x02264; &#8804; menor ou igual
&ge; &greaterequal; &geq; &#x02265; &#8805; maior ou igual
&le; &lessfullequal; &leqq; &#x02266; &#8806; menor que, igual
&ge; &greaterfullequal; &geqq; &#x02267; &#8807; maior do, igual
&lne; &lneqq; &#x02268; &#8808; menor que, diferente
&gne; &gneqq; &#x02269; &#8809; maior que, diferente
< &lt; &nestedlessless; &ll; &#x0226a; &#8810; muito menor que
> &gt; &nestedgreatergreater; &gg; &#x0226b; &#8811; muito maior que
&twixt; &between; &#x0226c; &#8812; entre
&nlt; &notless; &nless; &#x0226e; &#8814; não menor que
&ngt; &notgreater; &ngtr; &#x0226f; &#8815; não maior que
&nle; &notlessequal; &nleq; &#x02270; &#8816; nem menor que nem igual a
&nge; &notgreaterequal; &ngeq; &#x02271; &#8817; nem maior que nem igual a
&lsim; &lesstilde; &lesssim; &#x02272; &#8818; menor ou equivalente a
&gsim; &gtrsim; &greatertilde; &#x02273; &#8819; maior que ou equivalente a
&nlsim; &notlesstilde; &#x02274; &#8820; nem menor que nem equivalente a
&ngsim; &notgreatertilde; &#x02275; &#8821; nem maior que nem equivalente a
&lg; &lessgtr; &lessgreater; &#x02276; &#8822; menor ou maior que
&gl; &gtrless; &greaterless; &#x02277; &#8823; maior ou menor que
&ntlg; &notlessgreater; &#x02278; &#8824; nem menor que nem maior que
&ntgl; &notgreaterless; &#x02279; &#8825; nem maior que nem menor que
&pr; &precedes; &prec; &#x0227a; &#8826; precede
&sc; &succeeds; &succ; &#x0227b; &#8827; sucede
&prcue; &precedesslantequal; &preccurlyeq; &#x0227c; &#8828; precede ou igual a
&sccue; &succeedsslantequal; &succcurlyeq; &#x0227d; &#8829; sucede ou igual
&prsim; &precsim; &precedestilde; &#x0227e; &#8830; precede ou equivalente a
&scsim; &succsim; &succeedstilde; &#x0227f; &#8831; sucede ou equivalente a
&npr; &nprec; &notprecedes; &#x02280; &#8832; não precede
&nsc; &nsucc; &notsucceeds; &#x02281; &#8833; não sucede
&sqsub; &squaresubset; &sqsubset; &#x0228f; &#8847; imagem quadrada de
&sqsup; &squaresuperset; &sqsupset; &#x02290; &#8848; quadrado original de
&sqsube; &squaresubsetequal; &sqsubseteq; &#x02291; &#8849; imagem quadrada de ou igual a
&sqsupe; &squaresupersetequal; &sqsupseteq; &#x02292; &#8850; quadrado original de ou igual a
&sqcap; &squareintersection; &#x02293; &#8851; chapeu quadrado
&sqcup; &squareunion; &#x02294; &#8852; copo quadrado
&vdash; &righttee; &#x022a2; &#8866; rumo direito
&dashv; &lefttee; &#x022a3; &#8867; rumo esquerdo
&top; &downtee; &#x022a4; &#8868; aderência
&bottom; &bot; &perp; &uptee; &#x022a5; &#8869; aderência
&models; &#x022a7; &#8871; modela
&vdash; &doublerighttee; &#x022a8; &#8872; verdadeiro
&vdash; &#x022a9; &#8873; forças
&vdash; &#x022ab; &#8875; barra vertical dupla catraca direita dupla
&nvdash; &#x022ac; &#8876; não prova
&nvdash; &#x022ad; &#8877; Não é verdade
&nvdash; &#x022ae; &#8878; não força
&nvdash; &#x022af; &#8879; torniquete duplo direito barra vertical dupla negada
&prurel; &#x022b0; &#8880; precede em relação
&vltri; &vartriangleleft; &lefttriangle; &#x022b2; &#8882; subgrupo normal de
&vrtri; &vartriangleright; &righttriangle; &#x022b3; &#8883; contém como subgrupo normal
&ltrie; &trianglelefteq; &lefttriangleequal; &#x022b4; &#8884; subgrupo normal ou igual a
&rtrie; &trianglerighteq; &righttriangleequal; &#x022b5; &#8885; contém como subgrupo normal ou igual a
&origof; &#x022b6; &#8886; original de
&imof; &#x022b7; &#8887; imagem de
&mumap; &multimap; &#x022b8; &#8888; multimapa
&hercon; &#x022b9; &#8889; matriz conjugada hermitiana
&intcal; &intercal; &#x022ba; &#8890; intercalar
&veebar; &#x022bb; &#8891; xor lógico
&barvee; &#x022bd; &#8893; nor lógico
&angrtvb; &#x022be; &#8894; ângulo reto com arco
&lrtri; &#x022bf; &#8895; triângulo retângulo
&xwedge; &wedge; &bigwedge; &#x022c0; &#8896; n-ário lógico e
&xvee; &vee; &bigvee; &#x022c1; &#8897; n-ário lógico ou
&diam; &diamond; &diamond; &#x022c4; &#8900; operador de diamante
&sdot; &#x022c5; &#8901; operador de ponto
&sstarf; &star; &#x022c6; &#8902; operador estrela
&divonx; &divideontimes; &#x022c7; &#8903; vezes divisão
&bowtie; &#x022c8; &#8904; gravata-borboleta
&ltimes; &#x022c9; &#8905; produto semidireto de fator normal esquerdo
&rtimes; &#x022ca; &#8906; produto semidireto de fator normal correto
&lthree; &leftthreetimes; &#x022cb; &#8907; produto semidireto esquerdo
&rthree; &rightthreetimes; &#x022cc; &#8908; produto semidireto certo
&bsime; &backsimeq; &#x022cd; &#8909; til invertido é igual a
&cuvee; &curlyvee; &#x022ce; &#8910; ou lógico curvo
&cuwed; &curlywedge; &#x022cf; &#8911; e lógico curvo
&sub; &subset; &#x022d0; &#8912; subconjunto duplo
&sup; &supset; &#x022d1; &#8913; superconjunto duplo
&cap; &#x022d2; &#8914; cruzamento duplo
&cup; &#x022d3; &#8915; união dupla
&fork; &pitchfork; &#x022d4; &#8916; forquilha
&epar; &#x022d5; &#8917; igual e paralelo a
&ltdot; &lessdot; &#x022d6; &#8918; menos do que com ponto
&gtdot; &gtrdot; &#x022d7; &#8919; maior do que com ponto
&ll; &#x022d8; &#8920; muito menos do que
&gg; &ggg; &#x022d9; &#8921; muito maior do que
&leg; &lessequalgreater; &lesseqgtr; &#x022da; &#8922; menor que igual ou maior que
&gel; &gtreqless; &greaterequalless; &#x022db; &#8923; maior que igual ou menor que
&cuepr; &curlyeqprec; &#x022de; &#8926; igual a ou precede
&cuesc; &curlyeqsucc; &#x022df; &#8927; igual ou sucede
&nprcue; &notprecedesslantequal; &#x022e0; &#8928; não precede ou é igual
&nsccue; &notsucceedsslantequal; &#x022e1; &#8929; não sucede ou igual
&nsqsube; &notsquaresubsetequal; &#x022e2; &#8930; imagem não quadrada ou igual a
&nsqsupe; &notsquaresupersetequal; &#x022e3; &#8931; não quadrado original ou igual a
&lnsim; &#x022e6; &#8934; menor que, mas não equivalente a
&gnsim; &#x022e7; &#8935; maior que, mas não equivalente a
&prnsim; &precnsim; &#x022e8; &#8936; precede, mas não é equivalente a
&scnsim; &succnsim; &#x022e9; &#8937; sucede, mas não equivalente a
&nltri; &ntriangleleft; &notlefttriangle; &#x022ea; &#8938; subgrupo não normal de
&nrtri; &ntriangleright; &notrighttriangle; &#x022eb; &#8939; não contém um subgrupo normal
&nltrie; &ntrianglelefteq; &notlefttriangleequal; &#x022ec; &#8940; subgrupo não normal ou igual a
&nrtrie; &ntrianglerighteq; &notrighttriangleequal; &#x022ed; &#8941; não contém subgrupo normal ou igual
&vellip; &#x022ee; &#8942; elipse vertical
&ctdot; &#x022ef; &#8943; elipse horizontal da linha média
&utdot; &#x022f0; &#8944; elipse diagonal direita
&dtdot; &#x022f1; &#8945; elipse diagonal direita para baixo
&disin; &#x022f2; &#8946; elemento de com longo curso horizontal
&isinsv; &#x022f3; &#8947; elemento de com barra vertical no final do curso horizontal
&isins; &#x022f4; &#8948; pertence com traço vertical
&isindot; &#x022f5; &#8949; elemento de com ponto acima
&notinvc; &#x022f6; &#8950; elemento de com barra superior
&notinvb; &#x022f7; &#8951; pertence com barra superior
&nisd; &#x022fa; &#8954; contém com longo curso horizontal
&xnis; &#x022fb; &#8955; contém com barra vertical
&nis; &#x022fc; &#8956; pertence à esquerda com barra vertical
&notnivc; &#x022fd; &#8957; contém com barra superior
&notnivb; &#x022fe; &#8958; pequeno contém com barra superior
&barwed; &barwedge; &#x02305; &#8965; projetivo
&barwed; &doublebarwedge; &#x02306; &#8966; perspectiva
&lceil; &leftceiling; &#x02308; &#8968; teto esquerdo
&rceil; &rightceiling; &#x02309; &#8969; teto direito
&lfloor; &leftfloor; &#x0230a; &#8970; chão esquerdo
&rfloor; &rightfloor; &#x0230b; &#8971; chão certo
&drcrop; &#x0230c; &#8972; corte inferior direito
&dlcrop; &#x0230d; &#8973; corte inferior esquerdo
&urcrop; &#x0230e; &#8974; corte superior direito
&ulcrop; &#x0230f; &#8975; corte superior esquerdo
&bnot; &#x02310; &#8976; invertido, não assinar
&profline; &#x02312; &#8978; arco
&profsurf; &#x02313; &#8979; segmento
&telrec; &#x02315; &#8981; gravador de telefone
&target; &#x02316; &#8982; indicador de posição
&ulcorn; &ulcorner; &#x0231c; &#8988; canto superior esquerdo
&urcorn; &urcorner; &#x0231d; &#8989; canto superior direito
&dlcorn; &llcorner; &#x0231e; &#8990; canto inferior esquerdo
&drcorn; &lrcorner; &#x0231f; &#8991; canto inferior direito
&frown; &sfrown; &#x02322; &#8994; carranca
&smile; &ssmile; &#x02323; &#8995; sorriso
&cylcty; &#x0232d; &#9005; cilindricidade
&profalar; &#x0232e; &#9006; perfil generalizado
&topbot; &#x02336; &#9014; símbolo funcional apl i-beam
&ovbar; &#x0233d; &#9021; estilo de círculo de símbolo funcional apl
&solbar; &#x0233f; &#9023; barra de barra de símbolo funcional apl
&angzarr; &#x0237c; &#9084; ângulo reto com seta em zigue-zague para baixo
&lmoust; &lmoustache; &#x023b0; &#9136; seção superior esquerda ou inferior direita das chaves
&rmoust; &rmoustache; &#x023b1; &#9137; seção superior direita ou inferior esquerda das chaves
&tbrk; &overbracket; &#x023b4; &#9140; colchete superior
&bbrk; &underbracket; &#x023b5; &#9141; colchete inferior
&bbrktbrk; &#x023b6; &#9142; colchete inferior sobre colchete superior
&trpezium; &#x023e2; &#9186; trapézio branco
Emoticons
Símbolo HEX DEC Símbolo HEX DEC Símbolo HEX DEC Símbolo HEX DEC
#8986 #231A 💠 #128160 #1F4A0 🎈 #127880 #1F388 😩 #128553 #1F629
#8987 #231B 💡 #128161 #1F4A1 🎉 #127881 #1F389 😪 #128554 #1F62A
#9193 #23E9 💢 #128162 #1F4A2 🎊 #127882 #1F38A 😫 #128555 #1F62B
#9194 #23EA 💣 #128163 #1F4A3 🎋 #127883 #1F38B 😬 #128556 #1F62C
#9195 #23EB 💤 #128164 #1F4A4 🎌 #127884 #1F38C 😭 #128557 #1F62D
#9196 #23EC 💥 #128165 #1F4A5 🎍 #127885 #1F38D 😮 #128558 #1F62E
#9197 #23ED 💦 #128166 #1F4A6 🎎 #127886 #1F38E 😯 #128559 #1F62F
#9198 #23EE 💧 #128167 #1F4A7 🎏 #127887 #1F38F 😰 #128560 #1F630
#9199 #23EF 💨 #128168 #1F4A8 🎐 #127888 #1F390 😱 #128561 #1F631
#9200 #23F0 💩 #128169 #1F4A9 🎑 #127889 #1F391 😲 #128562 #1F632
#9201 #23F1 💪 #128170 #1F4AA 🎒 #127890 #1F392 😳 #128563 #1F633
#9202 #23F2 💫 #128171 #1F4AB 🎓 #127891 #1F393 😴 #128564 #1F634
#9203 #23F3 💬 #128172 #1F4AC 🎖 #127894 #1F396 😵 #128565 #1F635
#9208 #23F8 💭 #128173 #1F4AD 🎗 #127895 #1F397 😶 #128566 #1F636
#9209 #23F9 💮 #128174 #1F4AE 🎙 #127897 #1F399 😷 #128567 #1F637
#9210 #23FA 💯 #128175 #1F4AF 🎚 #127898 #1F39A 😸 #128568 #1F638
#9410 #24C2 💰 #128176 #1F4B0 🎛 #127899 #1F39B 😹 #128569 #1F639
#9748 #2614 💱 #128177 #1F4B1 🎞 #127902 #1F39E 😺 #128570 #1F63A
#9749 #2615 💲 #128178 #1F4B2 🎟 #127903 #1F39F 😻 #128571 #1F63B
#9757 #261D 💳 #128179 #1F4B3 🎠 #127904 #1F3A0 😼 #128572 #1F63C
#9800 #2648 💴 #128180 #1F4B4 🎡 #127905 #1F3A1 😽 #128573 #1F63D
#9801 #2649 💵 #128181 #1F4B5 🎢 #127906 #1F3A2 😾 #128574 #1F63E
#9802 #264A 💶 #128182 #1F4B6 🎣 #127907 #1F3A3 😿 #128575 #1F63F
#9803 #264B 💷 #128183 #1F4B7 🎤 #127908 #1F3A4 🙀 #128576 #1F640
#9804 #264C 💸 #128184 #1F4B8 🎥 #127909 #1F3A5 🙁 #128577 #1F641
#9805 #264D 💹 #128185 #1F4B9 🎦 #127910 #1F3A6 🙂 #128578 #1F642
#9806 #264E 💺 #128186 #1F4BA 🎧 #127911 #1F3A7 🙃 #128579 #1F643
#9807 #264F 💻 #128187 #1F4BB 🎨 #127912 #1F3A8 🙄 #128580 #1F644
#9808 #2650 💼 #128188 #1F4BC 🎩 #127913 #1F3A9 🙅 #128581 #1F645
#9809 #2651 💽 #128189 #1F4BD 🎪 #127914 #1F3AA 🙆 #128582 #1F646
#9810 #2652 💾 #128190 #1F4BE 🎫 #127915 #1F3AB 🙇 #128583 #1F647
#9811 #2653 💿 #128191 #1F4BF 🎬 #127916 #1F3AC 🙈 #128584 #1F648
#9823 #265F 📀 #128192 #1F4C0 🎭 #127917 #1F3AD 🙉 #128585 #1F649
#9855 #267F 📁 #128193 #1F4C1 🎮 #127918 #1F3AE 🙊 #128586 #1F64A
#9875 #2693 📂 #128194 #1F4C2 🎯 #127919 #1F3AF 🙋 #128587 #1F64B
#9889 #26A1 📃 #128195 #1F4C3 🎰 #127920 #1F3B0 🙌 #128588 #1F64C
#9898 #26AA 📄 #128196 #1F4C4 🎱 #127921 #1F3B1 🙍 #128589 #1F64D
#9899 #26AB 📅 #128197 #1F4C5 🎲 #127922 #1F3B2 🙎 #128590 #1F64E
#9917 #26BD 📆 #128198 #1F4C6 🎳 #127923 #1F3B3 🙏 #128591 #1F64F
#9918 #26BE 📇 #128199 #1F4C7 🎴 #127924 #1F3B4 🚀 #128640 #1F680
#9924 #26C4 📈 #128200 #1F4C8 🎵 #127925 #1F3B5 🚁 #128641 #1F681
#9925 #26C5 📉 #128201 #1F4C9 🎶 #127926 #1F3B6 🚂 #128642 #1F682
#9934 #26CE 📊 #128202 #1F4CA 🎷 #127927 #1F3B7 🚃 #128643 #1F683
#9935 #26CF 📋 #128203 #1F4CB 🎸 #127928 #1F3B8 🚄 #128644 #1F684
#9937 #26D1 📌 #128204 #1F4CC 🎹 #127929 #1F3B9 🚅 #128645 #1F685
#9939 #26D3 📍 #128205 #1F4CD 🎺 #127930 #1F3BA 🚆 #128646 #1F686
#9940 #26D4 📎 #128206 #1F4CE 🎻 #127931 #1F3BB 🚇 #128647 #1F687
#9961 #26E9 📏 #128207 #1F4CF 🎼 #127932 #1F3BC 🚈 #128648 #1F688
#9962 #26EA 📐 #128208 #1F4D0 🎽 #127933 #1F3BD 🚉 #128649 #1F689
#9968 #26F0 📑 #128209 #1F4D1 🎾 #127934 #1F3BE 🚊 #128650 #1F68A
#9969 #26F1 📒 #128210 #1F4D2 🎿 #127935 #1F3BF 🚋 #128651 #1F68B
#9970 #26F2 📓 #128211 #1F4D3 🏀 #127936 #1F3C0 🚌 #128652 #1F68C
#9971 #26F3 📔 #128212 #1F4D4 🏁 #127937 #1F3C1 🚍 #128653 #1F68D
#9972 #26F4 📕 #128213 #1F4D5 🏂 #127938 #1F3C2 🚎 #128654 #1F68E
#9973 #26F5 📖 #128214 #1F4D6 🏃 #127939 #1F3C3 🚏 #128655 #1F68F
#9975 #26F7 📗 #128215 #1F4D7 🏄 #127940 #1F3C4 🚐 #128656 #1F690
#9976 #26F8 📘 #128216 #1F4D8 🏅 #127941 #1F3C5 🚑 #128657 #1F691
#9977 #26F9 📙 #128217 #1F4D9 🏆 #127942 #1F3C6 🚒 #128658 #1F692
#9978 #26FA 📚 #128218 #1F4DA 🏇 #127943 #1F3C7 🚓 #128659 #1F693
#9981 #26FD 📛 #128219 #1F4DB 🏈 #127944 #1F3C8 🚔 #128660 #1F694
#9986 #2702 📜 #128220 #1F4DC 🏉 #127945 #1F3C9 🚕 #128661 #1F695
#9989 #2705 📝 #128221 #1F4DD 🏊 #127946 #1F3CA 🚖 #128662 #1F696
#9992 #2708 📞 #128222 #1F4DE 🏋 #127947 #1F3CB 🚗 #128663 #1F697
#9993 #2709 📟 #128223 #1F4DF 🏌 #127948 #1F3CC 🚘 #128664 #1F698
#9994 #270A 📠 #128224 #1F4E0 🏍 #127949 #1F3CD 🚙 #128665 #1F699
#9995 #270B 📡 #128225 #1F4E1 🏎 #127950 #1F3CE 🚚 #128666 #1F69A
#9996 #270C 📢 #128226 #1F4E2 🏏 #127951 #1F3CF 🚛 #128667 #1F69B
#9997 #270D 📣 #128227 #1F4E3 🏐 #127952 #1F3D0 🚜 #128668 #1F69C
#9999 #270F 📤 #128228 #1F4E4 🏑 #127953 #1F3D1 🚝 #128669 #1F69D
#10002 #2712 📥 #128229 #1F4E5 🏒 #127954 #1F3D2 🚞 #128670 #1F69E
#10004 #2714 📦 #128230 #1F4E6 🏓 #127955 #1F3D3 🚟 #128671 #1F69F
#10006 #2716 📧 #128231 #1F4E7 🏔 #127956 #1F3D4 🚠 #128672 #1F6A0
#10013 #271D 📨 #128232 #1F4E8 🏕 #127957 #1F3D5 🚡 #128673 #1F6A1
#10017 #2721 📩 #128233 #1F4E9 🏖 #127958 #1F3D6 🚢 #128674 #1F6A2
#10024 #2728 📪 #128234 #1F4EA 🏗 #127959 #1F3D7 🚣 #128675 #1F6A3
#10035 #2733 📫 #128235 #1F4EB 🏘 #127960 #1F3D8 🚤 #128676 #1F6A4
#10036 #2734 📬 #128236 #1F4EC 🏙 #127961 #1F3D9 🚥 #128677 #1F6A5
#10052 #2744 📭 #128237 #1F4ED 🏚 #127962 #1F3DA 🚦 #128678 #1F6A6
#10055 #2747 📮 #128238 #1F4EE 🏛 #127963 #1F3DB 🚧 #128679 #1F6A7
#10060 #274C 📯 #128239 #1F4EF 🏜 #127964 #1F3DC 🚨 #128680 #1F6A8
#10062 #274E 📰 #128240 #1F4F0 🏝 #127965 #1F3DD 🚩 #128681 #1F6A9
#10067 #2753 📱 #128241 #1F4F1 🏞 #127966 #1F3DE 🚪 #128682 #1F6AA
#10068 #2754 📲 #128242 #1F4F2 🏟 #127967 #1F3DF 🚫 #128683 #1F6AB
#10069 #2755 📳 #128243 #1F4F3 🏠 #127968 #1F3E0 🚬 #128684 #1F6AC
#10071 #2757 📴 #128244 #1F4F4 🏡 #127969 #1F3E1 🚭 #128685 #1F6AD
#10083 #2763 📵 #128245 #1F4F5 🏢 #127970 #1F3E2 🚮 #128686 #1F6AE
#10084 #2764 📶 #128246 #1F4F6 🏣 #127971 #1F3E3 🚯 #128687 #1F6AF
#10133 #2795 📷 #128247 #1F4F7 🏤 #127972 #1F3E4 🚰 #128688 #1F6B0
#10134 #2796 📸 #128248 #1F4F8 🏥 #127973 #1F3E5 🚱 #128689 #1F6B1
#10135 #2797 📹 #128249 #1F4F9 🏦 #127974 #1F3E6 🚲 #128690 #1F6B2
#10145 #27A1 📺 #128250 #1F4FA 🏧 #127975 #1F3E7 🚳 #128691 #1F6B3
#10160 #27B0 📻 #128251 #1F4FB 🏨 #127976 #1F3E8 🚴 #128692 #1F6B4
#10175 #27BF 📼 #128252 #1F4FC 🏩 #127977 #1F3E9 🚵 #128693 #1F6B5
#10548 #2934 📽 #128253 #1F4FD 🏪 #127978 #1F3EA 🚶 #128694 #1F6B6
#10549 #2935 📿 #128255 #1F4FF 🏫 #127979 #1F3EB 🚷 #128695 #1F6B7
#11013 #2B05 🔀 #128256 #1F500 🏬 #127980 #1F3EC 🚸 #128696 #1F6B8
#11014 #2B06 🔁 #128257 #1F501 🏭 #127981 #1F3ED 🚹 #128697 #1F6B9
#11015 #2B07 🔂 #128258 #1F502 🏮 #127982 #1F3EE 🚺 #128698 #1F6BA
#11035 #2B1B 🔃 #128259 #1F503 🏯 #127983 #1F3EF 🚻 #128699 #1F6BB
#11036 #2B1C 🔄 #128260 #1F504 🏰 #127984 #1F3F0 🚼 #128700 #1F6BC
#11088 #2B50 🔅 #128261 #1F505 🏳 #127987 #1F3F3 🚽 #128701 #1F6BD
#11093 #2B55 🔆 #128262 #1F506 🏴 #127988 #1F3F4 🚾 #128702 #1F6BE
#12336 #3030 🔇 #128263 #1F507 🏵 #127989 #1F3F5 🚿 #128703 #1F6BF
#12349 #303D 🔈 #128264 #1F508 🏷 #127991 #1F3F7 🛀 #128704 #1F6C0
#12951 #3297 🔉 #128265 #1F509 🏸 #127992 #1F3F8 🛁 #128705 #1F6C1
#12953 #3299 🔊 #128266 #1F50A 🏹 #127993 #1F3F9 🛂 #128706 #1F6C2
🀄 #126980 #1F004 🔋 #128267 #1F50B 🏺 #127994 #1F3FA 🛃 #128707 #1F6C3
🃏 #127183 #1F0CF 🔌 #128268 #1F50C 🏻 #127995 #1F3FB 🛄 #128708 #1F6C4
🅰 #127344 #1F170 🔍 #128269 #1F50D 🏼 #127996 #1F3FC 🛅 #128709 #1F6C5
🅱 #127345 #1F171 🔎 #128270 #1F50E 🏽 #127997 #1F3FD 🛋 #128715 #1F6CB
🅾 #127358 #1F17E 🔏 #128271 #1F50F 🏾 #127998 #1F3FE 🛌 #128716 #1F6CC
🅿 #127359 #1F17F 🔐 #128272 #1F510 🏿 #127999 #1F3FF 🛍 #128717 #1F6CD
🆎 #127374 #1F18E 🔑 #128273 #1F511 🐀 #128000 #1F400 🛎 #128718 #1F6CE
🆑 #127377 #1F191 🔒 #128274 #1F512 🐁 #128001 #1F401 🛏 #128719 #1F6CF
🆒 #127378 #1F192 🔓 #128275 #1F513 🐂 #128002 #1F402 🛐 #128720 #1F6D0
🆓 #127379 #1F193 🔔 #128276 #1F514 🐃 #128003 #1F403 🛑 #128721 #1F6D1
🆔 #127380 #1F194 🔕 #128277 #1F515 🐄 #128004 #1F404 🛒 #128722 #1F6D2
🆕 #127381 #1F195 🔖 #128278 #1F516 🐅 #128005 #1F405 🛠 #128736 #1F6E0
🆖 #127382 #1F196 🔗 #128279 #1F517 🐆 #128006 #1F406 🛡 #128737 #1F6E1
🆗 #127383 #1F197 🔘 #128280 #1F518 🐇 #128007 #1F407 🛢 #128738 #1F6E2
🆘 #127384 #1F198 🔙 #128281 #1F519 🐈 #128008 #1F408 🛣 #128739 #1F6E3
🆙 #127385 #1F199 🔚 #128282 #1F51A 🐉 #128009 #1F409 🛤 #128740 #1F6E4
🆚 #127386 #1F19A 🔛 #128283 #1F51B 🐊 #128010 #1F40A 🛥 #128741 #1F6E5
🈁 #127489 #1F201 🔜 #128284 #1F51C 🐋 #128011 #1F40B 🛩 #128745 #1F6E9
🈂 #127490 #1F202 🔝 #128285 #1F51D 🐌 #128012 #1F40C 🛫 #128747 #1F6EB
🈚 #127514 #1F21A 🔞 #128286 #1F51E 🐍 #128013 #1F40D 🛬 #128748 #1F6EC
🈯 #127535 #1F22F 🔟 #128287 #1F51F 🐎 #128014 #1F40E 🛰 #128752 #1F6F0
🈲 #127538 #1F232 🔠 #128288 #1F520 🐏 #128015 #1F40F 🛳 #128755 #1F6F3
🈳 #127539 #1F233 🔡 #128289 #1F521 🐐 #128016 #1F410 🛴 #128756 #1F6F4
🈴 #127540 #1F234 🔢 #128290 #1F522 🐑 #128017 #1F411 🛵 #128757 #1F6F5
🈵 #127541 #1F235 🔣 #128291 #1F523 🐒 #128018 #1F412 🛶 #128758 #1F6F6
🈶 #127542 #1F236 🔤 #128292 #1F524 🐓 #128019 #1F413 🛷 #128759 #1F6F7
🈷 #127543 #1F237 🔥 #128293 #1F525 🐔 #128020 #1F414 🛸 #128760 #1F6F8
🈸 #127544 #1F238 🔦 #128294 #1F526 🐕 #128021 #1F415 🛹 #128761 #1F6F9
🈹 #127545 #1F239 🔧 #128295 #1F527 🐖 #128022 #1F416 🛺 #128762 #1F6FA
🈺 #127546 #1F23A 🔨 #128296 #1F528 🐗 #128023 #1F417 🤐 #129296 #1F910
🉐 #127568 #1F250 🔩 #128297 #1F529 🐘 #128024 #1F418 🤑 #129297 #1F911
🉑 #127569 #1F251 🔪 #128298 #1F52A 🐙 #128025 #1F419 🤒 #129298 #1F912
🌀 #127744 #1F300 🔫 #128299 #1F52B 🐚 #128026 #1F41A 🤓 #129299 #1F913
🌁 #127745 #1F301 🔬 #128300 #1F52C 🐛 #128027 #1F41B 🤔 #129300 #1F914
🌂 #127746 #1F302 🔭 #128301 #1F52D 🐜 #128028 #1F41C 🤕 #129301 #1F915
🌃 #127747 #1F303 🔮 #128302 #1F52E 🐝 #128029 #1F41D 🤖 #129302 #1F916
🌄 #127748 #1F304 🔯 #128303 #1F52F 🐞 #128030 #1F41E 🤗 #129303 #1F917
🌅 #127749 #1F305 🔰 #128304 #1F530 🐟 #128031 #1F41F 🤘 #129304 #1F918
🌆 #127750 #1F306 🔱 #128305 #1F531 🐠 #128032 #1F420 🤙 #129305 #1F919
🌇 #127751 #1F307 🔲 #128306 #1F532 🐡 #128033 #1F421 🤚 #129306 #1F91A
🌈 #127752 #1F308 🔳 #128307 #1F533 🐢 #128034 #1F422 🤛 #129307 #1F91B
🌉 #127753 #1F309 🔴 #128308 #1F534 🐣 #128035 #1F423 🤜 #129308 #1F91C
🌊 #127754 #1F30A 🔵 #128309 #1F535 🐤 #128036 #1F424 🤝 #129309 #1F91D
🌋 #127755 #1F30B 🔶 #128310 #1F536 🐥 #128037 #1F425 🤞 #129310 #1F91E
🌌 #127756 #1F30C 🔷 #128311 #1F537 🐦 #128038 #1F426 🤟 #129311 #1F91F
🌍 #127757 #1F30D 🔸 #128312 #1F538 🐧 #128039 #1F427 🤠 #129312 #1F920
🌎 #127758 #1F30E 🔹 #128313 #1F539 🐨 #128040 #1F428 🤡 #129313 #1F921
🌏 #127759 #1F30F 🔺 #128314 #1F53A 🐩 #128041 #1F429 🤢 #129314 #1F922
🌐 #127760 #1F310 🔻 #128315 #1F53B 🐪 #128042 #1F42A 🤣 #129315 #1F923
🌑 #127761 #1F311 🔼 #128316 #1F53C 🐫 #128043 #1F42B 🤤 #129316 #1F924
🌒 #127762 #1F312 🔽 #128317 #1F53D 🐬 #128044 #1F42C 🤥 #129317 #1F925
🌓 #127763 #1F313 🕉 #128329 #1F549 🐭 #128045 #1F42D 🤦 #129318 #1F926
🌔 #127764 #1F314 🕊 #128330 #1F54A 🐮 #128046 #1F42E 🤧 #129319 #1F927
🌕 #127765 #1F315 🕋 #128331 #1F54B 🐯 #128047 #1F42F 🤨 #129320 #1F928
🌖 #127766 #1F316 🕌 #128332 #1F54C 🐰 #128048 #1F430 🤩 #129321 #1F929
🌗 #127767 #1F317 🕍 #128333 #1F54D 🐱 #128049 #1F431 🤪 #129322 #1F92A
🌘 #127768 #1F318 🕎 #128334 #1F54E 🐲 #128050 #1F432 🤫 #129323 #1F92B
🌙 #127769 #1F319 🕐 #128336 #1F550 🐳 #128051 #1F433 🤬 #129324 #1F92C
🌚 #127770 #1F31A 🕑 #128337 #1F551 🐴 #128052 #1F434 🤭 #129325 #1F92D
🌛 #127771 #1F31B 🕒 #128338 #1F552 🐵 #128053 #1F435 🤮 #129326 #1F92E
🌜 #127772 #1F31C 🕓 #128339 #1F553 🐶 #128054 #1F436 🤯 #129327 #1F92F
🌝 #127773 #1F31D 🕔 #128340 #1F554 🐷 #128055 #1F437 🤰 #129328 #1F930
🌞 #127774 #1F31E 🕕 #128341 #1F555 🐸 #128056 #1F438 🤱 #129329 #1F931
🌟 #127775 #1F31F 🕖 #128342 #1F556 🐹 #128057 #1F439 🤲 #129330 #1F932
🌠 #127776 #1F320 🕗 #128343 #1F557 🐺 #128058 #1F43A 🤳 #129331 #1F933
🌡 #127777 #1F321 🕘 #128344 #1F558 🐻 #128059 #1F43B 🤴 #129332 #1F934
🌤 #127780 #1F324 🕙 #128345 #1F559 🐼 #128060 #1F43C 🤵 #129333 #1F935
🌥 #127781 #1F325 🕚 #128346 #1F55A 🐽 #128061 #1F43D 🤶 #129334 #1F936
🌦 #127782 #1F326 🕛 #128347 #1F55B 🐾 #128062 #1F43E 🤷 #129335 #1F937
🌧 #127783 #1F327 🕜 #128348 #1F55C 🐿 #128063 #1F43F 🤸 #129336 #1F938
🌨 #127784 #1F328 🕝 #128349 #1F55D 👀 #128064 #1F440 🤹 #129337 #1F939
🌩 #127785 #1F329 🕞 #128350 #1F55E 👁 #128065 #1F441 🤺 #129338 #1F93A
🌪 #127786 #1F32A 🕟 #128351 #1F55F 👂 #128066 #1F442 🤼 #129340 #1F93C
🌫 #127787 #1F32B 🕠 #128352 #1F560 👃 #128067 #1F443 🤽 #129341 #1F93D
🌬 #127788 #1F32C 🕡 #128353 #1F561 👄 #128068 #1F444 🤾 #129342 #1F93E
🌭 #127789 #1F32D 🕢 #128354 #1F562 👅 #128069 #1F445 🥀 #129344 #1F940
🌮 #127790 #1F32E 🕣 #128355 #1F563 👆 #128070 #1F446 🥁 #129345 #1F941
🌯 #127791 #1F32F 🕤 #128356 #1F564 👇 #128071 #1F447 🥂 #129346 #1F942
🌰 #127792 #1F330 🕥 #128357 #1F565 👈 #128072 #1F448 🥃 #129347 #1F943
🌱 #127793 #1F331 🕦 #128358 #1F566 👉 #128073 #1F449 🥄 #129348 #1F944
🌲 #127794 #1F332 🕧 #128359 #1F567 👊 #128074 #1F44A 🥅 #129349 #1F945
🌳 #127795 #1F333 🕯 #128367 #1F56F 👋 #128075 #1F44B 🥇 #129351 #1F947
🌴 #127796 #1F334 🕰 #128368 #1F570 👌 #128076 #1F44C 🥈 #129352 #1F948
🌵 #127797 #1F335 🕳 #128371 #1F573 👍 #128077 #1F44D 🥉 #129353 #1F949
🌶 #127798 #1F336 🕴 #128372 #1F574 👎 #128078 #1F44E 🥊 #129354 #1F94A
🌷 #127799 #1F337 🕵 #128373 #1F575 👏 #128079 #1F44F 🥋 #129355 #1F94B
🌸 #127800 #1F338 🕶 #128374 #1F576 👐 #128080 #1F450 🥌 #129356 #1F94C
🌹 #127801 #1F339 🕷 #128375 #1F577 👑 #128081 #1F451 🥍 #129357 #1F94D
🌺 #127802 #1F33A 🕸 #128376 #1F578 👒 #128082 #1F452 🥎 #129358 #1F94E
🌻 #127803 #1F33B 🕹 #128377 #1F579 👓 #128083 #1F453 🥏 #129359 #1F94F
🌼 #127804 #1F33C 🕺 #128378 #1F57A 👔 #128084 #1F454 🥐 #129360 #1F950
🌽 #127805 #1F33D 🖇 #128391 #1F587 👕 #128085 #1F455 🥑 #129361 #1F951
🌾 #127806 #1F33E 🖊 #128394 #1F58A 👖 #128086 #1F456 🥒 #129362 #1F952
🌿 #127807 #1F33F 🖋 #128395 #1F58B 👗 #128087 #1F457 🥓 #129363 #1F953
🍀 #127808 #1F340 🖌 #128396 #1F58C 👘 #128088 #1F458 🥔 #129364 #1F954
🍁 #127809 #1F341 🖍 #128397 #1F58D 👙 #128089 #1F459 🥕 #129365 #1F955
🍂 #127810 #1F342 🖐 #128400 #1F590 👚 #128090 #1F45A 🥖 #129366 #1F956
🍃 #127811 #1F343 🖕 #128405 #1F595 👛 #128091 #1F45B 🥗 #129367 #1F957
🍄 #127812 #1F344 🖖 #128406 #1F596 👜 #128092 #1F45C 🥘 #129368 #1F958
🍅 #127813 #1F345 🖤 #128420 #1F5A4 👝 #128093 #1F45D 🥙 #129369 #1F959
🍆 #127814 #1F346 🖥 #128421 #1F5A5 👞 #128094 #1F45E 🥚 #129370 #1F95A
🍇 #127815 #1F347 🖨 #128424 #1F5A8 👟 #128095 #1F45F 🥛 #129371 #1F95B
🍈 #127816 #1F348 🖱 #128433 #1F5B1 👠 #128096 #1F460 🥜 #129372 #1F95C
🍉 #127817 #1F349 🖲 #128434 #1F5B2 👡 #128097 #1F461 🥝 #129373 #1F95D
🍊 #127818 #1F34A 🖼 #128444 #1F5BC 👢 #128098 #1F462 🥞 #129374 #1F95E
🍋 #127819 #1F34B 🗂 #128450 #1F5C2 👣 #128099 #1F463 🥟 #129375 #1F95F
🍌 #127820 #1F34C 🗃 #128451 #1F5C3 👤 #128100 #1F464 🥠 #129376 #1F960
🍍 #127821 #1F34D 🗄 #128452 #1F5C4 👥 #128101 #1F465 🥡 #129377 #1F961
🍎 #127822 #1F34E 🗑 #128465 #1F5D1 👦 #128102 #1F466 🥢 #129378 #1F962
🍏 #127823 #1F34F 🗒 #128466 #1F5D2 👧 #128103 #1F467 🥣 #129379 #1F963
🍐 #127824 #1F350 🗓 #128467 #1F5D3 👨 #128104 #1F468 🥤 #129380 #1F964
🍑 #127825 #1F351 🗜 #128476 #1F5DC 👩 #128105 #1F469 🥥 #129381 #1F965
🍒 #127826 #1F352 🗝 #128477 #1F5DD 👪 #128106 #1F46A 🥦 #129382 #1F966
🍓 #127827 #1F353 🗞 #128478 #1F5DE 👫 #128107 #1F46B 🥧 #129383 #1F967
🍔 #127828 #1F354 🗡 #128481 #1F5E1 👬 #128108 #1F46C 🥨 #129384 #1F968
🍕 #127829 #1F355 🗣 #128483 #1F5E3 👭 #128109 #1F46D 🥩 #129385 #1F969
🍖 #127830 #1F356 🗨 #128488 #1F5E8 👮 #128110 #1F46E 🥪 #129386 #1F96A
🍗 #127831 #1F357 🗯 #128495 #1F5EF 👯 #128111 #1F46F 🥫 #129387 #1F96B
🍘 #127832 #1F358 🗳 #128499 #1F5F3 👰 #128112 #1F470 🦀 #129408 #1F980
🍙 #127833 #1F359 🗺 #128506 #1F5FA 👱 #128113 #1F471 🦁 #129409 #1F981
🍚 #127834 #1F35A 🗻 #128507 #1F5FB 👲 #128114 #1F472 🦂 #129410 #1F982
🍛 #127835 #1F35B 🗼 #128508 #1F5FC 👳 #128115 #1F473 🦃 #129411 #1F983
🍜 #127836 #1F35C 🗽 #128509 #1F5FD 👴 #128116 #1F474 🦄 #129412 #1F984
🍝 #127837 #1F35D 🗾 #128510 #1F5FE 👵 #128117 #1F475 🦅 #129413 #1F985
🍞 #127838 #1F35E 🗿 #128511 #1F5FF 👶 #128118 #1F476 🦆 #129414 #1F986
🍟 #127839 #1F35F 😀 #128512 #1F600 👷 #128119 #1F477 🦇 #129415 #1F987
🍠 #127840 #1F360 😁 #128513 #1F601 👸 #128120 #1F478 🦈 #129416 #1F988
🍡 #127841 #1F361 😂 #128514 #1F602 👹 #128121 #1F479 🦉 #129417 #1F989
🍢 #127842 #1F362 😃 #128515 #1F603 👺 #128122 #1F47A 🦊 #129418 #1F98A
🍣 #127843 #1F363 😄 #128516 #1F604 👻 #128123 #1F47B 🦋 #129419 #1F98B
🍤 #127844 #1F364 😅 #128517 #1F605 👼 #128124 #1F47C 🦌 #129420 #1F98C
🍥 #127845 #1F365 😆 #128518 #1F606 👽 #128125 #1F47D 🦍 #129421 #1F98D
🍦 #127846 #1F366 😇 #128519 #1F607 👾 #128126 #1F47E 🦎 #129422 #1F98E
🍧 #127847 #1F367 😈 #128520 #1F608 👿 #128127 #1F47F 🦏 #129423 #1F98F
🍨 #127848 #1F368 😉 #128521 #1F609 💀 #128128 #1F480 🦐 #129424 #1F990
🍩 #127849 #1F369 😊 #128522 #1F60A 💁 #128129 #1F481 🦑 #129425 #1F991
🍪 #127850 #1F36A 😋 #128523 #1F60B 💂 #128130 #1F482 🦒 #129426 #1F992
🍫 #127851 #1F36B 😌 #128524 #1F60C 💃 #128131 #1F483 🦓 #129427 #1F993
🍬 #127852 #1F36C 😍 #128525 #1F60D 💄 #128132 #1F484 🦔 #129428 #1F994
🍭 #127853 #1F36D 😎 #128526 #1F60E 💅 #128133 #1F485 🦕 #129429 #1F995
🍮 #127854 #1F36E 😏 #128527 #1F60F 💆 #128134 #1F486 🦖 #129430 #1F996
🍯 #127855 #1F36F 😐 #128528 #1F610 💇 #128135 #1F487 🦗 #129431 #1F997
🍰 #127856 #1F370 😑 #128529 #1F611 💈 #128136 #1F488 🧀 #129472 #1F9C0
🍱 #127857 #1F371 😒 #128530 #1F612 💉 #128137 #1F489 🧐 #129488 #1F9D0
🍲 #127858 #1F372 😓 #128531 #1F613 💊 #128138 #1F48A 🧑 #129489 #1F9D1
🍳 #127859 #1F373 😔 #128532 #1F614 💋 #128139 #1F48B 🧒 #129490 #1F9D2
🍴 #127860 #1F374 😕 #128533 #1F615 💌 #128140 #1F48C 🧓 #129491 #1F9D3
🍵 #127861 #1F375 😖 #128534 #1F616 💍 #128141 #1F48D 🧔 #129492 #1F9D4
🍶 #127862 #1F376 😗 #128535 #1F617 💎 #128142 #1F48E 🧕 #129493 #1F9D5
🍷 #127863 #1F377 😘 #128536 #1F618 💏 #128143 #1F48F 🧖 #129494 #1F9D6
🍸 #127864 #1F378 😙 #128537 #1F619 💐 #128144 #1F490 🧗 #129495 #1F9D7
🍹 #127865 #1F379 😚 #128538 #1F61A 💑 #128145 #1F491 🧘 #129496 #1F9D8
🍺 #127866 #1F37A 😛 #128539 #1F61B 💒 #128146 #1F492 🧙 #129497 #1F9D9
🍻 #127867 #1F37B 😜 #128540 #1F61C 💓 #128147 #1F493 🧚 #129498 #1F9DA
🍼 #127868 #1F37C 😝 #128541 #1F61D 💔 #128148 #1F494 🧛 #129499 #1F9DB
🍽 #127869 #1F37D 😞 #128542 #1F61E 💕 #128149 #1F495 🧜 #129500 #1F9DC
🍾 #127870 #1F37E 😟 #128543 #1F61F 💖 #128150 #1F496 🧝 #129501 #1F9DD
🍿 #127871 #1F37F 😠 #128544 #1F620 💗 #128151 #1F497 🧞 #129502 #1F9DE
🎀 #127872 #1F380 😡 #128545 #1F621 💘 #128152 #1F498 🧟 #129503 #1F9DF
🎁 #127873 #1F381 😢 #128546 #1F622 💙 #128153 #1F499 🧠 #129504 #1F9E0
🎂 #127874 #1F382 😣 #128547 #1F623 💚 #128154 #1F49A 🧡 #129505 #1F9E1
🎃 #127875 #1F383 😤 #128548 #1F624 💛 #128155 #1F49B 🧢 #129506 #1F9E2
🎄 #127876 #1F384 😥 #128549 #1F625 💜 #128156 #1F49C 🧣 #129507 #1F9E3
🎅 #127877 #1F385 😦 #128550 #1F626 💝 #128157 #1F49D 🧤 #129508 #1F9E4
🎆 #127878 #1F386 😧 #128551 #1F627 💞 #128158 #1F49E 🧥 #129509 #1F9E5
🎇 #127879 #1F387 😨 #128552 #1F628 💟 #128159 #1F49F 🧦 #129510 #1F9E6

Tabela ASCII

Tecnologias Web

Plataformas WEB

Uma plataforma WEB é uma coleção de tecnologias desenvolvidas para a criação, desenvolvimento e distribuição de páginas e aplicativos na internet.

Front End

Elas envolvem tecnologias de níveis variados de complexidade sendo que as mais básicas, e mais amplamente empregadas, são a linguagem HTML, Hyper Text Markup Language e CSS, Cascade Style Sheets. HTML é responsável pela estrutura de uma página, marcando títulos, parágrafos, links, blocos de texto, inserção de imagens, etc. CSS são arquivos de formatação, usados para definir tamanhos e tipos de fontes, cores, espaçamentos, etc. A linguagem javascript é usada pela maioria dos sites para executar ações do lado do navegador, tais como atualização dinâmica de conteúdo, envio de respostas do usuário para o servidor e controle de arquivos multimídia. Diversas novas tecnologias são baseadas no javascript, como AngularJS, Node.js e React.

Back End

Além das tecnologias usadas no navegador quase sempre é necessário usar linguagens de programação que alteram a construção de páginas e aplicativos no lado do servidor. O acesso a um banco de dados é a utilização mais comum. As mais empregadas são o PHP, Java, Python, Ruby on Rails e ASP.Net. Cada uma delas oferece um número de plataformas, cada em com suas vantagens e desvantagens, como agilidade de desenvolvimento e velocidade de resposta das páginas. As plataformas são basicamente ambientes desenvolvidos para resolver problemas determinados. Por ex., o PHP é a base de CMSs como o WordPress, útil para a construção de blogs e exibição de textos, ou desenvolvimento de sites de vendas online. O Python é usado na plataforma Django, criado e muito empregado nos sites de jornalismo, onde as atualizações devem ser rápidas e constantes. Todas essas tecnologias demandam algum tempo de aprendizado.

A Arquitetura básica da Web

Partindo do usuário, a web é vista primeiro em um navegador, como o Firefox, Chrome ou Safari. O navegador envia uma URL para a rede e recebe em resposta páginas HTML, arquivos de estilo CSS, arquivos Javascript e de multimídia, como áudios e vídeos. O navegador é um aplicativo que pode agrupar esse conteúdo e apresentá-los de forma compreensível. Navegadores modernos podem exibir outros formatos de arquivo, como pdf e planilhas, nativamente ou por meio de plugins. Além disso eles podem interpretar e executar código javascript, também recebidos do servidor.

 

A requisição do cliente (o navegador) contém o endereço do servidor que são computadores dedicados à esse serviço em diversas partes do mundo. O servidor recebe a solicitação e monta uma resposta. Para isso ele pode usar diversas tecnologias diferentes, acesso a banco de dados, código de frameworks montadores de páginas, etc. O servidor pode, por sua vez, acessar outros servidores para montar o conteúdo que será enviado para o cliente. O resultado será uma página em formato HTML gerada pelo servidor e apropriada para ser exibida.

 

Para a comunicação entre cliente e servidor é usado o HTTP, um protocolo de transferência de hipertexto que define como dados devem trafegar na rede. O HTTPS é um aperfeiçoamento que adiciona uma camada de segurança entre a emissão e resposta. Páginas HTML enviadas pelo servidor podem ser estáticas, enviadas como estão gravadas, sem processamento. O mais comum hoje é que as páginas sejam dinâmicas, montadas por solicitação, processadas por código de programa, geralmente ASP ou .NET, Java, Rails ou  Python. Um servidor de aplicativos é o mecanismo que executa a lógica de negócios no lado do servidor que, em geral, está separado do servidor de páginas.

Containers e Docker



Um histórico sobre containers

Embora exista a palavra contêiner, (plural: contêineres) nos dicionários de português do Brasil, manterei nesse texto a terminologia container (plural: containers) para me referir aos objetos de software tratado neste texto.

Uma máquina virtual, ou uma VM (Virtual Machine) é uma simulação em software de um computador, incluindo CPU, memória, área de armazenamento de arquivos e conexão com à internet. Um aplicativo como o VMware, VirtualBox ou Gnome Boxes cria um arquivo no computador (a imagem) que se comporta como sendo outro computador, no sistema operacional escolhido. Essa imagem se torna um novo ambiente que pode ser isolado ou ter suas interações bem definidas com o computador hospedeiro. Máquinas virtuais foram a primeira resposta dada para o problema de executar diversos aplicativos simultaneamente e em isolamento nos servidores.

Containers são pacotes de software que contém um ambiente completo para a execução de um aplicativo. Isso inclui suas dependências, bibliotecas de sistemas, configurações e outros binários, além dos arquivos de configuração necessários para essa operação. Eles estão disponíveis no Linux e Windows e são extremamente úteis para executar da mesma forma os aplicativos, independentemente do ambiente usado. Eles são especialmente usados na fase de desenvolvimento, reduzindo os conflitos entre as equipes de TI por favorecerem a montagem de ambiente idêntico para todos os envolvidos no projeto. Elem disso facilitam a portabilidade de um para outro sistema operacional, ou mesmo entre diferentes modelos de arquitetura de hardware.

Nas máquinas virtuais um pacote de software inclui, além do aplicativo que se quer executar, um sistema operacional inteiro. No caso dos containers o kernel (o núcleo) do sistema operacional é compartilhado com outros containers que, por isso, usam menos recursos do que as máquinas virtuais. Um servidor físico executando três máquinas virtuais, por exemplo, teria que rodar três sistemas operacionais separados. O mesmo servidor executando três aplicativos em containers roda em um único sistema operacional, sendo que cada container tem acesso ao núcleo do sistema operacional hospedeiro. Dessa forma a máquina pode rodar um número muito superior de containers. Além disso aplicativos de container são iniciados mais rápido, porque não precisam inicializar todo o sistema operacional, e podem liberar recursos que não estão em uso.

Outro problema que os containers buscam resolver é questão dos diversos aplicativos instalados em uma máquina que partilham bibliotecas comuns. Não é raro que um dos aplicativos necessite de versão diferente da biblioteca, o que pode fazer com que partes do software instalado deixe de funcionar. Containers são, portanto, úteis para o desenvolvimento, a distribuição e instalação, e uso em produção de softwares.

Finalmente, containers introduzem um nível adicional de segurança aos computadores. Sabemos que existem programas deliberadamente projetados para atacar, invadir, roubar dados e tempo de máquina no computador hospedeiro. Mesmo programas bem-intencionados podem conter bugs perigosos para o sistema e usuário. Um programa rodando em um container só tem acesso ao seu próprio ambiente, a menos que tenha sido projetado de outra forma.

Todas essas caraterísticas tornam os containers especialmente úteis para a implementação de aplicativos e serviços na nuvem, usando ambientes tais como o Azure, AWS, Google Cloud, etc.

Docker

Docker é o formato de implementação de container mais conhecido. Ele foi desenvolvido primeiro para o uso no Linux, depois levado até plataformas com MacOS e Windows. Associado ao uso de docker temos o Kubernetes, um projeto de código aberto do Google que serve como orquestrador de aplicativos em container. Ele é um dos gerenciadores de containers e imagens do docker disponíveis.

O projeto é open-source e mantido pela empresa Docker Inc., EUA. Empresas e indivíduos contribuem para o projeto que é construído sobre uma arquitetura de plug-ins, onde componentes podem ser acrescentados ou removidos. A Open Container Iniciative, OCI, é um conselho fundado para zelar pela manutenção e observância de padrões, e é mantida pela Linux Foundation com a colaboração de Docker Inc. e outras.

Atualmente (em 2022) o docker roda nativamente no Linux. Para MacOs e Windows ele instala uma única máquina virtual que pode executar todos os containers. Em sistemas modernos é possível rodar dentro de containers aplicativos do Windows e macOS. Docker não é uma linguagem de programação nem um ambiente de desenvolvimento mas sim uma ferramenta de comando de linha que ajuda na solução de diversos problemas comuns ao processo de desenvolvimento, tais como a construção de projetos, sua distribuição, instalação, remoção, atualização e execução. Existem hoje diversos aplicativos GUI (com interfaces gráficas) para o gerenciamento do Docker.

Resumindo

Podemos pensar um container como uma caixa que contém aplicações. Dentro dela existe tudo o que é necessário para a execução das aplicações e o container se assemelha a um computador, com seu prório nome de máquina, endereço de IP address e até seus próprios discos, todos eles virtuais e criados pelo docker.

O aplicativo dentro do container não tem contato com nada fora dele, exceto o que for explicitamente indicado. Um exemplo disso seriam os casos de interação com o usuário via teclado, ou o acesso do aplicativo a um banco de dados externo.

Terminologia

Images (imagens do Docker) contêm o código-fonte do aplicativo executável, junto com as ferramentas, bibliotecas e dependências que o código precisa para ser executado. Elas podem ser construídas pelo desenvolvedor ou baixadas prontas de repositórios como o Docker Hub. Geralmente uma imagem é composta de várias outras imagens base que são empilhadas em camadas. Vários containers podem ser criados a partir de uma imagem.

Containers são as instâncias ativas e em execução das imagens. Enquanto imagens são arquivos de leitura somente (read-only) os containers são processos ativos que podem interagir com outros processos. Administradores podem ajustar suas configurações e condições usando comandos do Docker.

Dockerfiles (arquivos Docker) são arquivos de texto com as instruções sobre como criar imagens. Um Dockerfile automatiza o processo de criação de containers, contendo todos os passos na sequência que devem ser executados para a construção da imagem. Ele é similar a um script, declarando qual é a imagem base inicial a usar e com instruções para instalar os demais programas necessários, em camadas posteriores.

Build é a ação de criar de uma imagem usando as informações fornecidas pelo Dockerfile, desde que os arquivos adicionais estejam disponíveis no diretório onde a imagem é criada.

Tag (marcação) é uma marca ou rótulo aplicado em imagens para diferenciar versões. As tags podem ser usadas para, por exemplo, baixar uma imagem em versão específica.

Layers (camadas) são modificações aplicadas à uma imagem por meio de instruções do dockerfile. Por exemplo, dentro de uma imagem do ubuntu, que está na primeira camada, pode-se instalar outro aplicativo qualquer que será construído em uma segunda camada. Layers são identificadas por ids (hashes) que são usados para identificá-las.

Repository (repositório) é uma coleção de imagens do Docker rotuladas com marcação (tags) que indicam a versão da imagem. Podem conter imagem de versões diversas diferenciadas por tags, e variantes para as diversas plataformas.

Docker Hub é o repositório público padrão de imagens do Docker considerado a “maior biblioteca para imagens de containers”. Ele contém mais de 100.000 imagens de containers provenientes de fornecedores de software comercial ou de código aberto, e desenvolvedores individuais. Usuários do Docker Hub podem compartilhar suas imagens livremente ou baixar imagens base para usar no desenvolvimento de outros projetos. Existem outros repositórios, como o GitHub.

Registro do Docker é um sistema de armazenamento e distribuição de imagens do Docker. Ele permite rastrear versões de imagens em repositórios usando o git, uma ferramenta de controle de versão. O registro padrão para imagens mais frequentemente usado é o Docker Hub (propriedade da Docker como uma organização).

Daemon Docker é um serviço que cria e gerencia imagens Docker usando os comandos do cliente. Ele serve como central de controle da implementação do Docker. O servidor onde o daemon é executado é chamado de host do Docker, que pode ser o computador local ou um serviço em nuvem.

Docker Desktop é um aplicativo que facilita a criação e compartilhamento de aplicativos e microsserviços em containers. Junto com o Desktop são instaladas ferramentas como Kubernetes, Docker Compose, BuildKit e verificação de vulnerabilidades. Inicialmente disponível apenas para Windows e MacOS, foi disponibilizado para desenvolvedores no Linux em Maio de 2022. Docker Desktop também inclui extensões do Docker para facilitar a integração com ferramentas de outros desenvolvedores da equipe ou de terceiros.

Docker Dashboard é um aplicativo GUI para gerenciamento de imagens, containers e volumes do Docker. Ele é um aplicativo WEB que roda em Node, abrindo uma página no navegador e pode ser usado para gerenciar visualmente os recursos de container.

Docker Compose é uma ferramenta utilizada na definição e compartilhamento de aplicações multi-container. Com Compose podemos criar um arquivo YAML para definir os serviços usados. Dessa forma um aplicativo pode ser iniciado ou finalizado com um único comando. Ele também facilita a colaboração entre desenvolvedores.

Instalando o Docker


Existem duas versões disponíveis para instalação:

  • Community Edition (CE), edição da comunidade,
  • Enterprise Edition (EE), edição comercial.

Docker CE é gratuita. Docker EE é o mesmo pacote de software, mas com suporte técnico e acesso à outros produtos da empresa.

Instalação: Fedora Linux, Ubuntu Linux, Archlinux, Mac, Windows.

Como exemplo, listamos o procedimento de instalação no Fedora Linux:

# Primeiro habilitamos o repositório    
$ sudo dnf -y install dnf-plugins-core

$ sudo dnf config-manager \
     --add-repo \
     https://download.docker.com/linux/fedora/docker-ce.repo

# instalamos o docker engine
$ sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Para iniciar o serviço do docker via systemctl usamos start docker. Depois podemos testar se a instalação foi bem sucedida verificando a versão instalada e o status do serviço.

# para iniciar o Docker
sudo systemctl start docker

# para verificar a versão instalada
$ docker --version
  Docker version 20.10.17, build 100c701

# verificando o status do serviço
$ sudo service docker status
Redirecting to /bin/systemctl status docker.service
● docker.service - Docker Application Container Engine
     Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
     Active: active (running) since Fri 2022-08-12 18:52:19 -03; 1min 10s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 32787 (dockerd)
      Tasks: 13
     Memory: 111.8M
        CPU: 344ms     # (saída truncada)

Nessa instalação é criado um grupo de usuários que podem rodar o Docker. Por default o Docker exige privilégios de root para rodar containers, o que pode ser problemático para a segurança do sistema. Para rodar o Docker sem necessidade de emitir comandos como superuser adicionamos um usuário ao grupo do Docker.

$ sudo usermod -aG docker <nome_usuario>
# verificando
$ cat /etc/group | grep docker
  docker:x:973:nome_usuario

# para iniciar e finalizar o docker-desktop
$ systemctl --user start docker-desktop
$ systemctl --user stop docker-desktop

# para desinstalar o docker
$ sudo dnf remove docker-desktop

# para desinstalar Docker Engine, CLI, containerd, e Docker Compose
$ sudo dnf remove docker-ce docker-ce-cli containerd.io docker-compose-plugin


Temos dois componentes principais com a instalação do docker: o cliente e o docker daemon (também chamado de docker engine). Uma versão alternativa de docker version (sem os hífens) fornece mais dados sobre a instalação:

$ docker version
  Client: Docker Engine - Community
   Version:           20.10.17
   API version:       1.41
   Go version:        go1.17.11
   Git commit:        100c701
   Built:             Mon Jun  6 23:03:59 2022
   OS/Arch:           linux/amd64
   Context:           default
   Experimental:      true

Server: Docker Engine - Community (saída truncada)

Para instalar e verificar o docker-compose usamos:

$ sudo dnf install docker-compose  # (no fedora) ou
$ sudo apt install docker-compose  # (no ubuntu)

# Para verificar a instalação
$ docker-compose version 
  docker-compose version 1.29.2, build unknown
  docker-py version: 5.0.3
  CPython version: 3.10.5
  OpenSSL version: OpenSSL 3.0.5 5 Jul 2022

Obs.: Para atualizar o docker é necessário remover os pacotes antigos e realizar uma nova instalação.

Imagens do Docker

Uma imagem do docker é um objeto que contém um aplicativo e arquivos de sistema, suficientes para rodar esse aplicativo. Essas imagens podem ser construídas pelo desenvolvedor ou baixadas pela internet de algum repositório, onde outros desenvolvedores as disponibilizaram. Por default as imagens são procuradas no GitHub. Um exemplo simples pode ser dado com a imagem de hello_world. Digitamos no prompt de comando do terminal:

# o primeiro passo é baixar uma imagem
$ docker pull hello-world
# o container pode ser agora executado
$ docker run hello-world
  Hello from Docker!
  This message shows that your installation appears to be working correctly.
  (saída truncada)


O uso de docker pull <imagem> não é estritamente necessário. O comando docker run procura primeiro a imagem na máquina local e, se ela não foi previamente baixada, ele a procura em Docker Hub. Se a imagem for encontrada ele a baixa e constroi um container à partir dela. No caso desse exemplo a imagem contém um aplicativo simples de demonstração que imprime a mensagem listada (que foi aqui truncada) no console. A cada execução de run um novo container é criado e seu aplicativo subjacente é rodado. Imagens baixadas são previamente compiladas (built).

O comando docker image ls permite ver quais são as imagens já baixadas:

$ docker image ls
  REPOSITORY                   TAG       IMAGE ID       CREATED        SIZE
  hello-world                  latest    feb5d9fea6a5   9 months ago   13.3kB

A tag latest indica que a última versão disponível foi baixada. Em outros casos a tag é a versão da imagem. A imagem hello-world foi preparada para teste dos iniciantes e contém instruções interessantes. Para apagar um imagem baixada usamos docker rm imagem.

Vamos apagar essa imagem e repetir o processo anterior. O output da execução será em inglês (para essa imagem) mas a exibiremos aqui com partes traduzidas.

# para apagar a imagem
$ docker rm hello-world

# para rodar novamente essa imagem
$ docker container run hello-world

  Hello from Docker!
  Essa messagem mostra que sua instalação parece estar funcionando corretamente.
  
  Para gerar essa mensagem Docker seguiu os seguintes passos:
   1. O cliente Docker fez contato com o Docker daemon.
   2. O Docker daemon baixou (pulled) a imagem "hello-world" do Docker Hub. (amd64)
   3. O Docker daemon criou um novo container à partir dessa imagem. O container
      executou código que produziu o presente output.
   4. O Docker daemon transmitiu esse output para o Docker client, que o enviou para
      o terminal.
  
  Você pode tentar algo mais ambicioso, por ex. rodando o container do Ubuntu usando:
   $ docker run -it ubuntu bash
  
  Partilhe imagens, fluxos de trabalho automatizados e mais coisas usando um Docker ID gratuito:
    https://hub.docker.com/
  
  Para ver mais exemplos e ideias visite:
        https://docs.docker.com/get-started/

Aproveite a oportunidade para entrar no Docker Hub e criar uma conta.

Comandos do Docker

Recapitulando, podemos baixar uma imagem do docker usando pull.

$ docker pull <nome_imagem>:latest

Como vimos latest significa que baixamos a última e mais atualizada versão da imagem. Outra versão pode ser especificada se necessário como, por exemplo, fazendo $ docker pull <nome_imagem>:8.0. Para executar essa imagem usamos:

$ docker run -d --name=<nome_alternativo> -p m:n -<nome_imagem>

Além do comando run para criar e rodar um container usamos os parâmetros:

  • --name: para dar um nome alternativo para o container criado,
  • -p: para tornar a porta do docker visível fora do container,
  • <nome_imagem>: especifica a imagem a ser executada,
  • m:n: mapeia portas do container em portas do host. (porta_host:porta_container)

Podemos ver quais são os containers em execução:

docker ps
container ID  IMAGE               COMMAND                 CREATED         STATUS                 
642881d63879  mysql/mysql-server  "/entrypoint.sh mysq…"  16 minutes ago  Up 16 minutes (healthy)

PORTS                                                       NAMES
0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060-33061/tcp  mysql1

Para encerrar a execução, e remover o container usamos, respectivamente:

# encerrar    
$ docker stop <nome_imagem>
# remover container
$ docker rm <nome_imagem>

No linux a configração de docker e as imagens baixadas ficam no diretório /var/lib/docker. No Windows o endereço desses arquivos varia, de acordo com o modo de instalação. Esses arquivos nunca devem ser manipulados diretamente pelo usuário.

Uma página de ajuda é exibida com docker help, sobre a sintaxe de docker.

# ajuda geral    
$ docker help

# ajuda sobre um comando específico
$ docker <comando> help
# por exemplo, para o comando cp (copy)
$ docker cp help

Podemos ver todas as imagens baixadas no computador e apagá-las, se assim for desejado.

# para listar as imagens
$ docker image ls
REPOSITORY                   TAG       IMAGE ID       CREATED         SIZE
mysql/mysql-server           latest    5a9594052aec   3 months ago    438MB
hello-world                  latest    feb5d9fea6a5   10 months ago   13.3kB

# para apagar as imagens usamos os primeiros caracteres do id para identificá-las
$ docker image rm -f 5a
$ docker image rm -f fe

#alternativamente, o seguinte comando lista todos os containers
$ docker container ls --all --quiet

# podemos apagar todos os containers usando (veja descrição abaixo)
$ docker container rm --force $(docker container ls --all --quiet)

A sintaxe $() faz com que a saída de um comando seja enviada para o comando externo. Nesse caso o comando interno gera uma lista de containers instalado, que é enviada para o comando de apagamento forçado. A chave –force (ou -f) força o apagamento mesmo que exista uma imagem desse container em execução. Esse comando deve ser usado com cautela pois a remoção é feita sem nenhuma confirmação.

Você encontra mais ajuda no site do docker.

Imagem do MySQL

Como um exemplo mais completo e útil vamos carregar e executar uma imagem contendo o mysql. Começamos por baixar (pull) a imagem.

$ docker pull mysql/mysql-server:latest

Como vimos latest significa que baixamos a última e mais atualizada versão do mysql. Outra versão pode ser especificada se necessário, como em $ docker pull mysql/mysql-server:8.0. Para executar essa imagem usamos:

$ docker run --name=mysql1 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=meupassword -d mysql/mysql-server

Pode ocorrer que a porta designada esteja ocupada por outro serviço do computador host, o que provoca a emissão de uma mensagem de erro. Nesse caso podemos usar outra porta, tal como docker run --name=mysql1 -p 8080:3306....

Além do comando run para criar e rodar um container usamos os parâmetros:

  • --name: para dar um nome alternativo para o container criado,
  • -p: para tornar a porta do docker visível fora do container,
  • -e: para inserir um password de gerenciamento do mysql,
  • mysql/mysql-server: especifica a imagem a ser executada,
  • 3306:3306: mapeia portas do container em portas do host. (porta_host:porta_container)

Podemos ver quais são os containers em execução:

docker ps
container ID  IMAGE               COMMAND                 CREATED         STATUS                 
642881d63879  mysql/mysql-server  "/entrypoint.sh mysq…"  16 minutes ago  Up 16 minutes (healthy)

PORTS                                                       NAMES
0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060-33061/tcp  mysql1

A linha foi quebrada para facilitar a visualização. Para conectar com o servidor do mysql executamos:

$ docker exec -it mysql1 mysql -uroot -p
Enter password: meupassword

Welcome to the MySQL monitor.  ... (saída truncada)
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> 


Operações usuais sobre o banco de dados ficam disponíveis.

Por ex.:

# cria um banco de dados
mysql> CREATE DATABASE teste;
# use esse BD
mysql> USE teste;
# cria uma tabela 
mysql> CREATE TABLE cidades(nome varchar(60),id varchar(4));

# insere valores na tabela
mysql> INSERT INTO cidades SET nome='Belo Horizonte', id='1';
mysql> INSERT INTO cidades SET nome='Salvador', id='2';
mysql> INSERT INTO cidades SET nome='Curitiba', id='3';

mysql> SELECT * FROM cidades;
+----------------+------+
| nome           | id   |
+----------------+------+
| Belo Horizonte | 1    |
| Salvador       | 2    |
| Curitiba       | 3    |
+----------------+------+
3 rows in set (0.00 sec)

# para abandonar o programa
mysql> exit

Para encerrar a execução, e remover o container usamos, respectivamente:

# encerrar    
$ docker stop mysql1
# remover container
$ docker rm mysql1

Conectando com o container


A interação com um container se dá de modo similar à execução de comandos em um computador remoto. Para mostrar isso executaremos uma imagem do docker chamada diamol/base, criada e disponibilizada no Dockerhub por E. Stone para exemplificar um trecho de seu livro (veja bibliografia). Para interagir com o aplicativo em execução usamos as flags –interactive e –tty (ou simplesmente -i e -t) para fazer essa interação através do terminal. O primeiro comando abaixo baixa a imagem e a executa criando um container que expõe um prompt de terminal para o usuário. Os comandos aceitos no prompt dependem do sistema operacional: por ex., no linux usamos ls para listar os arquivos do diretório atual; no Windows usamos dir.

# para baixar e rodar a imagem    
$ docker container run --interactive --tty diamol/base
  Unable to find image 'diamol/base:latest' locally
  latest: Pulling from diamol/base
  31603596830f: Pull complete 
  792f5419a843: Pull complete 
  Digest: sha256:787fe221a14f46b55e224ea0436aca77d345c3ded400aaf6cd40125e247f35c7
  Status: Downloaded newer image for diamol/base:latest

# O aplicativo expõe o prompt do terminal (usaremos os sinais / ⧣)
/ ⧣
# no linux podemos listar os arquivos na pasta ativa
/ ⧣ ls
  bin    etc    lib    mnt    proc   run    srv    tmp    var
  dev    home   media  opt    root   sbin   sys    usr

# o nome da máquina em execução é o mesmo que o id da imagem
/ ⧣ hostname
2c33c99fcaf7
# para ver a data
/ ⧣ date
Sat Aug  6 15:38:21 UTC 2022

Sem fechar esse terminal podemos abrir um segundo terminal e listar detalhes dos containers em execução:

$ docker container ls
container ID  IMAGE        COMMAND    CREATED        STATUS       PORTS  NAMES
2c33c99fcaf7  diamol/base  "/bin/sh"  3 minutes ago  Up 3 minutes        strange_lehmann

# para listar os processos dentro desse container
$ docker container top 2c
UID      PID      PPID      C    STIME    TTY     TIME      CMD
root     13436    13410     0    12:36    pts/0   00:00:00  /bin/sh

# para listar as ações (log) executadas nesse container
$ docker container top 2c
# ls
bin    etc    lib    mnt    proc   run    srv    tmp    var
dev    home   media  opt    root   sbin   sys    usr

(retorna uma lista de comandos utilizados, aqui truncada)

# inspect retorna uma descrição detalhada do container
$ docker container inspect 2c
  [
    {
        "Id": "2c33c99fcaf7fb94393ce1e7e31ce05ab6f5bcc914fc5eac8c90e600cf711995",
        "Created": "2022-08-06T15:36:14.327115583Z",
        "Path": "/bin/sh",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,                        ... (saída truncada)
        },
    }
]        

Os comandos usam o atalho 2c para se referir ao ID do container 2c33c99fcaf7. inspect retorna dados no formato JSON, aqui truncados, incluindo informações sobre caminhos usados pelo filesystem virtual e muitos outros.

Podemos ver uma lista de imagens instaladas usando docker image list ou docker image ls, e apagar uma imagem que não será mais necessária com docker image rm <id>, onde <id> é o id listado no passo anterior. No nosso caso, se usamos:

# para listar imagens baixadas
$ docker image list
  REPOSITORY                   TAG       IMAGE ID       CREATED         SIZE
  mysql/mysql-server           latest    5a9594052aec   3 months ago    438MB
  hello-world                  latest    feb5d9fea6a5   10 months ago   13.3kB
  diamol/base                  latest    9fc3f74c8b53   17 months ago   7.13MB

# para apagar a imagem referente à diamol/base (a imagem está em uso no container 5f73d74675a7)
$ docker image rm 9fc3f74c8b53
  Error response from daemon: conflict: unable to delete 9fc3f74c8b53 (must be forced)
  - image is being used by stopped container 5f73d74675a7

# para forçar a interrupção desse container e apagar a imagem
$ docker image rm -f 9fc3f74c8b53
  Deleted: sha256:9fc3f74c8b533bed2ac40ccfc6a5235ebb626a26a230dc3731fcdf926d984106

# lista todos os containers (em execução ou não)
$ docker container ls --all
  container ID   IMAGE               COMMAND                  CREATED        STATUS                  PORTS    NAMES
  5f73d74675a7   9fc3f74c8b53        "/bin/sh"                3 hours ago    Exited (0) 2 hours ago            intelligent_wescoff
  2c33c99fcaf7   9fc3f74c8b53        "/bin/sh"                6 hours ago    Exited (0) 3 hours ago            strange_lehmann
  e0ca567900b9   nginx               "/docker-entrypoint.…"   25 hours ago   Created                           webserver
  5ccc8d7d5ff3   hello-world         "/hello"                 32 hours ago   Exited (0) 32 hours ago           hopeful_banach
  fb9dc473c7f5   hello-world         "/hello"                 32 hours ago   Exited (0) 32 hours ago           loving_swartz
  5e5c65bb00c4   hello-world         "/hello"                 32 hours ago   Exited (0) 32 hours ago           cool_cannon
  (... saída truncada)

Como nenhum container está ativo (no meu caso) eles têm o status Exited. Quando um container está em execução o mesmo ocorre com o aplicativo que ele contém. Quando o processo é terminado o container entra no estado de encerrado (Exited). Containers encerrados não usam recursos de memória nem tempo de CPU do computador mas são mantidos em disco e ocupam espaço. Containers não desaparecem quando são terminados mas continuam a existir e podem ser executados novamente. Nesse estado eles retêm logs e arquivos no sistema de arquivos. Para remover containers encerrados você deve emitir o controle específico para tal.

Outros comandos associados:

$ docker ps -a                  # Lista containers, informando a imagem que os gerou

$ docker images                 # Lista imagens 

$ docker rm <container_id>      # Remove um container interrompido (não em execução)

$ docker rm -f <container_id>   # Força a remoção de um container mesmo que em execução

$ docker rmi <image_id>         # Remove uma imagem que não esteja
                                # associada um container em execução

$ docker rmi -f <image_id>      # Força a remoção da imagem mesmo que esteja em uso

$ docker system prune -a        # remove: todos os containers parados,
                                # todas as redes não utilizadas 
                                # todas as imagens não ativas
                                # todo o cache de docker                           

Claro que o comando docker system prune -a deve ser usada com bastante cuidado.

Rodando um webserver


Outro bom exemplo de funcionamento do docker consiste em rodar um container preparado pelo DockerHub, que roda um tutorial interativo para iniciantes. Outras instruções podem ser encontradas em Docker Docs: Getting started.

$ docker run -d -p 8080:80 docker/getting-started

# as flags podem ser unidas
docker run -dp 8080:80 docker/getting-started

# para acessar o tutorial abra o navegador em http://localhost:8080. 

As flags ou chaves significam o seguinte:

  • -d: execute o container em mode destacado (detached ou em background).
  • -p 8080:80: associa a porta 8080 do host à porta 80 do container. Se a porta 8080 já estiver em uso especifica outra porta, como 80 ou 3000.
  • docker/getting-started especifica o nome da imagem a ser usada.

Como já vimos, a imagem é baixada (se não estiver no computador) e rodada. Ela cria uma seção de um servidor que pode ser acessado em http://localhost:8080. Essa imagem foi preparada pelos desenvolvedores do docker para fornecer instruções iniciais para seu uso.

Se iniciarmos uma imagem em segundo plano e quisermos um terminal interativo podemos executar

$ docker exec -it <container> bash

Rodando Oracle Linux


Mostraremos outro exemplo rodando um container com Oracle Linux.

Verificamos primeiro o status do docker:

$ service docker status
  Redirecting to /bin/systemctl status docker.service
  ○ docker.service - Docker Application Container Engine
       Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
       Active: inactive (dead)
  TriggeredBy: ○ docker.socket
       Docs: https://docs.docker.com

Para terminar um comando no prompt do bash usamos CTRL-C. Vemos que o docker está inativo, como exibido em Active: inactive (dead). Então reiniciamos o serviço:

$ sudo service docker start
  [sudo] password for guilherme: 
  Redirecting to /bin/systemctl start docker.service

$ service docker status
  Redirecting to /bin/systemctl status docker.service
  ● docker.service - Docker Application Container Engine
       Loaded: loaded (/usr/lib/systemd/system/docker.service; disabled; vendor preset: disabled)
       Active: active (running) since Mon 2022-08-15 16:32:32 -03; 2s ago
       (saída truncada)

Para baixar a imagem do Oracle Linux usamos pull oraclelinux:9, sendo 9 a versão mais atual. Caso seja necessária uma versão anterior usamos pull oraclelinux:8 (ou a versão necessária). No caso do Oracle Linux a tag latest foi removida, como se pode ver na página docker: oraclelinux.

$ sudo docker pull oraclelinux:9
  9: Pulling from library/oraclelinux
  16c5055e8b97: Pull complete 
  Digest: sha256:93514816e192d51fbb34c008a479c789c43fb1fe0c2f91bd5be1ba7919dafa77
  Status: Downloaded newer image for oraclelinux:9
  docker.io/library/oraclelinux:9

# para visualizar as imagens baixadas
$ docker images
REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
docker/getting-started   latest    cb90f98fd791   4 months ago   28.8MB


Podemos rodar essa imagem, gerando um container, com o comando docker run <nome_imagem>. Por default a imagem é criada, se já não existe, e roda em primeiro plano (attached mode). Para rodá-la no segundo plano (em background ou dettached mode) usamos o marcador (flag) -d. Para usar o terminal de modo interativo use as opções –i, –t. As flags podem ser juntas, como em –dit. A flag –rm serve para apagar o container quando ele for encerrado. A opção --name é usada para dar um nome para a imagem sendo rodada. Esse nome deve ser único (não pode ter o mesmo nome de um que já está rodando).

# para rodar a imagem em primeiro plano (attached mode)    
$ sudo docker run  
    
# para rodar a imagem em segundo plano ou background (dettached mode)
$ sudo docker run -d  

No caso de nossa imagem para o oracle linux usaremos run com as flags já descritas. Um terminal é apresentado, onde podem ser digitados comandos apropriados.

$ docker run -i -t  --rm --name oraclelinux oraclelinux:9
  [root@723e7c1a71ea /]#
# se executado em outro terminal (para não fechar o terminal do oraclelinux)
# podemos ver que temos uma imagem rodando. 
$ docker images
  REPOSITORY    TAG       IMAGE ID       CREATED      SIZE
  oraclelinux   9         a0ad236ea4f0   6 days ago   218MB

# no prompt digitamos comandos. Por ex.
[root@723e7c1a71ea /]# cat /etc/oracle-release
  Oracle Linux Server release 9.0

[root@03d35e4c35b1 teste_oracle]# cat /etc/os-release
  NAME="Oracle Linux Server"
  VERSION="9.0"

Os comandos usuais do linux pode ser executados no prompt:

[root@723e7c1a71ea /]# mkdir teste_oracle      # cria um novo diretório
[root@723e7c1a71ea /]# cd teste_oracle         # move cursor para o novo diretório

# envia texto para arquivo.txt
[root@723e7c1a71ea teste_oracle]# echo "teste de docker no oraclelinux" > arquivo.txt
# exibe conteúdo do arquivo.txt
[root@723e7c1a71ea teste_oracle]# cat arquivo.txt
  teste de docker no oraclelinux
  
# para interromper a execução usamos exit [root@723e7c1a71ea teste_oracle]# exit # retornamos ao prompt do sistema hospedeiro $

Docker Dashboard (GUI)

Um aplicativo web pode ser usado com Docker Dashboard. O Dashboard roda com Node.js que, portanto, precisa estar instalado em seu sistema. Depois você deve clonar um repositório do Github.

# clone repositório

$ git clone git@github.com:rakibtg/docker-web-gui.git

# mude para o novo directório criado

$ cd ./docker-web-gui

# execute o app app.js usando o node. Essa ação instalará os módulos necessários ainda não instalados.

$ node app.js

# agora você pode abrir no navegador a página http://localhost:3230/

O navegador abrirá uma página exibindo as imagens instaladas, containers em execução ou parados, visualização dos logs, criação e vizualização de grupos de containers.

Para baixar o código do aplicativo usando git clone git@github.com:rakibtg/docker-web-gui.git, você deve ter uma conta do GitHub com chaves certificadas de ssh. Instruções em Connecting to GitHub with ssh. Alternativamente você pode entrar no site do GitHub para esse aplicativo, baixar e instalar o código.

A continuação desse artigo está em preparação. Coloque nos comentários se você gostaria de ler mais sobre Docker, em particular sobre como preparar a sua própria imagem.

Bibliografia


Livros:

  • Ashley, David: Foundation Dynamic Web Pages with Python, Apress, 2020,
  • Miel, Ian: Sayers, Aidan H.: Docker in Practice, 2 Ed., Manning, 2019,
  • Nickoloff, Jeff; Kuenzli, Stephen: Docker in Action, 2 Ed., Manning, 2019,
  • Poulton, Nigel: Docker Deep Dive, disponível em Leanpub.com, 2018,
  • Stoneman, Elton: Learn Docker in a Month of Lunches, Manning, 2020,
  • Vohra, Deepak: Pro Docker, Apress, 2016.

Sites:

Filtros em Templates no Django


Django, Templates e Filtros

Já vimos em Templates do Django que os templates são modelos usados na arquitetura MTV para renderizar as requisições do cliente e gerar texto com marcação HTML. Dentro desses modelos podemos receber variáveis enviadas no contexto, com a marcação {{ var }}. Um contexto é um conjunto de valores, em um objeto tipo dicionário (um conjunto de chaves:valores), passado pelas views para o template. Os nomes das variáveis são passados como strings.

# se temos a variável    
» contexto = {"n1": "um"; "n2":"dois";}
# e o template
⎀ Temos no contexto {{ n1 }} e {{ n2 }}.
# será renderizado como
↳ Temos no contexto um e dois.

Filtros são formas de modificar as saídas de variáveis. Por ex.:

» contexto = {'django': 'o framework web para perfecionistas com prazos'}
⎀ {{ django|title }}
↳ O Framework Web Para Perfecionistas Com Prazos
# ou
» contexto = {'data': '2022-07-04'}
⎀ {{ data|date:"d/m/Y" }}
↳ 04/07/2022

Muitos outros filtros são predefinidos e outros mais podem ser definidos pelo programador.

Filtros do Django

add soma valor ao argumento. Tenta forçar a conversão de strings para numéricos.

» valor = 6
⎀ {{ valor|add:"2" }}
↳ 8

» lista1 = [2,3,4]; lista2 = [7,8,9]
⎀ {{ lista1|add:lista2 }}
↳ [2,3,4,7,8,9]

addslashes, insere “\” em aspas.

» valor = "Einstein disse: 'Não existe mais espaço e tempo'."
⎀ {{ valor|addslashes }}
↳ Einstein disse: \'Não existe mais espaço e tempo\'.

capfirst, capitaliza primeira letra de uma string.

» valor = "não existe mais espaço e tempo"
⎀ {{ valor|capfirst }}
↳ Não existe mais espaço e tempo

center, centraliza texto dentro do espaço dado.

» valor = "tempo"
⎀ {{ valor|center:"20" }}
↳ "       tempo        "

cut, remove valores do argumento do string dado.

» valor = "não existe mais espaço e tempo"
⎀ {{ valor|cut:" " }}
↳ nãoexistemaisespaçoetempo

cut, formata uma data (e hora) de acordo com especificação dada.

» data = "2022-02-30"
⎀ {{ data|date:"d/m/y" }}
↳ 30/02/22
⎀ {{ data|date:"d-m-Y" }}
↳ 30-02-2022

Alguns formatos são pre-definidos. Veja a lista completa em Django docs.

Caracter Descrição Exemplo
Dia
d dia do mes com dois dígitos ’01’ até ’31’
j dia do mes sem zeros. ‘1’ até ’31’
D dia da semana em texto, 3 letras. ‘Fri’
l dia da semana em texto completo. ‘Friday’
w dia da semana, numérico. ‘0’ (Domingo) até ‘6’ (Sábado)
z dia do ano. 1 to 366
Semana
W número da semana no ano. 1, 53
Mês
m mês, 2 dígitos. ’01’ até ’12’
n mês sem zeros. ‘1’ até ’12’
M mês, texto, 3 letras. ‘Jan’, ‘Dec’
b mês, texto, 3 letras, ninúsculas. ‘jan’, ‘dec’
F mês, texto, extenso. ‘January’
t quantos dias no mês. 28 to 31
Ano
y ano, 2 dígitos. ’00’ até ’99’
Y ano, 4 dígitos. ‘0001’, …, ‘1999’, …, ‘9999’
L Booleano, se ano é bissexto. True ou False
Hora
g hora, formato 12-hora sem zeros. ‘1’ até ’12’
G hora, formato 24-hora sem zeros. ‘0’ até ’23’
h hora, formato 12-hora. ’01’ até ’12’
H hora, formato 24-hora. ’00’ até ’23’
i minutos. ’00’ até ’59’
s segundos, 2 dígitos. ’00’ até ’59’
u microssegundos. 000000 até 999999
a ‘a.m.’ ou ‘p.m.’
A ‘AM’ ou ‘PM’.
f hora, format 12-horas e minutos ‘1:30’
P hora, formato 12-hora horas, minutos ‘a.m.’/’p.m.’
Timezone
e nome da timezone ‘GMT’, ‘-500’, ‘US/Eastern’, etc.

O formato pode ser um dos predefinidos como DATE_FORMAT, DATETIME_FORMAT, SHORT_DATE_FORMAT ou SHORT_DATETIME_FORMAT ou um formato construído com os especificadores acima. Formatos predefinidos podem depender do ajuste local.

# se data_valor é um objeto datetime, como o resultante de datetime.datetime.now() 
# com hora 23:45, dia 01/11/2021
⎀ {{ data_valor|date:"D d M Y" }} {{ data_valor|time:"H:i" }}
↳ Mon 01 Nov 2021 23:45

# se o locale for pt-BR
⎀ {{ data_valor|date:"SHORT_DATE_FORMAT" }}
↳ 01/11/2021

default, fornece valor default se argumento for False.

»  valor = ""   
⎀ {{ valor|defalt:"nada aqui" }} 
↳ nada aqui

default_if_none, fornece valor default se argumento for None.

» valor = None
⎀ {{ valor|defalt_if_none:"recebemos None" }} 
↳ recebemos None

dictsort recebe uma lista de dicionários e ordena a lista por alguma das chaves do docionário.

# considerando o dicionário:    
» dicio = [
»     {'nome': 'Zuenir', 'idade': 9},
»     {'nome': 'Antônia', 'idade': 12},
»     {'nome': 'Jaime', 'idade': 3},
» ]

⎀ {{ dicio|dictsort:"nome" }}
# resulta em
↳ dicio = [
↳     {'nome': 'Antônia', 'idade': 12},
↳     {'nome': 'Jaime', 'idade': 3},
↳     {'nome': 'Zuenir', 'idade': 9},
↳ ]

Exemplos mais complexos podem ser obtidos:

# se livros é    
» livros = [
»     {'titulo': '1984', 'autor': {'nome': 'George', 'idade': 45}},
»     {'titulo': 'Timequake', 'autor': {'nome': 'Kurt', 'idade': 75}},
»     {'titulo': 'Alice', 'autor': {'nome': 'Lewis', 'idade': 33}},
» ]

# o código em template
⎀ {% for livro in livros|dictsort:"autor.idade" %}
⎀     * {{ livro.titulo }} ({{ livro.autor.nome }})
⎀ {% endfor %}

# resultaria em
↳ * Alice (Lewis)
↳ * 1984 (George)
↳ * Timequake (Kurt)

dictsortreversed tem o mesmo efeito que dictsort, mas ordenando em ordem invertida.

divisibleby returna True se o valor é divisível pelo argumento.

» valor = 171
⎀ {{ value|divisibleby:"3" }}
↳ True

escape promove remoção de tags html.

# esse exemplo mostra a exibição final no navegador
» string_html = "<b>Negrito<b>"
⎀ {{ string_html }}
↳ <b>Negrito<b>

⎀ {{ string_html|scape }}
↳ <b>Negrito</b>

escape converte:

  • < em &lt;
  • > em &gt;
  • ' (aspas simples) em &#x27;
  • " (aspas duplas) em &quot;
  • & em &amp;

first retorna o 1º elemento de uma lista.

» lista = ["casa","da","sogra"]
⎀ {{ lista|first }}
↳ casa

floatformat promove o arredondamento de números flutuantes.

» valor = 34.23234
⎀ {{ valor|floatformat }}
↳ 34.2

» valor = 34.0000
⎀ {{ valor|floatformat }}
↳ 34

» valor = 34.26000
⎀ {{ valor|floatformat }}
↳ 34.3

O número de casas pode ser definido em valor|floatformat:n. Passando “0” como argumento o arredondamento será para o inteiro mais próximo. O sufixo g introduz separador de milhar, definido em THOUSAND_SEPARATOR.

valor template output
34.23234 {{ valor|floatformat:2 }} 34.23
34.00000 {{ valor|floatformat:2 }} 34.00
34.26000 {{ valor|floatformat:2 }} 34.26
34.23234 {{ valor|floatformat:”0″ }} 34
31.00000 {{ valor|floatformat:”0″ }} 31
39.56000 {{ valor|floatformat:”0″ }} 40
34232.34 {{ valor|floatformat:”2g” }} 34,232.34
34232.06 {{ valor|floatformat:”g” }} 34,232.1

get_digit retorna um inteiro na posição especificada, contando do final para o início. Se não for possível encontar esse dígito, retorna o valor original.

» valor = 9512845
⎀ {{ valor|get_digit:"4" }}
↳ 2

⎀ {{ valor|get_digit:"9" }}
↳ 9512845

join faz a união de elementos em uma lista em uma string (como em str.join(lista)).

» lista = ["casa", "da", "mãe", "Joana"]
⎀ {{ lista|join:" - " }}
↳ casa - da - mãe - Joana

last retorna o último elemento de uma lista.

»  lista = ["casa", "da", "mãe", "Joana"]
⎀ {{ lista|last}}
↳ Joana

len retorna o comprimento de uma lista.

»  lista = ["casa", "da", "mãe", "Joana"]
⎀ {{ lista|len}}
↳ 4

length_is retorna booleano, se o comprimento de uma lista é o dado em parâmetro.

»  lista = ["casa", "da", "mãe", "Joana"]
⎀ {{ lista|length_is:"4"}}
↳ True

linebreaks substitui quebras de linha em texto puro por uma quebra de linha html (<br>) e insere anova linha entre tags de parágrafo (<p> … </p>).

» texto_puro = "Essa é a linha 1\nEssa é a linha 2"
⎀ {{ texto_puro|linebreaks}}
↳ <p>Essa é a linha 1<br>Essa é a linha 2</p>

linebreaksbr faz a mesma coisa, sem inserir a linha em parágrafo.

linenumbers quebra texto em linhas e as numera.

» lista_compras = '''Leite
» Açucar
» Café
» Pão'''

⎀ {{ lista_compras|linenumbers }}
# resulta em
↳ 1. Leita
↳ 2. Açucar
↳ 3. Café
↳ 4. Pão

Observe que lista_compras = “Leite\nAçucar\nCafé\nPão”.

ljust alinha texto à esquerda dentro de espaço de n caracteres dado.

» comprar = "Pão"
⎀ {{ comprar|ljust:"9" }}
↳ "Pão      "

lower converte todas os caracters de uma string para caixa baixa (minúsculas).

» texto = "Pão COM Manteiga"
⎀ {{ texto|lower }}
↳ pão com manteiga

make_list retorna valor string ou inteiro em uma lista.

» texto = "Pão de Queijo"
⎀ {{ texto|make_list }}
↳ ["P", "ã", "o", " ", "d", "e", " ", "Q", "u", "e", "i", "j", "o"]

» numero = 1957
⎀ {{ numero|make_list }}
↳ ["1", "9", "5", "7"]

pluralize retorna sufixo para plurais se valor do parâmetro for maior que 1. Esse valor pode ser o comprimento do objeto. O sufixo default é s, mas isso pode ser alterado.

» itens_compra = 1
⎀ Você tem que comprar {{ itens_compra }} objeto{{ itens_compra|pluralize }}.
↳ Você tem que comprar 1 objeto.

» itens_compra = 23
⎀ Você tem que comprar {{ itens_compra }} objeto{{ itens_compra|pluralize }}.
↳ Você tem que comprar 23 objetos.

Sufixos alternativos podem ser inseridos como parâmetros:

» quantos = 1
⎀ Você fez o pedido de {{ quantos }} paste{{ quantos|pluralize:"l,is" }}.
↳ Você fez o pedido de 1 pastel.

» quantos = 45
⎀ Você fez o pedido de {{ quantos }} paste{{ quantos|pluralize:"l,is" }}.
↳ Você fez o pedido de 45 pasteis.

random retorna um elemento aleatório de uma lista.

»  lista = ["casa", "da", "mãe", "Joana"]
⎀ {{ lista|random }}
# um possível resultado é
↳ mãe

rjust alinha texto à direita dentro de espaço de n caracteres dado.

» comprar = "Pão"
⎀ {{ comprar|rjust:"9" }}
↳ "      Pão"

safe marca texto como não necessitando escapes.

escape promove remoção de tags html.

» string_html = "<b>Negrito<b>"
⎀ {{ string_html|escape }}
↳ <b>Negrito<b>

Se existirem tags html elas serão renderizadas no navegador.

slice retorna uma fatia (slice) de uma lista. Usa a mesma sintaxe de slicing de listas do python:

» lista = ["casa","da","sogra", "no", "domingo"]
⎀ {{ lista|slice:":3" }}
↳ ["casa","da","sogra"]

slugify converte texto em ASCII puro, convertendo espaços em hífens. Remove caracteres que não são alfanuméricos, sublinhados (underscores) ou hífens. Converte tudo para minúsculas eliminando espaços nas bordas.

» texto = " Artigo 31 das Notas "
⎀ {{ texto|slugfy }}
↳ artigo-31-das-notas

stringformat formata variável de acordo com o parâmetro especificador.

» valor = 10
⎀ {{ valor|stringformat:"E" }}
↳ 1.000000E+01

Mais caracteres de formatação em printf-style String Formatting.

striptags remove tags [X]Html sempre que possível.

» valor = "<b>Um texto pode ter</b> <button>várias tags</button> <span>(x)html</span>!"
⎀ {{ valor|striptags }}
↳ Um texto pode ter várias tags (x)html!

Observação: striptags não garante que o texto seja seguro. Não aplique a filtro safe sobre o resultado de striptags.

time formata variável tipo time de acordo com formato especificado.

» hora = datetime.datetime.now()
⎀ {{ hora|time:"H:i" }}
↳ 18:49

⎀ {{ hora|time:"H\h i\m" }}
↳ 01h 23m

No exemplo caracteres literais foram escapados (\h, \m).

timesince formata uma diferença de datas entre now (agora) e data fornecida em parâmetro.

# se artigo_gravado contem uma data e
» hora = datetime.datetime.now()
⎀ {{ artigo_gravado|timesince:hora }}
↳ 14 days, 18 hours

timeuntil é análoga à timesince mas retornando a diferença entre uma data data e data futura.

title formata string como título de artigos e livros, colocando em maiúsculas as primeiras letras de cada palavra.

» titulo = "análise auxiliar de enrolação científica"
⎀ {{ titulo|title }}
↳ Análise Auxiliar De Enrolação Científica

truncatechars realiza o truncamento de um texto em um número especificado de caracteres. O texto truncado é seguido de elipses .

» texto = "Esta é uma nota grande."
⎀ {{ texto|truncatechars:15 }}
↳ Esta é uma nota...
# nada é feito de o texto for menor que o parâmetro de truncamento
⎀ {{ texto|truncatechars:35 }}
↳ Esta é uma nota grande.

truncatechars_html é similar à truncatechars mas evitando o descarte de tags html.

» texto = "

Esta é uma nota grande.

" ⎀ {{ texto|truncatechars_html:15 }} ↳ <p>Esta é uma not...</p>

truncatewords trunca uma string após um número dado de palavras. O texto truncado é seguido de elipses . Quebras de linha são removidas.

» texto = "Um texto com\n muitas palavras pode ser cortado"
⎀ {{ texto|truncatewords:4 }}
↳ Um texto com muitas...

truncatewords_html é similar à truncatewords, mas evitando eliminar tags html. Quebras de linha são mantidas.

» texto = "<p>Um texto com\n muitas palavras pode ser cortado</p>"
⎀ {{ texto|truncatewords:4 }}
↳ <p>Um texto coman muitas...</p>

unordered_list constroi uma lista html à partir de listas e listas aninhadas, inserindo recursivamente sublistas quando necessário.

» lista = ['Estados', ['Minas Gerais', ['Juiz de Fora', 'Belo Horizonte'], 'Paraná']]
⎀ {{ lista|unordered_list }}
↳
<li>Estados
<ul>
        <li>Minas Gerais
        <ul>
                <li>Juiz de Fora</li>
                <li>Belo Horizonte</li>
        </ul>
        </li>
        <li>Paraná</li>
</ul>
</li>

Observe que as tags de abertura e fechamento da lista externa (<ul></ul>) não são incluídas.

upper converte todos os caracteres de uma string em maiúsculas.

» texto = "Um texto com algumas palavras."
⎀ {{ texto|upper }}
↳ UM TEXTO COM ALGUMAS PALAVRAS.

urlencode transforma uma string para uso como url.

» url = "https://phylos.net/foo?a=b"
⎀ {{ url|urlencode }}
↳ https%3A//phylos.net/foo%3Fa%3Db

urlize converte uma URL ou endereço de email em links clicáveis.

» url = "Visite minha página em phylos.net"
⎀ {{ url|urlize }}
↳ Visite minha página em phylos.net

# emails também são convertidos
» email = "Mande sua mensagem para usuario@exemplo.com"
⎀ {{ email|urlize }}
↳ Mande sua mensagem para usuario@exemplo.com

O atributo rel=”nofollow” é acrescentado.

urlizetrunc age como urlize mas truncando urls longas para a exibição>

{{ url|urlizetrunc:15 }}

wordcount retorna número de palavras no parâmetro.

» texto = "Um texto com algumas palavras."
⎀ {{ texto|wordcount }}
↳ 5

wordwrap quebra o texto em comprimento especificado, inserindo quebra de linhas. wordwrap:n não quebra palavras mas sim a linha em valores inferiores a n dado como comprimento.

Implements word wrapping by inserting a newline character every n characters. Useful for plain text, but not typically for HTML.

» texto = "Um texto com algumas palavras."
⎀ {{ texto|wordwrap:17 }}
↳ Um texto com
↳ algumas palavras.

yesno retorna uma string especificada para o valor do parâmetro True, False ou None (opcional).

» condicao = False
⎀ {{ condicao|"Sim, Não, Talvez" }}
↳ Não

» condicao = True
⎀ Você respondeu: {{ condicao|"Afirmativo, Negativo" }}
↳ Você respondeu: Afirmativo

Bibliografia

Livros

  • Newman, Scott: Django 1.0 Template Development, 2008 Packt, 2008.

Sites

todos acessados em julho de 2022.

Templates do Django

Templates do Django

Como vimos em Introdução ao Django (1), (2) e (3) o framework usa a arquitetura MVT, Model, View, Template, para gerar HTML dinamicamente. Um quadro pode ajudar a esclarecer o modelo.


Descrição do modelo MVT

  • O navegador envia uma requisição para o servidor rodando django (1).
  • A URL é recebida por urls.py que atribui uma view para tratamento da requisição.
  • A camada view consulta Model (2) para receber os dados requisitados (3).
  • Depois de obter os dados View consulta a camada Template (4) para formatar a apresentação final (5) e envia páginas html formatadas para o navegador cliente (6).

Templates são as partes estáticas da saída HTML desejada, adicionadas de tags e variáveis que usam uma sintaxe especial para descrever como o conteúdo dinâmico será inserido, chamada de Django Model Language, (DTL). Por default o django usa o Jinja2, que pode ser alterado se necessário. Dizemos que o template renderiza o modelo inserindo nas variáveis os valores recebidos de acordo com o contexto. O suporte para modelos e templates (o DTL) estão no namespace Django.template.
Advertência: Em todo esse artigo nos referimos a dois tipos diferentes de tags: tags html são as marcações usuais para páginas na web, como <body>, <p>, <h1>, <table> , e possuem o mesmo significados que em páginas estáticas. Já as tags do djangos são as marcações próprias do jinja2 (por default) para indicar a inserção de conteúdo fornecido dinamicamente pelo aplicativo. Usaremos aqui, para facilitar a leitura, a seguinte representação:

# comentários    
» Código python, geralmente dentro de uma view,
⎀ representação de marcação do django, geralmente em pasta app/template,
↳ resultado após a renderização.

Por exemplo:

» nome = "Policarpo"; sobrenome= "Quaresma"
⎀ Meu nome é {{ nome }}, meu sobrenome é {{ sobrenome }}
↳ Meu nome é Policarpo, meu sobrenome é Quaresma

Sintaxe

O sistema de templates substitui tags e variáveis por seus valores fornecidos pelo contexto. Todo o restante do texto e tags html são retornados literalmente.

Variáveis

Uma variável é substituída por seu valor no contexto, com a marcação {{ var }}. Um contexto é um conjunto de valores, em um objeto tipo dicionário (um conjunto de chaves:valores), passado pelas views para o template. Os nomes das variáveis são passados como strings.

» contexto = {"n1": "um"; "n2":"dois";}
⎀ Temos no contexto {{ n1 }} e {{ n2 }}
↳ Temos no contexto {{ um }} e {{ dois }}

Usamos a notação de ponto para fazer a busca em chaves de um dicionário, exibição de atributo em objetos e recuperação de valor em lista por meio de seu índice:

» dicionario.chave
» um_objeto.atributo
» uma_lista.0

Se o valor da variável for uma função ou outro objeto que pode ser chamado (um callable) o sistema fará uma chamada ao objeto e retornará seu valor.

Tags e Filtros

Tags são mecanismos de controle geral da renderização de templates. Elas podem controlar o fluxo de código (como em if, for), podem dar acesso à outro arquivo de template ou inserir modos de controle. Tags aparecem dentro da marcação {% tag %}. Por exemplo:

# suponha que temos o objeto usuario com atribuitos nome e super
» usuario.nome = "Fulaninho del'Valle"; usuario.super = True
⎀ Nome do usuário: {{ usuario.nome }}. {% if usuario.super }} É supersuser! {% endif %}
↳ Nome do usuário: Fulaninho del'Valle. É supersuser!

Um exemplo de tag de controle é {% csrf_token %}, que colocado dentro de um formulário impede o ataque tipo CSRF ao site.

Filtros são formas de modificar as saídas de variáveis. Por ex.:

» contexto = {'django': 'o framework web para perfecionistas com prazos'}
⎀ {{ django|title }}
↳ O Framework Web Para Perfecionistas Com Prazos
# ou
» contexto = {'data': '2022-07-04'}
⎀ {{ data|date:"d/m/Y" }}
↳ 04/07/2022

Tags do Django

Uma descrição mais extensa das tags pode ser lida em
Docs: Built-in Tags.

Comentários podem ser inseridos das seguintes formas:

# isto é um comentário em uma linha
⎀  {# isto é um comentário e será ignorado #}
# comentários em várias linhas
⎀  {% comment %}
⎀    <p>Linha 1</p>
⎀    <p>Linha 2</p>
⎀  {% endcomment %}

csrf_token fornece uma forma de defesa contra a falsificação de solicitações entre sites (Cross Site Request Forgeries).

⎀ <form>{% csrf_token %} ... conteúdo do formulario</form>

Block e Extends são as ferramentas básicas para se usar herança de templates. block define uma área em um template base que pode ser substituído por conteúdo no arquivo que o importa.
base.html

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  {% block bloco1 %} texto default 1 {% endblock %}
  {% block bloco2 %} texto default 2 {% endblock %}
</body>
</html>

e o template que o importa:
inicial.html

{% extends "base.html" %}
{% block title %} texto 1 {% endblock %}
{% block content %} texto 2 {% endblock %}

{% extends "base.html" %} faz com que inicial.html seja uma extensão do template base.html. Ele pode receber o nome (com caminho) do arquivo base ou uma variável que contém esse nome. Caso um bloco não exista na página que herda base.html o conteúdo inicial (ou nenhum, se não existir) é mantido.

{% extends arquivo %} pode ser usado de duas formas:

{% extends "base.html" %}
{% extends nome_do_arquivo %}

O arquivo base.html deve ser um arquivo html completo, com cabeçalhos, importações de css, etc. Ele contém blocks (que já descrevermos) que são substuídos pelos valores em nome_do_arquivo.html.

Por exemplo, considere o exemplo do arquivo base:
base.html

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <h1>Meu Aplicativo</h1>
  {% block title %}{% endblock %}
  
  {% block content %}Esse é meu site com o meu aplicativo  {% endblock %}
</body>
</html>

e o template que o importa:
inicial.html

{% extends "base.html" %}
{% block title %}<h1>Aplicativo de Notas</h1>{% endblock %}

{% block content %}
<h2>Esse é o conteúdo da bloco content</h2>
{% endblock %}

Quando o arquivo inicial.html é chamado ele usa base.html, substituindo nele os blocos title e content. O resultado final será a página com html puro:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <h1>Meu Aplicativo</h1>
  <h1>Aplicativo de Notas</h1>
  <h2>Esse é o conteúdo da bloco content</h2>
</body>
</html>

Deve-se ter o cuidado para indicar os caminhos corretos.

# ambos os arquivos estão no mesmo diretório    
{% extends "base.html" %}
# ou
{% extends "./base.html" %}
# base02.html está um diretório acima
{% extends "../base02.html" %}
# base03.html está em outro diretório dentro do atual
{% extends "./outro/base03.html" %}

Os caminhos são relativos ao diretório informado em settings.py:

TEMPLATES = [{
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [BASE_DIR / 'templates'],
}]

include é usado para inserir e renderizar outro template dentro do contexto atual. Ele pode ser usado com referências literais a um arquivo, ou com o nome referenciado em variável.

{% include "caminho/menu.html" %}
{% include nome_do_template %}

Por ex., suponha que o template
ola.html

{{ sauda }}, {{ nome }}!

é incluído em
saudacao.html

<h1>Página Inicial de nosso Site:</h1>    
{% include "ola.html" %}

Se temos um contexto contexto = {"sauda":"Bom dia";"nome":"Augusto dos Anjos"} e passamos esse contexto para saudacao.html temos o seguinte template final renderizado:

<h1>Página Inicial de nosso Site:</h1>
<p>Bom dia, Augusto dos Anjos!</h1p>

Variáveis pode ser passadas diretamente para o template usando keywords, ou com o parâmetro only para usar apenas essas variáveis, ignorando as demais.

{% include "ola.html" with sauda="Boa noite" nome="James Gomes" %}
{% include "ola.html" with sauda="Boa tarde" only%}

Observação: Blocos são avaliados antes da inclusão, ou seja, as variáveis de contexto são substituídas antes que a inserção em outro template seja feita.

load é usado para inserir no ambiente atual um conjunto de tags e filtros já importados em outro template. Por ex., se temos duas bibliotecas biblio1 e biblio2 localizadas em pacote1 e pacote2 respectivamente (como bibliotecas devem estar em diretórios contendo o arquivo __init__.py) poemos importar todos os templates e filtros neles definidos com a inserção de load. Para selecionar apenas alguns filtros ou tags usamos from.

{% load pacote1.biblio1 pacote2.biblio2 %}
{% load tag1 tag2 from pacote1.biblio1 %}

Essa tag é especialmente útil para tags e filtros customizados.

url retorna o caminho absoluto (a URL sem nome de domínio) apontando para uma view com seus parâmetros. Essa é uma forma de exibir links dentro de templates sem forçar a construção de URLs fixas, que podem ser quebradas se o aplicativo for realocado para outro diretório.

{% url 'nome_url' arg1 arg2 %}
# 'nome_url' é o padrão da url, os argumentos arg1 e arg2 são passados para a view

nome_url pode ser uma string literal ou variável de contexto. Os parâmetros são separados por espaços. Alternativamente podemos usar argumentos nomeados:

{% url 'nome_url' arg1=arg1 arg2=arg2 %}
Lembramos que um projeto django é formado por um ou mais aplicativos. O código relativo aos aplicativos ficam em subdiretórios do diretório geral do projeto. Temos portanto as pastas diretorio_projeto e diretorio_projeto/aplicativo.

Por ex., suponha que temos uma view para um aplicativo em aplicativo_views.livro que recebe um id para exibir um livro cadastrado, ou seja livro() é um método em aplicativo/views. Em aplicativo/urls.py existe a linha,

path('livro//', aplicativo_views.livro, name='view_livro')

No arquivo de urls do projeto (em nível acima), projeto.urls podemos ter

path('livros/', include('projeto.aplicativo.urls'))

Em um template podemos usar, para acessar essa view:

{% url 'view_livro' livro.id %}
# que vai ser renderizado como (por ex. para livro.id=456)
/livros/livro/456

Se a URL não tiver uma view correspondente uma exceção NoReverseMatch será lançada. Também é possível armazenar em variável uma url para uso posterior, dentro do mesmo bloco, como em

{% url 'nome_da_url' arg1 arg2 as minha_url %}
<a href="{{ minha_url }}">Visita a minha página em {{ minha_url }}</a>

Nenhum erro é lançado se a view não existe, o que torna essa forma útil para exibir links que podem não existir, no contexto:

{% url 'nome_da_url' arg1 arg2 as minha_url %}
{% if minha_url %}
  Link para a página opcional.
{% endif %}

Se a url estiver definida dentro de um namespace especificado, como meu_app (o que é útil para evitar conflitos de nomes, podemos usar seu nome “totalmente qualificado” (fully qualified name):

{% url 'meu_app:nome_da_view' %}

Os nomes de padrões para as urls devem ser colocados entre aspas ou serão interpretados como variáveis de contexto!

for é usado para percorrer um laço sobre uma sequência de valores passada no contexto.

# frutas é uma tupla no contexto
» frutas = ('pera','maça','limão')
⎀ <ul>
⎀   {% for fruta in frutas %}
⎀     <li>{{ fruta }}</li>
⎀   {% endfor %}
⎀ </ul>
# será renderizado como
↳ <ul>
↳   <li>pera</li>
↳   <li>maça</li>
↳   <li>limão</li>
↳ </ul>

# para percorrer a lista em ordem reversa usamos
⎀   {% for fruta in frutas reversed %}

Para percorrer uma lista de listas (ou outros objetos que são coleções ) é possível obter valores individuais em variáveis separadas. Por ex., se temos uma lista de pontos com suas respectivas coordenadas pode fazer:

# uma lista de pontos com lista de 2 coordenadas
» pontos = ((3,8), (7,4), (5,5))
⎀   {% for x, y in pontos %}
⎀     <p>Coordenada x={{ x }}, y={{ y }} </p>
⎀   {% endfor %}
# será renderizado como
↳ <p>Coordenada x=3, y=8</p>p>
↳ <p>Coordenada x=7, y=4</p>p>
↳ <p>Coordenada x=7, y=7</p>p>

Para listar ítens de dicionários, chaves e valores, fazemos calgo análogo:

# dado um dicionário dic_dados
⎀ {% for key, value in dic_dados.items %}
⎀     {{ key }}: {{ value }}
⎀ {% endfor %}

Observação: O operador de ponto para acesso à itens de dicionário tem precedência sobre o laço for. Se o dicionário contém uma chave ‘items’, dic_dados[‘items’] será retornado em lugar de dic_dados.items. Por isso se deve evitar dar nomes de chaves idênticos aos de métodos que podem ser usados em um template (como “items”, “values”, “keys”.)

for ... empty é uma condição alternativa de retorno quando a lista no laço está vazia. O texto explicitado é retornado:

# dias é uma lista vazia
» dias_livres = []
⎀ <ul>
⎀   {% for dia in dias_livres %}
⎀     <li>{{ dia }}</li>
    {% empty %}
⎀     <li>Não há nenhum dia livre!</li>
⎀   {% endfor %}
⎀ </ul>
# será renderizado como
↳ <ul>
↳   <li>Não há nenhum dia livre!</li>
↳ </ul>

Variáveis disponíveis no laço são variáveis que ficam disponíveis para uso durante um laço for.

Variável Descrição
forloop.counter o atual contador da iteração (base 1)
forloop.counter0 o atual contador da iteração (base 0)
forloop.revcounter quantas iterações faltam para o final do laço (base 1)
forloop.revcounter0 quantas iterações faltam para o final do laço (base 0)
forloop.first True apenas para a 1ª iteração no laço
forloop.last True apenas para a última iteração no laço
forloop.parentloop para laços aninhados esse é a iteração do laço externo

if é usado para testes condicionais. Sua sintaxe geral é:

{% if condicao1 %}
  Texto se condicao1==True
{% elif condicao2 %}
  Texto se condicao1==False e condicao2==True
{% else %}
  Texto se condicao1==False e condicao2==False
{% endif %}

Por ex., suponha que obj_carros é uma sequência de objetos carro com as propriedades carro.marca (str), carro.vendido (booleana). O template abaixo

  <p>Temos {{ obj_carros|length }} para venda</p>
  {% for carro in obj_carros %}
     Marca: {{ carro.marca }}  {%if carro.vendido %} Está vendido {% else %} Disponível {% endif %}
  {% empty %}
     Não há carros disponíveis!
  {% endfor %}

mostrará quantos carros existem para venda e uma listagem de todos os carros, com suas marcas e disponibilidade (ou não) para venda, ou a informação de que nenhum carro está disponível.

Operadores booleanos. As condições lógicas podem ser ligadas ou modificadas por operadores booleanos and, or e not, com significado usual no python.

{% if condicao1 and condicao2 %}
    Se ambas as condições são True.
{% endif %}

{% if condicao1 or condicao2 %}
    Se uma das, ou ambas as, condições são True.
{% endif %}

{% if not condicao1 %}
   Se a condições1 é False.
{% endif %}

{% if not condicao1 or condicao2 %}
    Se condicao1 == False ou condicao2==True.
{% endif %}

Se and e or são usadas na mesma tag and tem precedência sobre or. Ou seja

{% if condicao1 and condicao2 or condicao3 %}
# será interpretada como
{% if (condicao1 and condicao2) or condicao3 %}span>
# a sentença acima, no entanto, é inválida (parênteses não podem ser usados nesse ambiente)

Operadores: Os operadores válidos para construções de testes lógicos são ==, !=, <, >, <=, >=, in, not in, is, is not, com significado usual no python.

{% if var_string == "palavra" %} faça algo {% endif %}
{% if var_string != "palavra" %} faça algo {% endif %}
{% if var_numerica == 100 %} faça algo {% endif %}
{% if var_numerica !== 100 %} faça algo {% endif %}
{% if var_numerica > 100 %} faça algo {% endif %}
{% if var_numerica < 100 %} faça algo {% endif %}
{% if var_numerica <= 100 %} faça algo {% endif %}
{% if var_numerica >= 100 %} faça algo {% endif %}

# in procura por partes de uma string
{% if "adi" in "moradia" %}
  Texto caso "adi" seja parte (substring) de "moradia"
{% endif %}

{% if "adi" in var_string %}
  Texto caso "adi" seja substring da var_string
{% endif %}

{% if user in users %}
  Texto caso user (uma variável) seja um dos elementos da coleção users
{% endif %}

# is é um teste de identidade entre objetos. True se foram o mesmo objeto.
{% if var1 iscode> var2 %} var1 e var2 se referem ao mesmo objeto{% endif %}
{% if var iscode> None %} var é None ou não foi encontrada no contexto.{% endif %}

# is not é a negação do teste acima
{% if var1 is notcode> var2 %} var1 e var2 não se referem ao mesmo objeto{% endif %}
{% if var is notcode> None %}
  var não é None, portanto foi encontrada no contexto.
{% endif %}

Testes compostos podem ser construídos, seguindo a mesma ordem do python. Se A, B, C, E são booleanas ou expressões que avaliam em um booleano (expressões em negrito são inválidas):

{% if A ==code> B or C == D and E %}
# será interpretado como
{% (A ==code> B) or ((C ==code> D) and E) %}
# os parênteses não devem ser usados

# essa expressão é inválida
{% if A > B > C %}
# e deve ser escrita como
{% if A > B and B > C %}

# diferente do python isso não pode ser feito
{% if A > B > C %}
# faça
{% if A > B and B > C %}

Se for necessário usar regras de precedência diferentes use tags aninhadas.

if changed é uma tag que verifica se um ítem de uma lista foi alterado entre iterações dentro de um laço. Se passamos um contexto com uma lista de fornecedores como um objeto com nome e 3 ítens quaisquer, sendo que mais de um registro existe para cada um deles, podemos exibir o nome do fornecedor apenas uma vez.

<h1>Lista de Fornecedores</h1>
{% for fornecedor in fornecedores %}
    {% ifchanged %}<h3>Nome do fornecedor {{ fornecedor.nome }}</h3>{% endifchanged %}
    <p>{{ fornecedor.item1 }}, {{ fornecedor.item2 }}, {{ fornecedor.item3 }}</p>
{% endfor %}

Se mais de uma variável é verificada, cada mudança de valor produz uma saída. No template seguinte, supondo que o objeto fornecedor possui um atributo fornecedor.cidade e que cada um pode estar em mais de uma cidade, o nome é exibido sempre que trocado, e a cidade é exibida quando o nome ou a cidade são trocados.

<h1>Lista de Fornecedores</h1>
{% for fornecedor in fornecedores %}
    {% ifchanged fornecedor.nome %}
      <h3>Nome do fornecedor {{ fornecedor.nome }}</h3>
    {% endifchanged %}
    {% ifchanged fornecedor.cidade %}
      <h3>Cidade {{ fornecedor.cidade }}</h3>
    {% endifchanged %}
    <p>{{ fornecedor.item1 }}, {{ fornecedor.item2 }}, {{ fornecedor.item3 }}</p>
{% endfor %}

Claro que uma boa exibição desses templates depende se estar a lista ordenada nos campos verificados. Uma cláusula else pode ser fornecida para inserir conteúdo se não houver qualquer mudança.

<h1>Lista de Fornecedores</h1>
{% for fornecedor in fornecedores %}
    {% ifchanged fornecedor.nome %}<h3>Nome do fornecedor {{ fornecedor.nome }}</h3>
    {% else %} <p>(continuando...)</p>
    {% endifchanged %}
    <p>{{ fornecedor.item1 }}, {{ fornecedor.item2 }}, {{ fornecedor.item3 }}</p>
{% endfor %}

cycle é uma tag que retorna um de seus argumentos, em ciclo, a cada vez que é acessada. Quando todos os argumentos forem esgotados o primeiro deles é produzido novamente. Qualquer número de valores pode ser usado nos ciclos. Por ex.:

# considere que classe1 e classe2 estão definidas como classes no arquivo css
<table>
{% for obj in alguma_lista %}
    <tr class="{% cycle 'classe1' 'classe2' %}"><td>...</td></tr>
{% endfor %}
</table>

O template exibirá uma tabela com linhas alternadas com formatação da classe1 e classe2, até o fim do laço.
Variáveis podem ser usadas:

{% for obj in alguma_lista %}
    <tr class="{% cycle var_linha1 var_linha2 %}"> ... </tr>
{% endfor %}

# variáveis são escapadas. Isso pode ser alterado com
{% for obj in alguma_lista %}
    <tr class="{% autoescape off %}{% cycle rowvalue1 rowvalue2 %}{% endautoescape %}"> ... </tr>
{% endfor %}

# podemos usar strings e variáveis juntas
{% for obj in alguma_lista %}
    <tr class="{% cycle 'linha1' var_linha2 'linha3' %}"> ... </tr>
{% endfor %}

Podemos atribuir uma aliás ao ciclo e utilizá-lo depois, com seu valor atual. Para progredir no ciclo reusamos {% cycle %}:

# o template
⎀ <tr>
⎀     <td class="{% cycle 'linha1' 'linha2' as linhas %}">...</td>
⎀     <td class="{{ linhas }}">...</td>
⎀ </tr>
⎀ <tr>
⎀     <td class="{% cycle linhas %}">...</td>
⎀     <td class="{{ linhas }}">...</td>
⎀ </tr>

# será renderizado como
↳ <tr>
↳     <td class="linha1">...</td>
↳     <td class="linha1">...</td>
↳ </tr>
↳ <tr>
↳     <td class="linha2">...</td>
↳     <td class="linha2">...</td>
↳ </tr>

Podemos usar a tag resetcycle para zerar o ciclo. Por ex., se temos uma lista de objetos pessoa, com propriedades pessoa.nome (uma string) e pessoa.filhos (uma lista de strings):

{% for pessoa in lista_de_pessoas %}
    <h1>{{ pessoa.nome }}</h1>
    {% for filho in pessoa.filhos %}
        <p class="{% cycle 'par' 'impar' %}">{{ filho.nome }}</p>
    {% endfor %}
    {% resetcycle %}
{% endfor %}

Nesse template toda lista de filhos, para cada pessoa, começa com formatação de classe “par”.

firstof recebe diversas variáveis como argumento e exibe o primeiro argumento que não é False. Lembrando, são avaliadas como True as variáveis que existem, não são vazias ou Null, não são o booleano False e não são o 0 numérico. firstof não exibe coisa alguma se todas as variáveis incluídas são False.

# o template
{% firstof var1 var2 var3 %}

# é o mesmo que
{% if var1 %} 
  {{ var1 }}
{% elif var2 %}
  {{ var2 }}
{% elif var3 %}
  {{ var3 }}
{% endif %}

Um valor default pode ser exibido se nenhuma das variáveis é True

{% firstof var1 var2 var3 "valor default" %}

Os valores exibidos são “escapados”. Esse comportamento pode ser revertido com:

{% autoescape off %}
    {% firstof var1 var2 var3 "<b>Texto em negrito</b>" %}
{% endautoescape %}

# para escapar apenas algumas variáveis usamos o filtro escape sobre essas variáveis
{% firstof var1 var2|safe var3 "<b>Negrito</b>|safe" %}

# também podemos armazenar a variável em um aliás para uso posterior:
{% firstof var1 var2 var3 as valor_alias %}

regroup realiza um reagrupamento de uma lista de objetos baseado em um atributo comum. Por ex., suponha que temos um dicionário que descreve alunos de uma escola, listando seus nomes, idades e series:

alunos = [
    {'nome':'Marcos', 'idade':'8','serie':'1'}
    {'nome':'Ana', 'idade':'7','serie':'1'}
    {'nome':'Marta', 'idade':'10','serie':'1'}
    {'nome':'Pedro', 'idade':'9','serie':'2'}
]

Para exibir uma lista organizada hieraquicamente pela série, usamos {% regroup %}

{% regroup alunos by serie as aluno_serie %}
<ul>
{% for serie in aluno_serie %}
    <li>Série: {{ serie.grouper }}
    <ul>
        {% for aluno in aluno_serie %}
          <li>Nome: {{ aluno.nome }}, Idade: {{ aluno.idade }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}

 

O resultado desse template é o seguinte:

  • Serie: 1
    • Nome: Marcos, Idade: 8
    • Nome: Ana, Idade: 7
    • Nome: Marta, Idade: 10
  • Serie: 2
    • Nome: Pedro, Idade: 9

Nesse exemplo alunos a a coleção que queremos ordenar, serie o atributo usado na ordenação, e aluno_serie um alias para a lista resultante. O objeto gerado, nomeado por aluno_serie, é do tipo namedtuple com 2 campos:

  • grouper – o item usado no agrupamento, no caso as strings “1” e “2”.
  • list – uma lista dos ítems do grupo, no caso alunos com atributos aluno.nome e aluno.idade.

Considerando que aluno_serie é uma namedtuple o mesmo código poderia ser escrito dessa forma:

{% regroup alunos by serie as aluno_serie %}
<ul>
{% for serie, alunos in aluno_serie %}
    <li>Série:  {{ serie }}
    <ul>
        {% for aluno in alunos %}
          <li>Nome: {{ aluno.nome }}, Idade: {{ aluno.idade }}</li>
        {% endfor %}
    </ul>
    </li>
{% endfor %}
</ul>

É importante notar que {% regroup %} não faz um reordenamento dos listas, o que deve ser feito previamente, em geral dentro da view que retoena esses valores. Alternativamente, se os dados a serem agrupados estão em uma lista de dicionários, como no exemplo, podemos fazer um ordenamento dentro do template usando o filtro dictsort.

{% regroup alunos|dictsort:"serie" by serie as aluno_serie %}
# alunos|dictsort:"serie" retorna a lista ordenada no campo "serie"

Qualquer outra propriedade dos ojetos ordenados pode ser usada por regroup, incluindo propriedades de objetos, chaves e itens de dicionários.

with é usada para armazenar uma variável sob um nome simples. Essa variável pode envolver, por ex., uma operação complexa em uma query em um banco de dados:

{% with total=escola.professores.count %}
    Essa escola tem {{ total }} professor.
{% endwith %}

# alternativamente
{% with escola.professores.count as total %} ... {% endwith %}

# mais de uma variável pode ser definida
{% with alpha=1 beta=2 %}
    ...
{% endwith %}

A variável tem como escopo a intervalo entre tags {% with %} e {% endwith %}.

autoescape controla o comportamento de escape de marcações html dentro do bloco. Por default tags html são exibidos sem renderização, por motivo de segurança. Com autoescape on as tags se tornam funcionais, como em uma página usual de html. Recebe apenas os parâmetros on ou off.

# por ex.
» variavel = "<p>Texto de <b>teste</b>!</p>"
⎀ {% autoescape off %}
⎀    {{ variavel }}
⎀ {% endautoescape %}
# renderiza como
↳ <p>Texto de <b>teste</b>!</p>

# por outro lado, se autoescape on
⎀ {% autoescape on %}
⎀    {{ variavel }}
⎀ {% endautoescape %}
# o texto é renderizado dentro de um parágrafo html

Isso é equivalente a usar o filtro var|safe em todas as variáveis do bloco. Variáveis marcadas com var|safe são renderizadas mesmo que estejam dentro de bloco {% autoescape off %}.

lorem é a tag usada para exibir texto em latim, geralmente usado para testes. Seu uso é

{% lorem [count] [method] [random] %}

onde todos os argumentos são opcionais.

Argumento Descrição
count número ou variável com o número de parágrafos ou palavras a serem gerados. (default é 1).
method pode ser w, palavras, p parágrafos ou b para texto puro (default é b).
random “random” gera texto aleatório e não (“Lorem ipsum dolor sit amet…”).
{% lorem %}
# gera parágrafo "lorem ipsum".
{% lorem 3 p %}
# gera parágrafo "lorem ipsum" e 2 parágrafos aleatórios entre tags <p>.
{% lorem 2 w random %}
# gera 2 palavras latinas aleatórios.

now exibe data/hora corrente como string, em formato especificado. Veja sobre filtros para maiores descrição da formatação.

⎀ {% now "D M Y H T " %}
# dia, mes, ano, hora

⎀ It is the {% now "jS \o\f F" %}
↳ It is the 4th of February

⎀ Hoje é {% now "D/M/Y" %}
↳ Hoje é 04/06/2022

⎀ {% now "SHORT_DATETIME_FORMAT" %}

O último exemplo usa formatos predefinidos, como DATE_FORMAT, DATETIME_FORMAT, SHORT_DATE_FORMAT ou SHORT_DATETIME_FORMAT que são renderizados de acordo com as variáveis de ambiente.

A sintaxe {% now “Y” as ano_atual %} armazena uma string de representação da data na variável ano_atual.

{% now "Y" as ano_atual %}
Copyright {{ ano_atual }}

verbatim é uma tag usada para interromper a renderização dos templates e apresentá-los literalmente.

⎀ {% verbatim %}
⎀     {{if certo}} Está certo! {{ endif }}
⎀ {% endverbatim %}

# será exibido
↳ {{if certo}} Está certo! {{ endif }}

Essa tag pode ser usada para evitar conflito com código javascript inserido em templates.

spaceless é a tag usada para remover espaços am branco, controles de tab e newline inseridos entre tags.

⎀ {% spaceless %}
⎀     <p>
⎀         <a href="http://meu_link.com/">Meu site</a>
⎀     </p>
⎀  {% endspaceless %}

# retorna
↳ <p><a href="http://meu_link.com/">Meu site</a></p>

# Espaços entre tags e texto não são alterados
{% spaceless %}
    <strong>
        Olá mundo!
    </strong>
{% endspaceless %}
# não sofre alterações

Essa tag tem uso limitado pois a renderização usual de html pelos navegadores ignoram esses espaços, tabs e newlines.

templatetag é usado para exibir os caracteres de tags em templates, similar a um escape desses caracteres.

Argumento exibe Argumento exibe
openblock {% openbrace {
closeblock %} closebrace }
openvariable {{ opencomment {#
closevariable }} closecomment #}
⎀ {% templatetag openblock %}
# retorna
↳ {%

widthratio é tag usada para gráficos de barras. Ela calcula a razão entre um valor variável dado e um valor máximo e aplica o resultado à uma constante. Por exemplo,

<img src="barra.png" height="10" width="{% widthratio valor_variavel valor_maximo largura_maxima %}">
# esse valor pode ser armazenado para uso posterior
{% widthratio valor_variavel valor_maximo largura_maxima as largura_calculada %}

Se valor_variavel=175, valor_maximo=200 e largura_maxima=100 a imagem acima barra.png terá a largura de 88 pixels pois 175/200 = .875; .875 * 100 = 87.5 arredondada para 88. Ou seja largura_calculada = 88.

Internacionalização: traduzindo aplicativos

Existem tags e filtros voltados para a internacionalização de aplicativos, facilitando a tradução de textos especificados. Mais detalhes em Django Docs: Internacionalização e
Tradução.

i18n é uma biblioteca que permite especificar qual texto dentro de templates devem ser traduzidos. Para isso deve-se ajustar a variável USE_I18N=True e carregar o código necessário com a tag {% load i18n %}.

l10n é uma biblioteca que permite localizar valores dentro de templates. Ela deve ser carregada com a tag {% load l10n %}.

tz é biblioteca para estabelecer conversões entre time zones. Deve ser carregada com a tag {% load tz %}. Também se pode ajustar a variável USE_TZ=True para a conversão ser automática para a time zone local.

Outras tags importantes

static é uma tag para estabelecer links para arquivos estáticos gravados no diretório estabelecida na variável STATIC_ROOT, dentro de settings.py. Se django.contrib.staticfiles é um dos aplicativos instalados (ou seja, se é um dos ítem da lista INSTALLED_APPS) essa tag indicará o caminho dos arquivos estáticos usando o método url(), … especificado em STATICFILES_STORAGE.

{% load static %}
<img src="{% static 'imagens/ola.jpg' %}">

{% load static %}
<link rel="stylesheet" href="{% static arquivo_css %}" type="text/css" media="screen">

{% load static %}
{% static "imagens/ola.jpg" as ola %}
<img src="{{ ola }}">

get_static_prefix pode ser usada juntamente com static para garantir maior controle sobre o local onde STATIC_URL é inserida no template:

{% load static %}
<img src="{% get_static_prefix %}imagens/ola.jpg">

# se o valor será usado várias vezes ele pode receber uma alias
{% load static %}
{% get_static_prefix as STATIC_PREFIX %}

<img src="{{ STATIC_PREFIX }}imagens/logo.jpg">
<img src="{{ STATIC_PREFIX }}imagens/ola.jpg">

get_media_prefix é similar à get_static_prefix mas insere no template o valor em MEDIA_URL:

{% load static %}
<body data-media-url="{% get_media_prefix %}">

Arquivos estáticos

Arquivos adicionais, e não apenas texto html, fazem parte de qualquer website. Esses arquivos incluem arquivos de imagens ou vídeos, de formatação css e javascript e o django os denomina arquivos estáticos (static files). Para gerenciar esses arquivos o django inclui o módulo django.contrib.staticfiles.

Para servir arquivos estáticos precisamos fazer:

  1. incluir o módulo django.contrib.staticfiles na lista INSTALLED_APPS, em settings.py,
  2. ajustar a variável STATIC_URL', também em settings.py,
  3. armazenar arquivos estáticos em diretório static, dentro do diretório raiz do projeto,
  4. nos templates, use a tag static para construir a URL para uma posição relativa, configurada em STATICFILES_STORAGE.

Por exemplo:

# em settings.py
INSTALLED_APPS = [
           ...,
           'django.contrib.staticfiles',
           ...
]
...
STATIC_URL = 'static/'

# armazena
meu_app/static/imagens/meu_logo.jpg.

# no template
{% load static %}
<img src="{% static 'imagens/meu_logo.jpg' %}">

É possível também incluir no projeto arquivos estáticos usados mais de um aplicativo. Além dos diretórios applicativo/static podemos ter uma lista de diretórios alternativos na lista STATICFILES_DIRS em settings.py. O django vai procurar em todos eles esses arquivos estáticos.

Por exemplo,

from pathlib import Path
import os

BASE_DIR = Path(__file__).resolve().parent.parent
    
STATICFILES_DIRS = [
       os.path.join(BASE_DIR, 'static'),
       os.path.join(BASE_DIR, 'base_static'),
       ...,
]

Nesse caso BASE_DIR é o diretório um dois níveis acima daquela onde reside o arquivo settings.py. À partir deles contruimos BASE_DIR/static e BASE_DIR/base_static.

Advertência: durante a fase de desenvolvimento, com a variável DEBUG=True em settings.py e você está usando django.contrib.staticfiles, os arquivos estáticos são entregues pelo servidor quando se roda runserver. Esse é, no entanto, um método pouco eficiente e inseguro, não apropriado para a produção.

Uma consideração especial deve ser dada à fase de implantação em produção. Basicamente é necessário rodar o comando collectstatic para que os arquivos estáticos sejam coletados no diretório apropriado STATIC_ROOT. Em seguida esse diretório deve ser movido para local que depende de qual servidor é usado.

Filtros do Django

Continua a leitura em Filtros em Templates no Django.

Bibliografia

Livros

  • Newman, Scott: Django 1.0 Template Development, 2008 Packt, 2008.

Sites

todos acessados em julho de 2022.

Outros artigos nesse site:

Django, incrementando o Projeto

Sofisticando o modelo com chaves externas e datas

Vamos aprimorar o aplicativo classificando nossas notas por categorias, permitindo diversos cadernos separados. Para efeito didático usaremos apenas um nível de categoria. Idealmente essas poderiam ser subdivididas em subcategorias de vários níveis.

Para isso criaremos modelos para as seguintes tabelas:

Caderno
id pk, automatico
caderno texto
Categoria
id pk, automatico
categoria texto
Notas
id pk, automatico
caderno fk –> caderno
categoria fk –> categoria
titulo texto
texto texto
slug texto

Vamos fazer alterações nos modelos, que serão aplicadas no banco de dados com migrations.
app_notas/notas/models.py

from django.db import models
from django.utils.text import slugify

class Caderno(models.Model):
    titulo = models.CharField(default='', max_length=150)
    def __str__(self):
        return self.titulo

class Categoria(models.Model):
    categoria = models.CharField(default='', max_length=150)
    def __str__(self):
        return self.categoria

class Nota(models.Model):
    titulo = models.CharField(default='', max_length=150, help_text="Título da Nota")
    texto = models.TextField(default='', blank=True)
    slug = models.SlugField(default='', blank=True, max_length=255)
    data_criada = models.DateField(auto_now_add=True, help_text="Data de Criação")
    data_editada = models.DateField(auto_now=True, help_text="Data de Edição")
    caderno = models.ForeignKey(Caderno, on_delete=models.CASCADE)
    categoria = models.ForeignKey(Categoria, on_delete=models.CASCADE)

    def __str__(self):
        return self.titulo

    def save(self,*args,**kwargs):
        self.slug = slugify(self.titulo)
        super().save(*args,**kwargs)

As partes alteradas estão em negrito. Inserimos duas novas tabelas: Caderno e Categoria. Cada uma delas tem seu id criado automaticamente pelo django. Na tabela Nota acrescentamos caderno e categoria. Definidos como ForeignKey eles ficam associados às novas tabelas da seguinte forma:

nota.caderno (fk) ⟼ caderno.id
nota.categoria (fk) ⟼ categoria.id

O argumento help_text permite a especificação de texto descritivo que será exibido juntos com forms, como veremos.

Os campos do tipo DateField armazenam datas. (DateTimeField armazenam datas e horas). O argumento auto_now_add=True ajusta a campo para a hora do momento de criação do valor do campo enquanto
auto_now=True ajusta a campo na hora da última edição, quando o objeto é gravado.

A chave externa (fk) define uma relação de muitos-para-um. Por ex., cada nota tem apenas uma categoria mas podem haver diversas outras notas na mesma categoria.

O argumento on_delete informa como registros ligados por chaves externas devem ser tratados em caso de apagamento do registro principal. Ele pode receber diversos valores:

CASCADE força o apagamento de dados conectados pela chave externa.
PROTECT impede o apagamento de dados conectados pela chave externa. Levanta exceção ProtectedError.
RESTRICT similar à PROTECT, mas levanta erro RestrictedError.
SET_NULL mantém dados conectados pela chave externa, substituindo seu valor por NULL. O parâmetro deve ser ajustado na definição do modelo.
SET_DEFAULT similar à SET_NULL mas substitui o valor da chave por seu valor default. O parâmetro deve ser ajustado na definição do modelo.
SET( ) permite a construção de funções customizadas para atribuir valores ao campo fk apagado.
DO_NOTHING nenhuma atitude é tomada e os dados ligados por fk permanecem inalterados.
Uma descrição mais detalhadas por ser vista em Zerotobyte: On delete explained.

Além disso inserimos dois campos de data, data_criada e data_editada, que são preenchidas por default com a data do momento da operação.

Com essas alterações o banco de dados atual fica inconsistente, pois existem campos que demandariam fk e que estão vazios. Como estamos em desenvolvimento e os dados que temos são apenas experimentais, uma boa tática é a de resetar o banco de dados. Essa é também uma boa oportunidade para mencionar como isso é feito.

Resetando o banco de dados

Podemos ver todas as migrations feitas até agora com o comando:

$ python manage.py showmigrations

Para remover as alterações no BD e as migrations do django, removemos o conteudo do BD. No nosso caso, como estamos usando o SQLite, basta apagar o arquivo db.sqlite3 que fica na pasta do projeto app_notas/app_notas. Em seguido removemos todo o conteúdo da pasta migrations, na pasta de cada aplicativo (app_notas/notas no caso), exceto o arquivo __init__.py.

Podemos agora reconstruir o BD e refazer a conta de superuser:

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser


Observações: Se você está usando outro BD, como PostGRE ou MySQL você deve apagar os bancos usando a sintaxe própria de seu gerenciador. Outras informações podem ser encontradas em Techiediaries: Resetting Django Migrations, inclusive sobre como zerar as migrations em produção, manualmente ou usando um app para isso.

Usando o app ampliado

Depois de termos ampliado as definições de nossos modelos, devemos incluir nos novos modelos em admin.py para que eles possam ser gerenciados pelo admin.
app-notas/notas/admin.py

from django.contrib import admin
from notas.models import Caderno, Categoria, Nota

admin.site.register(Caderno)
admin.site.register(Categoria)
admin.site.register(Nota)

Agora é possível entrar dados visitando o site de administração do django. Notamos que, se definirmos antes categorias e cadernos o admin apresenta uma caixa dropdown contendo as escolhas possíveis desses dados sempre que inserimos ou editamos notas.

Para finalizar essa etapa vamos exibir os novos campos na página inicial e nos detalhes exibidos. Ajustamos o arquivo inicial de templates para receber os dados adicionais.
app_notas/templates/home.html

{% extends "base.html" %}
{% block title %}
  <h1>Aplicativo de Notas</h1>
{% endblock %}
{% block content %}
  <h1>Página inicial</h1>
  <p>Essas são as minhas notas (clique para visualizar).</p>
  <table class="dados_notas">
    <tr><th>Caderno</th><th>Categoria</th>
    <th>Nota</th><th>Criada em</th><th>Editada em</th>
    <th>clique para editar</th></tr>
    {% for nota in notas %}
      <tr>
        <td>{{ nota.caderno }}</td><td>{{ nota.categoria }}</td>
        <td>{{ nota }}</td>
        <td>{{ nota.data_criada|date:"d/m/Y" }}</td><td>
        {{ nota.data_editada|date:"d/m/Y" }}</td>
        <td><a href="/nota/{{ nota.pk }}/">{{ nota.pk }}</a></td>
      </tr>
    {%endfor%}
  </table>
{% endblock %}

E acrescentamos formatação css para essa tabela, definida sob a classe dados_notas (mostrando só os acréscimos):
app_notas/static/css/estilo.css

.dados_notas {text-align:left; border-collapse:collapse;}
.dados_notas tr, th, td {border:1px solid #aaa; padding:10px 30px;}    

Visitando a página incial veremos, no navegador:

Para visualizar detalhes da nota alteramos:
app_notas/templates/detalhe.html

{% extends "base.html" %}
{% block title %}
  <h1>Aplicativo de Notas: Detalhes</h1>
{% endblock %}

{% block content %}
  {% if secao == "erro" %}
    <h1>Nenhuma nota foi encontrada!</h1>
    <h2>Nota: {{ nota }}</h2>
  {% else %}
    <h1>Detalhe de uma nota</h1>
    <p><b>Caderno:</b> {{ nota.caderno }}</p>
    <p><b>Categoria:</b> {{ nota.categoria }}</p>
    <h2>Nota: {{ nota.titulo }}</h2>
    <p>{{ nota.texto|safe }}</p>
    <p>Postada em: {{nota.data_criada|date:"d/m/Y"}}</p>
    <p>Editada em: {{nota.data_editada|date:"d/m/Y"}}</p>
    <p><small>Id: {{ nota.pk }} Slug: {{ nota.slug }}</small></p>
  {% endif %}
{% endblock %}

Visitando uma página de detalhes (por exemplo clicando no link da página home) veremos:

Django Admin

A página de administração do django funciona como um app pre-desenvolvido. Ela é voltada para administradores e não deve ser usada para a interação de usuários com o site. Mesmo assim vale considerar algumas formas possíveis de aprimorar esse uso.

A referência para todo o controle do admin está em settings.py do aplicativo notas, enquanto a url que direciona para esse aplicativo está em urls.py do projeto:

# Em notas/settings.py:
INSTALLED_APPS = ['django.contrib.admin',  ... ]

# Em app_notas/urls.py
urlpatterns = [path('admin/', admin.site.urls), ...]

Para acessar o admin precisamos criar um usuário com poderes de administrador, (superuser)

$ python manage.py createsuperuser

Já vimos que, ao listar objetos de qualquer tabela, o admin usa a representação de string, definida com o modelo. Foi o que fizemos em, por ex. em models.py:
app_notas/notas/models.py

class Nota(models.Model):
    titulo = models.CharField(default='', max_length=150)
    ...
    def __str__(self):
        return self.titulo

Para customizar o texto exibido na página do admin acrescentamos as linhas à urls.py:
apps_notas/urls.py

urlpatterns = [ ... ]
admin.site.site_header  =  "Admin: Anotações"
admin.site.index_title  =  "Controle de Tabelas:  Anotações"

Vimos também quem nossos modelos são registrados em notas/admin.py. Podemos também ajustar a forma de exibição dos campos, acrescentar um campo de pesquisa sobre campos escolhidos da tabela e inserir filtros que limitam a exibição dos dados. Para isso criamos classes que herdam de admin.ModelAdmin e as registramos junto com os modelos.
apps_notas/notas/admin.py

from django.contrib import admin
from notas.models import Caderno, Categoria, Nota
 
class CadernoAdmin(admin.ModelAdmin):
    search_fields = ('titulo',)

class CategoriaAdmin(admin.ModelAdmin):
    list_filter = ('categoria',)
    search_fields = ('categoria',)

class NotaAdmin(admin.ModelAdmin):
    list_display = ('titulo', 'caderno', 'categoria', 'data_criada', 'data_editada',)
    list_filter = ('categoria', 'data_criada',)
    search_fields = ('titulo',)

admin.site.register(Caderno, CadernoAdmin)
admin.site.register(Categoria, CategoriaAdmin)
admin.site.register(Nota, NotaAdmin)

Feito isso visitamos a página ao admin e clicamos em Notas. A seguinte página é exibida:

O registro em admin.site disponibiliza o modelo para o app admin. Caso não se queira exibir um dos modelos no admin basta omitir esse registro.

Duas outras possibilidades são as de excluir um campo na página de edição das tabelas do admin e agrupar a exibição de dados sob um campo tal como datas.
apps_notas/notas/admin.py

class NotaAdmin(admin.ModelAdmin):
    ....
    date_hierarchy = 'data_criada'
    exclude = ( 'data_criada', 'data_editada',)

O primeiro filtro cria uma seleção acima da lista, por data de edição. O segundo exclue, no formulário do admin para edição de notas, os campos data_criada e data_editada. Isso é possível uma vez que ambas as datas são inseridas automaticamente com o parâmetro auto_now=True.

Formulários html no Django

Um formulário HTML é uma página construída com marcação html usada para o preenchimento de dados no navegador e habilitada para enviar esses dados para o servidor. Ela é formada por widgets que são campos de textos, caixas de seleção, botões, botões de rádio e checkboxes.


Formulários são uma das formas mais comuns de interação entre usuários e um aplicativo Web. O django traz mecanismos para facilitar a criação de formulários e integrá-los com o aplicativo. Para experimentar com os formulários vamos criar um formulário de inserção e outro de edição de notas existente.

Inserindo uma nota: Primeiro inserir uma url para requisitar uma páginas de inserção de dados. Podemos fazer isso como (mostrando só alterações):
notas/urls.py

urlpatterns = [
    ...
    path('inserir/', views.inserir, name='inserir'),
]

Claro que temos que criar uma view para receber essa solicitação. Mas antes criaremos uma classe derivada de django.forms.ModelForm, responsável pela geração de um formulário. Gravamos o seguinte arquivo em
app_notas/notas/forms.py

from django.forms import ModelForm
from .models import Nota

class NotaForm(ModelForm):
    class Meta:
        model=Nota
        fields=['titulo', 'texto', 'caderno', 'categoria']    

A classe ModelForm descreve um formulário. Ela pode acessar as informações sobre os campos do modelo e os representar em um formulário. A class Meta é usada para modificar as propriedades da classe externa onde foi definida. Dentro dela fazemos a associação com o modelo sendo exibido e os campos que se pretende editar.

Os campos data_criada, data_editada e slug são criados automaticamente e não precisam estar no formulário de inserção de notas. Para inserir todos os campos, caso isso seja o desejado, podemos usar fields = ‘__all__’. A ordem dos campos declarados será respeitada na exibição do formulário.

Inserimos uma view para processar a requisição:
app_notas/notas/views.py

from django.shortcuts import render, get_object_or_404, redirect
from .models import Nota
from .forms import NotaForm
...

def inserir(request):
    form=NotaForm()
    if request.method=='POST':
        form=NotaForm(request.POST)
        if form.is_valid():
            nota=form.save(commit=False)
            nota.save()
            return detalhePK(request, nota.pk)

    contexto = {'section':'inserir','form': form,}    
    return render(request, 'inserir.html', contexto)    

O parâmetro request recebe os dados inseridos na requisição. Se um formulário foi preenchido e enviado o método de requisição foi POST. Um objeto form é carregado com esses dados. Se os dados estão inseridos corretamente eles são gravados. Se não for válida (se algum campo necessário não foi preenchido, ou um email está em formato incorreto, por ex.) um formulário vazio é reexibido.

O uso de nota=form.save(commit=False) armazena na variável um objeto ainda não gravado. Neste intervalo uma validação ou manipulação manual de dados pode ser feita. Se considerado correto o objeto é gravado com nota.save(). Se a nota for gravada corretamente a função view detalhePK() é chamada para exibir a nota recém gravada. O template inserir.html deve conter os comandos para a exibição do formulário.
app_notas/templates/inserir.html

{% extends "base.html" %}
{% block title %}<h1>Inserindo uma nota</h1>{% endblock %}
{% block content %}
<form action="{% url 'inserir' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button class="button" type="submit">Inserir</button>
</form>
{% endblock %}

A ação disparada pelo clique no botão de inserir está descrita na tag action = {% url 'inserir' %}, um acesso à view descrita acima, com método POST. O token {% csrf_token %} acrescenta uma proteção contra ataques do tipo CSRF (Cross Site Request Forgeries), e {{ form.as_p }} faz a listagem HTML dos campos definidos em notas/forms.py, dispostos em parágrafos sucessivos. Também podemos dispor as campos do formulário como {{ form.as_ul }}, dispostos em uma lista não ordenada, ou {{ form.as_table }}, com os campos dentro de uma tabela.

Para acessar a página de inserção sem necessidade de escrever a URL completa, vamos colocar no cabeçalho de base.html um link com esse endereço. Alterando o código logo abaixo de <body> em (exibindo só trecho modificado):
app_notas/templates/base.htlm

<ul class="menu">
    <li><a class="{% if secao == 'home' %}
    active {% endif %}" href="{% url 'index' %}">Início</a></li>
    <li><a href="{% url 'inserir' %}">Inserir Nova Nota</a></li>
    <li><a href="/admin/">Admin</a></li>
  </ul>

Ao clicar em Inserir Nova Nota o seguinte formulário é exibido. Quando preenchido, se clicamos no botão Inserir a nota é gravada e exibida. Resta-nos agora escrever o código para editar ou apagar uma nota já inserida.

Editando uma nota: de forma similar, vamos alterar o projeto para que possamos editar notas existente e apagá-las. A url será recebida por:
app_notas/notas/urls.py

urlpatterns = [
    ...
    path('inserir/', views.inserir, name='inserir'),
    ...
    path('editar/<int:pk>/', views.editar, name='editar'),
]

Podemos usar o mesmo objeto NotaForm já criado, desde que aceitemos que os campos data_criada, data_editada e slug continuem sendo geridos automaticamente.

Uma nova view será incluída para a edição:
notas/views.py

...

def editar(request, pk):
    nota = get_object_or_404(Nota, pk=pk)
    form=NotaForm()
    if request.method=='POST':
        form=NotaForm(request.POST, instance=nota)
        if form.is_valid():
            nota=form.save(commit=False)
            nota.save()
            return detalhePK(request, nota.pk)

    contexto = {'section':'editar','form': form,}
    return render(request, 'editar.html', contexto)    

Como antes, um formulário preenchido corretamente é gravado e a nota editada é exibida. O template editar.html deve conter os comandos para a exibição do formulário.

app_notas/templates/editar.html

{% extends "base.html" %}
{% block title %}<h1>Editando uma nota</h1>{% endblock %}
{% block content %}
<form action="{% url 'editar' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button class="button" type="submit">Gravar</button>
</form>
{% endblock %}

Para dar acesso à página de edição, tendo escolhido uma nota, inserimos uma coluna na página que lista todas as notas em home.html (exibindo só a tabela que contém a lista de notas):
app_notas/templates/home.html

<table class="dados_notas">
    <tr><th>Caderno</th><th>Categoria</th><th>Nota</th>
    <th>Criada em</th><th>Editada em</th><th>visualizar</th>
    <th>editar</th></tr>
    {% for nota in notas %}
      <tr>
        <td>{{ nota.caderno }}</td>
        <td>{{ nota.categoria }}</td>
        <td>{{ nota }}</td>
        <td>{{ nota.data_criada|date:"d/m/Y" }}</td>
        <td>{{ nota.data_editada|date:"d/m/Y" }}</td>
        <td><a href="/nota/{{ nota.pk }}/">Índice = <b>{{ nota.pk }}</b></a></td>
        <td><a href="/editar/{{ nota.pk }}/">Editar: <b>{{ nota.pk }}</b></a></td>
      </tr>
    {%endfor%}
  </table>

Ao clicar em Editar um formulário idêntico ao formulário de edição aparece com os campos já preenchidos, permitindo sua edição. Novamente, ao serem gravados a tela de exibição de notas é mostrada.

Em app_notas/notas/views.py, na função editar(), uma nota específica é carregada e usada para instanciar um objeto NotaForm. Uma NotaForm(instance=nota) é exibida para edição no primeiro carregamento do formulário. Se o botão de Gravar foi clicado a view é acionada com request.method=='POST'. Isso faz com que os dados do formulário sejam gravados, se o formulário é válido.

Apagar uma nota já inserida: Para apagar uma nota já inserida, em urls.py inserimos
app_notas/notas/urls.py

urlpatterns = [
    ...
    path('apagar//', views.apagar, name='apagar'),
]

Em forms.py inserimos a definição de um formulário sem a exibição de campos:
notas/forms.py

class ApagarNotaForm(ModelForm):
    class Meta:
        model=Nota
        fields=[]

O próximo passo é acrescentar uma view para o apagamento em views.py
notas/views.py

...
from .forms import NotaForm, ApagarNotaForm
...
def apagar(request, pk=None):
    nota=get_object_or_404(Nota, pk=pk)
    if request.method=="POST":
        form=ApagarNotaForm(request.POST, instance=nota)
        if form.is_valid():
            nota.delete()
            return redirect('home')
    else:
        form=ApagarNotaForm(instance=nota)
        contexto={'section':'apagar', 'form': form, 'nota': nota,}
        return render(request,'blog/delete.html', contexto)

Um template para confirmar a escolha do usuário em apagar uma nota pode ter o seguinte conteúdo:
app_notas/templates/apagar.html

{% extends "base.html" %}
{% block title %}<h1>Apagando uma Nota</h1>{% endblock %}
{% block content %}
<form action="{% url 'apagar' nota.pk%}" method="post">
    {% csrf_token %}
    {{ form }}
    <h2>Título da Nota: {{ nota.titulo }}</h2>
    <p style="margin-left:10em;">{{ nota.texto | safe }}</p>
    <p>Tem certeza de que deseja apagar essa nota:? <button class="button" type="submit">Apagar</button>
    Cancelar</p>
</form>
{%endblock%}

Agora, na lista geral de notas, ao clicar Apagar somos levados à página com a opção de apagamento, que lista título e conteúdo da nota. Confirmando o apagamento a view.apagar é acionada com method=="POST". Clicando Cancelar a página inicial, com a lista de notas é exibida.

É claro que muitas otimizações podem ser feitas nesse código. Por exemplo, poderíamos ter apenas uma view para inserir | editar | apagar, passando parâmetros que executem cada uma das opções. Também podemos fazer muitos aperfeiçoamentos na interface visível para o usuário, melhorando os templates e usando CSS. Para ilustrar alguns possíveis aprimoramentos vamos melhorar a aparência da página inicial, que contém uma lista de notas, com as várias possibilidades de edição.

Primeiro alteramos views.py (mostrando só alterações):
app_notas/notas/views.py

from . import funcoes

def index(request):
    #notas = Nota.objects.all() (removemos essa linha)
    notas = Nota.objects.order_by('caderno','categoria', 'data_editada')
    notas = funcoes.transforma_notas(notas)
    contexto = {'secao':'home', 'mensagem':'Item de Menu', 'notas': notas,}
    return render(request, 'home.html', contexto )

Fazendo notas = Nota.objects.order_by('caderno','categoria', 'data_editada') o objeto notas será uma coleção de notas, agora ordenadas por ‘caderno’,’categoria’, ‘data_editada’, nessa ordem de prioridade.

Suponha agora que queremos montar uma lista onde nomes de cadernos e categorias repetidas não apareçam, na lista. Isto é: em cada linha cadernos e categorias, agora ordenados nessa ordem, não são exibidos repetidamente. Para isso acrescentamos um arquivo funcoes.py:
app_notas/notas/funcoes.py

def transforma_notas(notas):
    cat = ""
    cad = ""
    for nota in notas:
        if cat == nota.categoria.categoria:
            nota.categoria.categoria = ""
        else:
            cat = nota.categoria.categoria

        if cad == nota.caderno.titulo:
            nota.caderno.titulo = ""
        else:
            cad = nota.caderno.titulo
    return notas    

Essa função transforma em strings vazias os cadernos e categorias repetidos, de forma a que a lista só contenha essa informação quando ela é alterada. Por fim alteramos home.html:
templates/home.html

{% extends "base.html" %}
{% block title %}<h1 style="margin-top:80px;" >Aplicativo de Notas</h1>{% endblock %}

{% block content %}
<table class="dados_notas">
  <tr><th>Caderno</th><th>Categoria</th><th>Nota</th><th>Criada</th><th>Editada</th><th>Ver</th><th>Editar</th><th>Apagar</th></tr>
  <tr><td colspan="8" style="border-bottom: 1px solid #999;"></td></tr>
  <tr><td colspan="8"></td></tr>
  {% for nota in notas %}
  <tr>
    <td style="font-size:2em; font-weight: bold;">{{ nota.caderno }}</td>
    <td style="font-size:1.5em; font-weight: bold;">{{ nota.categoria }}</td>
    <td>{{ nota.titulo }}</td>
    <td>{{ nota.data_criada|date:"d/m/Y" }}</td>
    <td>{{ nota.data_editada|date:"d/m/Y" }}</td>
    <td><a href="/nota/{{ nota.pk }}/">📖</a></td>
    <td><a href="/editar/{{ nota.pk }}/">📝</a></td>
    <td><a href="/apagar/{{ nota.pk }}/">🚮</a></td>
  </tr>
  {%endfor%}
{% endblock %}

Os caracteres 📖, 📝 e 🚮 podem ser copiados e colados diretamente nos templates (ou qualquer outra página html). Alteramos ainda o arquivo estilo.css (mostrando só as alterações):
app_notas/static/estilo.css

.menu > li > a {
    background-color: rgb(21, 59, 141);
    border: 1px solid rgb(6, 6, 39);
    color: #fff;
    padding: 10px 20px;
    text-align: center;
    font-size: 16px;
    margin: 4px 2px;
    cursor: pointer;
}
.menu > li > a.ativa { font-weight:bold; }

Acessando nossa homepage temos agora:

Na imagem acima supomos que temos notas inseridas com esses cadernos, categorias, e títulos.

Muitos outras aperfeiçoamentos podem ser pensados para esse aplicativo de notas. Podemos, por exemplo, fazer as categorias serem dependentes dos cadernos, abrir um conjunto novo de notas para cada usuário, permitir a entrada de sub-categorias em vários níveis, criar um conjunto de tags para aplicar a cada nota, criando uma estrutura de relacionamentos entre tags de modo que o usuário possa navegar entre notas associadas a uma mesma ideia ou conceito.

Importante lembrar que, como estamos dentro de uma estrutura de programação do python, qualquer módulo importado para finalidades diversas pode ser facilmente incorporados no site ou aplicativo. Isso inclue pacotes de análise de texto, tratamento e exibição de dados, recursos de geo-referência, etc.

Artigos Django

1- Django, Websites com Python
2- Django, um Projeto
3- Django, incrementando o Projeto (esse artigo)

Bibliografia

Consulte a bibliografia no artigo inicial.

Um projeto Django


Desenhando um projeto

Para efeito de nosso aprendizado vamos desenvolver um aplicativo para a tomada de anotações. Começaremos com uma tabela simples de notas, com os seguintes campos:

Notas
id titulo texto slug
0 Introdução ao Python Texto da nota sobre python introducao-ao-python
1 Django Texto da nota sobre django django
2 Python para Web Texto da nota sobre python para web python-para-web

Os valores na tabela são ilustrativos. O slug, como veremos, é um tipo de atalho representativo da nota, criado automaticamente e usado na url de acesso à informação. Depois introduziremos aperfeiçoamentos à essa estrutura de dados.

Criando o projeto do django

Uma vez instalados os módulos necessários, com o ambiente virtual ativado, usamos um comando para criar um projeto do django. No primeiro uso do django algum ajuste deve ser providenciado. De dentro do diretório onde será construído o aplicativo executamos, no prompt de comando:

(env) $ django-admin startproject app_notas


Esse comando cria uma estrutura básica de um site. Observe que, diferente de aplicativos em PHP e outros, o aplicativo django não fica no diretório raiz de um servidor web, o que é uma boa coisa considerados os riscos de segurança.

Esses arquivos são:

app_notas diretório raiz do projeto, pode ser renomeado. Nós o chamaremos de app_notas.
manage.py utilitário de comando de linha para interação com o projeto Django.
app_notas subdiretório, é o módulo Python do aplicativo. Esse nome será usado nas importações futuras.
app_notas/__init__.py arquivo vazio para marcar esse diretório como um módulo.
app_notas/settings.py Configurações para esse projeto.
app_notas/urls.py as declarações de URLs para o projeto.
app_notas/asgi.py ponto de entrada para ASGI web servers.
app_notas/wsgi.py ponto de entrada para WSGI web servers.

O arquivo __init__.py (que pode ser vazio) serve para marcar seu diretório como um módulo do python. Dentro desse módulo podem existir classes que podem ser importadas em outras partes do projeto.

Se você usar controle de versão coloque o diretório projeto_django sob o controle do git. Um arquivo README.md deve ser colocado no diretório raiz, com especificações sobre o projeto.

manage.py

Mais informações em: Django Docs: Manage.py

Dentro do arquivo manage.py é estabelecida uma variável global que indica onde estão, para esse projeto, as configurações.
A variável global DJANGO_SETTINGS_MODULE aponta para o arquivo settings.py, onde se encontram diversas variáveis de controle do sistema.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app_notas.settings')
Em settings.py existem diversas variáveis. Entre elas vamos listar apenas algumas:

# Caminho para projeto
BASE_DIR = Path(__file__).resolve().parent.parent

# Debug, booleano. True durante o desenvolvimento. Deve ser tornada False na produção
DEBUG = True

INSTALLED_APPS = [...]             # um dicionário com aplicatyivos instalados

ROOT_URLCONF = 'app_notas.urls'    # onde estão os encaminhamentos de URLs padrão

TEMPLATES = [ ... ]                # referências para os templates instalados

DATABASES                          # referência para o banco de dados usados. Por default SQLite

# varáveis de internationalização
LANGUAGE_CODE = 'pt-br'            # código de língua   (alterado para pt-br, português do Brasil)
TIME_ZONE = 'BRT'                  # faixa horária      (alterado para BRT, hora de Brasília)
USE_TZ = True                      # use time-zone

# onde estão arquivos estáticos (CSS, JavaScript, Images)
STATIC_URL = 'static/'

Feito isso temos um site (ou um aplicativo) montado e pronto para ser rodado. O Django fornece um servidor de desenvolvimento (que só pode ser executado se DEBUG = True).
Observação importante: Em nenhuma hipótese se deve rodar o servidor de desenvolvimento durante a produção, por motivo de segurança.

O servidor de desenvolvimento é iniciado com

$ python manage.py runserver
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Observação: nessas notas vamos representar o prompt simplesmente por $, lembrando que estamos trabalhando em um ambiente virtual.

Se digitarmos na barra de endereço do browser a url http://127.0.0.1:8000/ poderemos ver a tela de boas vindas do django.

Outros comandos ficam disponíveis com manage.py, entre eles:

runserver inicia o servidor de desenvolvimento.
startapp cria um app no Django.
shell inicia uma shell no interpretador com o projeto Django carregado.
dbshell inicia uma shell conectada ao banco de dados em uso, onde se pode rodar queries manualmente.
makemigrations generate alterações no banco de dados definidas pelos modelo.
migrate aplica as alterações geradas por makemigrations.
test roda testes automatizados.

Mais informações no site do Django: Admin e manage.py.

Criando Apps

No Django um projeto pode ter diversos Apps. Um app é um módulo contendo uma parte da funcionalidade do projeto. Por exemplo, nosso projeto de notas pode conter um app para a acesso e modificação das notas, um outro para registrar notas que são agendamentos e calendário, e ainda um terceiro para gerenciar a acesso de vários usuários cadastrados para uso do app.

Para criar um app usamos a comando de linha:

$ python manage.py startapp <nome_do_app>
# no nosso caso
$ python manage.py startapp notas

A seguinte estrutura de diretórios é gerada:

admin.py permite ajuste da página de administração, automaticamente crada pelo django.
apps.py configuração específica do app.
models.py deve conter os modelos geradores das tabelas do banco de dados.
tests.py armazena os testes para debugging.
views.py definição das visualizações.
migrations pasta que armazena as alterações em banco de dados, para sincronização.

O arquivo db.sqlite3, o arquivo do SQlite, é gerado e modificado com as migrações.

Como inserimos um novo app ao projeto, temos que informar isso no arquivo settings.py, na lista INSTALLED_APPS. Fazemos a seguinte modificação:
notas/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    ... linhas omitidas
    'notas',
]

Inserimos a linha em negrito, com referência para o app notas. As demais linhas foram criadas pelo django.

Quando uma requisição é feita ao navegador, por meio de uma URL, o django passa essa requisição pelo arquivo urls.py. Se nenhuma das linhas de url forem satisfeitas uma mensagem de erro é emitida. O erro é bastante detalhado em modo de desenvolvimento, com DEBG = True, e exposto no navegador.

Vamos portanto criar uma url que pode ser reconhecida pelo aplicativo. Inicialmente vamos receber a url sem parâmetro e retornar uma página de Boas Vindas. Para isso inserimos no arquivo:
notas/urls.py

import notas.views
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', notas.views.index),
]

A próxima etapa é a criação de uma view. Para isso editamos o arquivo notas.views e inserimos uma função denominada index. Vamos fazer isso em etapas, para compreender o funcionamento de views.
notas/views.py

from django.http import HttpResponse

def index(request):
    return HttpResponse("Meu primeiro site!")

Se você executar python manage.py runserver e abrir o navegador (em http://127.0.0.1:8000/) veremos, na página do navegador, a frase:

Tags html podem ser passadas, por exemplo para enviar esse texto como um título nível 1, como em return HttpResponse("<h1>Meu primeiro site!</h1>")

Alternativamente podemos retornar uma página pronta de html. Mudamos o conteúdo de views para:
notas/views.py

from django.shortcuts import render

def home(request):
    return render(request,'home.html')

Observação: Os objetos (no caso funções) HttpResponse e render são importadas de seus respectivos módulos de origem. No segundo caso a função render chama um “template” (um modelo de página html) passando para ele o conteúdo de render, como veremos.

Para criar o template vamos gerar nova pasta e um novo arquivo html: Arquivo home.html, lembrando que app_notas é a pasta raiz do projeto:
app_notas/templates/home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h1>Página inicial do aplicativo de Notas</h1>
    <p>Essas são as minhas notas.</p>
</body>
</html>    

Finalmente informamos em settings.py que usaremos um diretório em os.path.join(BASE_DIR,'templates'), lembrando que BASE_DIR é a pasta raiz do projeto. Não podemos esquecer de importar o módulo os.
app_notas/settings.py

import os

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        ': [os.path.join(BASE_DIR,'templates')],
        'APP_DIRS': True,
        ...
    },
]

Agora, quando executamos o servidor e abrimos a página no navegador veremos, devidamente renderizado:


Observação: No arquivo settings.py temos a variável TEMPLATES que é uma lista de dicionários. Nela estão definidas DIRS e APP_DIRS. DIRS define uma lista de diretórios onde o código deve procurar por arquivos de template, em ordem de prioridade. APP_DIRS é booleana. Se True o código deve procurar por templates dentro dos diretórios de aplicativos instalados.

Vimos, até agora, que aplicativos são usados para adicionar recursos ao projeto. O aplicativo é conectado ao projeto com a adição de sua classe de configuração à lista INSTALLED_APPS do arquivo settings.py. O reconhecimento das URLs recebidas pela navegador é feito no arquivo urls.py, que associa a requisição à visualização em views.py que retorna um conteúdo para um template.

Templates

Um template é um arquivo de texto com conteúdo em html e marcação adicional de tags do django que permitem a inserção dinâmica de conteúdo no template. Essas tags podem conter variáveis e determinar fluxo de código, bem similar à própria sintaxe do python. Por exemplo, suponha que enviamos para um template (já veremos como fazer isso) uma lista (ou qualquer objeto iterável) em um variável notas = ["nota1", "nota2","nota3"]. O seguinte template seria traduzido e enviado para o navegador na forma mostrada à direita:

  <ul>
  {% for nota in notas %}
    <li>{{ nota }}</li>
  {%endfor%}
  </ul>  
<ul>
<li>nota1</li>
<li>nota2</li>
<li>nota3</li>
</ul>
Todas as tags, classes e ids html podem ser formatados com CSS, da maneira usual. Observe que estamos chamando de tags dois objetos diferentes: tags html são as usuais p, h1, ul, .... Tags do django são marcações para inserir conteúdos em templates.

Tags do django são adicionadas com a sintaxe: {% tag %} enquanto variáveis são avaliadas e expostas para o código html com {{ variavel }}. Exemplos disso são:{% for %} conteúdo {% endfor %}. Objetos podem ter seus atributos consultados com a notação de ponto, {{ objeto.atributo }}. Filtros podem ser usados para modificar valores de variáveis, usando-se uma barra vertical (|). Por ex.: {{ nota|truncatechars:5 }} trunca a string em 5 caracteres.

Herança de templates

Supondo que a maior parte (ou todas) das páginas de nosso site têm a mesma estrutura, o que costuma ocorrer, podemos criar um template base importado e extendido pelas páginas.

No diretório do aplicativo templates criamos o arquivo base.html:
template/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Notas</title>
</head>
<body>
  <ul class="menu">
    <li><a href="{% url 'index' %}">Home</a></li>
  </ul>
  {% block title %}
  {% endblock %}
  {% block content %}
    Esse é meu site de Notas
  {% endblock %}
</body>
</htmlglt;

O arquivo home.html fica modificado assim:
template/home.html

{% extends "base.html" %}
{% block title %}
  <h1>Aplicativo de Notas</h1>
{% endblock %}

{% block content %}
  <h1>Página inicial</h1>
  <p>Essas são as minhas notas.</p>
{% endblock %}

Esse arquivo home.html herda a estrutura de base.html, substituindo nele o conteúdo dos blocos de mesmo nome. Um bloco pode ser omitido no arquivo filho e, nesse caso o conteúdo original de base.html é exibido.

Antes de executar esse site vamos fazer mais algumas alterações: o arquivo urls.py fica da seguinte forma:
app_notas/urls.py

    
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    ...
    path('', include('notas.urls')),
]

Após importar include, indicamos que o padrão de url vazio (ou outro que não for casado com as urls atuais) será redirecionado para notas.urls que criaremos a seguir. Na pasta do aplicativo gravamos urls.py:
notas/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

Esse arquivo importa views que está na mesma pasta (.) e remete chamadas para views.index. Quando executamos python manage.py runserver e abrimos o navegador vemos a página:

O link <a href="{% url 'index' %}">Home</a> no topo do arquivo base.html será exibido em todas as páginas que herdam esse código. Ele contém um referência ativada pela função url do django, com o parâmetro index. Esta é uma forma de evitar que façamos referências com urls fixas nas páginas do aplicativo.

Arquivos Estáticos e CSS


Claro que páginas html sem nenhuma formatação não satisfazem a demanda de nenhum usuário moderno. Para que as páginas html tenham acesso aos arquivos de formatação CSS precisamos indicar para o django onde encontrar esses arquivos, além imagens e outros arquivos estáticos.

Vamos começar criando os diretórios static e static/css dentro do diretório raiz do projeto. Dentro criamos o arquivo estilo.css (usando aqui apenas algumas poucas configurações, apenas para demonstrar o funcionamento).
static/css/estilo.css

body { width:80%; margin: 5% 10%; }
h1 { color:navy; }    
.menu { list-style:none; padding-left:0; }
.menu > li { display:inline-block; }
.menu > li > a { text-decoration:none; color:#000; margin:00.3em; }
.menu > li > a.active { padding-bottom:0.3em; border-bottom:2px solid #528DEA; }

Temos que informar ao django onde estão os arquivos estáticos, modificando settings.py:
app_notas/settings.py

STATIC_URL='/static/'

STATICFILES_DIRS = [os.path.join(BASE_DIR,'static'),]

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    'notas',
]

Lembrando que já temos, como mostrado acima, o dicionário
INSTALLED_APPS = [ …, ‘django.contrib.staticfiles’,…]
com a indicação para acessar arquivos estáticos. Toda essa configuração só deve ser usada com projetos na fase de desenvolvimento. Veremos depois qual deverá ser o ajuste na produção.

No template base.html fazemos as alterações (apenas as modificações são mostradas):
templates/base.html

<!DOCTYPE html>
{% load static %}
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{% static'css/estilo.css' %}">
    ...
</head>
...

A tag {% load %} torna disponíveis tags e filtros para esse template. Aqui usamos staticpara dar acesso à URL do arquivo CSS. Alteramos agora
app_notas/notas/views.py

from django.shortcuts import render

def index(request):
    contexto = {'secao':'home', 'mensagem':'Vou colocar outro link aqui!'}
    return render(request,'home.html', contexto)

O dicionário contexto contém variáveis que podem ser usadas dentro de home.html. O nome da variável é passado como string. Alteramos o template base para receber essas variáveis:
templates/base.html

<body>
  <ul class="menu">
    <li><a class="{% if secao=='home' %} active {% endif %}
    "href=" {% url 'index' %}">Início</a></li>
    <li>{{ mensagem }}</li>
  </ul>
  {% block title %}

Esse código faz o seguinte: se secao == 'home' o primeiro ítem da lista terá classe active com formatação definida no arquivo CSS. A variável mensagem = 'Vou colocar...' foi usada para demonstração e se torna o segundo ítem da lista. O conteúdo da variável mensagem é servido para o html com a sintaxe {{ mensagem }}. O resultado será uma exibição formatada na navegador.

Se o link sublinhado for clicado a mesma página será carregada. Claro que o link pode se referir a qualquer outra página.

Você pode ver o que foi carregado olhando page source (CTRL-U, no firefox). Nessa página um clique em estilo.css deverá exibir os estilos.

Pode ocorrer que você faça alterações no arquivo css e elas não se reflitam na renderização do navegador, mesmo após um recarregamento. Isso provavelmente ocorre porque o navegador coloca em cache parte das informações recebidas pelo servidor. Isso pode ser resolvido forçando o navegador a ver o arquivo de estilo como um novo arquivo. Para isso colocamos um parâmetro após o nome do estilo:
<link rel="stylesheet" href="{% static 'css/estilo.css' %}?v=1">.

Modelos

Até agora não mencionamos como o django lida com o gerenciamento dos dados a serem servidos nos aplicativos. Essa é a parte de construção, gerenciamento e leitura em bancos de dados. Continuaremos com nosso banco de dados do SQlite, que não exige uma instalação específica de servidor de dados.

Modelos estão definidos no django em django.db.models. Para usá-los criamos subclasses desse classe. Editamos o arquivo de modelos:
notas/model.py

from django.db import models

class Nota(models.Model):
    titulo = models.CharField(default='', max_length=150)
    texto = models.TextField(default='', blank=True)
    slug = models.SlugField(default='', blank=True, max_length=255)

    def __str__(self):
        return self.titulo

O django tem código pré-definido para transformar essas definições em modificações no banco de dados. Isso é feito com os comandos manage.py makemigrations e manage.py migrate, que já utilizaremos. Quase sempre cada modelo representa uma tabela no BD. No nosso caso definimos um campo titulo, de texto com comprimento máximo de 150 caracteres, e um campo texto, de texto sem comprimento definido e que pode ser gravada vazia. O campo slug será destinados a armazenar partes das URLs de acesso a cada nota. Todos eles tem uma string vazia como valor default e pode conter apenas caracteres, números, hífens e sublinhados.

O método __str__ define o que será usado como representação da classe. Por exemplo print(Nota) imprimirá o título da nota. Ids são definidos automaticamente.

Vamos primeiro registrar esse modelo para ser usado com o app admin.py, pre-definido pelo django e ainda não usado por nós.

notas/admin.py
notas/admin.py

from django.contrib import admin
from blog.models import Nota

admin.site.register(Nota)

Feito isso podemos aplicar as alterações no bando de dados.

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
# caso se necessite trocar a senha usamos:
$ python manage.py changepassword <nome_do_usuário>

O comando makemigrations interpreta as modificações feitas em código e as armazena na pasta notas/migrations. Elas serão usadas mais tarde para aplicar as mesmas modificações na fase de produção. Quando usamos o comando migrate as alterações são aplicadas no SQlite. Se o arquivo app_notas/db.sqlite3 for inspecionado (por exemplo com o DB Browser for SQLite) veremos que estão armazenadas as estrutura da tabela notas, como mostra a figura.

Com createsuperuser se abre um diálogo para inserirmos nome, email e password de um superuser que gerenciará o admin, com todas as permissões, por default.

Se você carregar o servidor com python manage.py runserver e abrir a navegador em http://127.0.0.1:8000/admin/ verá uma página de administração do site, com possibilidade de gerenciar grupos e usuários, tudo predefinido pelo django. Além disso temos acesso para operações de CRUD da tabela Notas.

Nesse ponto, como um exercício, abra o gerenciador e faça inserções de notas no aplicativo. Observe que alguma validação é feita, por exemplo impedindo que gravemos uma nota sem título.

Link para Admin

Para acessar a página do admin sem necessitar escrever uma url completa para isso vamos alterar o arquivo base.hmtl e inserir nele esse link.
app_notas/templates/base.hmtl

<body>
  <ul class="menu">
    <li><a class="{% if secao == 'home' %} active {% endif %}"
    href=" {% url 'index' %}">Início</a></li>
    <li><a href="/admin/">Admin</a></li>
  </ul>
  ...

Exibimos aqui apenas a lista não ordenada que representa o nosso menu. Agora basta clicar no link Admin no cabeçalho para acessar o gerenciador.

Exibindo dados

Se você entrou dados no banco de dados usando a página de admin agora você pode exibí-las em seu site. Para isso modificamos views.py.
app_notas/notas/views.py

from django.shortcuts import render
from .models import Nota

def index(request):
    notas = Nota.objects.all()
    contexto = {'secao':'home', 'mensagem':'Item de Menu', 'notas': notas,}
    return render(request, 'home.html', contexto)

O método Nota.objects.all() faz o acesso ao banco de dados e retorna uma QuerySet com todos os campos nele registrados. Esse código é internamente transformados em consultas sql que são submetidas ao banco de dados. Outros tipos de consulta são possíveis, com filtros e ordenamentos. O resultado da consulta, armazenado em notas, é passado em contexto para o template.

Prosseguindo, home.html deve ser modificado para receber e exibir as notas passadas em contexto.
app_notas/templates/home.html

{% extends "base.html" %}
{% block title %}
  <h1>Aplicativo de Notas</h1>
{% endblock %}

{% block content %}
  <h1>Página inicial</h1>
  <p>Essas são as minhas notas.</p>
  <ul>
  {%for nota in notas %}
    <li>{{ nota.pk }}: {{ nota }}</li>
  {%endfor%}
  </ul>
{% endblock %}

Observe que {{ nota }} exibirá a representação default do objeto, que é seu título. {{ nota.pk }} é o id (uma primary key, que poderá ser usada para identificar uma nota individual.

O navegador agora exibe, além do cabeçalho anterior, a lista contendo ids e títulos de notas (que digitei na página do admin):

Exibindo dados do Banco de Dados

Um passo adicional para melhorar nosso aplicativo consiste em exibir detalhes de uma nota selecionada pelo usuário. Para fazer isso vamos começar alterando notas/models.py.
app_notas/notas/models.py

from django.db import models
from django.urls import reverse
from django.utils.text import slugify

class Nota(models.Model):
    titulo = models.CharField(default='', max_length=150)
    texto = models.TextField(default='', blank=True)
    slug = models.SlugField(default='', blank=True, max_length=255)
    
    def __str__(self):
        return self.titulo

    def save(self,*args,**kwargs):
        self.slug = slugify(self.title)
        super().save(*args,**kwargs)
        
    def get_absolute_url(self):
        return reverse('blog:detail', args=[str(self.slug)])

Nas partes inseridas (em negrito) sobreescrevemos o método save para executar código customizado antes que o objeto seja armazenado em banco de dados. O método super().save() se refere ao método de gravação definido na classe mãe.

A função slugify converte strings para uma forma de slug. Por exemplo: A string “Um Curso sobre Django” será transformado em “um-curso-sobre-django” e esse slug será usado nas URLs.

O método get_absolute_url() retorna a URL padrão para um objeto, usando reverse que considera a view usada e a slug.

Antes de executar o código de acesso ao detalhe das notas verifique se todas as slugs estão preenchidas, usando a página de Admin.

Vamos criar duas entradas de URLs em urls.py, para encontrar uma nota através de sua slug ou por meio de seu id (aqui chamado de pk, ou primary key).
notas/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    path('index', views.index, name='index2'),
    path('slug/<slug:slug>/', views.detalhe, name='detalhe'),
    path('nota/<int:pk>/', views.detalhePK, name='detalhe2'),
]

Agora, se digitarmos http://127.0.0.1:8000/slug/django/ o slug (a palavra django, nesse caso) será capturado e enviado para a views.detalheSLUG. Digitando, por ex., http://127.0.0.1:8000/nota/1/, o pk=1 será enviando para views.detalhePK. Precisamos criar então essas views:
app_notas/notas/views.py

from django.shortcuts import render, get_object_or_404
from .models import Nota

def index(request):
    notas = Nota.objects.all()
    contexto = {'secao':'home', 'mensagem':'Item de Menu', 'notas': notas,}
    return render(request, 'home.html', contexto )

def detalheSLUG(request, slug):
    nota = get_object_or_404(Nota, slug=slug)
    contexto = {'secao':'detalhe', "nota": nota,}
    return render(request, "detalhe.html", contexto)

def detalhePK(request, pk):
    nota = get_object_or_404(Nota, pk=pk)
    contexto = {'secao':'detalhe', "nota": nota,}
    return render(request, "detalhe.html", contexto)    

O método get_object_or_404() é um atalho que executa as operações de get(), fazendo a busca e retornando o objeto procurado ou levantando uma exceção Http404 se o objeto não existir.

Finalmente gravamos template detalhe.html que receberá e exibirá esses dados:
app_notas/templates/detalhe.html

{% extends "base.html" %}
{% block title %}
  <h1>Aplicativo de Notas: Detalhes</h1>
{% endblock %}

{% block content %}
  <h1>Detalhe de uma nota</h1>
  <h2>Nota: {{ nota.titulo }}</h2>
  <p>{{ nota.texto }}</p>
  <p><small>Id: {{ nota.pk }} Slug: {{ nota.slug }}</small></p>
{% endblock %}

Agora, digitando http://127.0.0.1:8000/django/ na barra de endereços do navegador temos:

Digitando http://127.0.0.1:8000/nota/1/ temos:

Essas exibições no navegador assumem que existem notas com os títulos, ids e slugs mostrados, além do texto da nota. Observe que as tags html não estão renderizadas como tal, ou seja, as quebras de linhas são representados como <br>, e não como quebras de fato. Isso ocorre porque o django faz escape de tags, por motivo de segurança. Para que tags sejam passadas e exibidas podemos inserir o filtro {{ variavel|safe }}.

Modifique detalhe.hmtl (exibindo somente o bloco de conteúdo):
app_notas/templates/detalhe.html

{% block content %}
  <h1>Detalhe de uma nota</h1>
  <h2>Nota: {{ nota.titulo }}</h2>
  <p>{{ nota.texto|safe }}</p>
  <p><small>Id: {{ nota.pk }} Slug: {{ nota.slug }}</small></p>
{% endblock %}

Agora visitando a url http://127.0.0.1:8000/nota/1/ veremos:

Claro que não se pode esperar que usuários conheçam as slugs ou ids de uma nota. Esse dado deve ser obtido dentro do próprio aplicativo. Vamos fazer isso alterando home.html. Abaixo está exibido apenas o código dentro de block content:
app_notas/templates/home.html

{% block content %}
  <h1>Página inicial</h1>
  <p>Essas são as minhas notas (clique para visualizar).</p>
  <ul>
  {%for nota in notas %}
    <li><a href="/nota/{{ nota.pk }}/">{{ nota }}</a></li>
  {%endfor%}
  </ul>
{% endblock %}

Aqui foi feita a alteração, na listagem das notas apresentadas: ao invés de apenas listar os ids ele é usado para construir a url href="/nota/{{ nota.pk }}/.
Por motivo didático construimos duas views para receber slugs ou ids. Poderíamos ter construido uma única view que separe parâmetros inteiros (pk) de strings (slugs).

Se uma URL for inserida em busca de um id ou slug não existente uma tela de erros aparece, indicando onde está o erro. Podemos, e devemos, capturar esse erro indicando ao usuário que a busca não foi bem sucedida. Para isso alteramos o arquivo
app_notas/notas/views.py

def detalheSLUG (request, slug):
    try:
        nota = get_object_or_404(Nota, slug=slug)
        contexto = {'secao':'detalhe', 'nota': nota,}
    except:
        contexto = {'secao':'erro', 'nota': 'Nota não encontrada!',}
    return render(request, "detalhe.html", contexto)

def detalhePK (request, pk):
    try:
        nota = get_object_or_404(Nota, pk=pk)
        contexto = {'secao':'detalhe', "nota": nota,}
    except:
        contexto = {'secao':'erro', 'nota': 'Nota não encontrada!',}
    return render(request, "detalhe.html", contexto)    

O template recebe as variáveis de contexto, com um teste para a variável seção:
app_notas/templates/home.html

{% block content %}
  {% if secao == "erro" %}
    <h1>Nenhuma nota foi encontrada!</h1>
    <h2>Nota: {{ nota }}</h2>
  {% else %}
    <h1>Detalhe de uma nota</h1>
    <h2>Nota: {{ nota.titulo }}</h2>
    <p>{{ nota.texto|safe }}</p>
    <p><small>Id: {{ nota.pk }} Slug: {{ nota.slug }}</small></p>
  {% endif %}
{% endblock %}

Agora, se um slug não existente for digitado, a mensagem é exibida:

Observe que os espaços em torno do comparador “==” devem ser mantidos em {% if secao == "erro" %}, ou um erro será lançado.

Artigos Django

1- Django, Websites com Python
2- Django, um Projeto (esse artigo)
3- Django, incrementando o Projeto

Bibliografia

Consulte a bibliografia no artigo inicial.

Django, websites com Python

O que é Django

Django foi desenvolvido como um projeto interno no jornal Lawrence Journal-World em 2003 para atender à necessidade de implementar novos recursos com muito pouco prazo, e tornado disponível publicamente em julho de 2005. Ele é mantido pela Django Software Foundation (DSF), uma organização independente estabelecida nos EUA como uma organização sem fins lucrativos. Alguns sites conhecidos que usam Django incluem Instagram, Spotify, YouTube, Mozilla, Disqus, The Washington Post, Dropbox e muitos outros.

Django é um framework web gratuito, de código aberto e baseado em Python. Seu principal objetivo é a construção de sites complexos baseados em banco de dados, de forma rápida e de fácil manutenção. Sua estrutura prioriza a reutilização de componentes, usando menos código e o princípio DRY (não se repita). O Python é usado extensivamente na configuração, no acesso aos bancos de dados e na camada de exibição.

Como framework o Django é completo (diferente do Flask), podendo ser usado sem a adição de pacotes adicionais, embora possa receber plugins para incrementar sua funcionalidade. O Django fornece uma interface de administração (um painel do usuário) opcional gerada dinamicamente por introspecção que possibilita as operações de CRUD no banco de dados e tem suporte para bancos de dados SQLite, PostgresSQL e MySQL (e outros).

Arquitetura MTV

Django segue o padrão da arquitetura MTV, modelo–template–visualização (model–template–views).

  • Model: os dados a serem apresentados pelo aplicativo. Geralmente lidos em um banco de dados.
  • View: um gerenciador de requisições que seleciona o template apropriado.
  • Template: um arquivo básico (com estrutura HTML) contendo o layout da página web com marcadores para preenchimento dos dados requisitados.

Um quadro pode ajudar a esclarecer o modelo.


Descrição do modelo MVT
(Leia esse quadro e retorne a ele mais tarde, depois de ter lidos sobre as várias camadas do django.

  • O navegador envia uma requisição para o servidor rodando django (1),
  • a URL é recebida por urls.py que atribui uma view para tratamento da requisição,
  • a camada view consulta Model (2) para receber os dados requisitados e recebe dela esses dados (3),
  • depois de obter os dados View consulta a camada Template (4) para formatar a apresentação final (5) e
  • envia páginas html formatadas para o navegador cliente (6).

Sobre aprender Django

Para se obter um entendimento razoável do Django é necessário ter alguns pre-requisitos, que não são cobertos nessas notas. Primeiro é necessário entender como as páginas na web são formadas com html e formatadas com css. Um conhecimento do Python também é essencial, em particular sobre estruturas de dados: uso de listas e tuplas, dicionários e, principalmente, o uso de programação orientada a objetos.

Esses artigos adotam a abordagem de cobrir os aspectos básicos do django para dar uma visão geral do processo de criação e manutenção de sites e aplicativos web. Após uma leitura desse texto e a experimentação com o código proposto a consulta à documentação oficial do django deverá ser compreensível para o leitor.

Instalações

Para usar essas instruções o ideal seria ter uma instalação das últimas versões do Python e do Django. Usaremos o banco de dados SQlite que não necessita nenunha instalação especial. Também poderiam ser usados o MySQL, o PostgreeSQL ou vários outros bancos de dados.

Embora não obrigatório é sempre bom trabalhar em uma área isolada usando um ambiente virtual. Instruções sobre ambientes virtuais podem ser lidas aqui: Ambientes Virtuais, pip e conda. Para isso crie um diretório destinado a conter o projeto do django, e um ambiente virtual:

$ mkdir ~/Projetos/django
$ cd  ~/Projetos/django
$ python3.10 -m venv env
# para usar o ambiente virtual
$ source env/bin/activate
# o prompt de comando muda para
(env) $
# para desativar o ambiente virtual (quando for o caso)
(env)$ deactivate

As linhas de código acima criam o diretório ~/Projetos/django (lembrando que no linux ~ representa a pasta do usuário). No Windows os comandos devem ser alterados de acordo com a sintaxe do sistema. Criando um ambiente virtual alguns diretórios específicos (bin, include, lib) são gravados com uma cópia da instalação do Python, e algumas variáveis de ambiente são redefinidas. Pacotes instalados com o pip (ou outro gerenciador) serão colocados nesse ambiente.

Estando dentro do ambiente virtual, instalamos a última versão do django (que era a 4.0.5 em junho de 2022) usamos:

(env) $ pip install Django==4.0.5
# para verificar a instalação
(env) $ python -m django --version
4.0.5

Prosseguiremos com a construção de um projeto em django no artigo 2- Django, um Projeto.

Artigos Django

1. Django, Websites com Python (esse artigo): Introdução, instalação.

2. Um Projeto no Django: Criação e gerenciamento de projetos, criação de apps, templates, herança de templates, arquivos Estáticos e CSS, Modelos de dados, admin, exibição de dados.

3. Incrementando o Projeto Django: chaves externas e datas, Resetando o banco de dados, personalizando o Admin, formulários.

Bibliografia

Livros:

  • Ashley, David: Foundation Dynamic Web Pages with Python, Apress, 2020.
  • Bendoraitis, Aidas; Kronika, Jake: Django 3 Web Development Cookbook, 4th. Ed., Packt, Mumbai, 2020.
  • Feldroy, D.; Feldroy, A.: Two Scoops of Django 3.x, 5ª Ed., Two Scoops Press, 2021.
  • Shaw, B., Badhwar, S., Bird, A, Chandra, Guest C.: Web Development with Django, Packt, 2011.
  • Vincent, William S.: Django for Professionals, Production websites with Python & Django, disponível para aquisição em Leanpub.com, 2020.

Sites:

todos eles acessados em junho de 2022.

Testes Unitários


Testando o código

Vimos no artigo Testando o Código algumas abordagens iniciais para realizar testes no código, tais como usar a declaração assert e as docstrings. Recursos mais avançados epoderosos estão disponíveis, como é o caso do módulo unittest, que veremos agora.

Testes unitários com unittest

Testes unitários permitem que unidades de código possam ser testadas em diversas de suas características. Uma unidade pode ser uma função individual, um método ou procedimento de uma classe ou objeto. Ele é feito durante o desenvolvimento pelo programador.

O módulo unittest, incluído na biblioteca padrão, fornece ferramentas para testes unitários. Com ele podemos projetar um conjunto de testes que verificam se uma função (por exemplo) se comporta como o esperado sob situações variadas. Um bom conjunto de testes considera os possíveis tipos de entrada que uma função pode receber, incluindo testes de cada dessas situações. Uma cobertura completa de testes, em um projeto grande, pode ser muito difícil e, nesses casos pode ser considerado suficiente cobrir os casos críticos de uso do bloco testado. Diversos editores e IDEs, incluindo Jupyter Notebook, PyCharm e VSCode, podem usar unittest integrado.

Para usar unittest vamos escrever uma função a ser testada. Em seguida importamos o módulo unittest e criamos uma classe que herda de unittest.TestCase. Objetos dessa classe chamam e verificam o comportamento dessa função testada ao serem inicializados. Métodos diversos podem ser inseridos para verificar o funcionamento da função sob a inserção de parâmetros diferentes.

Para observar o funcionamento dos testes unitários vamos gravar dois arquivos do python, formata_nomes.py e nomes.py. O primeiro contém a função que queremos testar, o segundo chama essa função.

#formata_nomes.py

def ler_nome_formatado(nomes):
    msg = ""
    if nomes.strip() == "":
        msg = ""
    partes = nomes.split()
    nome = partes[0].title()
    msg = f"Primeiro nome: {nome}"
    if len(partes) > 1:
        sobre = " ".join(partes[1:]).title()
        msg = f"{msg}, Sobrenome: {sobre}"
    return msg


Essa função recebe nomes e sobrenomes separados por espaços e retorna esse nome formatado como Primeiro nome: nome, Sobrenome: sobrenomes . Ela considera todas as palavras após a primeira como sobrenome. Para usar essa função gravamos e executamos o arquivo nomes.py.

from formata_nome import ler_nome_formatado as nf
print("Digite nome e sobrenomes.")
print("Deixe em branco para terminar.")
while True:
    nomes = input("\nDigite o nome completo: ")
    formatado = nf(nomes)
    if formatado=="": break
    print(f"\tNome formatado: {formatado}.")

Podemos iniciar uma sessão no console (terminal) e executar python nomes.py. O output aparece no código abaixo.

$ python nomes.py
  Digite nome e sobrenomes.
  Deixe em branco para terminar.

  Digite o nome completo: PEDRO
	  Nome formatado: Primeiro nome: Pedro.

  Digite o nome completo: pedro de alcantara
	  Nome formatado: Primeiro nome: Pedro, Sobrenome: De Alcantara.

  Digite o nome completo: pedro II
	  Nome formatado: Primeiro nome: Pedro, Sobrenome: Ii.

  Digite o nome completo:

Na última linha foi inserida uma string vazia, o que termina o loop. Aparentemente a função retorna o que se espera. Mesmo assim vamos testar nossa função: em um novo módulo importamos unittest e a função que pretendemos testar. Depois criamos uma classe que herda de unittest.TestCase e acrescentamos diversos métodos para verificar aspectos diferentes da função. Cada um dos métodos test_1, test_2, test_3 verifica um comportamento da função para diferentes tipos de inputs.

# teste_formata_nomes.py
import unittest
from formata_nome import ler_nome_formatado as nf

class TestaFormataNomes(unittest.TestCase):
    """Testes para 'formata_nome.py'."""

    def test_1(self):
        """testando o nome 'palito'."""

        formatado = nf('palito')
        self.assertEqual(formatado, 'Primeiro nome: Palito')

    def test_2(self):
        """testando nomes com maísculas."""

        formatado = nf('MARCO POLO')
        self.assertEqual(formatado, 'Primeiro nome: Marco, Sobrenome: Polo')

    def test_3(self):
        """testando strings vazias."""

        formatado = nf('')
        self.assertEqual(formatado, '')

if __name__ == '__main__':
    unittest.main()

Ao rodar esse teste observamos o output:

$ python teste_formata_nome.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.000s
OK

Nenhum erro foi encontrado em nenhum dos três testes, como mostrado no console. Essa classe pode ter qualquer nome embora seja boa ideia dar um nome representativo de seu objetivo. Ela contém três métodos para testar a função formata_nome.ler_nome_formatado. Qualquer classe que herda de unittest.TestCase executa automaticamente todos os seus métodos que começam com test_ quando é invocada. O retorno da função testada é comparado com o resultado na linha self.assertEqual(formatado, 'string esperada') (um dos método de unittest.TestCase) e gera a informação sobre se o teste foi bem sucedido ou não, com as devidas mensagens.

O bloco if no final, como já vimos, verifica o valor da variável especial __name__. Se o arquivo estiver sendo rodado como programa principal, como ocorreu no nosso caso, ela assume o valor __name__ = "__main__". Nesse caso unittest.main() é chamado e os testes executados.

Suponha que queremos expandir nossa função ler_nome_formatado para que ela retorne uma mensagem de erro caso algum dígito esteja entre os caracteres dos nomes. Se um nome for digitado como “lu1s quinze” a função deve retornar: “Erro: dígito encontrado!”

Vamos então acrescentar um teste em teste_formata_nomes.py. O código abaixo mostra só o acréscimo ao arquivo.

# teste_formata_nomes.py
...
    def test_4(self):
        """testando dígitos no nome."""

        formatado = nf('lu1z paulo')
        self.assertEqual(formatado, 'Erro: dígito encontrado!')
...

Rodamos o teste novamente: desta vez um nome inserido com um dígito não retorna o resultado correto e uma mensagem de erro informa qual o teste falhou, onde e porque.

$ python teste_formata_nome.py
...F
======================================================================
FAIL: test_4 (__main__.TestaFormataNomes)
testando dígitos no nome.
----------------------------------------------------------------------
Traceback (most recent call last):
  File "teste_formata_nome.py", line 29, in test_4
    self.assertEqual(formatado, 'Erro: dígito encontrado!')
AssertionError: 'Lu1Z Paulo' != 'Erro: dígito encontrado!'
- Lu1Z Paulo
+ Erro: dígito encontrado!
----------------------------------------------------------------------
Ran 4 tests in 0.001s
FAILED (failures=1)

Claro que esse defeito deve ser corrigido em formata_nomes.py. Alteramos o código da seguinte forma:

# formata_nomes.py
def ler_nome_formatado(nomes):
    msg = ""
    if nomes.strip() == "":
        msg = ""
    elif True in [i.isdigit() for i in nomes]:
        msg = "Erro: dígito encontrado!"
    else:
        partes = nomes.split()
        nome = partes[0].title()
        msg = f"Primeiro nome: {nome}"
        if len(partes) > 1:
            sobre = " ".join(partes[1:]).title()
            msg = f"{msg}, Sobrenome: {sobre}"
    return msg

No código temos a condição True in [i.isdigit() for i in nomes] que testa cada caracter da variável de string nomes, retornando uma lista de valores booleanos. O teste resulta verdadeiro se uma ou mais das entradas dessa lista for True, ou seja, se existirem dígitos no nome. Com essa alteração rodarmos o teste mais uma vez e veremos que todas as condições testadas foram satisfeitas.

$ python teste_formata_nome.py
...
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK

Para ignorar um dos testes podemos decorar a função com @unittest.skip.

...
    @unittest.skip('Esse teste já foi executado!')
    def test_4(self):
        formatado = nf('lu1z paulo')
        self.assertEqual(formatado, 'Erro: dígito encontrado!')
...

Uma classe inteira pode ser ignorada.

@unittest.skip("Uma classe a ser ignorada")
class Classe_de_Teste(unittest.TestCase):
    def um_metodo_qualquer(self):
        pass

Os seguintes decoradores estão disponíveis na classe:

@unittest.skip(msg) ignore o teste em qualquer caso,
@unittest.skipIf(bool, msg) ignore o teste se bool==True,
@unittest.skipUnless(bool, msg) ignore o teste, exceto se bool==True,
@unittest.expectedFailure marca o teste como falha esperada. Se o teste falhar mensagem de sucesso é emitida e, se passar um erro é lançado,
exception unittest.SkipTest(msg) Uma exceção é levantada ao ignorar em teste,

msg é a mensagem retornada com a exceção, que deve ser descritiva do problema ocorrido. bool é qualquer expressão que retorne um booleano.

Métodos setUp() e tearDown()


Os métodos setUp() e tearDown() são usados para definir instruções executadas antes e depois dos testes definidos. setUp() é executado antes de cada teste no módulo e tearDown() depois de cada um deles. Eles podem ser usados, por exemplo, definir variáveis, abrir e fechar uma conexão com banco de dados ou ler dados em um arquivo.

Erros levantados dentro setUp() ou tearDown() serão considerados erros comuns e não uma falha do teste. A implementação default não realiza nenhuma ação (como em pass). Por exemplo, suponha que pretendemos testar nossa classe Calculadora onde

# classe Calculadora
class Calculadora:
    def __init__(self):
        pass

    def soma(self, a, b):
        return a + b

    def subtrai(self, a, b):
        return a - b

    def muliplica(self, a, b):
        return a * b

    def divide(self, a, b):
        if b != 0:
            return a / b

Na classe de teste teríamos que inicializar uma calculadora para cada teste. Alternativamente podemos inicializar uma calculadora no método setUp().

class TestCalculadora(unittest.TestCase):

    def setUp(self):
        self.calc = Calculadora()

    def tearDown(self):
        self.calc = None

    def test_soma(self):
        self.assertEqual(self.calc.add(4, 7), 11)

    def test_subtrai(self):
        self.assertEqual(self.calc.sub(10, 5), 5)

    def test_multiplica(self):
        self.assertEqual(self.calc.mul(3, 7), 21)

    def test_divide(self):
        self.assertEqual(self.calc.div(10, 2), 5)

Também podemos usar métodos semelhantes para classes e módulos: isso é feito com setUpClass() e tearDownClass() em classes, e setUpModule() e tearDownModule() em módulos.

Testes ignorados não acionam setUp() nem tearDown(), caso estejam definidos. Da mesma forma classes ignoradas não acionam setUpClass() nem tearDownClass(). Módulos ignorados não acionam setUpModule() nem tearDownModule().

Para ver uma lista de opções de uso do unittest podemos digitar:

python -m unittest -h

Métodos assert em unittest.TestCase

Nos testes usando unittest.TestCase podemos usar um assert puro ou um dos seguintes métodos definidos no módulo unittest.TestCase:

Método levanta erro se a condição não se verifica
assertEqual(m, n) m == n
assertNotEqual(m, n) m != n
assertTrue(a) a é True
assertFalse(a) a é False
assertIn(item, lista) item está na lista
assertNotIn(item, lista) item não está na lista
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x == None
assertIsNotNone(x) x != None
assertIsInstance(a, b) a é uma instância de b
assertNotIsInstance(a, b) a não é uma instância de b
assertAlmostEqual(a, b[, n]) se a == b, precisão de n decimais (default: n = 7)
assertNotAlmostEqual(a, b[, n]) negação de assertAlmostEqual(a, b[, n])
assertGreater(a, b) a > b
assertGreaterEqual(a, b) a >= b
assertLess(a, b) a > b
assertLessEqual(a, b) a <= b
assertRegex(s, r) regex r.search(s)
assertNotRegex(s, r) regex not r.search(s)
assertCountEqual(a, b) a e b tem os mesmos elementos e em igual número, independente da ordem.
Método
fail() sempre gera erro

Os erros são levantados quando o teste for falso. Em todos os casos um parâmetro opcional pode ser usado para determinar a mensagem de erro mostrado, como em TestCase.assertEqual(m, n [, mensagem]). Devemos nos lembrar, como dito acima, que um teste com assert pode ser desligado com o ajuste da variável __debug__ = False.

Simulações (Mocks)

Geralmente o estado de uma função, classe ou um de seus métodos depende de objetos externos para a coleta de dados ou outra interação qualquer, tais como arquivos em disco a serem lidos ou acesso a bancos de dados, ou uma peça de hardware a ser acionada. Como não é boa prática acessar em fase de desenvolvimento os objetos na produção desenvolveu-se a abordagem de criar “objetos simulados” ou mocks. Um objeto mock substitui e imita o comportamento de um objeto real, no ambiente de teste. Usando mocks fica mais fácil gerar situações que podem ser raras no ambiente real, por exemplo para o teste de blocos except ou testes condicionais if. Ainda ocorrem casos em que os objetos (que podem ser blocos de código) ainda não foram desenvolvidos ou oferecem respostas muito lentas para efeito de teste. Com esses objetos é possível verificar se e como um método foi chamado, e com qual frequência.

O módulo unittest inclui um subpacote chamado unittest.mock com ferramentas úteis para essa simulação. Ele também oferece uma função patch() que substitui os objetos reais no código por instâncias mocks. patch() pode ser usado como um decorador ou gerenciador de contexto, facilitando a escolha de qual escopo será simulado. Ao final do teste patch() retornará no código as referências aos objetos originais.

O objeto mock

Um objeto mock pode ser instanciado e a ele podemos atribuir métodos e propriedades.

from unittest.mock import Mock
mock = Mock()
print(mock)
<Mock id='140292494179968'>

# ao objeto podemos atribuir métodos e propriedades
mock.propriedade
mock.metodo()

Além da classe unittest.mock (que é a base das classes simuladas) o módulo também contém uma subclasse unittest.mock.MagicMock que fornece implementações de vários métodos mágicos como .__len__(), __str__() e .__iter__().
Por exemplo, gravamos o arquivo dia_semana.py, que imprime fim de semana se o dia for sábado ou domingo, e dia da semana para os demais dias.

from datetime import datetime
def is_fds():
    dia_semana = datetime.today().weekday()
    return dia_semana > 4

print('fim de semana' if is_fds() else 'dia da semana')

O módulo datatime retorna weekday() = 0 para segunda feira, weekday() = 5, 6 para sábado e domingo. O resultado desse código depende do dia em que está sendo executado. Para uma execução feita na terça feira temos:

$ python dia_semana.py
# é impresso no console
dia da semana

É claro que seria interessante testar o código para outros dias, sem ter que esperar a data correta, nem alterar o relógio do computador. Para fazer isso fazemos um mock de datetime.

import datetime
from unittest.mock import Mock

# fixamos 2 dias para serem usados no teste
ter = datetime.datetime(year=2022, month=3, day=1)  # terça feira (1)
sab = datetime.datetime(year=2022, month=3, day=5)  # sábado (5)

# Mock datetime para controlar a data
datetime = Mock()

def is_fds():
    dia_semana = datetime.datetime.today().weekday()
    return dia_semana > 4

# força datetime para retornar a data em ter (terça feira)
datetime.datetime.today.return_value = ter
# teste para dia = terça
print('fim de semana' if is_fds() else 'dia da semana')

# força datetime para retornar a data em sab (sábado)
datetime.datetime.today.return_value = sab
# teste para dia = sábado
print('fim de semana' if is_fds() else 'dia da semana')

Agora, ao executar o script temos duas respostas:

$ python dia_semana.py
dia da semana
fim de semana

Nesse exemplo, quando fazemos datetime = Mock() tornamos datetime.datetime.today um método simulado, que pode receber a propriedade datetime.datetime.today.return_value a critério do programador. Com isso o método interno .today() retorna a data especificada.

Bibliografia

Livros:

  • Ceder, Vernon; Mcdonald, Kenneth: The Quick Python Book, 2nd. Ed., Manning, Greenwich, 2010.
  • Hunt, John: Advanced Guide to Python 3 Programming, Springer, Suíça, 2019. Disponível em Academia.edu.

Sites:

todos eles visitados em março de 2020.