miércoles, 9 de octubre de 2013

Django en Producción con Centos, PostgreSQL, Virtualenv, Gunicorn, Nginx y Supervisor

En este articulo vamos llevar a un entorno de Producción nuestra aplicación que creamos con Django, para ello vamos a utilizar varias herramientas:
Nos basaremos en el sistema operativo Centos 6.4 que es el clon libre del Red Hat Enterprise Server, un sistema operativo Linux hecho para servidores. en el caso de otras distribuciones de Linux, solo busca como instalar los paquetes y donde están ubicados sus archivos de configuración.

Puedes hacer esto en un servidor local, o puedes utilizar cualquiera de los proveedores que dan servicio de VPS.

Voy a asumir que usted conoce o sabe como configurar su servidor para apuntar un dominio a la dirección IP del servidor.

1.- Crear un usuario donde va estar nuestro proyecto y darle permisos de Administrador


como usuario root, ejecutamos:
[root@localhost ~]#  useradd django
luego editamos el archivo /etc/sudoers, con tu editor de preferencia
[root@localhost ~]# vim /etc/sudoers
allí agrega debajo de la linea donde esta root:
## Allow root to run any commands anywhere
root    ALL=(ALL)       ALL
django        ALL=(ALL)       ALL

2.- Instalar PostgreSQL


Yo particularmente recomiendo que cuando este en el proceso de instalación de Centos, escoge la opción de Servidor de Base de datos, para tener ya PostgreSQL instalado, en caso contrario, instalarlo con el siguiente comando:
[root@localhost ~]#  yum install postgresql postgresql-server postgresql-contrib
Ahora iniciamos el servidor PostgreSQL
 [root@localhost ~]# service postgresql initdb
Arrancamos el servicio:
 [root@localhost ~]# service postgresql start
Y lo configuramos para que arranque cada vez que iniciemos el servidor:
 [root@localhost ~]# chkconfig postgresql on
Luego configuramos los archivos pg_hba.conf y postgres.conf para poder conectarnos al PostgreSQL, por favor revise la documentación para ver las opciones de configuración para su servidor. Editamos postgres.conf
[root@localhost ~]# vi /var/lib/pgsql/data/postgresql.conf
Descomentar las siguientes lineas y agregamos * en la variable listen_addresses si queremos administrar nuestro PostgreSQL desde cualquier equipo:
listen_addresses = '*'
port = 5432
Ahora editamos pg_hba.conf:
[root@localhost ~]# vi /var/lib/pgsql/data/pg_hba.conf
Y cambiamos la siguiente linea por esto:
 # TYPE  DATABASE    USER        CIDR-ADDRESS          METHOD
# "local" is for Unix domain socket connections only
local   all         all                               trust
Ahora reiniciamos nuestro servicio:
[root@localhost ~]# service postgresql restart
Le asignamos una clave al usuario que viene por defecto llamado postgres en la Base de Datos
[root@localhost ~]# su postgres
bash-4.1$
bash-4.1$ psql
could not change directory to "/root"
psql (8.4.13)
Type "help" for help.
postgres=# ALTER ROLE postgres WITH PASSWORD 'abc123';
postgres=# \q
bash-4.1$
En este caso se le asigno la clave abc123, coloque la que mas le conviene.

Y esta nuestro servidor funcionando, ahora creamos nuestro usuario y base de datos para nuestra aplicación:
bash-4.1$ createuser django -P
could not change directory to "/root"
Enter password for new role:
Enter it again:
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n
bash-4.1$ 
Ya tenemos creado el usuario django en PostgreSQL, ese es el usuario que colocaremos en el archivo settings.py de Django, ahora creamos la base de datos de nuestra aplicación:
bash-4.1$ psql
could not change directory to "/root"
psql (8.4.13)
Type "help" for help.
postgres=# CREATE DATABASE mibasedatos OWNER django;
GRANT
postgres=# \q
bash-4.1$ exit

3.- Instalar el repositorio EPEL (Extra Packages for Enterprise Linux):

[root@localhost ~]# wget http://ftp.riken.jp/Linux/fedora/epel/RPM-GPG-KEY-EPEL-6
[root@localhost ~]# rpm --import RPM-GPG-KEY-EPEL-6
[root@localhost ~]# rm -f  RPM-GPG-KEY-EPEL-6
Instalamos en este caso para Centos de 64:
[root@localhost ~]# wget http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
[root@localhost ~]# rpm -ivh epel-release-6-8.noarch.rpm
[root@localhost ~]# yum update

4.- Instalamos Nginx

[root@localhost ~]# yum install nginx
Arrancamos el servicio y lo configuramos para que arranque cada vez que inicie la maquina:
[root@localhost ~]# service nginx start
[root@localhost ~]# chkconfig nginx on
Nota: debemos inhabilitar el firewall que viene por defecto o configurar para acepte peticiones, en nuestro caso lo vamos a inhabilitar:
[root@localhost ~]# service iptables stop
Probamos nuestro servidor Web en nuestro navegador: http://localhost

