Un año más llegamos a la IX edición de este -a veces fatalmente incomprendido- llamamiento a la paz, la esperanza y, por que no decirlo, el amor conocido como el Big Culo Day. Esta vez toca un clásico europeo, Natasha, La azafata de maravillosas formas dibujada por François Walthér, y que ahora esta siendo recuperada en una serie de integrales de Dolmen.
-
Actualizar un contador en un modelo en Django
A veces necesitamos realizar algún tipo de operación aritmética sencilla en el modelo, y queremos que persista en la base de datos. Un ejemplo común es un contador, que queremos incrementar o decrementar en determinadas circunstancias.
Una solución sencilla sería hacerlo directamente en Python, algo como esto:
product = Item.objects.get(pk=4523) product.stock += 1 product.save()
Funciona perfecto, pero tiene dos problemas: Uno es una posible condición de carrera, si tenemos la mala suerte de que otro proceso está cambiando el valor del contador
stock
justo durante el intervalo entre que hemos leído los datos de la base de datos y el momento es que volvemos a salvarlos.El segundo problema es otro aspecto del mismo, estamos haciendo dos consultas a la base de datos, una para obtener el valor antiguo y otra para guardar el valor nuevo.
Podemos resolver ambos problemas (Ya que los dos tienen la misma base) con un único paso, que realizará de forma atómica la propia base de datos, usando lo que Django llama expresiones F. Las expresiones F nos permiten realizar una actualización de un campo con valores relativos otros campos de la tabla.
Para el ejemplo anterior, la solución mejorada podría ser:
from django.db.models import F product = Item.objects.get(pk=4523) Item.stock = F("stock”) + 1 product.save(update_fields=['stock'])
El resultado sería una única interacción con la base de datos, que ejecutaría un código SQL similar al siguiente :
UPDATE Item SET stock = stock + 1 WHERE id = 4523;
0Añadir un comentario
-
Dos añitos sin publicar nada... Igual debería retomar esto0
Añadir un comentario
-
Los primeros 8 bytes de un fichero PNG son siempre los mismos. En decimal, serían:
137 80 78 71 13 10 26 10
Para poder examinar esos 8 bits en linux/unix, podemos usar la utilidad
head
, y conhexdump
podemos verlos en hexadecimal
head --bytes=8 ejemplo.jpg | hexdump -C
El resultado debería ser:
00000000 89 50 4e 47 0d 0a 1a 0a |.PNG....| 00000008
Buscar archivos PNG con otra extensión
El siguiente codigo en Python 3 localiza archivos de tipo
PNG
sin extensión o con una extensión que no sería la correcta:
import os PNG_SIGNATURE = b'\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' for (path, dirnames,filenames) in os.walk('/home/jileon'): for filename in filenames: full_name = os.path.join(path, filename) if os.access(full_name, os.R_OK): with open(full_name, 'rb') as f: head = f.read(8) if head == PNG_SIGNATURE: if not full_name.lower().endswith('.png'): print(full_name)
0Añadir un comentario
-
Supongamos una función muy sencilla, pero correctamente comentada:
def suma(a, b): '''Esta función acepta dos parámetros, y devuelve la suma de ellos. Ejemplo: >>> result = suma(2, 4) >>> assert result == 6 ''' return a + b
Vemos que funciona perfectamente:
suma(2,390)
Si pedimos información de la función conhelp
, nos da la documentación que incluimos como docstring:
help(suma)
Vamos ahora a decorarla con un decorador muy sencillo, simplemente escribe a consola cuando llamamos a la función y, después de ejecutarla, imprime también el resultado:
def log(functor): def wrapper(*args, **kwargs): print('Llamando a la función: {}'.format(functor.__name__)) result = functor(*args, **kwargs) print('Resultado: {}'.format(result)) return result return wrapper @log def suma(a, b): '''Esta función acepta dos parámetros, y devuelve la suma de ellos. Ejemplo: >>> result = suma(2, 4) >>> assert result == 6 ''' return a + b
Vemos que el decorador funciona correctamente:
suma(2,3)
El problema es que la nueva funciónsuma
ha perdido todo los metadatos de la función original, entre ellos la documentación:
help(suma) suma.__name__
El decoradorwraps
del módulofunctools
lo que hace es resolver este problema, inyectando en la función decorada todos los metadatos de la función original, entre ellos la docstring (Por eso necesita como parámetro la función original).
import functools def log(functor): @functools.wraps(functor) def wrapper(*args, **kwargs): print('Llamando a la función: {}'.format(functor.__name__)) result = functor(*args, **kwargs) print('Resultado: {}'.format(result)) return result return wrapper @log def suma(a, b): '''Esta función acepta dos parámetros, y devuelve la suma de ellos. Ejemplo: >>> result = suma(2, 4) >>> assert result == 6 ''' return a + b
Vemos que todo sigue funcionando igual...
suma(2,3)
Pero la funciónhelp
muestra la documentación de la función original...
help(suma)
0Añadir un comentario
-
Nota: La versión más actualizada de este documento está siempre en
https://github.com/euribates/Charla-DSL/blob/master/Ejemplo-uso-pyparsing.ipynb
Ejemplo de uso de pyparsing
Ejemplo de datos a procesar
Una gramatica para estos ficheros
Implementación con pyparsing
Parseando fechas
In [2]:In [3]:In [4]:In [5]:In [6]:Reglas de parseo
In [7]:In [8]:In [9]:In [10]:In [11]:Vamos a parsear valores lógicos
In [12]:In [13]:Parsear los importes
In [14]:Out[14]:In [15]:In [16]:In [17]:Cadenas de texto con o sin delimitador
In [18]:In [19]:In [20]:In [21]:In [22]:In [23]:In [24]:Ventajas de Pyparsing
Ejercicios para el lector
Mas información
0Añadir un comentario
-
El próximo 1 de junio, jueves, a partir de las 18:00 tendremos un nuevo encuentro de desarrolladores para hablar de nuestros temas favoritos: desarrollo, unicornios y cervezas. En esta ocasión tendremos tres charlas:
- AWS: Desplegando Python en Amazon
- PyTesting e integración continua con Travis
- Vue.js: El framework javascript para muggles
Por favor, confirmad la asistencia en el meetup, para poner las cervezas a enfriar.
https://www.meetup.com/Agile-Canarias/events/235289799/
Esta vez nos hemos venido arriba y tenemos hasta cartel promocional:
0Añadir un comentario
-
El 11 de marzo tendremos el primer PyDay en Canarias.Un PyDay es una jornada de un día completo sobre el lenguaje de programación Python, con el formato de unas conferencias pero un poco más dinámico. La cita es en la Sala de Estudio Caja Canarias Campus Anchieta. Como el foro es limitado, debéis inscribiros en esta página, pero, por favor, hacedlo sólo si vais a asistir seguro, si no le estareis quitando la plaza a otros ser humano interesado.
En este caso constará de dos tracks independientes, uno de talleres introductorios y otro de charlas más avanzadas.
Para los alumnos de la Universidad de La Laguna, la asistencia es compatible con un crédito ECTS.
En la web podéis consultar todo el programa del próximo PyDay Tenerife.
Por último, si promocionas el evento por tus redes sociales, te agradeceríamos mucho si incluyeras el Hashtag
#PyDayTf
.0Añadir un comentario
-
Un año más llegamos a la IX edición de este -a veces fatalmente incomprendido- llamamiento a la paz, la esperanza y, por que no decirlo, el amor conocido como el Big Culo Day. Esta vez toca un clásico europeo, Natasha, La azafata de maravillosas formas dibujada por François Walthér, y que ahora esta siendo recuperada en una serie de integrales de Dolmen.
0Añadir un comentario
-
¿Cómo probar las páginas de errores (404, etc...) en desarrollo?
Para ver esas páginas de error, hay que desactivar la opción DEBUG de la configuración. Pero si lo hacemos, los contenidos estáticos dejan de servirse, así que no podemos estar seguros de como se verán las páginas finales, a no ser que instalemos nuestro propio servidor de contenidos estáticos.
O, por otro lado, podemos usar el flag insecure al llamar a runserver:
manage.py runserver --insecure
Esto obliga al servidor de desarrollo a servir los ficheros a partir de los directorios static de las aplicaciones, aunque la variable settings.DEBUG sea False.
De ninguna manera debemos usar este truco para poner en explotación un servidor de desarrollo de Django. Citando la documentación oficial:
[...] By using this you acknowledge the fact that it’s grossly inefficient and probably insecure. This is only intended for local development, should never be used in production [...]
¿Cómo analizar las consultas SQL que está realizando Django?
Normalmente el primer paso para poder optimizar Django consiste en analizar el número de consultas, así como los tiempos de ejecución de las mismas. Para ello hay una extensión muy recomendable: Django-debug-toolbar.
The Django Debug Toolbar is a configurable set of panels that display various debug information about the current request/response and when clicked, display more details about the panel’s content.
Lo ideal es poner esta app en desarrollo, y no en el despliegue final. Véase la siguiente entrada¿Cómo tener diferentes entornos de desarrollo/explotación/pruebas?
Mi solución actual, adaptada del muy recomendable libro Two Scoops of Django, consiste en tener un settings.py que será el que se use en despliege y luego un fichero development.py, que simplemente importa todo el contenido del settings.py y realiza las modificaciones que crea oportunas.
Por ejemplo:
from main.settings import * DEVELOPMENT = True INSTALLED_APPS += ( 'debug_toolbar', )
Para arrancar en desarrollo uso:
manage.py runserver --settings=main.development
¿Cómo hago para que mi método booleano se vea bonito en el admin?
Esta documentado, pero a menudo resulta complicado de encontrar. Si escribimos un método de un modelo que devuelve solo True o False, y lo consultamos en el admin, este nos muestra texto. Sin embargo, para campos definidos como booleanos (BooleanField) nos muestra un icono. Podemos hacer que utilice esos mismos iconos si añadimos un atributo boolean al método.
por ejemplo:
def nacio_en_bisiesto(self): year = self.birthday.year return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) nacio_en_bisiesto.boolean = True
¿Cómo hago para mostrar mi propio contenido html en el admin?
Para que el admin interprete cualquier texto producido por un método como Html, sin escaparlo, debemos asignarle al método en cuestión el atributo allow_tag a True. Es recomendable que nos escudemos de posibles fallos de seguridad usando la función format_html() siempre que incluyamos en la salida texto generado por el usuario final.
Por ejemplo:def colored_name(self): return format_html('<span style="color: #{};">{} {}</span>', self.color_code, self.first_name, self.last_name) colored_name.allow_tags = True
0Añadir un comentario
Añadir un comentario