Clase 3

Descargar como docx, pdf o txt
Descargar como docx, pdf o txt
Está en la página 1de 26

Introducción a Python

Por supuesto, si quieres aprender realmente un lenguaje de programación


debes hacer eso, programar por un tiempo. Aquí vamos a asumir que tienes
conocimiento de programación, aunque sea en algún lenguaje distinto de
Python. Si nunca has programado antes, te vamos a recomendar algunos
libros y tutoriales.

Si te atreves con el inglés, en esta misma plataforma tienes un curso que te


ayudará a empezar desde 0 con Python:

 Introduction to Python: Absolute Beginner (edX).

Si el inglés no es lo tuyo, te recomendamos los siguientes libros y tutoriales


(algunos son de pago).

 Python 3 para no programadores (Segunda edición).

 Curso Maestro de Python 3: Aprende Desde Cero (Udemy).

 Curso de Python Básico Gratis (Web Código Facilito).

En el resto de esta explicación, las palabras importantes las resaltaremos,


así las puedes reconocer fácilmente. También presta atenciónporque, para
intentar ser breves, algunas cosas las introduciremos directamente con
ejemplos de código y solo las comentaremos brevemente.
Propiedades de Python
 Python está orientado a objetos, es decir, todo valor es un objeto con una
clase asociada.

 Las variables y expresiones en Python tienen un tipo, también conocido


como clase.

 El tipo de una variable se determina dinámicamente, lo cual supone que no


hace falta declarar variables.

 En este ejemplo de código puedes ver la asignación de dos variables: en a


(nombre) se asigna un entero (clase) y en b (nombre) un string (clase).

>>> a = 10
>>> type(a)
<class 'int'>
>>> b = 'cadena'
>>> type(b)
<class 'str'>
>>> a+b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

La forma más simple de conocer el valor de una variable es usar el


comando print.

Además, Python es sensible a la diferencia entre mayúsculas y


minúsculas (case sensitive). En este ejemplo, "VAR", "Var" y "var" son tres
variables diferentes:

>>> VAR = "todas mayúsculas"


>>> Var = "solo la primera"
>>> var = "ninguna es mayúscula"
>>> VAR
'todas mayúsculas'
>>> Var
'solo la primera'
>>> var
'ninguna es mayúscula'

Esto es muy importante, porque es fácil que nos equivoquemos al principio


con esto.
Obtener ayuda
En Python la ayuda siempre está disponible a través del intérprete. Si quieres
saber cómo funciona un objeto, todo lo que tienes que hacer es llamar “help
(<objeto>)”

>>> help(5)
Help on int object:

class int(object)
| int(x=0) -> integer
| int(x, base=10) -> integer
|
| Convert a number or string to an integer, or return 0 if no
arguments are given.
| If x is a number, resturn x.__int__(). For floating point numbers,
this
| truncates towards zero.
|
| If x is not a number or if base s given, then x must be a string,
bytes, or
| bytearray instance representing an ineger literal in the given
base. The literal
| can be preceded by '+' or '-' and be surrounded by whitespace. The
base defaults
| to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the
base from the
| string as integer literal.
| >>> int('0b100', base=0)
| 4
|
| Methods defined here:
|
| __abs__(self, /)
| abs(self)
| __add__(self, value, /)
| Return self+value.

(La ayuda te muestra realmente más información que la reflejada en el


código anterior, pero no vamos a enseñarte todo porque no es relevante.)

Sintaxis
Python no tiene un carácter especial para indicar el fin de una sentencia
como el lenguaje C, que utiliza un punto y coma (;). Sin embargo, para indicar
que un conjunto de instrucciones forma un bloque, se deben poner
con indentación. Por ejemplo:

if una_condicion:
instruccion 1
instruccion 2
instruccion 3
instruccion 4

En este ejemplo, si se cumple la condición una_condicion, se ejecutarían las


instrucciones 1 a 3, y a continuación la 4.

La forma más habitual de poner comentarios es con el símbolo almohadilla


