Diferencia entre revisiones de «Usuario:Lmorillas/desarrollo web servidor/flask/ejemplo completo»

De WikiEducator
Saltar a: navegación, buscar
(Entrar y salir)
 
(33 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}}
 +
 +
 +
== Previo ==
 +
<br />
  
 
{{Actividad|
 
{{Actividad|
Línea 8: Línea 14:
 
* http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-adding-a-contact-page/
 
* http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-adding-a-contact-page/
 
* http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-signing-in-and-out/
 
* http://net.tutsplus.com/tutorials/python-tutorials/intro-to-flask-signing-in-and-out/
 +
 +
Repositorio de código de clase:
 +
* https://bitbucket.org/lmorillas/contactos-flask
 +
}}
 +
 +
{{Actividad|
 +
 +
$ sudo apt-get install mysql-server mysql-client
 +
$ sudo apt-get install libmysqlclient-dev
 +
 +
$ mysql -u username -p
 +
Enter password:
 +
mysql> CREATE DATABASE development;
 +
 +
$ pip install mysql-python
 +
 +
|Title=Instalar MySQL
 +
}}
 +
 +
== Estructura inicial ==
 +
{{Objetivo | 1=
 +
* Crear proyecto en eclipse
 +
* Preparar intérprete con virtualenv
 +
* Crear estructura inicial del proyecto
 +
* Iniciar repositorio local
 +
* .hgignore
 +
{{Tip| 1=[https://bitbucket.org/lmorillas/contactos-flask/src/0b579feed5e9d633a919848e20cb906c361a8980/?at=1.%20Estructura%20inicial Estructura inicial] }}
 +
}}
 +
 +
== Páginas estáticas. Home y about ==
 +
 +
{{Objetivo |
 +
* Páginas estáticas home y about
 +
* Estructura de templates:
 +
  layout.html
 +
  home.html
 +
  about.html
 +
* Vistas y rutas (decoradores)
 +
{{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/8c27cdf40ae381d710bf8e32534e1cdd11a34fa9/?at=2.%20P%C3%A1ginas%20est%C3%A1ticas.%20Home%20y%20about Páginas estáticas] }}
 +
}}
 +
 +
== Formulario de contacto ==
 +
 +
 +
{{Objetivo |
 +
* Formulario para contacto: instalar flask-wtf
 +
* Creación del formulario
 +
* Uso del formulario: GET y POST
 +
* Template
 +
{{Tip|1=[https://bitbucket.org/lmorillas/contactos-flask/src/7099cce05ca0b3b03e0798179caceeea16eca8bf/?at=3.%20Formulario%20de%20contacto Páginas estáticas]}}
 +
}}
 +
 +
== 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 ==
 +
{{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 ==
 +
 +
{{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 ==
 +
{{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 ==
 +
* 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 ==
 +
'''layout.html'''
 +
  <li><a href="{{ url_for('contacto') }}">Contacto</a></li>
 +
 +
== 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 ==
 +
'''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 ==
 +
 +
== 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 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



Previo







Icon activity.jpg

Instalar MySQL

$ sudo apt-get install mysql-server mysql-client
$ sudo apt-get install libmysqlclient-dev
$ mysql -u username -p
Enter password:
mysql> CREATE DATABASE development;
$ pip install mysql-python





Estructura inicial

Icon objectives.jpg

Objetivo

* Crear proyecto en eclipse
  • Preparar intérprete con virtualenv
  • Crear estructura inicial del proyecto
  • Iniciar repositorio local
  • .hgignore
Icon present.gif


Páginas estáticas. Home y about

Icon objectives.jpg

Objetivo

  • Páginas estáticas home y about
  • Estructura de templates:
 layout.html
 home.html
 about.html
  • Vistas y rutas (decoradores)
Icon present.gif



Formulario de contacto

Icon objectives.jpg

Objetivo

  • Formulario para contacto: instalar flask-wtf
  • Creación del formulario
  • Uso del formulario: GET y POST
  • Template
Icon present.gif



Estilo en formulario de contacto

Icon objectives.jpg

Objetivo

  • Modificar hoja de estilos
Icon present.gif



Validación de formularios

Icon objectives.jpg

Objetivo

  • Campos obligatorios
  • Campo email
Icon present.gif



Mensajes de error

Icon objectives.jpg

Objetivo

  • Mensajes de error genéricos
  • Mensajes de error por campo
Icon present.gif



Mensajes específicos de error

Icon objectives.jpg

Objetivo

  • Mostrar un mensaje específico de error para cada input del formulario
{% 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

  • <a href="Plantilla:Url for('contacto')">Contacto</a>
  • 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