gunicorn + virtualenv + django + debian


Requerimientos

pip install gunicorn django

Creacion de proyecto con django

Creamos un proyecto con django, en esta caso voy a utilizar django 1.6, con python 3.3.

$ cd /opt/
$ virtualenv -p python3.3 test
$ cd test
$ source bin/activate
$ pip install django gunicorn
$ django startproject test
$ cd test

Configuracion de gunicorn

Dentro del proyecto de django creamos un archivo para la ejecución de  gunicorn que en esta caso llamaremos, gunicorn_server.py

$ pwd
/opt/test/test/
$ vim gunicorn_server.py

command='/opt/test/bin/gunicorn'
pythonpath='/opt/test/test/'
bind = '0.0.0.0:80'
logfile = "/var/log/test.gunicorn.log"
workers =2
loglevel = 'error'
#daemon = True
debug = True
#user = 'www-data'


Guardamos y cerramos, para detalles de configuracion puedes visitar la documentación.

Verificamos que todo esta bien hasta el momento. y ejecutamos la siguiente linea.

/opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi

Si todo nos va bien,  debemos ver la pagina en nuestro navegar con solo ingresar 127.0.0.1 si estamos dentro de nuestra maquina local y no tenemos otro servidor http con lo que nos pueda dar alguno error, si tienes algún otro servidor http corriendo en el puerto 80 cambia la configuración de gunicorn al puerto 81 o al que mas te guste.

Si tenemos problemas con los archivos estáticos, hacemos lo siguiente cambios en la urls.py

from django.contrib.staticfiles.urls import staticfiles_urlpatterns
urlpatterns += staticfiles_urlpatterns()


Esta caso solo es para desarrollo para producción es mejor colocar a gunicorm detrás de un servidor como nginx, puedes ver en gunicorn una configuración básica para el servidor nginx aqui

Configuración de supervisor

Que es supervisor?

Supervisor es un sistema para controlar y mantener el estado de proceso, similar a lo que hace init, pero no pretende ser una sustitución init.

Se gestionará PROCESOS o grupos de procesos que necesitan ser iniciado y detenido en orden individual, y es posible controlar el estado del proceso individual a través de un mecanismo RPC, lo que permite a los usuarios normales reinician procesos.


# aptitude install supervisor


Al instalar supervisor se crea un directorio /etc/supervisor/conf.d/ , dentro de ese directorio vamos a crear un archivo que se llamara test.conf.

# cd /etc/supervisor/conf.d/
# vim test.conf 
[program:test]
process_name=test
command=/opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi
directory=/opt/test/test/
environment=PYTHONPATH=/opt/test/bin/:/opt/test/lib/
#user=www-data
autostart=true
autorestart=true
stdout_logfile=/var/log/test.supervisord.log
redirect_stderr=true
Guardamos y cerramos. Recargamos supervisor para que lee los nuevos archivos, e iniciamos el proceso de lineas.
#supervisorctl
supervisor> 
supervisor> help
default commands (type help <topic>):
=====================================
add    clear  fg        open  quit    remove  restart   start   stop  update 
avail  exit   maintail  pid   reload  reread  shutdown  status  tail  version
supervisor> reload
Really restart the remote supervisord process y/N? y
Restarted supervisord
supervisor> reread
test: available
supervisor> status
test                           BACKOFF    Exited too quickly (process log may have details)
supervisor> restart test
test: stopped
test: started
verificamos que tengamos el puerto 80 abierto
netstat -pan | grep :80


Debe aparecer algos como esto

# netstat -pan | grep :80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      17100/python3.3
tcp        0      0 127.0.0.1:80        190.145.63.69:40390     TIME_WAIT   -               
tcp        0      0 127.0.0.1:80        190.145.63.69:40391     ESTABLISHED 17105/python3.3 
tcp        0      0 127.0.0.1:80        190.145.63.69:40392     ESTABLISHED 17103/python3.3 

y revisamos cuantos trabajadores hay creados por gunicorn.


# ps ax | grep gunicorn
17100 ?        S      0:00 /opt/test/bin/python3.3 /opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi
17103 ?        S      0:00 /opt/test/bin/python3.3 /opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi
17104 ?        S      0:00 /opt/test/bin/python3.3 /opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi
17105 ?        S      0:00 /opt/test/bin/python3.3 /opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi
17106 ?        S      0:00 /opt/test/bin/python3.3 /opt/test/bin/gunicorn -c /opt/test/test/gunicorn_server.py test.wsgi
17118 pts/0    S+     0:00 grep gunicorn

Con esto podemos configurar nuestro servidor http preferido ne mi caso siempre utilizo nginx, por lo facil de configurar y nivelar las cargas.

Indice de sonidos en asterisk


Esto trata de ser un indice de los archivos de audio de asterisk y su significado, con el fin de no tener que escuchar todas la grabaciones para encontrar una que necesitemos. 

También se puede utilizar con el fin de hacer mini aplicaciones, como un cambio de contraseña, algo mas llamativo para el cliente.



 Archivos de sonido Generales





agente-alreadyon: Ese agente ya ha iniciado sesión. Por favor, introduzca su número de agente seguido por la tecla numeral.


agente-incorrect: Login incorrecto . Por favor, introduzca su número de agente seguido por la tecla numeral. 
agente-loggedoff: Agente desconectado.
agente-loginok: Agent ha autentificado
agente-newlocation: Por favor ingrese una nueva extensión , seguido por libra.pass-agente: Por favor, introduzca su contraseña seguida por la tecla numeral.agente-user: Agente de inicio de sesión. Por favor, introduzca su número de agente seguido por la tecla numeral. 
auth-incorrect: Contraseña incorrecta. Por favor, introduzca su contraseña , seguido de la tecla numeral. 
auth-thankou: Gracias.
beep
: es un tono sencillobeeperr : se trata de un pitido de error 

conf- adminmenu: Por favor, pulse 1 para activar o desactivar usted mismo, 2 para bloquear o desbloquear la conferencia, 3 para expulsar el último usuario , 4 o 6 para disminuir o aumentar el volumen de conferencias, 7 o 9 para subir o bajar el volumen , o 8 para salir .
d
emo-abouttotry : Estoy a punto de intentar una conexión a Inter - Asterisk Exchange a un servidor de demostración situada en Digium . Para que esto funcione ya debe estar conectado a Internet. Por favor, espere un momento mientras yo trato de hacer la conexión.

