Tutorial de Flask parte 5 (login de los usuarios )
Continuando con la serie de tutoriales sobre flask, en este caso se usará lo visto en la parte de base de datos para usarlo para iniciar sesión por parte de los usuarios.
Los artículos anteriores son:
- Tutorial
Flaskde 1.parte - Tutorial
Flaskde 2 (parte html).plantilla - Tutorial
Flaskde 3 (parte web).formulario - Tutorial
Flaskde 4 (Base de datos).parte
Compatibilidad con python3
Para que openid tenga soporte para python3 se hizo una actualización al archivo Dockerfile donde en vez de usar el openid de pip se baja la versión del repositorio github:
FROM python
WORKDIR /code
RUN pip install --upgrade pip
RUN pip install flask
RUN pip install flask-login
RUN pip install git+git://github.com/mitsuhiko/flask-openid.git
RUN pip install flask-mail
RUN pip install flask-
sqlalchemy RUN pip install sqlalchemy-migrate
RUN pip install flask-
whooshalchemy RUN pip install flask-
wtf RUN pip install flask-babel
RUN pip install guess_language
RUN pip install
flipflop RUN pip install coverage
RUN pip install redis
EXPOSE 5000
ADD . /code
CMD python run.py
La estructura de archivos y directorios del proyecto para este artículo es la siguiente:
tutorial -flask
├── app
│ ├── forms. py
│ ├── __init__. py
│ ├── models. py
│ ├── __pycache__
│ ├── templates
│ │ ├── base.html
│ │ ├── edit.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── post.html
│ │ └── user.html
│ └── views. py
├── app. db
├── config. py
├── db_create. py
├── db_downgrade. py
├── db_migrate. py
├── db_repository
│ ├── __init__. py
│ ├── manage. py
│ ├── migrate.cfg
│ ├── __pycache__
│ ├── README
│ └── versions
│ ├── 001_migration. py
│ ├── 002_migration. py
│ ├── __init__. py
│ └── __pycache__
├── db_upgrade. py
├── docker-compose. yml
├── Dockerfile
├── __pycache__
├── README. md
├── run. py
└──tmp
├── app
│ ├── forms
│ ├── __init__
│ ├── models
│ ├── __pycache__
│ ├── templates
│ │ ├── base.html
│ │ ├── edit.html
│ │ ├── index.html
│ │ ├── login.html
│ │ ├── post.html
│ │ └── user.html
│ └── views
├── app
├── config
├── db_create
├── db_downgrade
├── db_migrate
├── db_repository
│ ├── __init__
│ ├── manage
│ ├── migrate.cfg
│ ├── __pycache__
│ ├── README
│ └── versions
│ ├── 001_migration
│ ├── 002_migration
│ ├── __init__
│ └── __pycache__
├── db_upgrade
├── docker-compose
├── Dockerfile
├── __pycache__
├── README
├── run
└──
Revisión de usuario en models.py
El archivo models.py contiene cambios en la clase User, esta actualización hace que sea amigable para usar flask-login:
#de app se importa db
from app importdb #Secrea la tabla Userque heredade db . Modelclass User( db . Model):#Secrea la columna idcomo clave primaria e integerid =db . Column( db . Integer, primary_key=True)#Se crea la columna nickname como string de tamagn 64, como unico.usuario = db.Column(db.String(64), index=True, unique=True)#Columnacorreo ,de 120de tamagno del string y unico .correo = db.Column(db.String(120), index=True, unique=True)#Posts.que tiene relacion con laclase Post (tabla post),posts =db . relationship( 'Post', backref='author', lazy='dynamic')#Se usa el decorador property, se consulta si esta atenticado@propertydef is_authenticated( self):return True#Se usa el decorador property y se consulta si esta activo@propertydef is_active( self):return True#Se usa el decorador property, se consulta si es anonimo el usuario@propertydef is_anonymous( self):return False#Se tra el id del usuariodef get_id( self):try :return unicode( self. id)# python 2except NameError:return str ( self. id)# python 3def __repr__( self):return '<User %r>' % (self.usuario)#Tabla Postque hereda de modelclass Post( db . Model):#Se crea el id del post como entero y clave primariaid =db . Column( db . Integer, primary_key=True)#Secrea la columna bodycomo stringde 140caracteres cuerpo =db . Column( db . String( 140))#Se define la marca detiempo .timestamp =db . Column( db . DateTime)#Se define el id del usuario, es una clave foranea de la tabla usuario#Columna id.user_id =db . Column( db . Integer,db . ForeignKey( 'user. id'))def __repr__( self):return '<Post %r>' % (self. cuerpo )
Archivo views. py
El archivo views.py maneja ahora el inicio de sesión y el fin del mismo. A continuación el contenido del archivo:
#Se
render_template, flash importa redirect, session , url_for, request y g ,
from flask import render_template, flash, redirect, session, url_for, request, g#Se importa login_user,logout_user,current_user y login_required
from flask. ext. login import login_user, logout_user, current_user, \login_required
#Se importa la aplicacion app, db, lm y oid
from app import app,db ,lm ,oid #De forms.py se importa LoginForm
from . forms importLoginForm #Se
importa Userde models
from . models import User
#Se retorna el usuario a partir el id de la base de datos
#la
funcion se usara por partede flask-login@lm.user_loader
def load_user( id):
return User. query. get ( int ( id))
#Se define g.user a partir
del usuario actual.#Esta funcion corre cada vez que una solicitud se realiza a
#fin de saber si el usuario hizo login y es el usuario actual
@app
. before_request
def before_request( ):g.user = current_user
#Se define la pagina index por defecto y se requiere que haga login el usuario
@app
. route( '/')@app
. route ( '/index')@login_required
def index( ):#Ahora no se usa un usuario por defecto, se comenta esa linea
#Ahora se toma el usuario g.user el cual es el usuario actual
user = g.user#user = {'
usuario ': 'Ernesto'}
posts = [{
'
autor ': {'usuario ': 'John'},'asunto': 'Un gran dia en Edimburgo!'
},
{
'
autor ': {'usuario ': 'Jane'},'
asunto ': 'Civil War,una granpelicula !'}
]
return render_template( 'index.html',title='Inicio',
user=user,
posts=posts)
#Se define login con url /login con
metodos GET y POST#Se define
el manejadorde login.@app
. route( '/login', methods=[ 'GET', 'POST'])@oid
. loginhandler
def login( ):#Se consulta si el usuario existe, y si esta autenticado
#Se
redrcciona a lapagina index
if g.user is not None and g.user.is_authenticated:
return redirect( url_for( 'index'))#Se crea una instancia de LoginForm
form = LoginForm( )#Se
consulta si validateexiste
if form. validate_on_submit( ):#Se maneja la sesion a partir del formulario con la variable recuerdame
session [ 'recuerdame '] = form. recuerdame . data#Se returna el inicio de login y correo.
return oid . try_login( form. openid . data, ask_for=[ 'usuario ', 'correo '])#Se
renderiza la plantillade login.
return render_template( 'login.html',title='Inicio
sesion ',form=form,
providers=app
. config[ 'PROVEEDORES_OPENID'])
#Se define after_login para la
llamada de flask-login@oid
. after_login
def after_login( resp ):#Si no existe el campo correo o esta vacio
#Se devuelve un mensaje de login invalido y se redirecciona
#a la pagina
de login
if resp . correo is None orresp . correo == "":flash('Login invalido, intente de nuevo.')
return redirect( url_for( 'login'))#Se trae los datos del usuario de la base de datos
user = User. query. filter_by( email=resp. correo ). first( )#SI
el usuario noexiste
if user is None:#Se trae el usuario de la resp
usuario = resp.usuario#si usuario no existe o esta en blanco
#Se toma el nombre del usuario del correo
if usuario is None orusuario == "":
usuario =resp . correo . split( '@') [0]#Se agrega el usuario y correo a la base de datos.
user = User( usuario=usuario, correo=resp. correo )
db . session. add ( user)db.session.commit
( )#Se define la variable
recuerdame como falsa
recuerdame = False#Si la variable
recuerdame esta en el manejode session
if 'recuerdame ' in session:#Se asigna el valor que maneja recuerdame en la sesion
recuerdame = session[ 'recuerdame ']
session . pop( 'recuerdame ', None)#Se
inicia login,pasando el usuario y la variablerecuerdame #Se redirecciona de pagina
login_user
( user, remember=recuerdame)
return redirect( request. args . get( 'next') or url_for( 'index'))
#Se define el fin de la sesion cuando se ve la pagina logout
#Se redirecciona a la pagina index pero primero se finaliza la session
@app.route('/logout')
def logout():
logout_user()
return redirect( url_for( 'index'))
A continuación se muestra el archivo __init__.py:
#Se impostan os, Flask, sqlalchemy, LoginManager, OpenID y basedir.
import os
from flask import Flask
from flask. ext. sqlalchemy import SQLAlchemy
from flask. ext. login import LoginManager
from flask. ext. openid import OpenID
from config import basedir
#Se crea la instancia de Flask
app = Flask( __name__)
#Se carga la configuracion de config. py
app . config. from_object( 'config')
#Se carga la info de la base de datos
db = SQLAlchemy( app)
#Se maneja login. con la app.
lm = LoginManager( )
lm . init_app( app)
lm . login_view = 'login'
#Se define la carga de la info de OPenID
oid = OpenID( app, os . path. join( basedir , 'tmp '))
#Se importa views y models de app
from app import views, models
Archivo base.html
Este archivo ahora maneja el manejo de sesión que se hizo en views.py:
<
> html <
> head {% if title %}
<
>{ title title { } - } </title> microblog {% else %}
<
> title </title> microblog {%
%} endif </
> head <
> body <
>Microblog: div <
href="{ a url_for { 'index') ( }">Home</a> } {% if g.user.is_authenticated %}
| <
a href="{{ url_for( 'logout')} }">Logout</a>{%
%} endif </
> div <
hr >{% with messages = get_flashed_messages
( ) %}{% if messages %}
<
ul >{% for message in messages %}
<
li >{{ message} } </li >{%
endfor %}</
ul >{%
%} endif {%
endwith %}{% block content %
} {%endblock %}</
body ></
html >
Al iniciar la aplicación se tiene la siguiente salida:
Recreating tutorialflask_tutorial_1
Attaching to tutorialflask_tutorial_1
tutorial_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
tutorial_1 | * Restarting with stat
tutorial_1 | * Debugger is active!
tutorial_1 | * Debugger pin code: 733-227-386
El código fuente en gitlab tiene los siguientes cambios:
- Manejo del código y la página en Inglés (ya que en artículo futuro se usará internacionalización).
- Es necesario deshabilitar las notificaiones de track de sqlalchemy en el archivo config.py:
- SQLALCHEMY_TRACK_MODIFICATIONS = False
- Se cambian módulos deprecados por otros actualizados, los módulos atualizados son:
- flask_login
- flask_openid
- flask_sqlalchemy
Al abrir el navegador en http://localhost:5000 se tiene la siguiente figura:
Al llenar el formulario se tiene la siguiente figura (el mensaje que pide iniciar sesión para acceder a la página se eliminó):
Para hacer logout se abre el siguiente enlace http://localhost:5000/logout , lo cual cierra la sesión regresa a la página de inicio.
Losmensajes de la aplicación son los siguientes:
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:13:39] "GET / HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:13:39] "GET /login?next=%2F HTTP/1.1" 200 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:15:48] "POST /login?next=%2F HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:15:48] "GET /login?next=/ HTTP/1.1" 200 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:18:23] "GET /logout HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:18:23] "GET /index HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:18:23] "GET /login?next=%2Findex HTTP/1.1" 200 -
Senota los redireccionamientos de la página index a la de login y la de logout a la index y luego a login.
Para seguir correctamente el tutorial se recomienda bajar el códgo fuente del repositorio.
Para hacer logout se abre el siguiente enlace http://localhost:5000/logout , lo cual cierra la sesión regresa a la página de inicio.
Los
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:13:39] "GET / HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:13:39] "GET /login?next=%2F HTTP/1.1" 200 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:15:48] "POST /login?next=%2F HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:15:48] "GET /login?next=/ HTTP/1.1" 200 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:18:23] "GET /logout HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:18:23] "GET /index HTTP/1.1" 302 -
tutorial_1 | 172.17.0.1 - - [25/Sep/2016 01:18:23] "GET /login?next=%2Findex HTTP/1.1" 200 -
Se
Para seguir correctamente el tutorial se recomienda bajar el códgo fuente del repositorio.
No hay comentarios:
Publicar un comentario