Mostrando entradas con la etiqueta path. Mostrar todas las entradas
Mostrando entradas con la etiqueta path. Mostrar todas las entradas

martes, 28 de febrero de 2023

12.- Desarrollo Práctico de una aplicación. Creación Proyecto, Aplicación, Primeras vistas y Plantillas.

Vamos a crear una aplicación completa de Django y a la vez profundizando más en varios conceptos. La aplicación que crearemos tendrá cinco vistas:

  • Inicio
  • Servicios
  • Tienda 
  • Blog
  • Contacto
Todo lo que sigue ya lo hemos visto en capitulos previos, asi que no me detendré a comentarlo. Comenzaremos creando el proyecto con el nombre que queramos, en mi caso usaré PracticaDjango:


$ django-admin startproject PracticaDjango

Esto nos creará un directorio con la siguiente estructura:

PracticaDjango       > Directorio Padre 
    manage.py        > Archivo de gestión de Django.
    PracticaDjango --> Es un directorio

Es siguiente caso es crear la aplicación que gestionará la aplicación web. Así que entramos en el directorio padre y creamos la aplicación:

PracticaDjango$ python manage.py startapp Proyecto_web_app

Salida:

PracticaDjango       > Directorio Padre 
    manage.py        > Archivo de gestión de Django.
    PracticaDjango --> Es un directorio
    Proyecto_web_app > El directorio de la app recien creada.

Recuerda que una cosa es el proyecto y otra la aplicación. En Django dentro de un proyecto puedes tener muchas aplicaciones.

Ahora es un buen momento para verificar que nuestro proyecto funciona, así que vamos a la consola y ejecutamos el servidor:

PracticaDjango$ python manage.py runserver

Abrimos el navegador y entramos en localhost:8000 y si todo ha ido bien verás la siguiente pantalla:

pantalla inicio de Django

Seguidamente creamos de forma sencilla las vistas de cada una de las cinco url de la aplicación. Para ello vamos al archivo views.py y para hacer algunas pruebas importamos la clase HttpResponse.

PracticaDjango/Proyecto_web_app/views.py

from django.shortcuts import render, HttpResponse

# Create your views here.

def home(request):
    return HttpResponse('Home')

def servicios(request):
    return HttpResponse('Servicios')

def tienda(request):
    return HttpResponse('Tienda')

def blog(request):
    return HttpResponse('Blog')

def contacto(request):
    return HttpResponse('Contacto')

Ahora nos vamos a registrar las URLS. Importamos las vistas y luego registramos las direcciones.

PracticaDjango/PracticaDjango/urls.py

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home, name='home'),
    path('servicios/', views.servicios, name='servicios'),
    path('tienda/', views.tienda, name='tienda'),
    path('blog/', views.blog, name='blog'),
    path('contacto/', views.contacto, name='contacto'),
]

Volvemos a ejecutar el servidor, si lo hemos cerrado y probamos cada una de las url, para ver que todas las vistas funcionan.

Ej para la vista blog.

vista blog funcionando correctamente


Reorganización de las url para un mejor funcionamiento.


Hay que tener en cuenta que nosotros creamos un proyecto en Django y este puede tener varias aplicaciones. De igual forma es muy frecuente que una aplicación Django la puedas reaprovechar en diferentes proyectos. Hasta ahora registramos nuestras Urls dentro del archivo urls.py que estaba en PracticaDjango. Pero imaginaros si tuviéramos que registrar en el las urls, no de una única aplicación, sino de tres, cuatro o quince aplicaciones. En este caso este archivo tendría muchísimo código y nos seria complicado saber que urls son de una aplicación y cual de otra. Así que lo bueno es que las urls de cada aplicación estén dentro de su directorio. 

Para ello seguimos las instrucciones que ya nos indica Django en el archivo urls.py del Proyecto.

1.- Creamos un archivo llamado urls.py pero dentro de la aplicación.

2.- Luego dentro de ese archivo importamos el path

PracticaDjango/Proyecto_web_app/urls.py

from django.urls import path

3.- A continuación importamos las vistas de la aplicación.

PracticaDjango/Proyecto_web_app/urls.py

from django.urls import path
from . import views