(#) y van hasta el final de la línea. También se pueden poner comentarios de
múltiples líneas encerrando el texto entre comillas triples.

Se asignan valores a variables con el símbolo igual (=) y comparamos


igualdad con dos iguales (==), mientras que la exclamación de cierre y un
igual (!=) consulta desigualdad.

# asigno el valor 3 a la variable mi_variable


mi_variable = 5

"""El siguiente codigo te puede parecer


tonto, pero piensa que es solo un ejemplo"""
if mi_variable == 5:
print("es igual a 5")

Cadenas (strings)
Para las cadenas se pueden usar comillas simples (') o dobles (").

una_cadena = "ejemplo de cadena"


otra_cadena = 'otro ejemplo'

Tipo de Datos
Las estructuras de datos disponibles en Python
son: listas, tuplas y diccionarios.

 Las listas son como arrays de una dimensión, pero se pueden construir listas
de listas.

 Los diccionarios son arrays asociativos, también llamados tablas hash.


 Las tuplas son arrays de una dimensión inmutables.

En todos estos casos, los elementos contenidos pueden ser de cualquier


tipo; por lo tanto, se pueden mezclar, por ejemplo, enteros con cadenas de
caracteres.

Recuerda que en la mayoría de lenguajes de programación el índice del


primer elemento es siempre 0. Por su parte, los índices negativos se usan
para referenciar los elementos de una lista contando desde el final: -1 es el
último elemento.

ejemplo = [1, ["otra", "lista"], ("una", "tupla")]


mi_lista = ["Primer elemento", 2, 3.14]
mi_lista[0] = "Nuevo primer elemento" # estamos cambiando el valor del primer
elemento
print(mi_lista[-1]) # imprime 3.14

mi_diccionario = {"clave1": "valor1", "pi": 3.14, 1:2}


print(mi_diccionario["pi"]) #imprime 3.14
print(mi_diccionario[1]) # imprime 1
# recuerda que el índice de un diccionario es una clave, no una posición
Sentencias de Control de Flujo y
Funciones
Sentencias de control de flujo
Las sentencias de control de flujo son if, while y for. En general, se
usa for para iterar sobre los miembros de una lista. Para obtener una lista
de números (sobre los que iterar), se utiliza range(<número>).

lista_numeros = range(10) #lista del 0 al 9


for numero in lista_numeros:
# verifica si numero pertecene a una tupla
if numero in (1, 3, 5, 7, 9):
printf("es impar")
elif numero in (2, 4, 6, 8):
print("es par")
else:
print("debe ser el 0")

Funciones
Las funciones son declaradas con la palabra clave def.

def una_funcion(arg1, arg2):


"""comentario con una descripcion de la funcion"""
print("Soy la funcion una_funcion con argumentos")

Hay muchos conceptos adicionales básicos a Python (como por ejemplo las
clases y el manejo de excepciones), pero que escapan fuera del alcance de
este curso. Recomendamos revisar la lista de fuentes para aprender Python
que dimos al comienzo del apartado.
Implementar el Servidor Web en
Python
Como ya hemos dicho varias veces, una aplicación web es un diálogo entre
una componente llamada cliente y otra componente llamada servidor. Este
diálogo se produce utilizando el protocolo HTTP: es el "lenguaje" en el que
ambas componentes se comunican. Este lenguaje no es el lenguaje de
programación en el que las componentes están implementadas. Cada una
estará implementada,
normalmente, en un lenguaje
diferente. Es como dos
personas que hablan idiomas
diferentes, una castellano y
otra alemán, que para
comunicarse entre sí hablan en
un tercer idioma: el inglés.

El cliente normalmente es
un navegador web; ya hay
muchos de ellos disponibles,
casi todos gratuitos, así que no
será necesario que nos preocupemos de su implementación. Nuestro
problema será la implementación del servidor, que haremos en Python. Y
nuestro servidor se comunicará con los clientes utilizando ese lenguaje
común HTTP.

Si bien ya conocemos el lenguaje que utilizan para comunicarse, todavía no


sabemos qué mensajes se envían. El cliente envía mensajes solicitando recursos, a
los que hace referencia a través de una URL. Por ejemplo, la siguiente URL
hace referencia a la página de horarios de un supuesto cine:
http://www.cine_de_mi_barrio.com/horarios.html

Se puede observar que esta URL en particular apunta a un fichero HTML:


este es el tipo de recurso que normalmente solicita el cliente, porque es la base de casi
todos los documentos alojados en la web. Incluso cuando el recurso que
solicita el cliente no existe previamente (o al menos no existe un documento
HTML que lo represente), el servidor genera dinámicamente una respuesta
en forma de documento HTML. Es prácticamente lo único que son capaces
de mostrar los navegadores.

Si el cliente, además de describir el recurso al que quiere acceder,


necesita enviar al servidor parámetros para que el servidor pueda adaptar su
respuesta, estos parámetros pueden ser parte de la URL (si el cliente utiliza
el método GET del protocolo HTTP) o estar incrustados dentro de la
solicitud HTTP (si el cliente utiliza el método PUT).

En todo caso, para que el cliente no tenga que especificar esta petición
HTTP "a mano", normalmente en el navegador se le ofrecen formas
amigables de realizar las peticiones.

Así, cuando el cliente pincha un enlace (link), el navegador construye una


petición HTTP que contenga una URL que apunte al recurso (documento)
representado por ese enlace. De la misma forma, si tiene que enviar
parámetros adicionales, se le ofrece un medio relativamente sencillo para
especificarlos: los formularios, de los cuales ya te hablamos en el apartado
de HTML.

Nuestro actual Servidor


Para facilitar el trabajo de implementar el servidor, vamos a basarnos en la
biblioteca Flask. Al comienzo del curso ya vimos cómo descargarnos esa
biblioteca e incorporarla al entorno de trabajo.

El siguiente código es parte de lo que hemos usado hasta el momento


para atender las peticiones generadas desde el código HTML de las unidades anteriores,
del cual te dijimos que no prestases inicialmente atención.

from flask import Flask, request


app = Flask(__name__)

@app.route('/', methods="['GET']")
def index():
return app.send_static_file('index.html')

@app.route('/home', methods="['GET']")
def home():
return app.send_static_file('home.html')

@app.route('/login', methods="['GET']")
def login():
return app.send_static_file('login.html')

@app.route('/singup', methods="['GET']")
def signup():
return app.send_static_file('signup.html')
Don't panic! Es normal que no entiendas nada. Vamos a ir viendo poco a
poco el sentido de este código, aprenderemos a hacerlo por nosotros mismos
e incluso a mejorarlo bastante.
Respondiendo Peticiones
Antes de comenzar a trabajar en el sevidor de nuestra red social, vamos a
trabajar con uno muy sencillo que simplemente nos muestra un mensaje por
pantalla.

Utilizaremos como base el fichero server-01-initial.py, así que guárdalo ya en


tu carpeta de descargas. Este es su código:

# -*- coding: iso-8859-15 -*-

import sys

from flask import Flask


app = Flask(__name__)

@app.route('/')
def index():
"""
It process the '/' url.
:return: basic HTML
"""
return "Hello Word! This is the answer of the server"

# start the server with the 'run()' method


if __name__ == '__main__':
if sys.platform == 'darwin': # different port if running on
MacOsX
app.run(debug=True, port=8080)
else:
app.run(debug=True, port=80)

En el vídeo de más abajo te mostramos cómo añadir el fichero a un proyecto


recién creado, U3, pero vamos a analizarlo primero.

Inicialización
Para que nuestro servidor Python pueda recibir las peticiones de los clientes,
se debe realizar una instancia de aplicación. Para ello normalmente se crea
una instancia de la clase Flask (previa importación para que el intérprete de
Python la conozca). Este es el código a utilizar:
from flask import Flask
app = Flask(__name__)

Esta instancia debe recibir un nombre de aplicación; lo más normal es pasar el


parámetro __name__ (cuidado, son 2 guiones bajos (_) antes y después de
"name"), que contiene el nombre del programa que se está ejecutando.

Ejecución del servidor


Para lanzar el programa, que finalmente será el que esté esperando las
peticiones del cliente, se utiliza el método run de la instancia de Flask
antes creada y que hemos almacenado en la variable app.

if __name__ == '__main__':
app.run(debug=True)

La condición __name__ == '__main__' nos garantiza que el


servidor únicamente se iniciará cuando el script sea ejecutado directamente, no cuando sea
importado por otro script.

Una vez que el servidor comience a ejecutarse, se meterá en un bucle


esperando por peticiones y atendiéndolas a medida que llegan. Esto
continuará hasta que la aplicación sea detenida (por ejemplo, tecleando Ctrl-
C en una consola, o dando al botón de stop en PyCharm).

Hay varios argumentos que se pueden pasar al método app.run. En el


ejemplo de arriba le decimos que active el modo debugger, lo que siempre es
conveniente mientras estamos desarrollando. En el código que os damos,
además, se utiliza otro parámetro: port.

if __name__ == '__main__':
if sys.platform == 'darwin': // different port if running on
MacOsX
app.run(debug=True, port=8080)
else:
app.run(debug=True, port=80)

Cuando se ejecute este código, primero se verificará si la plataforma sobre


la que está ejecutando es el sistema operativo de Apple MacOs (en cuyo
caso el valor de la variable de sistema sys.platform será ‘darwin’). Si la
respuesta es positiva, inicia el servidor con indicación de que espere
peticiones en el puerto 8080; si es negativa, el servidor recibirá peticiones a
través del puerto 80.

Si no es especifica el puerto, se asume el puerto por omisión del protocolo


HTTP: el puerto 80.
Rutas y Funciones
Ya hemos analizado del fichero server-01-initial.py, el código de
inicialización y el código de ejecución, pero nos hemos dejado algo por
mencionar:

# -*- coding: iso-8859-15 -*-

import sys

from flask import Flask


app = Flask(__name__)

@app.route('/')
def index():
"""
It process the '/' url.
:return: basic HTML
"""
return "Hello Word! This is the answer of the server"

# start the server with the 'run()' method


if __name__ == '__main__':
if sys.platform == 'darwin': # different port if running on
MacOsX
app.run(debug=True, port=8080)
else:
app.run(debug=True, port=80)

Como hemos visto en el vídeo anterior, el mensaje que mostraba en el


navegador es el que está definido en esta parte del código. Como hemos
visto, lo que estamos haciendo es definir el mensaje a mostrar cuando se
llama a la URL especificada (las triples comillas deñalan comentarios).

Retornos de Funciones mas


Complejas
En la práctica anterior hemos creado una nueva ruta que nos mostraba una
cadena de caracteres, al igual que ya mostraba la función de la ruta raíz. Por
su parte, el servidor de nuestra red social referencia a un documento HTML.
El paso intermedio entre estas dos formas de referencias es indicar el
propio documento HTML en el retorno.

Opciones de Ruta
El mecanismo de asociar URLs con funciones (las rutas) permite describir
más situaciones. Por ejemplo, es posible especificar que una ruta solo se
asocie con una URL cuando se utilice un método específico dentro del
protocolo HTTP; es decir, según se la petición se haga con GET o con POST.

Recuerda que, en general, se utiliza el método POST para enviar datos de


formularios, mientras que GET se utiliza en las peticiones que no envían
datos.
Así, la ruta "@app.route('/')" para la URL '/' vista hasta el momento se podría
especificar también así:

@app.route('/', methods=['GET'])

En este caso, la ruta solo se aplicará cuando la petición HTTP solicite la URL
'/' utilizando el método GET.

Si quisiéramos, en cambio, que se active cuando se utilice el método POST,


la especificación sería:

@app.route('/', methods=['POST'])

Si no especificamos si debe aplicarse para GET o POST (como hicimos en los


primeros ejemplos), se entiende que se debe aplicar a ambos métodos. Esto
también se puede hacer explícito, para que quede más claro, de la siguiente
forma:

@app.route('/', methods=['GET', 'POST'])

Varias URLs a la misma función


Otra variante útil es poder asociar varias rutas con la misma función. Por
ejemplo, es bastante normal que la url '/index' sea un alias de la raíz de un
sitio web (url '/').

Por lo tanto, se puede definir lo siguiente (se muestra solo el encabezado de


la función):

@app.route('/', methods=['GET'])
@app.route('/index', methods=['GET'])
def index():
Veremos ejemplos de estas formas de definir rutas cuando avancemos en el
desarrollo de nuestra aplicación.
Procesamiento de Ficheros HTML
Separados
En ejemplos anteriores hemos visto que una forma de responder a una
petición HTTP es que la función asociada retorne directamente código
HTML. Esto sin embargo tiene, al menos, dos inconvenientes:

1. El primero está relacionado con la legibilidad del código. Excepto en


ejemplos triviales, normalmente el código HTML que hay que retornar es
complejo, y tenerlo escribo dentro del código Python dificulta entender tanto
el código HTML como el propio código Python.

2. El segundo inconveniente está relacionado con la llamada “separación de


responsabilidades”, un principio muy valorado en el desarrollo de
aplicaciones.

El principio de separación de responsabilidades (separation of concerns en


inglés), en este caso, implica que se debería separar (en ficheros distintos) el
problema de procesar las peticiones, es decir, resolver qué hacer en cada caso
(también denominada “lógica del negocio”), de la información que se
presentará como respuesta (página HTML). Esta separación facilita el
mantenimiento de las aplicaciones y, en el caso del trabajo en equipo,
facilita que distintos miembros del equipo se hagan cargo de estas distintas
responsabilidades.
Por este motivo, normalmente no escribiremos el código HTML dentro de los
propios ficheros Python, sino que lo haremos en ficheros independientes (con
extensión HTML) y desde el código Python haremos referencia a ellos. Para
esto Flask nos da una función con este objetivo, llamada “send_static_file”
(técnicamente, es un método de la clase Flask, cuya instancia está
referenciada por la variable app).

Por ejemplo, el código que antes vimos:

@app.route('/ex2')
def function_for_ex2():
"""
It process the '/' url.
:return: basic HTML for /ex2
"""
return '<!DOCTYPE html> ' \
'<html lang="es">' \
'<head>' \
'<title> This is the page title </title>' \
'</head>' \
'<body> <div id ="container">' \
'<h1>Example of HTML Content</h1>' \
'Return to the homepage by clicking <a href="/">here</a> </html>' \
'</html>'

Se convertiría en:

@app.route('/ex2')
def function_for_ex2():
"""
It process the '/' url.
:return: basic HTML for /ex2
"""
return app.send_static_file("ejemplo.html")

Donde el fichero “ejemplo.html” contendría el código HTML correspondiente.

Importante: para que esto funcione, el fichero a procesar (ejemplo.html en


este caso) debe estar ubicado dentro de la carpeta static.
Objeto Request
Cuando una aplicación Flask recibe una petición desde un cliente, necesita
crear algunos objetos y ponerlos a disposición de la función que vaya a
gestionar la petición. Un buen ejemplo es el objeto request que, como su
nombre indica, encapsula los datos de la petición http enviada por el cliente.

Para evitar tener que agregar argumentos a las funciones para cada uno de
los objetos de este tipo que deban estar disponibles, Flask los convierte
en objetos de contexto: significa simplemente que están disponibles dentro de las
funciones que atienden peticiones HTTP.

De esta forma se puede escribir el siguiente código:

from flask import request

@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your browser is %s</p>' % user_agent

En este ejemplo, a través del objeto request se recupera la información


sobre el tipo de navegador (user_agent) que está usando el usuario que hace
la petición.

Copia el código anterior en el último fichero Python utilizado, server-


02.py y modifícalo: ponle una ruta distinta (por ejemplo ‘/agente’) y un
nombre de función distinto (por ejemplo agente()).

Ahora abre en tu navegador la URL http://127.0.0.1:80/agente (o


http://127.0.0.1:8080/agente según tengas configurado el arranque del
servidor) y mira qué puede saber un servidor sobre el navegador que utilizas.
Ejemplo del Objeto Request
En nuestro código para el servidor ya hemos usado el objeto request. Mira el
siguiente fragmento:

@app.route('/processLogin', methods=['POST'])
def process_login():
missing = []
fields = ['email', 'passwd', 'login_submit']
for field in fields:
value = request.form.get(field, None)
if value is None:
missing.append(field)
if missing:
return "Warning: Some fields are missing"

return '<!DOCTYPE html> ' \


'<html lang="es">' \
'<head>' \
'<link href="static/css/my-socnet-style.css" rel="stylesheet"
type="text/css"/>' \
'<title> Home - SocNet </title>' \
'</head>' \
'<body> <div id ="container">' \
'<a href="/"> SocNet </a> | <a href="home"> Home </a> | <a href="login">
Log In </a> | <a href="signup"> Sign Up </a>' \
'<h1>Data from Form: Login</h1>' \
'<form><label>email: ' + request.form['email'] + \
'</label><br><label>passwd: ' + request.form['passwd'] + \
'</label></form></div></body>' \
'</html>'

Este ejemplo es muy importante, así que iremos poco a poco.

En primer lugar, debemos analizar en qué situaciones se ejecutará este


código. Si miras la ruta asociada, sabemos que se activará cuando el cliente
navegue a la URL /processLogin. Ahora bien, si miras el código de fichero
login.html, verás que es un formulario que en el campo actiontiene
justamente ese valor y que como método de solicitud HTTP tiene
configurado post. Por lo tanto, al pulsar “Submit” en el formulario anterior,
se terminará ejecutando la función que se muestra arriba.
Dentro del código, lo primero que se hace es construir una lista con los
nombres de los campos input del formulario. Y luego, el cuerpo del
bucle for se ejecuta una vez para cada nombre de campo.

Precisamente, lo que queremos hacer es verificar si cada campo existe en el


formulario y tiene un valor distinto de nulo (None en Python). Esto se hace a través de
la propiedad form del objeto request (en request.form se puede acceder a
los datos provenientes de un formulario, cuando la solicitud HTTP es
resultado de enviar precisamente un formulario). Si este campo no tiene un
valor asociado (es decir, si el usuario no completó el campo antes de enviar
el formulario), se pone en la lista de campos faltantes.

Hay otra forma de obtener el valor del campo de un formulario, que usando el
comando request.form[‘nombre’], donde entre comillas simples (‘ ‘) se pone
el nombre del campo cuyo valor se quiere acceder. Pero cuando se hace de
esta forma, si el campo no tiene un valor o no existe, la ejecución del
comando dará un error. Por eso en nuestro caso, como no sabemos si los
campos tienen un valor asociado, usamos get, que en caso de que no haya
un valor asociado retorna el segundo argumento (None, en nuestro caso).
Después de for, si la lista missing no está vacía, se muestra el
correspondiente mensaje de error al usuario.

Si todo ha ido bien, se retorna un nuevo documento HTML, donde los valores
de los campos del formulario se han utilizado para personalizar lo que se
responde al usuario.
Introducción a la Codificación de
Templates
Seguramente has notado que hay algunas cosas realmente feas en el código
de nuestro servidor, como el siguiente fragmento:

@app.route('/processLogin', methods=['GET', 'POST'])


def processLogin():
missing = []
fields = ['email', 'passwd', 'login_submit']
for field in fields:
value = request.form.get(field, None)
if value is None:
missing.append(field)
if missing:
return "Warning: Some fields are missing"

return '<!DOCTYPE html> ' \


'<html lang="es">' \
'<head>' \
'<link href="static/css/my-socnet-style.css" rel="stylesheet"
type="text/css"/>' \
'<title> Home - SocNet </title>' \
'</head>' \
'<body> <div id ="container">' \
'<a href="/"> SocNet </a> | <a href="home"> Home </a> | <a href="login">
Log In </a> | <a href="signup"> Sign Up </a>' \
'<h1>Data from Form: Login</h1>' \
'<form><label>email: ' + request.form['email'] + \
'</label><br><label>passwd: ' + request.form['passwd'] + \
'</label></form></div></body>' \
'</html>'

¿Por qué decimos que este código es feo? Porque mezcla Python con HTML,
y el resultado no solo es difícil de leer sino que no cumple el principio de
separación de responsabilidades. Ya hemos visto que una solución a esto
sería colocar el código HTML en un fichero independiente. Imagina que se
pudiera hacer algo por el estilo:

@app.route('/processLogin', methods=['GET', 'POST'])


def processLogin():
missing = []
fields = ['email', 'passwd', 'login_submit']
for field in fields:
value = request.form.get(field, None)
if value is None:
missing.append(field)
if missing:
return "Warning: Some fields are missing"
return app.send_static_file("process_login.html")

De esta forma el código estaría más claro, y separado el Python del HTML.
Pero … ¿y cómo sería el fichero HTML? En concreto, ¿cómo se sabría dentro
del fichero HTML qué dirección de mail se debe poner en esta línea, por
ejemplo?

<form><label>email: ' + ¿¿¿ mail ???

Por lo tanto, tenemos un conflicto: nos gustaría que el código HTML esté
separado del Python, pero el código HTML debe acceder de alguna forma a
valores disponibles en Python y generar HTML de acuerdo a esos valores.
Para esto utilizamos los templates (“moldes” sería una buena traducción,
pero toda la comunidad de desarrolladores se refiere a ellos
como templates).

Un template es un fichero que contiene texto para generar una respuesta,


con variables para las partes dinámicas, cuyo valor solo será conocido en el contexto
de una petición. El proceso de reemplazar las variables por los valores reales y
retornar un texto final es llamado rendering (dice el traductor online ese-
que-tú-ya-sabes que se puede traducir como representación; pero, de nuevo,
todo el mundo utiliza la palabra inglesa, y si dices “representar el molde”
nadie sabrá de qué estás hablando). Para la tarea de hacer rendering de
templates, Flask usa un intérprete de templates llamado Jinja2. Pero
tranquilo, lo hace automáticamente por nosotros, que podemos desarrollar
una aplicación completa sin saber que por debajo se usa algo
llamado Jinja2 ;).

