domingo, 29 de septiembre de 2013

jQuery Autocomplete en Django

Hola todos, esta vez vamos hacer algo muy útil cuando tenemos un campo estilo ForeignKey y se nos hace muy grande cuando tenemos muchos datos, como sabemos un campo ForeignKey nos hace un campo select en el formulario.

Tenemos el siguiente modelo:
class Persona(models.Model):
    cedula = models.CharField(unique=True, max_length=10)
    nombres = models.CharField(verbose_name='Nombres', max_length=60)
    apellidos = models.CharField(verbose_name='Apellidos', max_length=60)
class Proyecto(models.Model):
     titulo = models.CharField(max_length=200)
     autor = models.ForeignKey(Persona)
Ahora vamos a crear el forms.py, donde vamos a crear un campo adicional, que vamos a llamar persona_display:
class ProyectoForm(forms.ModelForm):
     persona_display = forms.CharField(max_length=100, help_text='tipee Cedula, Nombre o Apellido') 
     class_Meta:
          model = Proyecto
          fields=('titulo', 'persona_display', 'autor',) 
     def __init__(self, *args, **kwargs):
          super(ProyectoForm, self).__init__(*args, **kwargs)
          self.field['persona_display'].label = "Agrege el Autor"
          self.fields['autor'].widget = forms.HiddenInput()
¿Qué hemos hecho hasta aquí? 

Primero creamos nuestro formulario donde agregamos un campo adicional llamado persona_display, en el constructor de la clase hemos dado algunas características al campo persona lo hemos ocultado y le hemos agregado una etiqueta al nuevo campo persona_display

Ahora creamos nuestra url en urls.py:
url(r'^persona/autocomplete/$', 'persona_auto_complete', name='persona_auto_complete'),
Ahora la vista en views.py:
 def persona_auto_complete(request):
q = request.REQUEST['term']
if q:
qset = (
Q(cedula__istartswith=q) |
Q(nombres__icontains=q) |
Q(apellidos__icontains=q)
)
personas = Persona.objects.filter(qset).distinct()
else:
personas = []
personas_list = []
for p in personas:
value = '%s, (%s, %s)' % (p.cedula, p.nombres, p.apellidos)
p_dict = {'id': p.id, 'label': value, 'value': value}
personas_list.append(p_dict)
return HttpResponse(simplejson.dumps(personas_list))
Ahora podemos probar lo que hemos hecho, escribiendo en nuestro navegador:
http://localhost:8000/persona/autocomplete/?term=an
En este caso estamos buscando las personas que contengan en el nombre o apellido las letras "an" y nos mostrara un resultado como este:

[{"id": 3, "value": "1234567, (Ana Cecilia, La Cruz)", "label": "1234567, (Ana Cecilia, La Cruz)"}]

Si tenemos este resultado, ya podemos ahora agregar el código JQuery del plugin AutoComplete en nuestro template:

Recuerde que debe agregar los css del Jquery:
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
el formulario:
<form action="" method="post">{% csrf_token %}
<div class="forms">
    {{ form }}
    <input type="submit" name="submit" value="Salvar" />
</div>
</form>
Y ahora el JQuery y JQuery UI con el script de que hace accionar nuestro autocomplete:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<script>
       $( document ).ready( function() {
        $("#id_persona_display").autocomplete({
        source: "{%  url 'persona_auto_complete' %}",
        dataType: "jsonp",
        selectFirst: true,
        minLength: 2,
        select: function(event,ui) {
        $("#id_autor").val(ui.item.id)
        }
        });
       });
</script>
Explicando un poco, estamos activando el auto complete en el campo que creamos en el form.py llamado persona_display, llamamos la url del autocomplete en la vista y le decimos por el parámetro minLength que empiece a buscar a partir del segundo carácter y al escoger le agregamos el id al campo autor, para que no nos de error, ya que es un campo requerido por ser clave.

Este es un ejemplo de como nos quedara el autocomplete,


Y eso es todo, espero que les sirva y lo pongan en practica en sus proyectos. Espero sus comentarios. Hasta la próxima.

martes, 17 de septiembre de 2013

Chequear disponibilidad de Campo Clave en Django 1.5

Hoy vamos a hacer algo para validar un campo en un  formulario, cuando le quitamos el foco al campo, este revisara en la base de datos, si ya existe, y no al final al hacer el submit. Esto es útil, para verificar si esta tomado el nombre de usuario, en caso de registro de usuarios, así como saber si el numero de identificación o cédula ya existe, entre otros.

Empecemos:

