Clase 3
Clase 3
Clase 3
>>> 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'
>>> 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.
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
Cadenas (strings)
Para las cadenas se pueden usar comillas simples (') o dobles (").
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.
Funciones
Las funciones son declaradas con la palabra clave def.
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.
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.
@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.
import sys
@app.route('/')
def index():
"""
It process the '/' url.
:return: basic HTML
"""
return "Hello Word! This is the answer of the server"
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__)
if __name__ == '__main__':
app.run(debug=True)
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)
import sys
@app.route('/')
def index():
"""
It process the '/' url.
:return: basic HTML
"""
return "Hello Word! This is the answer of the server"
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.
@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.
@app.route('/', methods=['POST'])
@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:
@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")
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.
@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<p>Your browser is %s</p>' % user_agent
@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"
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:
¿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:
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?
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).
Ejemplo de template
Un template sencillo podría ser:
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’])
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.
<!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).
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’).
@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>'