demo- congrats: Felicitaciones. Ha instalado y ejecutado el PBX de código abierto Asterisk con éxito. También ha instalado un conjunto de sonidos de ejemplo y los archivos de configuración que le ayudarán a empezar. Al igual que un PBX normal de que vas a navegar esta demostración por los dígitos de marcación. Si está utilizando un controlador de canal de la mesa en lugar de un teléfono real, usted puede utilizar el dial , contestar y colgar comandos para simular las acciones de un teléfono estándar.
demo
-echodone: La prueba de eco se ha completado .
d
emo-echotest: Estás a punto de entrar en una prueba de eco . En este modo, todo lo que dices se repetirá de nuevo a usted tan pronto como la reciba . El propósito de esta prueba es para darle una sensación audible de la latencia entre el usuario y la máquina que ejecuta la aplicación de prueba de eco. Usted puede terminar la prueba colgando o pulsando la tecla numeral .

demo-enterkeywords: Por favor ingrese una o varias palabras clave separadas por * y luego presione la tecla numeral.
demo-instruct: Si a usted le gustaría aprender más información técnica sobre Asterisk marcar 2 ahora. Si desea poner a prueba la capacidad de voz sobre IP de Asterisk puede marcar 500 para intentar un intercambio Inter - Asterisk o conexión IAX a un servidor de demostración en Digium . Para que esta prueba funcione tendrá que estar conectado a Internet y tener al menos un módem de 28,8 kilobits . Para ejecutar una prueba de eco de línea 600. Esta prueba es muy útil cuando se ha conectado a este servidor Asterisk desde una ubicación remota . La configuración de ejemplo también tiene un único usuario con la extensión 1234 y la contraseña 4242 . Ese usuario está configurado para que suene la consola cuando se marca su extensión. Si marca 1234 se puede tratar de llamar a la consola. Si la consola está ocupado o no está disponible se le dará la opción de dejar mensajes de voz. Para comprobar el correo de voz de la extensión de línea de usuario 8500 para entrar en el sistema de correo de voz. Por último , puede presionar la tecla numeral para desconectarse de la central.
d
emo-moreinfo: Asterisk es un código abierto con todas las funciones PBX y plataforma IVR que se ejecuta en el sistema operativo Linux. Para obtener más información, visite www.asterisk.org .
d
emo-nogo: Me temo que era incapaz de crear una conexión con el servidor Asterisk Digium demostración . Usted puede encontrar información de depuración útil en la consola de Asterisk.
d
emo-nomatch: Lo siento No hay resultados para las palabras clave 

demo-tranks:  Adiós . Gracias por probar el Asterisk Open Source PBX.
dictate/both_help: pulse * para alternar pausa, presione # para introducir un nuevo nombre de archivo de dictado
dictate
/enter_filename: Introduzca un nombre de archivo de dictado numérico seguido de # # o simplemente para salir
dictate
/forhelp: presione 0 para ayuda 

dictate/pausa: Pausa
dictate
/pausa: Pausado 

dictate/play_help: pulse 1 para cambiar al modo de grabación, pulse 2 para alternar la reproducción rápida , pulse 7 para saltar hacia atrás, pulse 8 para saltar hacia delante 
dictate/playback: Reproducción 
dictate/playback_mode: el modo de reproducción
dictate
/record: record
dictate
/record_help: pulse 1 para cambiar al modo de reproducción, pulse 8 para truncar el archivo y volver a empezar
dictate
/record_mode: modo de grabación
dictate
/truncating_audio: audio truncando
dollars
: Dólares

followme/call- from.wav : llamada entrante de 
followme/no-recording.wav: usted tiene una llamada entrante 
followme/options.wav: pulse 1 para aceptar esta llamada, o 2 para rechazarla 
followme/pls -hold- mientras-try.wav: favor espere mientras trato de localizar a la persona que está llamando 
followme/sorry.wav: Lo siento, pero no he podido localizar a la persona que está llamando
followme/status.wav: la persona que llama no está en su escritorio , voy a tratar de localizarlos para usted 
hello-world: ¡Hola, mundo.
hours: horas
invalid: Lo siento , eso no es una extensión válida . Por favor, inténtelo de nuevo. 
minutes: minutos
pbx-invalid: lo siento , eso no es una extensión válida . Por favor, inténtelo de nuevo. 
pbx-invalidpark: Lo siento , no hay una llamada estacionada en esa extensión. Por favor, inténtelo de nuevo. 
pbx-transfer : Transferencia. 
phonetic/9_p: niner 
phonetic/a_p: alpha 
phonetic/b_p: bravo 
phonetic/c_p : charlie 
phonetic/D_P: delta
phonetic/E_p: echo
phonetic/f_p: foxtrot 
phonetic/G_P: golf
phonetic
/h_p: Hotel 

phonetic/i_p: india
phonetic/J_P: juliet
phonetic/k_p: kilo 
phonetic/l_p: lima 
phonetic/m_p: mike 
phonetic/n_P: noviembre
phonetic/niner: niner 
phonetic/O_P: oscar
phonetic/p_p: papá
phonetic/q_p: quebec
phonetic/r_p: romeo
phonetic/s_p: sierra
phonetic
/t_p: tango