primero creamos nuestra url en urls.py
url(regex=r'^check_cedula/(?P<cedula>\d+)/$',
view=CedulaCheck.as_view(),
name='check_cedula'
),
Ahora nos vamos a nuestra vista views.py:
class CedulaCheck(TemplateView): 
def dispatch(self, *args, **kwargs):
self.cedula = self.kwargs['cedula']
return super(CedulaCheck, self).dispatch(*args, **kwargs) 
def get(self, request, *args, **kwargs):
if Persona.objects.filter(cedula__iexact=self.cedula):
return HttpResponse(False)
else:
return HttpResponse(True)
ya con esto podemos probar nuestra url con un numero de cédula y nos devolverá si existe o no
http://localhost:8000/check_cedula/12345678/
Ahora nos vamos a poner en acción nuestra API  recién creada con un formulario, primero creamos nuestra clase en views.py:
class PersonaCreateView(CreateView):
model = Persona
template_name = 'home/add_persona.html'
En nuestro html, nos vamos al campo que vamos a validar, es de notar que hice mi formulario a mano, no usando la plantilla de Django para crear los campos:
<div class="control-group">
<label for="cedula" class="control-label">
Cedula:
</label>
<div class="controls">
<input name="cedula" type="text" value="" id="cedula">
<span class="help-inline" id="idval">  Escriba su Cedula de Identidad</span>
</div>
</div>
y le agregamos el siguiente código jquery
<script>
function CheckId() {
$.get('/check_cedula/'+escape($('#cedula').val())+'/',
function(data){
if(data == "True"){
$('#idval').html("<i class='icon-ok'></i> Cedula disponible");
} else {
$('#idval').html("<i class='icon-remove'></i> Cedula existe");
}
});
}
function onChange(){
$("#cedula").change( function() {CheckId()});
}
$(document).ready(onChange);
</script> 
 Y listo, cuando coloquemos una cédula que ya existe, al dejar el foco del campo, nos dará una advertencia que ya existe y así no tenemos que escribir todos los campos y darle submit para que nos de el error.

Hasta la proxima

domingo, 15 de septiembre de 2013

Fotos con webcam usando Django 1.5 y JpegCam

Hola nuevamente

Tenia un poco abandonado mi blog, debido a razones de trabajo, hoy vengo nuevamente a hablarles de Django, el framework de Python, esta vez, vamos a ver cosas interesantes como el tomar una instantánea o fotografía desde nuestra webcam utilizando una librería de Flash y Javascript.

Jpegcam es un interesante componente de Flash/JavaScript para capturar y guardar imágenes desde la webcam del usuario. Se puede utilizar por ejemplo para permitir a los usuarios hacer una imagen de sí mismos como avatares, o para completar su perfil.

Es un widget que a través de flash muestra una vista en vivo de la webcam y botones con funciones Javascript asociadas  que puede capturar y enviar una imagen a un servidor en modo POST.

¿Qué debemos hacer?
  1. Descargar la ultima versión de Jpegcam y agrégalo a tu carpeta de statics en tu proyecto Django, esta consta de archivos que son: shutter.mp3, webcam.js, webcam.swf
  2. Editamos el archivo webcam.js y buscamos para configurar los path de swf_url y shutter_url. En mi caso:
              swf_url: '/static/jpegcam/webcam.swf'
             shutter_url: '/static/jpegcam/shutter.mp3'
          Y mas abajo en el archivo, aproximadamente en la linea 166, agregar el path:
             this.shutter_url = url ? url : '/static/jpegcam/shutter.mp3';
Ahora nos vamos a Django, quiero manifestar que esta guía esta hecha para personas que han programado con Django y tienen conocimientos básicos del framework.

Primero creamos nuestro modelo de datos, donde vamos a almacenar los datos de la persona a registrar y su fotografía.
models.py
from django.db import models
import os
def get_image_name(instance, filename):
f, ext = os.path.splitext(filename)
archivo = '%s%s' % (instance.cedula, ext)
return os.path.join('webcamimages', archivo) 
class Persona(models.Model):
"""docstring for Personal"""
cedula = models.CharField(max_length=10)
nombres = models.CharField(max_length=60)
apellidos = models.CharField(max_length=60)
foto = models.ImageField(upload_to=get_image_name, blank=True, null=True) 
def __unicode__(self):
return self.cedula 
@models.permalink
def get_absolute_url(self):
return ('persona', [str(self.id)]) 
def _get_full_name(self):
"Retorna los nombres completos de Persona"
return '%s %s' % (self.nombres, self.apellidos) 
full_name = property(_get_full_name)
Les explico un poco el modelo, primero tenemos una función llamada get_image_name, esta función nos sirve para llamar los archivos que subamos por la opción file del formulario, siempre tendrán el nombre del campo cedula con su extensión, si mi cédula es 12345 y estoy cargando el archivo foto.jpg, esta función lo renombrada y lo guardara como 12345.jpg en la carpeta webcamimages dentro de media.
foto = models.ImageField(upload_to=get_image_name, blank=True, null=True) 
Ahora nos vamos a nuestro url.py, donde tenemos estas dos entradas:
url(regex=r'^persona/$',
     view= PersonaCreateView.as_view(),
         name='add_persona'
    ),