4.- Tenemos que trabajar con la lista urlpatterns y para no tener que escribir tanto, vamos al url del proyecto, copiamos el urlpatterns y lo pegamos aquí. Lógicamente quitamos la vista admin que no corresponde a la aplicación, sino al proyecto.

PracticaDjango/Proyecto_web_app/urls.py

from django.urls import path
from Proyecto_web_app import views

app_name = "Proyecto_web_app"

urlpatterns = [
    path('', views.home, name='home'),
    path('servicios/', views.servicios, name='servicios'),
    path('tienda/', views.tienda, name='tienda'),
    path('blog/', views.blog, name='blog'),
    path('contacto/', views.contacto, name='contacto'),
]


'app_name' en Django se utiliza para definir un nombre único para la aplicación, evitando conflictos de nombres con otras aplicaciones que pudiéramos crear luego.

'name' se asigna a cada URL dentro de la aplicación y proporciona una etiqueta única para esta URL en nuestro proyecto.

Estos atributos nos ayudarán a a organizar y referenciar fácilmente las URLs en nuestro código y en las plantillas de Django.  Por ejemplo cuando más adelante tengamos que construir una URL en una plantilla en vez de usar una referencia absoluta para referirnos a la vista 'home' haremos referencia a ella como 'Proyecto_web_app:home'.

Y como hemos movido todas las vistas de la aplicación, en el archivo urls.py del PROYECTO quitamos todo lo que hemos movido y dejamos solo el admin. Quitamos también la importación de las vistas. En definitiva lo dejamos como estaba al principio. Solo nos falta enlazar el url del proyecto con el url de la aplicación, lo haremos a través del path como nos pone Django en la documentación de este mismo archivo, usando el comando 'include' que previamente habremos de importar.

PracticaDjango/PracticaDjango/ulrs.py

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

urlpatterns = [
    path('admin/', admin.site.urls), 
    path('proyecto_web_app/', include('Proyecto_web_app.urls')), 
]

Llegado este punto, tenemos que comprobar que funcionan bien las urls teniendo en cuenta el path que acabamos de escribir. Para acceder a las vistas tendríamos que hacer lo siguiente. Vamos a ver como acceder a una con un ejemplo.

aceso a url tienda
Pero al hacer esto, hemos complicado un poco la url porque tenemos que agregar 'proyecto_web_app', que es el nombre de nuestro proyecto. Si queremos teclear las urls como antes, que sin poner nada nos llevaba a las vistas, tenemos que ir al urls del proyecto y en el path tenemos que dejar vacía la raíz para que no haya que poner nada y así, funcionaría como al principio.

PracticaDjango/PracticaDjango/urls.py 

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('Proyecto_web_app.urls')),  
]

url acortada de la aplicación

Plantillas de las vistas.


Nos vamos a la carpeta de la aplicación y creamos una carpeta llamada 'templates' y dentro de esta carpeta crearemos otra con el nombre también de la aplicación que será donde depositaremos las plantillas html. Tenemos que crear una plantilla para cada una de las vistas. Vamos a hacer una como ejemplo para la vista inicio pero tenemos que crear otras iguales para el resto.

PracticaDjango/Proyecto_web_app/templates/Proyecto_web_app/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>Home</title>
</head>
<body>
    Home
</body>
</html>
Ahora debemos modificar el archivos de las vistas de la aplicación para que renderice las plantillas que habremos creado. Pero para que esto funcione es IMPORTANTE registrar nuestra aplicación. Tenemos que ir al archivo settings.py del proyecto y el la lista de INSTALLED_APPS tenemos que registrar la aplicación.

PracticaDjango/PracticaDjango/settings.py

...
# Application definition

