Aplicación con citas
De WikiEducator
Contenido
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
- http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
- http://www.w3.org/TR/html401/interact/forms.html#h-17.13.1
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'})