Aplicación con citas

De WikiEducator
Saltar a: navegación, buscar


Modelo

class Cita(Base):
    """Una cita en el calendario."""
    __tablename__ = 'cita'
 
    id = Column(Integer, primary_key=True)
    creada = Column(DateTime, default=datetime.now)
    modificada = Column(DateTime, default=datetime.now,
                      onupdate=datetime.now)
    evento = Column(String(255))
    inicio = Column(DateTime, nullable=False)
    fin = Column(DateTime, nullable=False)
    todoeldia = Column(Boolean, default=False)
    lugar = Column(String(255))
    descripcion = Column(Text)
    user_id = Column(Integer, ForeignKey('usuarios.id'), nullable=False)
    usuario = relationship(User, lazy='joined', join_depth=1, viewonly=True)
    def duracion(self):
        tiempo = self.fin - self.inicio
        return tiempo.days * 24 * 60 * 60 + tiempo.seconds
 
    def __str__(self):
        return "{evento} [{fecha}}".format(evento=self.evento,
            fecha=self.inicio)

Listado de citas

Controlador

@app.route('/citas/')
@login_required
def lista_citas():
    """Lista de todas las citas en la base de datos."""
    # Query: Recupera las citas del usuario, ordenadas por fecha.
    citas = (db.session.query(Cita)
             .filter_by(user_id=session['uid'])
             .order_by(Cita.inicio.asc()).all())
    return render_template('cita/index.html', citas=citas)

Tempate

Macro auxiliar

{% macro detalle(cita) %}
  <div class="detalle-cita">
     <h3>{{ cita.evento or '(sin titulo)' }}</h3>
 
    {% if cita.lugar %}<p><i class="icon-home"></i> {{ cita.lugar }}</p>{% endif %}
    {% if cita.todoeldia %}
      <p><i class="icon-calendar"></i> {{ cita.inicio }}</p>
    {% else %}
      <p><i class="icon-calendar"></i> {{ cita.inicio }}. Duración: {{ cita.duracion() }}</p>
    {% endif %}
      </div>
{% endmacro %}

cita/index.html'

{% extends 'layout.html' %}
 
{% from 'cita/comun.html' import detalle %}
 
{% block content %}
<div class="row">
  {% for cita in citas %}
    <div class="cita">
      {{ detalle(cita) }}
    </div>
  {% else %}
    <h3 class="span12">No hay citas.</h3>
  {% endfor %}
</div>
{% endblock content %}


Creación de nuevas citas

Controlador

@app.route('/cita/crear/', methods=['GET', 'POST'])
@login_required
def crear_cita():
    """Muestra el formulario para crear una cita"""
    form = FormCita(request.form)
 
    if request.method == 'POST' and form.validate():
        cita = Cita(user_id=session['uid'])
        form.populate_obj(cita)
        db.session.add(cita)
        db.session.commit()
        # Exito: devuelve al usuario a la lista de citas
        return redirect(url_for('lista_citas'))
    # Error o GET.
    return render_template('cita/editar.html', form=form)

Formulario

class FormCita(Form):
    """Formulario para el modelo Citas. Genera HTML y valida entradas 
    """
    titulo = TextField('Cita', [validators.Length(max=255)])
    inicio = DateTimeField('Inicio', [validators.Required()])
    fin = DateTimeField('Fin')
    todoeldia = BooleanField('Todo el día')
    lugar = TextField('Lugar', [validators.Length(max=255)])
    descripcion = TextAreaField('Descripción')

Template

{% extends "layout.html" %}
 
{% block content %}
  <h2>Crear Cita</h2>
 
  {% for message in form.titulo.errors %}
    <div class="flash">{{message }}</div>
  {% endfor %}
 
  {% for message in form.inicio.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
  {% for message in form.fin.errors %}
    <div class="flash">{{ message }}</div>
  {% endfor %}
 
 
  <form action="{{ url_for('crear_cita') }}" method=post>
    {{ form.hidden_tag() }}
 
    {{ form.titulo.label }}
    {{ form.titulo }}
 
    {{ form.inicio.label }}
    {{ form.inicio }}
 
    {{ form.fin.label }}
    {{ form.fin }}
 
    {{ form.todoeldia.label }}
    {{ form.todoeldia}}
 
    {{ form.lugar.label }}
    {{ form.lugar}}
 
    {{ form.descripcion.label }}
    {{ form.descripcion}}
 
 
    {{ form.crear }}
  </form>
 
{% endblock %}

Mostrar el detalle de una cita

from flask import abort
...
@app.route('/cita/<int:cita_id>/')
@login_required
def detalle_cita(cita_id):
    """Detalle de una cita dada."""
    # Query: obtiene el objeto por ID 
    cita = db.session.query(Cita).get(cita_id)
    if cita is None or cita.user_id != session.uid:
        # Abortar con Not Found.
        abort(404)
    return render_template('cita/detalle.html', cita=cita)

Editar cita existente

@app.route('/citas/<int:cita_id>/editar/', methods=['GET', 'POST'])
@login_required
def editar_cita(cita_id):
    """Prepara el formulario HTML para editar una cita."""
    cita = db.session.query(Cita).get(cita_id)
    if cita is None:
        abort(404)
    if cita.user_id != session[uid]:
        abort(403)
    form = FormCita(request.form, cita)
    if request.method == 'POST' and form.validate():
        form.populate_obj(cita)
        db.session.commit()
        # Si hay éxito, vuelve a la vista de detalle de la cita
        return redirect(url_for('detalle_cita', cita_id=cita.id))
    return render_template('cita/edit.html', form=form)

Eliminar cita

Usando HTTP DELETE

javascript en plantilla

      $(function() {
        $(".cita-delete-link").on("click", function() {
          var delete_url = $(this).attr('data-delete-url');
          $.ajax({
            url: delete_url,
            type: 'DELETE',
            success: function(response) {
              if (response.status == 'OK') {
                window.location = {{ url_for('lista_citas') }};
              } else {
                alert('Error al borrar.')
              }
            }
          });
          return false;
        });
      });
    </script>
from flask import jsonify
...
@app.route('/citas/<int:cita_id>/eliminar/', methods=['DELETE'])
@login_required
def eliminar_cita(appointment_id):
    """Elimina un recurso usando HTTP DELETE, responde con JSON por JavaScript."""
    cita = db.session.query(cita).get(cita_id)
    if cita is None:
        # Abortar: no encontrado
        response = jsonify({'status': 'Not Found'})
        response.status_code = 404
        return response
    if cita.user_id != session.uid:
        # Abortar con respuesta simple: prohibido.
        response = jsonify({'status': 'Forbidden'})
        response.status_code = 403
        return response
    db.session.delete(cita)
    db.session.commit()
    return jsonify({'status': 'OK'})