url(regex=r'^save_image/(?P<cedula>\d+)/$',
     view=SaveImage.as_view(),
      name='salvar_imagen'
),

La que nos interesa en este momento es la de save_image, ya que es la url que usaremos para tomar la fotografía, la otra es para que el formulario guarde la información dada.

Ahora crearemos nuestro views.py
from django.views.generic import TemplateView, CreateView, DetailView, ListView
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from django.conf import settings
from .models import Persona

class SaveImage(TemplateView): 
@csrf_exempt
def dispatch(self, *args, **kwargs):
self.filename = self.kwargs['cedula']+'.jpg'
return super(SaveImage, self).dispatch(*args, **kwargs) 
def post(self, request, *args, **kwargs): # save it somewhere
f = open(settings.MEDIA_ROOT + '/webcamimages/'+ self.filename, 'wb')
f.write(request.body)
f.close()
# return the URL
return HttpResponse("/media/webcamimages/" + self.filename) 
def get(self, request, *args, **kwargs):
return HttpResponse('No esta pasando el POST')

class PersonaCreateView(CreateView):
model = Persona
template_name = 'home/add_persona.html' 
def form_valid(self, form):
form.instance.foto = 'webcamimages/'+ form.instance.cedula + ".jpg"
return super(PersonaCreateView, self).form_valid(form)

Como ven, tenemos dos clases, la primera SaveImage, que es la que nos toma la instantánea y la guarda en la carpeta webcamimages con la cédula de la persona, la otra es la que guarda en base de datos y toma la dirección donde esta guardada la imagen y la agrega en el campo foto.

Ahora hacemos el html, llamado en nuestro caso add_persona.html
{% extends "base.html" %}
{% block extra_css %}
<!-- Primero, incluir la libreria de JpegCam -->
<script type="text/javascript" src="{{ STATIC_URL }}jpegcam/webcam.js"></script>
{% endblock extra_css %}
{% block contenido %}
<h1>Registrar Persona</h1>
<form class="span10" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="row well">
<div class="span6">
<div class="control-group">
<label for="cedula" class="control-label">
Cedula:
</label>
<div class="controls">
<input name="cedula" type="text" value="" id="cedula">
<span class="help-inline">  Escriba su Cedula de Identidad</span>
</div>
</div>
<div class="control-group">
<label for="nombres" class="control-label">
Nombres:
</label>
<div class="controls">
<input name="nombres" type="text" value="" id="nombres">
</div>
</div>
<div class="control-group">
<label for="apellidos" class="control-label">
Apellidos:
</label>
<div class="controls">
<input name="apellidos" type="text" value="" id="apellidos">
</div>
</div>
<div class="control-group">
<label for="foto" class="control-label">
Foto:
</label>
<div class="controls">
<input name="foto" type="file" value="" id="foto">
<a href="#takephoto" role="button" data-toggle="modal" class="btn btn-warning">Tomar Foto</a>
</div>
</div>
</div>
<div class="span3" id="fotoview">
</div>
</div>
<button type="submit" class="btn btn-primary pull-right">Guardar</button>
</form>
<!-- Advanced Modal -->
<div id="takephoto" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
   <h3 id="myModalLabel">Camara</h3>
</div>
<div class="modal-body">
<!-- Configure a few settings -->
<script language="JavaScript">
document.write( webcam.get_html(320, 240) );
</script>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Cerrar</button>
<button class="btn btn-primary" onClick="take_snapshot()" >Fotografíar</button>
</div>
</div>
{% endblock contenido %}

Lo primero es cargar la librería jpegcam.js, luego creamos nuestro formulario, aquí estoy usando bootstrap para ayudarme con los estilos, utilizo una ventana modal para activar la cámara y tomar la fotografía. Aquí algunos captura de pantalla de la aplicación final:



Y listo, quiero manifestar que hay formas de utilizar la cámara sin usar flash, el cual esta muy cuestionado por el tema de seguridad, mas esto les puede servir en un entorno empresarial, esta aplicación la pueden descargar desde github es este enlace, Espero sus recomendaciones y mejoras.

Saludos