5.- Instalar Python y librerías necesarias


Instalamos Python y las librerías y componentes necesarios para que nuestra aplicación funcione correctamente con PostgreSQL
[root@localhost ~]# yum install python python-devel python-setuptools python-pip libxml2-devel
Ademas vamos a instalar gcc, el compilador de C y postgresql-devel. que nos lo exige el psycopg2 para conectarnos desde Django a PostgreSQL
[root@localhost ~]# yum install gcc postgresql-devel

6.- Instalación de Virtualenv 


Para instalar virtualenv, utilizamos pip, ya aquí podemos ingresar con nuestro usuario que creamos al principio:

[django@localhost ~]$ sudo pip install virtualenv
Al terminar de instalar, procedemos a crear nuestro ambiente para la aplicación que vamos colocar en producción:
[django@localhost ~]$ virtualenv ~/env
Para activar ejecutamos:
[django@localhost ~]$ source ~/env/bin/activate
Y miren como nos cambia el prompt
(env)[django@localhost ~]$ 
Todo lo que instalemos aquí vía pip, solo estará en este ambiente, esto es muy útil cuando vamos a tener varias aplicaciones con diferentes versiones de Django

7.- Instalamos Django, psycopg2 y nuestra aplicación


Dentro de nuestro ambiente virtual, este pendiente que tengamos en el prompt los paréntesis con nuestro ambiente "(env)[django@localhost ~]$", instalamos lo que requerimos para nuestra aplicación, primero por supuesto Django:
 (env)[django@localhost ~]$ pip install django
La librería para conectarnos a PostgreSQL:
(env)[django@localhost ~]$ pip install psycopg2
La aplicación la copiamos al directorio que queramos, puede ser en /opt/webapps, o donde ustedes lo consideren:
(env)[django@localhost ~]$ sudo mkdir -p /opt/webapps
[sudo] password for django:
Lo mas importante es que el directorio tenga permiso de lectura:
drwxr-xr-x. 4 root root 4096 oct  9 19:31 /opt
Cambiamos el usuario de la carpeta webapps:
(env)[django@localhost ~]$cd /opt
(env)[django@localhost opt]$ sudo chown django:nginx webapps
[sudo] password for django:
 Solo para probar creamos una aplicación en Django:
 (env)[django@localhost opt]$ cd webapps/
(env)[django@localhost webapps]$ django-admin.py startproject demo
(env)[django@localhost webapps]$ cd demo
(env)[django@localhost demo]$ python manage.py runserver
Validating models...
0 errors found
October 09, 2013 - 19:12:55
Django version 1.5.4, using settings 'demo.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Ya podemos ver que nuestro servidor de desarrollo funciona en nuestro navegador con http://127.0.0.1:8000

Nos queda configurar Django con la Base de datos y podemos empezar a crear las tablas de nuestra aplicación:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'mibasedatos',                    
        'USER': 'django',
        'PASSWORD': 'abc123',
        'HOST': 'localhost',                
        'PORT': '',                
    }
}
Solo queda que montes tu aplicación y crear las tablas de tu base de datos en el servidor, ahora a configurar Gunicorn y Nginx:

8.- Instalando y configurando Gunicorn


Gunicorn es también conocido como Green Unicorn (Unicornio Verde), es un servidor WSGI HTTP para Python. Es un pre-fork del proyecto Unicorn de Ruby. Gunicorn es compatible con varios frameworks, soporta WSGI, Django y Paster de forma nativa; consume pocos recursos en ejecución y es bastante rápido. Aquí solo voy a tratar lo básico, cualquier configuración adicional, por favor busque la documentación. Para instalarlo:
(env)[django@localhost opt]$ pip install gunicorn
Probemos nuestra pequeña aplicación con Gunicorn:
(env)[django@localhost demo]$ gunicorn -w3 demo.wsgi:application --bind 127.0.0.1:8000
2013-10-09 20:14:49 [8571] [INFO] Starting gunicorn 18.0
2013-10-09 20:14:49 [8571] [INFO] Listening at: http://127.0.0.1:8000 (8571)
2013-10-09 20:14:49 [8571] [INFO] Using worker: sync
2013-10-09 20:14:49 [8576] [INFO] Booting worker with pid: 8576
2013-10-09 20:14:49 [8577] [INFO] Booting worker with pid: 8577
2013-10-09 20:14:49 [8578] [INFO] Booting worker with pid: 8578
Ahora podemos probar en nuestro navegador  http://127.0.0.1:8000 y allí tenemos nuestra aplicación corriendo en un servidor WSGI HTTP

El comando gunicorn ofrece una extensa lista de opciones para configurar su comportamiento, pero las que he utilizado en esta oportunidad son:
-w INT: número de procesos workers que se arrancarán para servir las peticiones.Este dependerá en medida de la carga de trabajo que tenga la aplicación, también depende del hardware con que se cuente, de acuerdo a la siguiente formula: 2*CPU +1, Para las maquinas que tienen un solo procesador escriba 3.
demo.wsgi:application: la ubicación del archivo wsgi.py y escribimos después de los dos puntos application
--bind ADDRESS: es posible especificar tanto HOST:PUERTO para decirle a gunicorn por donde va recibir peticiones

