Diferencia entre revisiones de «Usuario:Lmorillas/desarrollo web servidor/flask/ejemplo completo»
De WikiEducator
(→Formulario de contacto) |
(→Entrar y salir) |
||
(24 revisiones intermedias por el mismo usuario no mostrado) | |||
Línea 1: | Línea 1: | ||
{{MiTitulo|Una aplicación más compleja}} | {{MiTitulo|Una aplicación más compleja}} | ||
+ | |||
+ | {{TOC | right}} | ||
Línea 16: | Línea 18: | ||
* https://bitbucket.org/lmorillas/contactos-flask | * https://bitbucket.org/lmorillas/contactos-flask | ||
}} | }} | ||
− | |||
{{Actividad| | {{Actividad| | ||
Línea 62: | Línea 63: | ||
* Uso del formulario: GET y POST | * Uso del formulario: GET y POST | ||
* Template | * Template | ||
− | |||
− | |||
{{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/7099cce05ca0b3b03e0798179caceeea16eca8bf/?at=3.%20Formulario%20de%20contacto Páginas estáticas]}} | {{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/7099cce05ca0b3b03e0798179caceeea16eca8bf/?at=3.%20Formulario%20de%20contacto Páginas estáticas]}} | ||
}} | }} | ||
== Estilo en formulario de contacto == | == Estilo en formulario de contacto == | ||
+ | |||
+ | {{Objetivo | | ||
+ | * Modificar hoja de estilos | ||
+ | {{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/77101c5e7031fd4ea93bacb6dfefd2d7b2264e10/?at=4.%20Estilo%20en%20formulario%20de%20contacto Estilo en formulario de contacto] }} | ||
+ | }} | ||
== Validación de formularios == | == Validación de formularios == | ||
+ | {{Objetivo | | ||
+ | * Campos obligatorios | ||
+ | * Campo email | ||
+ | {{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/248694ddde1c9d4c7c153fca4a18faa8bc7ae678/?at=5.%20Validaci%C3%B3n%20de%20formularios Validación del formulario] }} | ||
+ | }} | ||
== Mensajes de error == | == Mensajes de error == | ||
+ | |||
+ | {{Objetivo | | ||
+ | * Mensajes de error genéricos | ||
+ | * Mensajes de error por campo | ||
+ | {{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/e6a630259182ab038775d81bfa30eda1917ddd77/?at=6.%20Mensajes%20de%20error Mensajes de error] }} | ||
+ | }} | ||
== Mensajes específicos de error == | == Mensajes específicos de error == | ||
+ | {{Objetivo | | ||
+ | * Mostrar un mensaje específico de error para cada input del formulario | ||
+ | <source lang="python"> | ||
+ | {% for mensaje in form.nombre.errors %} | ||
+ | <div class="flash">{{ mensaje }}</div> | ||
+ | {% endfor %} | ||
+ | |||
+ | {% for mensaje in form.email.errors %} | ||
+ | <div class="flash">{{ mensaje }}</div> | ||
+ | {% endfor %} | ||
+ | |||
+ | {% for mensaje in form.asunto.errors %} | ||
+ | <div class="flash">{{ mensaje }}</div> | ||
+ | {% endfor %} | ||
+ | |||
+ | {% for mensaje in form.mensaje.errors %} | ||
+ | <div class="flash">{{ mensaje }}</div> | ||
+ | {% endfor %} | ||
+ | </source> | ||
+ | }} | ||
== Enviando correo == | == Enviando correo == | ||
+ | * Estensión: flask-mail | ||
+ | pip install flask-mail | ||
+ | <source lang="python"> | ||
+ | from flask.ext.mail import Message, Mail | ||
+ | |||
+ | # configuración | ||
+ | MAIL_SERVER = "smtp.gmail.com" | ||
+ | MAIL_PORT = 465 | ||
+ | MAIL_USE_SSL = True | ||
+ | MAIL_USERNAME = 'dwesdaw@gmail.com' # en mi ejemplo | ||
+ | MAIL_PASSWORD = 'tu password' | ||
− | == | + | def contacto(): |
+ | form = ContactForm() | ||
+ | |||
+ | ... | ||
+ | else: | ||
+ | msg = Message(form.asunto.data, sender='dwesdaw@gmail.com', | ||
+ | recipients=['destinatario']) | ||
+ | msg.body = """ | ||
+ | From: %s <%s> | ||
+ | %s | ||
+ | """ % (form.nombre.data, form.email.data, form.mensaje.data) | ||
+ | |||
+ | mail.send(msg) | ||
+ | return render_template('contacto.html', exito=True, nombre = form.nombre.data ) | ||
+ | ... | ||
+ | </source> | ||
== Menú contactos == | == Menú contactos == | ||
+ | '''layout.html''' | ||
+ | <li><a href="{{ url_for('contacto') }}">Contacto</a></li> | ||
== Configuración base de datos == | == Configuración base de datos == | ||
+ | * Instalar mysql y python-mysql | ||
+ | <source lang="python"> | ||
+ | SQLALCHEMY_DATABASE_URI = 'mysql://userflask:******@localhost/appFlask' | ||
+ | |||
+ | from models import db | ||
+ | |||
+ | db.init_app(app) | ||
+ | </source> | ||
== Modelo de usuario == | == Modelo de usuario == | ||
+ | '''models.py''' | ||
+ | <source lang="python"> | ||
+ | db = SQLAlchemy() | ||
+ | |||
+ | |||
+ | class User(db.Model): | ||
+ | __tablename__ = 'usuarios' | ||
+ | uid = db.Column(db.Integer, primary_key = True) | ||
+ | nombre = db.Column(db.String(100)) | ||
+ | apellido = db.Column(db.String(100)) | ||
+ | email = db.Column(db.String(120), unique=True) | ||
+ | pwdhash = db.Column(db.String(54)) | ||
+ | |||
+ | def __init__(self, nombre, apellido, email, password): | ||
+ | self.nombre = nombre.title() | ||
+ | self.apellido = apellido.title() | ||
+ | self.email = email.lower() | ||
+ | self.set_password(password) | ||
+ | |||
+ | def set_password(self, password): | ||
+ | self.pwdhash = generate_password_hash(password) | ||
+ | |||
+ | def check_password(self, password): | ||
+ | return check_password_hash(self.pwdhash, password) | ||
+ | </source> | ||
== Página de perfil == | == Página de perfil == | ||
== Entrar y salir == | == Entrar y salir == | ||
+ | |||
+ | == Refactorización == | ||
+ | Decidimos cambiar el modelo de la base de datos y utilizar el modelo declarativo de http://flask.pocoo.org/docs/patterns/sqlalchemy/ Eso exige cambios en la aplicación: | ||
+ | |||
+ | === models.py === | ||
+ | Las clases heredarán de '''declarative_base()''' | ||
+ | |||
+ | <source lang="python"> | ||
+ | from sqlalchemy.ext.declarative import declarative_base | ||
+ | from sqlalchemy import Boolean, Column | ||
+ | from sqlalchemy import DateTime, Integer, String, Text | ||
+ | |||
+ | Base = declarative_base() | ||
+ | class User(Base): | ||
+ | __tablename__ = 'usuarios' | ||
+ | uid = Column(Integer, primary_key = True) | ||
+ | nombre = Column(String(100)) | ||
+ | ... | ||
+ | </source> | ||
+ | |||
+ | Creación automática de las tablas: | ||
+ | Base.metadata.create_all(engine) | ||
+ | |||
+ | Las búsquedas se hacen ahora desde la sesión: | ||
+ | session.query(User).all(): | ||
+ | |||
+ | === app.py === | ||
+ | |||
+ | <source lang="python"> | ||
+ | ... | ||
+ | from models import Base, User | ||
+ | from flask.ext.sqlalchemy import SQLAlchemy | ||
+ | ... | ||
+ | |||
+ | # Configuración SQL Alchemy | ||
+ | db = SQLAlchemy(app) | ||
+ | db.Model = Base | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Para añadir ahora un usuario: | ||
+ | db.session.add(usuario) | ||
+ | db.session.commit() | ||
+ | |||
+ | === forms.py === | ||
+ | Si el formulario necesita un acceso a la base de datos para la validación, hay que darle el objeto correspondiente: | ||
+ | |||
+ | <source lang="python"> | ||
+ | class InscripcionForm(Form): | ||
+ | ... | ||
+ | # necesita parámetro db para hacer validación | ||
+ | def validate(self, db): | ||
+ | if not Form.validate(self): | ||
+ | return False | ||
+ | user = db.session.query(User).filter_by(email = self.email.data.lower()).first() | ||
+ | </source> | ||
+ | |||
+ | Y en la llamada al método validate desde '''app.py''': | ||
+ | if form.validate(db) == False: | ||
== Despliegue en Apache == | == Despliegue en Apache == | ||
== Despliegue en Heroku == | == Despliegue en Heroku == | ||
+ | |||
+ | |||
+ | == Configuración == | ||
+ | |||
+ | '''conf.py''' | ||
+ | |||
+ | <source lang="python"> | ||
+ | SECRET_KEY = '\x84\xed\xca\xe36\x8d\x17\xd4\xb3X\xfd1\xdfJx\xc6\xe9\xcf\x00\xdf\x9e \xa9l' | ||
+ | |||
+ | MAIL_SERVER = "smtp.gmail.com" | ||
+ | MAIL_PORT = 465 | ||
+ | MAIL_USE_SSL = True | ||
+ | MAIL_USERNAME = 'dwesdaw@gmail.com' # en mi ejemplo | ||
+ | MAIL_PASSWORD = 'tu password' | ||
+ | |||
+ | SQLALCHEMY_DATABASE_URI = 'mysql://userflask:*******@localhost/appFlask' | ||
+ | </source> | ||
+ | |||
+ | '''app.py''' | ||
+ | <source lang="python"> | ||
+ | from conf import * | ||
+ | |||
+ | ... | ||
+ | |||
+ | app = Flask(__name__) | ||
+ | app.config.from_object(__name__) | ||
+ | |||
+ | ... | ||
+ | </source> | ||
+ | |||
+ | == Documentación adicional == | ||
+ | <br /> | ||
+ | {{Conocimiento previo| | ||
+ | ; wtf | ||
+ | : http://wtforms.readthedocs.org/en/latest/ | ||
+ | : https://flask-wtf.readthedocs.org/en/latest/ | ||
+ | ; flask-SQLAlchemy | ||
+ | : http://flask.pocoo.org/docs/patterns/sqlalchemy/ | ||
+ | : http://pythonhosted.org/Flask-SQLAlchemy/index.html | ||
+ | ; Despliegue | ||
+ | : http://flask.pocoo.org/docs/deploying/ | ||
+ | ; apache | ||
+ | : http://flask.pocoo.org/docs/deploying/mod_wsgi/ | ||
+ | : https://beagle.whoi.edu/redmine/projects/ibt/wiki/Deploying_Flask_Apps_with_Apache_and_Mod_WSGI | ||
+ | ; heroku | ||
+ | : https://devcenter.heroku.com/articles/getting-started-with-python | ||
+ | : http://tech.pro/tutorial/1259/how-to-deploy-simple-and-larger-flask-apps-on-heroku | ||
+ | : http://blog.y3xz.com/blog/2012/08/16/flask-and-postgresql-on-heroku/ | ||
+ | ; postgresql | ||
+ | : http://killtheyak.com/use-postgresql-with-django-flask/ | ||
+ | ; openshift | ||
+ | : https://www.openshift.com/blogs/build-your-app-on-openshift-using-flask-sqlalchemy-and-postgresql-92 | ||
+ | |Title=Por dónde continuar ... | ||
+ | }} |
Última revisión de 04:25 14 nov 2013
Contenido
- 1 Previo
- 2 Actividad
- 3 Instalar MySQL
- 4 Objetivo
- 5 Objetivo
- 6 Objetivo
- 7 Objetivo
- 8 Objetivo
- 9 Objetivo
- 10 Objetivo
- 11 Por dónde continuar ...
Previo
Seguiremos los siguientes tutoriales:
Repositorio de código de clase:
|
Estructura inicial
* Crear proyecto en eclipse
Tip: Estructura inicial
|
Páginas estáticas. Home y about
layout.html home.html about.html
Tip: Páginas estáticas
|
Formulario de contacto
Tip: Páginas estáticas
|
Estilo en formulario de contacto
|
Validación de formularios
|
Mensajes de error
Tip: Mensajes de error
|
Mensajes específicos de error
{% for mensaje in form.nombre.errors %} <div class="flash">{{ mensaje }}</div> {% endfor %} {% for mensaje in form.email.errors %} <div class="flash">{{ mensaje }}</div> {% endfor %} {% for mensaje in form.asunto.errors %} <div class="flash">{{ mensaje }}</div> {% endfor %} {% for mensaje in form.mensaje.errors %} <div class="flash">{{ mensaje }}</div> {% endfor %} |
Enviando correo
- Estensión: flask-mail
pip install flask-mail
from flask.ext.mail import Message, Mail # configuración MAIL_SERVER = "smtp.gmail.com" MAIL_PORT = 465 MAIL_USE_SSL = True MAIL_USERNAME = 'dwesdaw@gmail.com' # en mi ejemplo MAIL_PASSWORD = 'tu password' def contacto(): form = ContactForm() ... else: msg = Message(form.asunto.data, sender='dwesdaw@gmail.com', recipients=['destinatario']) msg.body = """ From: %s <%s> %s """ % (form.nombre.data, form.email.data, form.mensaje.data) mail.send(msg) return render_template('contacto.html', exito=True, nombre = form.nombre.data ) ...
Menú contactos
layout.html
Configuración base de datos
- Instalar mysql y python-mysql
SQLALCHEMY_DATABASE_URI = 'mysql://userflask:******@localhost/appFlask' from models import db db.init_app(app)
Modelo de usuario
models.py
db = SQLAlchemy() class User(db.Model): __tablename__ = 'usuarios' uid = db.Column(db.Integer, primary_key = True) nombre = db.Column(db.String(100)) apellido = db.Column(db.String(100)) email = db.Column(db.String(120), unique=True) pwdhash = db.Column(db.String(54)) def __init__(self, nombre, apellido, email, password): self.nombre = nombre.title() self.apellido = apellido.title() self.email = email.lower() self.set_password(password) def set_password(self, password): self.pwdhash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.pwdhash, password)
Página de perfil
Entrar y salir
Refactorización
Decidimos cambiar el modelo de la base de datos y utilizar el modelo declarativo de http://flask.pocoo.org/docs/patterns/sqlalchemy/ Eso exige cambios en la aplicación:
models.py
Las clases heredarán de declarative_base()
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Boolean, Column from sqlalchemy import DateTime, Integer, String, Text Base = declarative_base() class User(Base): __tablename__ = 'usuarios' uid = Column(Integer, primary_key = True) nombre = Column(String(100)) ...
Creación automática de las tablas:
Base.metadata.create_all(engine)
Las búsquedas se hacen ahora desde la sesión:
session.query(User).all():
app.py
... from models import Base, User from flask.ext.sqlalchemy import SQLAlchemy ... # Configuración SQL Alchemy db = SQLAlchemy(app) db.Model = Base
Para añadir ahora un usuario:
db.session.add(usuario) db.session.commit()
forms.py
Si el formulario necesita un acceso a la base de datos para la validación, hay que darle el objeto correspondiente:
class InscripcionForm(Form): ... # necesita parámetro db para hacer validación def validate(self, db): if not Form.validate(self): return False user = db.session.query(User).filter_by(email = self.email.data.lower()).first()
Y en la llamada al método validate desde app.py:
if form.validate(db) == False:
Despliegue en Apache
Despliegue en Heroku
Configuración
conf.py
SECRET_KEY = '\x84\xed\xca\xe36\x8d\x17\xd4\xb3X\xfd1\xdfJx\xc6\xe9\xcf\x00\xdf\x9e \xa9l' MAIL_SERVER = "smtp.gmail.com" MAIL_PORT = 465 MAIL_USE_SSL = True MAIL_USERNAME = 'dwesdaw@gmail.com' # en mi ejemplo MAIL_PASSWORD = 'tu password' SQLALCHEMY_DATABASE_URI = 'mysql://userflask:*******@localhost/appFlask'
app.py
from conf import * ... app = Flask(__name__) app.config.from_object(__name__) ...
Documentación adicional