phonetic/u_p: uniforme 
phonetic/V_P: victor
phonetic/w_p: whisky
phonetic/x_p: radiografía
phonetic/y_p: yankee 
phonetic/Z_p: zulu 
priv-callee-opciones: Marque 1 si desea que esta persona que llama para comunicarnos con usted directamente ahora y en el futuro. Marque 2 si usted desea enviar esta persona que llama al correo de voz ahora y para siempre . Marque 3 para enviar esta persona que llama a los menús de tortura , ahora y para siempre . Marque 4 para enviar esta persona que llama a un menú cortés "no llamar " , ahora y para siempre . Marque 5 para permitir que esta persona que llama para venir directamente a través de usted en el futuro , pero sólo por esta vez , los envían al correo de voz.
priv- callpending: Tengo una espera de llamadas, que se presenta a sí mismos como:
priv introsaved: Gracias. Por favor aguarde , mientras que intento conectar con su fiesta! 
priv-recordintro: Al escuchar el tono , por favor diga su nombre.
privacy-incorect: Lo siento , ese número no es válido. 
privacy-prompt: Por favor, introduzca su número de teléfono, empezando por el código de área. 
privacy-thanyou: Gracias.
privacy-unindent: La persona a la que está tratando de llegar no acepta llamadas no identificadas. 
queue-callswaiting: El esperar a hablar con un representante 
queue-holdtime: El tiempo de espera estimado es actualmente
queue-less-than: menos de 
queue-minutes: Minutos 
queue-periodic-announce: Todos nuestros represenatives Actualmente ocupado. Por favor, permanezca en la línea y su llamada será atendida por el siguiente representante disponible.
queue-reporthold: Tiempo de mantenimiento 
queue-segundos: Segundos 
queue-gracias: Gracias por su paciencia 
queue-thereare: Usted se encuentra el número de llamadas 
queue-youarenext: Su llamada es ahora el primero en línea y será atendida por el siguiente representante disponible.
screen-callee-options: Usted tiene las siguientes opciones: Marque 1 si quiere conectar inmediatamente a la llamada entrante. Marque 2 si usted desea enviar esta persona que llama al correo de voz . Marque 3 para enviar esta persona que llama a los menús de tortura. Marque 4 para enviar esta persona que llama a un menú cortés "no llamar " . 
seconds: segundos 
spy-agent: Agente
spy-dahdi: DAHDI 
spy-h323: H.323
spy-iax2: IAX ( nota: no dice "2")
spy-iax: IAX 
spy-mgcp: MGCP
spy- sip: SIP
spy-skinny: Skinny 
spy-zap: Zap 
ss-noservice: El número que ha marcado no está en servicio. Por favor, compruebe el número y vuelva a intentarlo. 
transfer: Por favor espere mientras yo trato de que la extensión. 
tt-allbusy: Todos los representantes de la familia se encuentran asistiendo a otros agentes de telemercadeo . Por favor, mantenga y su llamada será atendida en el orden en que fue recibida. 
tt-monkeys: sonido de los monos gritando
tt-monkeysintro: Se les ha llevado por los monos 
tt-somethingwrong: Algo está terriblemente mal 
tt-weasels: comadrejas han comido nuestro sistema telefónico
dir-firstlast: ... letras del nombre o apellido de su partido. 
dir-pasado: ... letras del apellido de su partido.
dir-Multi1: Presionar ... 
dir-MULTI2 : ... para ...
dir-MULTI3: ... extensión ... 
dir-Multi9: Presione 9 para más entradas.dir-pls-enter: Por favor ingrese el primer ... 
dir-usingkeypad: ... con el teclado de marcación por tonos . Utilice la tecla 7 para Q , y la tecla 9 para la Z. 
dir-welcome: Bienvenido al directorio 
queue-quantity1: En la actualidad , hay más de ... 
queue-quantity2: ... las personas que llaman a la espera de hablar con un representante. 
spy- console: Consola 
spy-jingle: Jingle
spy-local: Local 
spy-misdn : M I S D N 
spy-mobile: Bluetooth Mobile 
spy-nbs: N B S 
spy-unistim: Unistim
spy-usbradio: Radio USB
vm-duration: Este mensaje tiene una duración ... 
vm-invalid-password: Eso contraseña no cumple los requisitos mínimos para este buzón. Por favor, inténtelo de nuevo.
vm-invalidpassword: Eso no es una contraseña válida. Por favor, inténtelo de nuevo.
vm-marked-nonurgent: situación urgente eliminado.
vm-marked-urgent : Mensaje marcado urgente. 
vm-onefor-full: Pulse uno para escuchar ... 
vm-opts-fulls: pulse 2 para acceder a los mensajes guardados en otras carpetas. Pulse 3 para grabar un mensaje para otro buzón. Pulse 0 para los saludos y la gestión de contraseñas.
vm-review-nonurgent: Presione 4 para eliminar el estado de urgencia de este mensaje. 
vm-review-urgent: Pulse 4 para marcar este mensaje como urgente. 
vm-tmpexists: Hay un saludo temporal , que anula sus saludos estándar.
vm-urgent: urgente


Sonidos demos 


demo-abouttotry: Estoy a punto de intentar una conexión a Inter-Asterisk Exchange a un servidor de demostración situada en Digium . Para que esto funcione ya debe estar conectado a Internet. Por favor, espere un momento mientras yo trato de hacer la conexión.
demo-congrats: Felicitaciones. Ha instalado y ejecutado el PBX de código abierto Asterisk con éxito. También ha instalado un conjunto de sonidos de ejemplo y los archivos de configuración que le ayudarán a empezar. Al igual que un PBX normal, que vas a navegar esta demostración por los dígitos de marcación. Si está utilizando un controlador de canal de la mesa en lugar de un teléfono real, usted puede utilizar el dial , contestar y colgar comandos para simular las acciones de un teléfono estándar. 
demo-echodone: La prueba de eco se ha completado.
demo-echotest: Estás a punto de entrar en una prueba de eco . En este modo, todo lo que dices se repetirá de nuevo a usted tan pronto como la reciba . El propósito de esta prueba es para darle una sensación audible de la latencia entre el usuario y la máquina que ejecuta la aplicación de prueba de eco. Usted puede terminar la prueba al colgar o presionar la tecla numeral.
demo-enterkeywords: Por favor ingrese una o varias palabras clave separadas por la estrella y luego presione la tecla numeral.
demo-instruct: Si a usted le gustaría aprender más información técnica acerca de Asterisk , marque 2 ahora. Si desea probar la voz - sobre- IP capacidades de Asterisk , puede marcar quinientos intentar un intercambio Inter - Asterisk, o la conexión a un servidor IAX demostración en Digium . Para que esta prueba funcione, usted tendrá que estar conectado a Internet y tener al menos un módem de veintiocho puntos y ocho kilobit . Para ejecutar una prueba de eco , marque seiscientos . Esta prueba es muy útil cuando se ha conectado a este servidor Asterisk desde una ubicación remota . La configuración de ejemplo también tiene un solo usuario con la extensión de uno, dos , tres, cuatro , y una contraseña de cuatro dos cuatro dos. Ese usuario está configurado para que suene la consola, cuando se marca su extensión. Si marca un dos tres cuatro, se puede tratar de llamar a la consola. Si la consola está ocupado o no está disponible , se le dará la opción de dejar mensajes de voz. Para comprobar el correo de voz de la extensión de línea de usuario ocho cinco cero cero para entrar en el sistema de correo de voz. Por último , puede presionar la tecla numeral para desconectarse de la central.
demo-moreinfo: Asterisk es una fuente abierta , la plataforma con todas las funciones de PBX y de IVR que se ejecuta en el sistema operativo Linux. Para obtener más información, visite www punto dot org asterisco.
demo-nogo: Me temo que era incapaz de crear una conexión con el servidor Asterisk Digium demostración . Usted puede encontrar información de depuración útil en la consola de Asterisk.
demo-nomatch : Lo siento , no hay resultados para las palabras clave.
demo-tranks: Adiós . Gracias por probar el Asterisk Open Source PBX .
  