INSTALLED_APPS = [
    # Nuestras aplicaciones.
    'Proyecto_web_app.apps.ProyectoWebAppConfig',
    # Aplicaciones por defecto.
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
...

Podemos aprovechar este mismo archivo para modificar nuestra hora local (TIME_ZONE) y el idioma (LANGUAGE_CODE) como ya vimos en el capitulo 1.

Después de lo anterior, ya solo nos queda modificar el archivo de las vistas de la aplicación para que se rendericen cada una de las vistas cuando se llame a la url correspondiente.

PracticaDjango/Proyecto_web_app/views.py

from django.shortcuts import render, HttpResponse

# Create your views here.

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

def servicios(request):
    return render(request, 'Proyecto_web_app/servicios.html')

def tienda(request):
    return render(request, 'Proyecto_web_app/tienda.html')

def blog(request):
    return render(request, 'Proyecto_web_app/blog.html')

def contacto(request):
    return render(request, 'Proyecto_web_app/contacto.html')

domingo, 6 de septiembre de 2020

Flask 17. Manejo de Cookies en Flask.

 


Anteriormente. Flask 16. Mostrando mensajes en Flask.


Manejo de Cookies en Flask.


Cuando estamos creando una aplicación o proyecto en Flask se nos puede dar el caso de que tengamos que guardar provisionalmente alguna información en el lado del cliente, es decir que necesitemos gestionar algún dato que nos proporciona el usuario pero desde su lado, sin subirlo al servidor. 

¿Quien no conoce el clásico caso de los carritos de la compra de las páginas web de venta de artículos online?. En ellos seleccionas los productos que quieres comprar y los incluyes en el carrito, para más tarde completar el proceso de compra. Incluso si cierras el navegador y vuelves a esa página más tarde, tu carrito de la compra seguirá ahí conteniendo los productos que ya habías seleccionado. 

Esto se consigue con las cookies. Tal como nos pone la wikipedia: 

"Una cookie es un término que hace referencia a una pequeña información enviada por un sitio web y almacenada en el navegador del usuario, de manera que el sitio web puede consultar la actividad previa del navegador." 

En resumen es una pequeña información que guardamos en el navegador del usuario información que no sea sensible y que después desde el lado del servidor podemos consultar.

Para gestionar cookies en Flask tenemos que afrontarlo en dos pasos. En primer lugar tenemos que establecer el contenido de la Cookie y crearla y en un segundo paso recuperar del servidor el valor de esa cookie.

Guardar una Cookie


Para guardar una cookie tenemos que tener presente, que la información que queremos guardar debe viajar y guardarse en el cliente, por lo tanto debemos devolverla en una respuesta, por lo que vamos a utilizar el objeto respuesta para devolver esa información.

Para crear el objeto respuesta utilizaremos el método make_reponse().

 respuesta = make_response('Hemos guardado la cookie')

A este método make_response() le podemos pasar:

  • Una página
respuesta = make_response(redirect('/'))

Depués de guardar la cookie nos devuelve, en este caso, a la página inicial. 

  • Un contenido en texto
respuesta = make_response('Hemos guardado la cookie')

Guarda la cookie y nos muestra un texto en pantalla. 

  • Json.


Por ser lo más sencillo vamos a pasarle un texto ('Hemos guardado la Cookie'). 

Ahora sobre respuesta aplicaremos la cookie  con el método set_cookie()

respuesta.set_cookie(Key="nombre de la cookie", value="valor a guardar", secure = True)

Pongo el argumento "secure = True" ya que sino, FireFox y otros navegadores aunque no lo muestren directamente,  en opciones del desarrollador nos da la siguiente alerta:

"Cookie “galleta” will be soon rejected because it has the “SameSite” attribute set to “None” or an invalid value, without the “secure” attribute. To know more about the “SameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Headers/Set-Cookie/SameSite"

Esto también lo podríamos haber hecho usando las opciones de configuración de Flask con:

SESSION_COOKIE_SECURE = True


Bien, ya solo nos quedará devolver el objeto respuesta como return de la petición.

La aplicación de flask podría quedar de esta forma:

from flask import Flask, make_response
app = Flask(__name__)


@app.route('/')
def home():
    return 'Esta es la pagina inicial'


@app.route('/set_cookie/')
def save_cookie():
    respuesta = make_response('Hemos guardado la cookie')
    respuesta.set_cookie('mi_cookie', 'esto se guarda en la cookie', secure = True)
    return respuesta

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

y si la ejecutamos:


Para ver la cookie podemos ir a herramientas del desarrollador web y dentro de la consola veremos nuestra cookie.




Otra opción es chequear la pestaña de almacenamiento dentro de las herramientas del desarrollador, y allí podrás ver la cookie:






Leer una Cookie.


Para recuperar una cookie del navegador, que no es más que una cadena de caracteres, utilizaremos el objeto request.cookies.get de la siguiente forma:

leer_cookie = request.cookies.get('nombre de la cookie')

Nuestro archivo quedaría de la siguiente forma:

from flask import Flask, make_response, request
app = Flask(__name__)


@app.route('/')
def home():
    return 'Esta es la pagina inicial'


@app.route('/set_cookie/')
def save_cookie():
    respuesta = make_response('Hemos guardado la cookie')
    respuesta.set_cookie('mi_cookie', 'esto se guarda en la cookie')
    return respuesta


@app.route('/get_cookie/')
def read_cookie():
    leer_cookie = request.cookies.get('mi_cookie')
    return f'Contenido de la cookie -> {leer_cookie}'


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

y al ejecutarse:


Las Cookies pueden expirar: Los argumentos max_age y expires.


Por defecto, las cookies expiran cuando el usuario cierra la sesión, esto es en cuanto se cierra el navegador. Para conservar una cookie podemos utilizar los atributos expires o max_age.

Cuando se usan ambos max_age tiene preferencia sobre expires. 

En Lugar de expirar cuando se cierra el navegador, las cookies permanentes expiran en una fecha especifica (expires) o tras un periodo de tiempo especifico en segundos (max_age).

Nota: Cuando se establece una fecha de expiración, la fecha y hora que se establece es relativa al cliente en el que se establece la cookie, no del servidor.

Por ejemplo para poner una cookie que se conserve durante 5 minutos podemos usar la siguiente vista:

Cookie con una duración de 5 minutos.

@app.route('/set_cookie5m/')
def save_cookie5m():
    # make_response = La respuesta será 
    respuesta=make_response('Hemos guardado la cookie')
    # en respuesta ira la cookie 'nombre' 'la cookie en si'
    # La duración es max_age es 5 minutos lo que en segundos es 60 s * 5
    respuesta.set_cookie('galleta','Estoy guardado en el navegador', secure=True, max_age=60*5)
    return respuesta

si queremos borrar la cookie ponemos max_age = 0.

Un ejemplo de como establecer una cookie valida durante 90 días con el atributo expires. 

expires => Debe ser un objeto de datetime o la fecha puesta en formato Unix

Cookie con una duración de 90 días, usando expires y un objeto datetime.

@app.route('/set_cookie90d/')
def save_cookie90d():
    '''Pone una cookie en vigor durante 90 dias'''
    # make_response = La respuesta será 
    respuesta=make_response('Hemos guardado la cookie')
    # en respuesta ira la cookie 'nombre' 'la cookie en si'
    # expire = es un objeto de datetime().
    final = datetime.datetime.now()
    final = final + datetime.timedelta(days=90)
    respuesta.set_cookie('galleta','Estoy guardado en el navegador', secure=True, expires=final)
    return respuesta


Las Cookies están limitadas al directorio. El atributo path.


Si establecemos una cookie con el atributo path, esta queda asociada a esa determinada ruta. Una cookie con un atributo de ruta determinado no se puede enviar a una ruta con la que no esta relacionada, incluso si ambas rutas conviven en el mismo dominio.

Cuando se omite el atributo path durante la creación de la ruta, esta se aplica a todo el dominio "/".

Por ejemplo el código:

respuesta.set_cookie('galleta','Estoy guardado en el navegador', secure=True, 
    path="/get_cookie/")

hace que la cookie quede asociada a esa ruta y no se pueda leer desde ninguna otra.

También las cookies están asignadas a un determinado dominio salvo que se especifique otra cosa con el atributo domain.


¡No toques mi cookie! El atributo HttpOnly.


El atributo HttpOnly al establecer una cookie garantiza que un código escrito en JavaScript no pueda acceder a las cookies. Esta es la forma mas importante de protección contra ataques XSS

Para marcar una cookie como HttpOnly en flask usaremos el siguiente  atributo:

respuesta.set_cookie(key="<nombre>", value="<valor>", httponly=True)


Una cookie marcada como httponly no puede ser accedida desde JavaScript: si utilizamos el inspector de consola del navegador, en documento.cookie aparecerá una cadena de texto vacía.

¿Cuando usar HttpOnly? Pues siempre que puedas. Las cookies deben ser httponly, a menos que tengas un buen motivo para que ser tenga que acceder a ellas usando Javascript en tiempo de ejecución.