Ejemplo de template
Un template sencillo podría ser:

<h1>Hello, {{ name }}!</h1>

Las dobles llaves ({{ }}) se usan para indicar que lo que se escribe dentro es
el nombre de una variable, que se debe reemplazar en el momento de
hacer rendering. Es decir, el documento HTML final no tendrá la cadena
“name”, sino el valor representado por la variable “name”.
Ahora nos falta ver cómo decirle a Flask que para responder una petición
queremos que procese un template, con unos valores específicos asociados
a las variables. Para el caso anterior, esto se podría hacer con el siguiente
código:

# ...

@app.route('/greetings')
def user(name):
return render_template(‘saludo.html', request.for[‘name’])

Este código supone que el template está en un fichero “saludo.html” y que el


nombre se debe obtener a partir del campo name del formulario enviado.

Un último detalle importante: para que esto funcione, el fichero


del template debe estar dentro del directorio app/templates.
SocNet Templates
Vamos a ver un ejemplo de nuestra aplicación SocNet. Para ello, vamos a
mejorar una funcionalidad ya implementada. Cuando el usuario olvide un
campo de un formulario, en vez de un simple mensaje sin formato, vamos a
generar código HTML similar al usado para las otras páginas con el mensaje
de error correspondiente. De esa forma, nuestra aplicación será más
consistente, ya que todas las páginas tendrán el mismo estilo, incluso los
mensajes de error.

Por supuesto, podríamos hacer esto generando un fichero HTML para cada
combinación posible de campos faltantes: deberíamos tener un fichero con
un mensaje específico de error para el campo email, otro para el campo
passwd, otro para login_submit, otro para cuando falte el primero y el
segundo… Si recuerdas tus clases de álgebra, sabrás que con solo 3 campos
hay 7 combinaciones posibles (23-1), lo que implica que si fueran tan solo 4
campos, ya habría 15 combinaciones posibles (24-1), ¡cada una con su propio
fichero HTML! A propósito, siempre es -1 porque hay combinación “buena”,
donde todos los campos están presentes y por lo tanto no hace falta generar
mensaje de error.

Si los cálculos no son lo tuyo, no importa: lo importante es que veas que no


se puede generar un fichero para cada combinación posible. Entonces, de
nuevo, los templates viene a nuestro rescate. Mira el siguiente código:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link href="static/css/my-socnet-style.css" rel="stylesheet" type="text/css"/>
<title>Home - SocNet</title>
</head>
<body>
There are some missing fields:
<ul>
{% for input in inputs %}
<li> {{ input }}</li>
{% endfor %}
</ul>
<div>
<form action="{{ next }}" method="get">
<div class="inputs">
<input id="next" name="next" type="submit" value="Continue"/>
</div>
</form>
</div>
</body>
</html>

Si lees con atención, entenderás que este template generará un HTML con
un mensaje de error (“There are some missing fields”), con la lista de campos
faltantes. Para eso usamos la estructura for (importante: el texto entre {% %}
es código Python que debe ser ejecutado) para iterar sobre cada uno de los
elementos de la variable inputs. El valor de esta variable se asigna en el
momento de solicitar el rendering. Además, se crea un botón "Continuar"
para que, una vez leído el mensaje, se vaya a la página que corresponda
(indicada por la variable next).

Así que el código de nuestra función login será:

@app.route('/login', methods=['GET', 'POST'])


def login():
"""
It process '/login' url (form for login into the system)
:return: firstly it will render the page for filling out the login data.
Afterwards it will process these data.
"""
if request.method == 'POST':
missing = []
fields = ['email', 'passwd', 'login_submit']
for field in fields:
value = request.form.get(field, None)
if value is None or value == '':
missing.append(field)
if missing:
return render_template('missingFields.html', inputs=missing,
next=url_for("login"))
return load_user(request.form['email'], request.form['passwd'])
return app.send_static_file('login.html')

La primera línea a analizar es return render_template('missingFields.html',


inputs=missing, next=url_for("login")), donde se solicita el rendering del
template 'missingFields.html', y se proveen los valores para las dos
variables, inputs y next. Para esta última se utiliza otra función auxiliar de
Flask, url_for, que nos retorna justamente eso: la cadena que representa la
url completa cuando queremos acceder a /login en nuestra aplicación
(devolverá algo similar a http://128.0.0.1:8080/login, según sea la
configuración de nuestro servidor).
Además, este código muestra otra técnica muy útil: si se utiliza el método
GET, se asume que se quiere el formulario para ingresar los
datos (app.send_static_file(‘login.html’)). Si se utiliza el método POST, se
asume que se envían los datos del formulario para ser procesados (se
ejecuta load_user(request.form['email'], request.form['passwd']), pero el código
de la función load_user lo veremos en la próxima unidad).

En el código este podrás encontrar los templates tanto para los errores de
falta de campos ('missingFields.html’) como uno para errores
genéricos (‘error.html’).

# -*- coding: iso-8859-15 -*-


@app.route('/ex1')
def function_for_ex1():
"""
It process the '/ex1' url.
:return:text for /ex1
"""
return "Content of the url /ex1"

@app.route('/ex2')
def function_for_ex2():
""""
It process the '/' url.
:return:basic HTML for /ex2
"""
return '<!DOCTYPE html>' \
'<html lang = "es">' \
'<head>' \
'<title> This is the page title </title>' \
'</head>' \
'<body> <div id ="container">' \
'<h1>Example of HTML Content</h1>' \
'Return to the homepage by clicking <a href ="/">here</a> </html>' \
'</html>'

También podría gustarte