Fuente:
http://www.voip-info.org/wiki/view/Asterisk%20sound%20files 

Convertir 'H:M:S' a segundos.

En ocacaciones necesitamos convertir formato 'H:M:S' y hacerlo de una manaera mas pythonista... Lo vamos hacer bajo python3.3


>>> t = "1:23:45"
>>> print (sum(int(x) * 60 ** i for i,x in enumerate(reversed(t.split(':')))))
5025



>>> import time
>>> from datetime import datetime
>>> ahora = time.time()
>>> print (ahora)
1395079433.995347
>>> diferencia = datetime.fromtimestamp(time.time() - ahora).srtftime('%H:%M:%S')
>>> print (diferencia)
'00:01:01'
>>> print (sum(int(x) * 60 ** i for i,x in enumerate(reversed(diferencia.split(':')))))
61


Python, Redis una combinacion estupenda.

Recientemente me dedica a aprender redis, que es redis para los que no tengan una idea.

Que es redis

Es un base de datos NoSQL en memoria, basado en el almacenamiento de llave-valor (key-value), Su nombre es acrónimo de Servidor de DIccionario REmoto, esta bajo la licencia BSD por lo que es considerado software de código abierto. Puede ser usado para una base de datos duradera y persistente.

Redis es sumamente veloz, esto debido a su sencilla estructura ausente de logica relacional y su punto mas importante, la base de datos esta cargada en memoria por ellos sus altos niveles de velocidad.

Los comandos de redis no son complejos, fáciles de aprender en la pagina oficial encuentras la lista de todos los comandos que se pueden utilizar.

Soporta replicación de tipo maestro-esclavo, de esta manera un maestro puede replicar la información en muchos esclavos, y un esclavo puede ser maestro de otro esclavo.

Pagina oficial: http://redis.io/
Codigo fuente: https://github.com/antirez/redis

Tipo de datos

  • Cadenas de caracteres.
  • Listas
  • Sets
  • Sorted sets
  • Hashes



Clientes

Redis tiene mucho librerías con la que podemos interactuar puedes echar un vistazo. http://redis.io/clients


Cliente para python: redis-py

voy a utilizar https://github.com/andymccurdy/redis-py
documentación:  http://redis-py.readthedocs.org/en/latest/


Instalacion

La instalación la hice en debian 7 y python3.3

# aptitude install redis-server
# pip install redis


Utilización de redis en python

Conexión e ingreso a redis
ingresar al cli de redis.
$ redis-cli
redis 127.0.0.1:6379>ping
PONG

conexión desde python
>>> import redis
>>> r = redis.Redis()
>>> r.ping
True

Cadena de caracteres

Esta es quizás la más fácil de entender, simplemente una llave posee un valor común y corriente tipo string:
Formato de la cadena de caracteres primero la llave y después el valor.

SET key valor
GET key valor

redis:
redis 127.0.0.1:6379> SET nombre Jhon
OK
redis 127.0.0.1:6379> SET apellido Doe
OK
redis 127.0.0.1:6379> SET nombre:codigo 123ASDF
OK

redis 127.0.0.1:6379> GET nombre
"Jhon"
redis 127.0.0.1:6379> GET apellido
"Doe"
redis 127.0.0.1:6379> GET nombre:codigo
"123ASDF"

python:
>>> r.set('nombre', 'Jhon')
True
>>> r.set('apellido', 'Doe')
True
>>> r.set('nombre:codigo', '123ASDF')
True
>>> r.get('nombre')
'Jhon'
>>> r.get('nombre')
'Doe'
>>> r.get('nombre:codigo')
123ASDF

Listas

Listas son simplemente listas de cadenas, ordenadas por orden de inserción. Es posible añadir elementos a una lista de Redis empujando nuevos elementos en la cabeza (a la izquierda) o en la cola (a la derecha) de la lista.

redis:
redis 127.0.0.1:6379> RPUSH mylist "uno"
(integer) 1
redis 127.0.0.1:6379> RPUSH mylist "dos"
(integer) 2

redis 127.0.0.1:6379> LRANGE mylist 0 2
1) "uno"
2) "dos"

python: 
>>> r.rpush('list', '1')
1L
>>> r.rpush('list', '2')
2L
>>> r.lrange('list', '0', '2')
['1', '2']

Sets

Son una colección desordenada de cadena de caracteres "strings". Es posible agregar, quitar, y la prueba de la existencia de miembros.
 
Son conjuntos que ordenados o no ordenas, en este pequeño ejemplo solo incluiré los no ordenados.

redis:
redis 127.0.0.1:6379> SADD conjunto 1
(integer) 1
redis 127.0.0.1:6379> SADD conjunto 2
(integer)2redis 127.0.0.1:6379> SMEMBERS conjunto
1) "1"
2) "2"


python:
>>> r.sadd('conjunto', '1')
1
>>> r.sadd('conjunto', '2')
1
>>> r.smembers('conjunto')
set(['1', '2', '23'])


Hashes

Son mapas entre los campos de cadena y valores de la cadena, por lo que son el tipo perfecto de datos para representar los objetos (por ejemplo: Un usuario con una serie de campos como el nombre, apellidos, edad, etc).
  