Bien, hagamos un script para que levante el entorno virtual y ejecute nuestra aplicación con Gunicorn. lo creamos en nuestro home
(env)[django@localhost opt]$ cd
(env)[django@localhost opt]$ vi gunicorn_start.sh
y escribimos en nuestro archivo:
#!/bin/sh
NAME="demo"
DJANGODIR="/opt/webapps/demo"
NUM_WORKERS=3
echo "Arrancando Demo"
cd $DJANGODIR
source ~/env/bin/activate
exec gunicorn -w $NUM_WORKERS $NAME.wsgi:application --bind 127.0.0.1:8000
Salimos del ambiente virtual y ejecutamos para probarlo:
(env)[django@localhost ~]$ deactivate
[django@localhost ~]$ chmod +x gunicorn_start.sh
[django@localhost ~]$ ./gunicorn_start.sh
Arrancando Demo
2013-10-09 20:41:26 [8686] [INFO] Starting gunicorn 18.0
2013-10-09 20:41:26 [8686] [INFO] Listening at: http://127.0.0.1:8000 (8686)
2013-10-09 20:41:26 [8686] [INFO] Using worker: sync
2013-10-09 20:41:26 [8695] [INFO] Booting worker with pid: 8695
2013-10-09 20:41:26 [8696] [INFO] Booting worker with pid: 8696
2013-10-09 20:41:26 [8697] [INFO] Booting worker with pid: 8697
Como podemos ver nos queda nuestro terminal con la aplicación y no nos deja hacer mas nada y si lo cerramos, se cae la aplicación, esto lo vamos a resolver con Supervisor

9.- Instalando y Configurando Supervisor


Supervisor es un sistema cliente servidor que permite a los usuarios monitorear y controlar una serie de procesos en los sistemas operativos tipo UNIX.
Comparte algunos de los mismos objetivos de los programas como launchd , daemontools y runit . A diferencia de algunos de estos programas, no está diseñada para ejecutarse como un sustituto de init. En cambio, está destinado a ser utilizado para el control de procesos relacionados con un proyecto o un cliente, y está destinado a iniciar como cualquier otro programa en el arranque.
le damos Ctrl-C, para salir de Gunicorn y liberar la terminal, instalamos ahora Supervisor con yum:
[django@localhost ~]$ sudo yum install supervisor
Una vez instalado configuramos supervisor para nuestra aplicación en /etc/supervisord.conf. Hay que hacerlo como root:
[django@localhost ~]$ su -
Contraseña:
[root@localhost ~]# vi /etc/supervisord.conf
Agregamos lo siguiente al final del archivo:
[program:demo]
command=/home/django/gunicorn_start.sh
user=django
autostart=true
stdout_logfile=/opt/webapps/demo/logs/gunicorn_supervisor.log
redirect_stderr=true
Creamos la carpeta y archivo para los logs:
[root@localhost ~]# exit
[django@localhost ~]$ mkdir -p /opt/webapps/demo/logs
[django@localhost ~]$ touch /opt/webapps/demo/logs/gunicorn_supervisor.log
Arrancamos Supervisor y lo configuramos para que arranque siempre
[django@localhost ~]$ sudo service supervisord start
[django@localhost ~]$ sudo chkconfig supervisord on
Reiniciamos el servicio propiamente de Supervisor:
[django@localhost ~]$sudo supervisorctl reload
Restarted supervisord
Y ahora veremos nuestra aplicación corriendo:
[django@localhost ~]$ sudo supervisorctl status demo
[sudo] password for django:
demo    RUNNING    pid 8883, uptime 0:11:29
Para detenerla:
[django@localhost ~]$ sudo supervisorctl stop demo
Para reiniciarla:
[django@localhost ~]$ sudo supervisorctl restart demo
Listo ya tenemos nuestra aplicación lista para que arranque al iniciar el servidor

10.- Configuración de Nginx


Buscamos el archivo de configuración de nginx en /etc/nginx/conf.d llamado default.conf y lo cambiamos totalmente por esto:
server {
    listen 80;
    server_name ejemplo.com;
    access_log  /var/log/nginx/server.log;
    root /opt/webapps/demo;
    location /static/ {
    autoindex on;
    alias  /opt/webapps/demo/static/;
    }
    location /media/ {
    autoindex on;
    alias  /opt/webapps/demo/media/;
    }
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
  }
Lo importante, tu carpeta static y media, es importante colocar la ruta exacta hacia esos archivos, de lo contrario, no te permitirá ver los estilos de tu aplicación. Otra cosa importante es la dirección por donde esta sirviendo tu aplicación el Gunicorn.

Reiniciamos nuestro servicio de Nginx y a probar nuestra aplicación
[django@localhost ~]$ sudo service nginx start
Listo, espero sus comentarios y sugerencias, hasta la próxima.



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