Los hash de redis son como los diccionarios de python

redis:
redis 127.0.0.1:6379> HSET mihash field1 "Hola"
(integer) 0 
redis 127.0.0.1:6379> HSET mihash field2 "Mundo"
(integer) 0
redis 127.0.0.1:6379> HGET mihash field1
"Hola"
redis 127.0.0.1:6379> HGETALL mihash
1) "Hola"
2) "Mundo"


python:
>>> r.hset('mihash', 'field1', 'hola')
1L
>>> r.hset('mihash', 'field2', 'mundo')
1L
>>> r.hget('mihash', 'field1')
'hola'
>>> r.hgetall('mihash')
{'field2': 'mundo', 'field1': 'hola'}

Casos de exito

Es un clon de reddit, escrito por el autor de redis
http://lamernews.com/

YouPorn
http://mundogeek.net/archivos/2012/02/16/el-porno-prefiere-redis/http://mundogeek.net/archivos/2012/02/16/el-porno-prefiere-redis/

Quien utiliza redis
http://redis.io/topics/whos-using-redis


Fuentes:

http://codehero.co/como-instalar-configurar-y-usar-redis/
https://es.wikipedia.org/wiki/Redis
http://redis.io/

 

Convertir disco virtualbox.ova a qemu.qcow2


Migrar discos de VirtualBox a QEMU/KVM.


El Open Virtualization Format (OVF) es un estándar abierto para el envasado y distribución de dispositivos virtuales para el uso en máquinas virtuales. Un archivo OVA es una alternativa un archivo para empaquetar un directorio de archivos OVF múltiples. Este es un pequeño ejemplo de cómo convertir OVA para el uso en un entorno KVM debian 7.1. El archivo OVA fue descargado, y para no instalar virutalbox y toque el kernel.

Extraccion del Archivo OVA


tar xvf VirtualBox.ova
VirtualBox.ovf
VirtualBox.vmdk
Usted consigue Virtual_Appliance_Debian-disk.vmdk que es un archivo de disco de máquina virtual. El formato de archivo VMDK fue desarrollado para el uso en VMWare o VirtualBox. Es un formato abierto.

Convertir VMDK to QCOW2

$ qemu-img convert -O qcow2 VirtualBox.vmdk qemu_kvm.qcow2 

Corres la maquina virtual por la consola.

kvm -m 512 -usbdevice tablet -hda ubuntu.qcow


Bienvenidos a KVM!! :)

CRUD Class View Django

Explicaremos de la manera  mas sencilla posible como hacer para crear un sistema de Lectura, escritura, actualizacion  y borrado mas conocido como CRUD utilizando Class View en django 1.5 y 1.6

Crearemos las Class View, asumiendo que ya tienen creado el model en este caso llamado BlogAztrock

La CreateView y UpdateView se encargan de Crear y Actualizar contenido en el model y crear el formulario para el ingreso de la información

# Importamos las clases para creacion, actualizacion y borrado.
# Importamos las clases para creacion, actualizacion y borrado.
from django.views.generic.edit import CreateView, UpdateView, DeleteView
# Importamos la clase para listar
from django.views.generic import ListView
# Importamos una funcion para reversar las url por el nombre 
from django.core.urlresolvers import reverse_lazy
# Importamos un modelo. 
from myapp.models import BlogAztrock

class AztrockCreate(CreateView):
    model = BlogAztrock
    fields = ['name'] 
    template_name = 'form.html'
    success_url = reverse_lazy('aztrock-listar') 

class AztrockUpdate(UpdateView):
    model = BlogAztrock
    fields = ['name']
    template_name = 'form.html' 
    success_url = reverse_lazy('aztrock-listar') 

class AztrockDelete(DeleteView):
    model = BlogAztrock 
    template_name = 'eliminar_blog.html' 
    success_url = reverse_lazy('aztrock-listar')

class AztrockListar(ListView):
    model = BlogAztrock
    template_name = 'blog-aztrock.html'
Creamos las URL
from django.conf.urls import patterns, url
from apps.views import AztrockCreate, AztrockUpdate, AztrockDelete, AztrockListar

urlpatterns = patterns('',
    url(r'aztrock/$', AztrockListar.as_view(), name='aztrock-listar'), 
    url(r'aztrock/agregar/$', AztrockCreate.as_view(), name='aztrock_agregar'),
    url(r'aztrock/(?P\d+)/$', AztrockUpdate.as_view(), name='aztrock_actualizar'),
    url(r'aztrock/(?P\d+)/borrar/$', AztrockDelete.as_view(), name='aztrock_eliminar'),
)
template form.hml CreateView y UpdateView
{# ==== TEMPLATE DE LA CLASE CREATEVIEW Y UPDATEVIEW ==== #}
{% extends "base.html" %}

{% block content %}
    

listar

{{ form }} {% endblock %}
blog_aztrock.html
{# ==== TEMPLATE DE LA CLASE LISTVIEW ==== #}
{% extends 'base.html'>

Aztrock Blog

    {% for a in object_list %}
  • {{ a.nombre }}
  • {% endfor %}
{% endblock %}
eliminar_blog.html
{# ==== TEMPLATE DE LA CLASE DELETEVIEW ==== #}
{% extends 'base.html' %}

{% block content %}

eliminar ?

{% csrf_token %}
{% endblock %}
 
 
En caso de que que no definamos template_name en AztrockUpdate y AztrockCreate, la clase
buscara en el directorio template general y el directorio template de la aplicacion, el siguiente
template por defecto.  aztrock_form.html.
 
 
En el caso de AztrockDelete, la clase buscara apps/aztrock_confirm_delete.html, se debe confirmar
la eliminacion, para poder eliminar lo que queremos.
 
En este ejemplo no necesitamos crear un Form ya que la clase crea el formulario y podemos seleccionar
el campo que deseamos ver.

En produccion: nginx + uwsgi + django

Puesta en producción de django, utilizando nginx y uwsgi, que ambos me parecen muy eficientes, toda la vida utilizando apache y pasar a nginx fue una odicea total pero estoy muy a gusto con el rendimiento que ha mostrado nginx, en cuanto a uwsgi es el único wsgi(Web Server Gateway Interface) que e utilizado, incluyendo módulos para interprete de python o cualquier cosa con la que puede colocar en producción un proyecto de django.

Algunos conceptos básicos:

Nginx: Es un servidor proxy inverso de codigo abierto para HTTP, HTTPS, SMTP, POP3 e IMAP, asi como también un equilibrador de carga, cache HTTP y un servidor web. El proyecto ngnix comenzó con un fuerte enfoque en la alta concurrencia, alto rendimiento y bajo consumo de memoria.

WSGI: (Web Server Gateway Interface), es un interfaz simple y universal entre los servidor web y applicaciones web o marcos para el lenguaje de programación Python.

uWSGI: Es un software de código abierto BSD, para el modulo de integración de nginx, GPL para el modulo de integración de apache2. uWSGI Entrega una pila completa para aplicaciones WEB conectadas en red/cluster, implementacion mensajes/paso de objetos, cache, RPC y comunicacion entre procesos. Se puede ejecutar en los modos de preforking, threaded, asynchronous/eventos y soporta diversas formas de hils tales (uGreen, Greenlet, Fiber), Ofrece varios métodos de configuración: atraves de linea de comandos, variables de entorno, atra vez de XML, INI, archivos YAML, atravez de LDAP y muchos mas.

DJANGO: Es un framework web en python de alto nivel que fomenta el rápido desarrollo y el diseño limpio y pragmático.

La instalación y toda las configuración la voy a realizar con python 3, django 1.5, GNU/linux Debian 7, ngnix y virtualenv.

1. Instalamos todo lo necesario (Si el directorio www no existe, lo puedes crear o puede crear el entorno donde tu quieres)
# aptitude install nginx uwsgi uwsgi-plugin-python uwsgi-pluing-python3 python-virtualenv


2. Creamos un entorno nuevo de python para django.
# cd /var/www/

# virtualenv -p python3.2 pruebas_uwsgi


3. Ingresamos al entorno de pruebas_uwsgi e instalamos django
# source pruebas_uwsgi/bin/activate

# pip install django==1.5.5


4. Creamos un nuevo proyecto de django.
# cd pruebas_uwsgi 

# django-admin.py startproject pruebas


5. Configuramos uwsgi.
# vim /etc/uwsgi/apps-available/pruebas.ini

[uwsgi]

    plugins = python3

    virtualenv = /var/www/pruebas_uwsgi/

    chdir = /var/www/pruebas_uwsgi/pruebas/

    pythonpath = ..

    env = DJANGO_SETTINGS_MODULE=pruebas.settings

    module = django.core.handlers.wsgi:WSGIHandler()

    touch-reload = /var/www/pruebas_uwsgi/pruebas/pruebas/settings.py



    processes = 2

    threads = 1

    workers =1

    stats = 127.0.0.1:1717



# /etc/init.d/uwsgi restart


plugins = python3: Seleccionamos la version de python a ejecutar nuestro proyecto
virtualenv: Le decimos la dirección absoluta de nuestro entorno virual de python
chdir: Le decimos la dirección absoluta del proyecto.
env: ubicacion del archivo settings.py
module:
touch-reload: Archivos que si son tocados uwsgi se recargara, puedes tener varias lineas como desees.
processes: Cantidad de procesos. esta opción depende de la maquina en que se esta trabajando
threads: Cantidad de hilos se crearan con esta apps, esta opción depende de la maquina en que se este trabajando.
workers: Cantidad de trabajadores uwsgi
stats: Estadísticas de uwsgi

6. Luego creamos un enlace para uwsgi tenga presente que apps estan activas.
# cd /etc/uwsgi/apps-enabled/ 
 
# ln -s ../apps.available/pruebas.ini

# /etc/init.d/uwsgi restart


7. Configuramos nginx.
# vim /etc/nginx/sites-available/pruebas

server {

    listen 80;

    server_name 127.0.0.1



    access_log /var/log/nginx/local_access.log;

    error_log /var/log/nginx/local_error.log;



    location /static/ {

         gzip_static on;

         alias /var/www/pruebas_uwsgi/pruebas/static/;

     }



     location / {

          uwsgi_read_timeout 600;

          gzip_static on;

          include uwsgi_params;

          uwsgi_pass unix:///var/run/uwsgi/app/prueba/socket;

     }

} 
  
# cd /etc/nginx/sites-enabled/
# ln -s /etc/nginx/sites-available/pruebas
# /etc/init.d/nginx restart

8. Probamos en un navegador web preferido. en mi caso utilizo como principal iceweasel. 127.0.0.1, Debe aparecer el mensaje clásico "It worked!" y si ingresamos con 127.0.0.1:1717, podemos ver las estadísticas de funcionamiento de uwsgi.





nota: He utilizado un maquina virtual, KVM y manejador virt-manager. jejeje por eso se ve algo anti-estetico.

cualquier duda, comentario o corrección no duden en dejar el comentario, y alguna sugerencia sobre algún tema, sera bienvenida.

Convertir decimal a formato monetario python 3

Convertir un decimal a un formato monetario que se desee La siguiente función sacada de la documentación de python y utilizando python 3.3.
from decimal import *

def moneyfmt(value, places=2, curr='', sep=',', dp='.', pos='', neg='-', trailneg=''):

    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
    digits = list(map(str, digits))
    build, next = result.append, digits.pop
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else '0')
    if places:
        build(dp)
    if not digits:
        build('0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(curr)
    build(neg if sign else pos)
    return ''.join(reversed(result))
Con tan solo pasarle el decimal a converit, la fucnion devuelve un str, con el formato $1.000.00.
>>>moneyfmt(Decimal('1000000.00'))
'1,000,000.00'
Si le pasamos los datos como la cantidad de centavos por defecto es '2', el signo de pesos dolar o euro por defecto es '$', el tipo de separador para los ciento y miles '.'
>>> moneyfmt(Decimal('1000000.00'), 2, '$', ',')
'$1,000,000.00'
Otros parámetros como el separador de los centavos por defecto es ',' el carácter si el decimal es positivo por defecto '', el carácter si es negativo por defecto es '-' y el carácter final en tal caso que sea negativo.
>>> moneyfmt(Decimal('1000000.00'), 2, '$', ',', ',', '+', '(', ')' )
'+$1,000,000,00'
>>> moneyfmt(Decimal('-1000000.00'), 2, '$', ',', ',', '+', '(', ')' )
'($1,000,000,00)'
Fuente: http://docs.python.org/3.3/library/decimal.html#recipes

Primeros pasos con class-based view (vistas basas en clases) en django

Estoy muy acostumbrado a crear mis vistas con funciones eso fue lo que aprendes en cualquier tutorial que encuentras googleando (tambien utilizo mucho duckduckgo) y con funciones fue como escribí mi primer "Hola Mundo¡", pero leyendo las documentación de django 1.5, en esots dias encontré algo que me a gustado mucho y se ve muy practico que son las class-based views, y se ven muy interesante y practivo, además de darme otro punto de vista de como reutilizar código.

Ejemplo de function view, sacados de la pagina https://docs.djangoproject.com
# view.py
from django.http HttpResponse

def my_vista(request):
    if request.method == 'GET':
        return HttpResponse('result')

# urls.py
from django.conf.urls import patterns

urlpatterns = ('',
    url(r'^funcion_view/', 'miapp.views.mi_vista', name="vista_funcion"),
)
Ejemplo de Class-base view
#views.py
from django.http import HttpResponse
from django.views.generic.base import View

class MiVista(View):
    def get(self, request):
        return HttpResponse('result')
#urls.py
from django.conf.urls import patterns
from miapp.views import MiVista

urlpatterns = patterns('',
    url(r'^class_base_view/', MiVista.as_view(), name="vista_class"),
)
Tanto la función como la clase devuelven exactamente lo mismo. en la clase, también puede declarar otros metodos como post, dispatch, un ejemplo mas completo seria con un formulario
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.core.urlresolvers import reverse
from django.views.generic.base import View

from .forms import MyForm

class MiVistaFormulario(View):
    form_class = MiFormulario
    initial = {'llave': 'value'}
    template_name = 'formulario.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            return HttpResponseRedirect(reverse('formulario_guardado'))

        return render(request, self.template_name, {'form': form})

Según la documentación de django, no pretende remplazar la funciones, pero si tiene algunas ventajas frente a las funciones, puede ser mas organizado, organiza el código con métodos para POST, GET, etc, en lugar de condiciones, utilizar la técnica de orientación a objetos, soporte mixes, herencia múltiple y puede ser mas reutilizable.

Fuente: https://docs.djangoproject.com/en/1.5/topics/class-based-views/intro/

Modelo de usuario personalizado en django 1.5

Esta publicación explica como configurar django 1.5, para usar modelos de usuario personalizados.

voy a suponer que ya tienen nociones de python y django, y voy a ir directo al grano.

ya creado nuestro proyecto. creamos una app.

python manage.py startapp usuarios

y ingresamos al directorio nuevo y creamos una archivo.

touch admin.py

nos quedaría un directorio de la siguiente manera.

apps/usuarios/
├── admin.py
├── form.py
├── __init__.py
├── models.py
├── tests.py
└── views.py

editamos el archivo models.py y creamos nuestro modelos para el usuario. en tal caso yo cree el mio con la siguiente informacion.


from django.utils import timezone
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.contrib.auth.models import UserManager
from django.db import models

class Usuarios(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=254, unique=True, db_index=True)
    first_name = models.CharField(max_length=30, blank=True)
    last_name = models.CharField(max_length=30, blank=True)
    email = models.EmailField(verbose_name='correo electronico', max_length=255, unique=True, db_index=True)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField()
    es_sub_admin = models.BooleanField()
    es_reseller = models.BooleanField()
    es_cliente = models.BooleanField()
    date_joined = models.DateTimeField()
    date_of_birth = models.DateField(blank=True, null=True)

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

una breve explicación del código anterior.

AbstractBaseUser:  Proporciona la implementación base de un modelo de usuario, incluyendo contraseñas hash y tokenized restablece la contraseña.  

BaseUserManager: Es un gestor personalizado para su modelo de usuario. Si su modelo de usuario define username, email, is_staff, is_active, is_superuser, last_login y campos date_joined lo mismo que el modelos User de Django, puede usar sólo UserManager de Django

PermissionsMixin: Para que sea más fácil incluir marco permiso de Django en su propia clase de usuario, Django proporciona PermissionsMixin. Este es un modelo abstracto que puede incluir en la jerarquía de clases para su modelo de usuario, que le da todos los métodos y campos de base de datos necesarios para apoyar modelo de permiso de Django. pero si quiere utilizar tus propios métodos para permisos lo puede crear sin ningún problema.

UserManager: También debe definir un gestor personalizado para su modelo de usuario. Si su modelo de usuario define nombre de usuario, correo electrónico, is_staff, is_active, is_superuser, last_login y campos date_joined lo mismo que de Django por defecto del usuario, puede instalar sólo UserManager de Django, sin embargo, si el modelo de usuario define los diferentes campos, tendrá que definir una gestor personalizado que se extiende BaseUserManage.

los campos, username, email, password, is_staff, is_superuser, is_active,
date_joined, first_name, last_name. Son necesarios para poder iniciar la sesión del administrador de django y no complicarnos la vida.

es_sub_admin, es_reseller, es_cliente, date_of_birth esto campos son agregamos a mi gusto, lo mismo pueden hacer ustedes.

USERNAME_FIELD = 'username' , definimos cual va ha ser el campo del username, podemos utilizar email, first_name o un campo nuevo como nombre_de_usuario, puede ser un campo de texto que se encuentre dentro del modelos que estamos creando.
REQUIRED_FIELDS = ['email'] , definimos los campos requeridos para la creación del usuario.

Guardamos nuestro modelo y abrimos admin.py e ingresamos el siguente codigo. este archivo fue copiado de django.contrib.auth, con unos pequeños retoques. para que funcione con nuestro modelo.

from django.db import transaction
from django.conf import settings
from django.contrib import admin
from django.contrib.auth.forms import (UserCreationForm, 
    AdminPasswordChangeForm)
from django.contrib.auth.models import User, Group
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, Http404
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.utils.html import escape
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext, ugettext_lazy as _
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from apps.usuarios.models import Usuarios
csrf_protect_m = method_decorator(csrf_protect)
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())

class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = Usuarios
        fields = ('username', 'email', 'date_of_birth')
    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2
    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user

class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = Usuarios

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]

class UserAdmin(admin.ModelAdmin):
    add_form_template = 'admin/auth/user/add_form.html'
    change_user_password_template = None
    fieldsets = (
        (None, {'fields': ('username', 'password')}),
        (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
        (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'is_admin', 'es_sub_admin', 'es_reseller', 'es_cliente',
                                       'groups', 'user_permissions',)}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined', 'date_of_birth')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('username', 'password1', 'password2')}
        ),
    )
    form = UserChangeForm
    add_form = UserCreationForm
    change_password_form = AdminPasswordChangeForm
    list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff',)
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    search_fields = ('username', 'first_name', 'last_name', 'email')
    ordering = ('username',)
    filter_horizontal = ('groups', 'user_permissions',)

    def get_fieldsets(self, request, obj=None):
        if not obj:
            return self.add_fieldsets
        return super(UserAdmin, self).get_fieldsets(request, obj)

    def get_form(self, request, obj=None, **kwargs):
        """
        Use special form during user creation
        """
        defaults = {}
        if obj is None:
            defaults.update({
                'form': self.add_form,
                'fields': admin.util.flatten_fieldsets(self.add_fieldsets),
            })
        defaults.update(kwargs)
        return super(UserAdmin, self).get_form(request, obj, **defaults)

    def get_urls(self):
        from django.conf.urls import patterns
        return patterns('',
            (r'^(\d+)/password/$',
             self.admin_site.admin_view(self.user_change_password))
        ) + super(UserAdmin, self).get_urls()

    def lookup_allowed(self, lookup, value):
        # See #20078: we don't want to allow any lookups involving passwords.
        if lookup.startswith('password'):
            return False

        return super(UserAdmin, self).lookup_allowed(lookup, value)
    @sensitive_post_parameters_m
    @csrf_protect_m
    @transaction.commit_on_success
    def add_view(self, request, form_url='', extra_context=None):
        # It's an error for a user to have add permission but NOT change
        # permission for users. If we allowed such users to add users, they
        # could create superusers, which would mean they would essentially have
        # the permission to change users. To avoid the problem entirely, we
        # disallow users from adding users if they don't have change
        # permission.
        if not self.has_change_permission(request):
            if self.has_add_permission(request) and settings.DEBUG:
                # Raise Http404 in debug mode so that the user gets a helpful
                # error message.
                raise Http404(
                    'Your user does not have the "Change user" permission. In '
                    'order to add users, Django requires that your user '

                    'account have both the "Add user" and "Change user" '
                    'permissions set.')
            raise PermissionDenied
        if extra_context is None:
            extra_context = {}
        username_field = self.model._meta.get_field(self.model.USERNAME_FIELD)
        defaults = {
            'auto_populated_fields': (),
            'username_help_text': username_field.help_text,
        }
        extra_context.update(defaults)
        return super(UserAdmin, self).add_view(request, form_url,
                                               extra_context)

    @sensitive_post_parameters_m
    def user_change_password(self, request, id, form_url=''):
        if not self.has_change_permission(request):
            raise PermissionDenied
        user = get_object_or_404(self.queryset(request), pk=id)
        if request.method == 'POST':

            form = self.change_password_form(user, request.POST)
            if form.is_valid():
                form.save()
                msg = ugettext('Password changed successfully.')
                messages.success(request, msg)
                return HttpResponseRedirect('..')
        else:
            form = self.change_password_form(user)

        fieldsets = [(None, {'fields': list(form.base_fields)})]
        adminForm = admin.helpers.AdminForm(form, fieldsets, {})

        context = {
            'title': _('Change password: %s') % escape(user.get_username()),
            'adminForm': adminForm,
            'form_url': form_url,
            'form': form,
            'is_popup': '_popup' in request.REQUEST,
            'add': True,
            'change': False,
            'has_delete_permission': False,
            'has_change_permission': True,
            'has_absolute_url': False,
            'opts': self.model._meta,
            'original': user,
            'save_as': False,
            'show_save': True,
        }
        return TemplateResponse(request,
            self.change_user_password_template or
            'admin/auth/user/change_password.html',
            context, current_app=self.admin_site.name)

    def response_add(self, request, obj, post_url_continue=None):
        """
        Determines the HttpResponse for the add_view stage. It mostly defers to
        its superclass implementation but is customized because the User model
        has a slightly different workflow.
        """
        # We should allow further modification of the user just added i.e. the
        # 'Save' button should behave like the 'Save and continue editing'
        # button except in two scenarios:
        # * The user has pressed the 'Save and add another' button
        # * We are adding a user in a popup
        if '_addanother' not in request.POST and '_popup' not in request.POST:
            request.POST['_continue'] = 1
        return super(UserAdmin, self).response_add(request, obj,
                                                   post_url_continue)

admin.site.register(Usuarios, UserAdmin)

Este le proporciona el form para crear y editar usuarios al que ya estamos acostumbrados con django, pueden agregar, quitar campos que quieran que se vean cuando se cree un usuario, cuando se modifique un usuario o los filtros necesario para los usuarios.

para terminar editarmos el setting.py de nuestro proyecto.

y agregamos la siguiente linea

AUTH_USER_MODEL = 'usuarios.Usuarios'

y queda todo listo para funcionar con el nuevo modelo personalizado para usuarios. con esto podemos tener nuestro perfil de usuario en el mismo modelos del usuario.

para asignar ForeignKey debes importa la configuración de nuestro proyecto.  seria de la siguente manera.

from django.db import models
from django.conf import settings

class Balance(models.Model):     """     Historial de cargas y deducciones del usuario     """     fecha = models.DateTimeField(auto_now_add=True, blank=True)     usuario = models.ForeignKey(settings.AUTH_USER_MODEL)     def __str__(self):         return self.usuario     class Meta:         db_table = 'balance'
 
Al terminar todas las modificaciones sincronizamos nuestra base de datos.
python manage.py syncdb

deja tu comentario si tienes alguna duda o sugerencia.