Cansada de ser feliz

Bienvenidos a mi flujo de conciencia

Múltiples bases de datos en Django

| Comments

Tenemos dos tablas con esquemas iguales dos en bases de datos diferentes: production y history.

settings.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DATABASE_ROUTERS = ['routers.HistoricRouter']

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'localhost',
        'NAME': 'production',
        'USER': 'secret',
        'PASSWORD': 'secret',
    },
    'historical': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'localhost',
        'NAME': 'history',
        'USER': 'secret',
        'PASSWORD': 'secret',
    }
}

Escribimos un router para definir a qué base de datos hacer la petición:

routers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class HistoricRouter(object):
    """
    A router to control all database operations on models in the historic application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read historic models go to historical.
        """
        if model._meta.app_label == 'historic':
            return 'historical'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write historic models go to historical.
        """
        if model._meta.app_label == 'historic':
            return 'historical'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Allow relations if a model in the historic app is involved.
        """
        if obj1._meta.app_label == 'historic' or \
            obj2._meta.app_label == 'historic':
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the historic app only appears in the 'historical' database.
        """
        if app_label == 'historic':
            return db == 'historical'
        return None

En nuestros modelos especificamos app_label para referirnos a la base de datos distinta a default:

models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Author(models.Model):

    class Meta:
        db_table = 'authors'

class AbstractBook(models.Model):
    author = models.ForeignKey('books.Author', related_name='books')
    created_at = models.DateTimeField()

    class Meta:
        unmanaged = True
        abstract = True

class Book(Book):

    class Meta:
        db_table = 'books'

class HistoricBook(Book):

    class Meta:
        unmanaged = True
        db_table = 'books'
        app_label = 'historic'

La petición Book.objects.all() va a traer libros de la base de datos production y HistoricBook.objects.all() - de la base de datos history.

Modificando allow_relation en nuestro router, podemos permitir llaves foráneas entre modelos de diferentes bases de datos:

1
2
def allow_relation(self, obj1, obj2, **hints):
    return True

Enlaces:

Cómo hacer integración con Tpaga API usando Python

| Comments

Tpaga es una plataforma que permite recibir pagos electrónicos. Tiene una estructura sencilla para entender y fácil para usar.

Para obtener nuestros claves de acceso y conectarnos con el API de Tpaga, creamos una cuenta en el “sandbox” de la plataforma: sandbox.tpaga.co.

Al registrarnos podemos ver que ahora tenemos dos claves que podemos usar para la autenticación: Private Api Key y Public Api Key:

No llevas leña

| Comments

El bus lleno de Buratinos* va a toda velocidad. En algún momento el conductor frena duro, y escucha las voces enojadas del los pasajeros: “¡Ey, señor conductor, no lleva la leña!”

*Buratino es Pinocchio en ruso.

El colegio

| Comments

Nunca me gustaba ir al colegio y siempre he contado cuánto falta hasta el último día de encarcelamiento.

Entré al colegio cuando tenía 7 años. Normalmente los niños entran un año antes, pero a mis 6 me dijeron que la niña era demasiado pequeña. Durante 12 meses no crecí mucho, pero ya que no tuvieron razones de no aceptarme.

Mi primer día no me gustó: habían muchos niños a quienes no conocía y no quería conocerlos, que se movían por el salón de una forma caótica y hacían mucho ruido. Me pusieron en la última mesa del aula junto a la ventana. En la primera clase a profesora nos dijo que sacaramos nuestros cuadernos de las maletas y dibujaramos algo. Eso fue un estrés total: no pude abrir mi maleta. Como tuve que manejar la cerradura por primera vez en mi vida, no supe cómo funcionaba y claro que el panico, que llegó inmediatamente, no me permitió pensar con claridad.

Del primer grado sólo recuerdo cómo nos enseñaban caligrafía: tuvimos que escribir las mismas letras muchas veces. Entonces al final se quedaron varias páginas con “а”, “б”, “в”, etc.

En el segundo grado nos muvieron a otro edificio y empezamos a prestar servicio. Cada grupo por turnos tuvo que estar de servicio en el colegio durante toda la semana. Esto segnificaba que tuvimos que llegar 30 minutos antes que todos los demás. Nos reunían en el primer piso y decían a cada no su cargo. Habían varios tipos de cargos: unos tuvieron que estar en la entrada del edificio y en las entradas de cada piso y decir a cada niño que pasaba: “¡Smenka!”. “Smenka” significa “los zapatos de cambio”. En el invierno la nieve se metía en las indiduras de los zapatos y cuando uno entraba al edificio se derretía y dejaba charcos. Por eso por el orden “¡Smenka!” el estudiante tuvo que levantar el pie y mostrar la suela. Si estaba mojada “el guardia” tuvo que detenerlo y mandarlo a la casa por los zapatos de cambio. Ese cargo no me gustaba porque los otros estudiantes lo tomaban como un deporte lograr pasar con los zapatos que llevaban puestos en la calle, entonces inventaban trucos para confundir la guardia o simplemente corrían de nosotros. Entonces había otro cargo: vigilar los corredores, detener los estudiantes que corrían y mandarles al director del colegio.

Existía un cargo que era el más agradable de todos: estar en la cafetería. Había que ordenar las mesas y las sillas y antes de las horas del desayuno y almuerzo había que servir la comida y después recoger los platos.

Después de las clases nos quedábamos por una hora para limpiar todo el colegio.

Tuvimos 4 periodos y 4 vacaciones: 3 cortas y umas largas de tres meses en verano. Para las vacaciones de verano nos daban una lista de libros para leer. Como durante los estudios casi no tuvimos tiempo para la lectura, tuvimos que pasar por todos los libros del curso de literatura antrs de que empezara el año escolar. A pesar de que leía de 3 a 5 horas al día no alcanzaba leer todos los libros del listado. Me gusta leer mucho, pero odiaba las clases de literatura. La parte más tonta era memorizar los poemas. Tuvimos que recitar de memoria los poemas de Homero, Pushkin, Lermontov, Tiútchev, Mayakovski, Blok y muchos otros. A cada uno llamaban a la pizarra y tuvimos que declamar frente al grupo. Así los profesores lograron que hasta la mejor poesía pudiera provocar un reflejo vomitivo. Pero creo que era la mejor forma de instalar los pensamientos de otras personas a las mentes inmaduras. Hasta ahora cuando veo una tormenta fuerte a veces se me ocurren las estrofas de Tiútchev:

Люблю грозу в начале мая (Me gustan las tormentas a principios de mayo) / Когда весенний первый гром (Cuando el primer trueno primaveral) / Как бы резвяся и играя (Como haciendo travesuras y jugando) / Грохочет в небе голубом (Trona en el cielo azul).

Monitoreo de acciones de usuarios en Flask (SQLAlchemy)

| Comments

Suponemos que en nuestro proyecto de Flask hay un modelo Client definidao usando Flask-SQLAlchemy y queremos monetorear los cambios que se realizan sobre los objetos de ese modelo.

Para esto agregamos tres señales:

models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from sqlalchemy import event

from main.signals import receive_before_update, receive_before_insert, receive_before_delete

class Client(db.Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(255))
    email = Column(String(255), unique=True)
    phone = Column(String(255))

event.listen(Client, 'before_insert', receive_before_insert)
event.listen(Client, 'before_update', receive_before_update)
event.listen(Client, 'before_delete', receive_before_delete)

EN el gódigo de cada señal llamamos los métodos inspect() y get_history() para detectar los cambios sobre el objeto:

1
2
3
state = db.inspect(client_object)
for attr in state.attrs:
     hist = state.get_history(attr.key, True)

Por ejemplo, para el atributo name:

1
2
3
4
{
    'added': ['Company A'],
    'deleted': ['Company B'],
}

El mégoto get_history() nos devuelve un objeto sqlalchemy.orm.attributes.History que tiene los siguientes atributos:

  • added - listado de valores agregados al atributo de nuestro objeto
  • deleted - listado de valores eliminados del atributo de nuestro objeto
  • unchanged - listado de valores del atributo de nuestro objeto que se quedaron intactos
  • has_changes() - método que retorna True si no habían ningunos cambios de valor de nuestro atributo.

Ahora, usando la variable current_user de la biblioteca Flask-Login, podemos guargar el usuario que realizó los cambios:

signals.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from flask_login import current_user

def save_changes(target, action):
    try:
        state = db.inspect(target)
        changes = {}
        for attr in state.attrs:
            hist = state.get_history(attr.key, True)
            if not hist.has_changes():
                continue
            added = format_changes_value(hist.added)
            deleted = format_changes_value(hist.deleted)
            if added != deleted:
                changes[attr.key] = {
                    'added': added,
                    'deleted': deleted,
                }
        if changes:
            mongo.db.user_logs.insert_one(dict(
                current_user_id=current_user.id,
                action=action,
                table_name=target.__tablename__,
                object_id=target.id,
                changes=changes,
                created_at=datetime.datetime.utcnow(),
            ))
    except Exception as e:
        pass

def receive_before_insert(mapper, connection, target):
    save_changes(target, 'create')


def receive_before_update(mapper, connection, target):
    save_changes(target, 'update')


def receive_before_delete(mapper, connection, target):
    save_changes(target, 'delete')

Las temporadas

| Comments

A veces me parece que aquí el tiempo no se mueve. A veces me digo: “El verano pasado…” – o – “El próximo invierno”, y luego me doy cuenta que no hay ningún verano, ni invierno, ni primavera, ni otoño. Y eso da el sentido que no cambia nada, pero cuando uno mira atrás, así llega uno a horrorizarse de cuánto tiempo ha pasado.

Pero antes todo era diferente…

Todo el verano sentía euforia de que por fin hacía calor y todo alrededor estaba verde. Podía mirar por horas cómo se movían las ramas de los árboles con el viento, aspirar el aire lleno de olores y escuchar los sonidos del bosque. Parecía que todo el mundo estaba feliz: los niños por fin tenían sus vacaciones, sacaban sus bicicletas y patines y salían a jugar afuera, las muchachas se desvestían tratando de exponer más de su cuerpo al sol. Constantemente había que acordarse de que eso se iba a acabar pronto, de que teníamos muy poco tiempo para disfrutarlo, entonces había que aprovechar cada momento, y por eso la gente se enloquecía.

En junio o julio a veces hace tanto calor que parece que el asfalto se funde. Realmente, todo se funde. Pero en agosto la llegan los vientos del norte trayendo las decepciones. La mente todavía no puede aceptar que el verano se acabó tan rápido, y la mente no manda al cuerpo que tiene que vestirse. Entonces llegamos al otoño con mocos y tos.

El otoño es un poco incómodo, porque uno no sabe cómo vestirse: de día es todavía bastante cálido, pero por la mañana hace mucho frío. Muchísimo. Entonces todos andan muy abrigados con muchas prendas quitándose sus sacos uno por uno. Después del verano caliente, cinco grados centígrados se siente como uno estuviera en el Polo norte. Pero ese es el precio que pagamos para ver esa abundancia de colores: las hojas de los árboles pierden el pigmento verde y el bosque se convierte en un lugar mágico. Siempre trataba de no levantar mucho las piernas cuando andaba para hacer crujir las hojas cecas de los árboles.

Luego, inesperadamente (como cada año), llega la nieve, y es inesperado porque los servicios de aseo nunca están preparados. De verdad, nadie está preparado. Creo que es porque todos tienen muy por dentro una pequeña esperanza de que puede ser éste año el invierno no llegue. Los árboles se vuelven completamente siniestros y se ven como muertos.

El diciembre el mundo se queda sumido en la oscuridad. Lo más doloroso era despertarse por la mañana para ir al colegio:

A las siete de la mañana suena el despertador, uno abre los ojos, pero todo está completamente oscuro, como a la medianoche. Los ojos se cierran lentamente y te vuelves a terminar de ver el último sueño. Pero luego llega el dolor – alguien prendió la luz. La luz entra a través de los párpados cerrados, y es insoportable. Uno trata de entreabrir los ojos, pero el brillo amarillo les ciega, entonces uno tiene que cerrarlos inmediatamente. Así con los ojos medio abiertos uno desayuna, se viste y sale de la casa a la oscuridad para patullar por la nieve. Claro que el barrendero también tuvo pereza y lo había limpiado las calles, entonces tocaba de nuevo abrir el camino hasta el colegio.

Las imágenes tomadas de https://ssl.panoramio.com/user/2301915.

“Rewire”

| Comments

Hace unos meses me di cuenta de que casi no leía noticias de Colombia, y era bastante difícil empezar a hacerlo, a pesar de que ya vivía aquí por un tiempo significativo. En Rusia, empezando desde el bachillerato, me demoré varios años leyendo toda la basura que publicaban en la prensa y en Internet para al fin formar mi lista de periódicos favoritos y periodistas que escribían textos interesantes y relativamente objetivos. Mientras que aquí en Colombia todo era desconocido: los artículos que encontraba al principio me parecían muy secos, llenos de descripciones de los hechos y casi sin análisis. No los disfrutaba ni siquiera desde el punto de vista literario y al final, después de torturarme con varios textos de la revista Semana, pasaba a mi mundo cómodo y organizado de mis fuentes de noticias rusas preseleccionadas.

Claro que me demoré tanto en empezar a leer las noticias locales en parte por la falta de perspectiva: sin conocer los nombres de los políticos y sus pareceres, sin conocer bien la historia y la mentalidad de la gente, es bastante difícil percibir la nueva información que proviene de ese contexto histórico y cultural.

Analizando esa situación, me puse a reflexionar acerca del cosmopolitismo general, y qué tantas noticias recibimos acerca de los otros países. Así, encontré el libro de Ethan Zuckerman, llamado “Rewire”: Cosmopolitas Digitales en la Era de la Conexión.

Ethan Zuckerman es director del MIT Center for Civic Media, blogger y activista de Internet. En su libro él escribe acerca de cómo las redes sociales y las nuevas tecnologías cambian nuestras vidas. Ahora, cuando tanta gente tiene acceso a Internet, parece que estamos rodeados de textos y noticias de todo el mundo, pero en realidad, todas esas herramientas nos ayudan a encontrar lo que buscamos y ya queremos saber, pero no la información que debemos conocer y que sería útil para nosotros.

En estos días, prestamos más la atención a las noticias de nuestro barrio, a lo qué ha pasado con nuestros amigos de Facebook, y no a lo que está pasando al otro lado del océano. Zuckerman escribe: “La vida de nuestros amigos nos interesa más que la vida de algunos desconocidos.” Y eso se refleja en las fuentes de información que usamos desde los periódicos hasta las redes sociales: ellos no nos ayudan a encontrar lo que necesitamos saber, sino lo que queremos saber. Zuckerman hizo un análisis de los periódicos de del pasado, comparando con los de hoy y se dió cuenta de que a pesar de que tenemos televisión satelital e Internet, que nos permiten transmitir la información desde cualquier parte del mundo, nos enfrentamos con una imagen del mundo más limitada que antes. Ahora el problema no está en la disponibilidad de la información, sino en la atención que estamos dispuestos a darla. Y ese problema se ve agravado por nuestro deseo natural de prestar más atención a lo que está pasando en el nuestro entorno y lo que tiene impacto directo sobre nosotros, nuestros amigos y familiares.

Zuckerman dice que debemos empezar a vernos no sólo como a los ciudadanos de un país en particular, sino también como los ciudadanos del mundo. Pero aunque uno de los mayores logros de Internet es la posibilidad de encontrar en la red las cosas más inesperadas, en la práctica lo que encontramos más a menudo, es algo que ya está cerca a nuestra casa. Al leer demasiados artículos de los autores que comparten nuestras opiniones, y al comunicarnos exclusivamente con las personas de ideas afines corremos el riesgo de radicalizarlos.

El proyecto de Ethan Zuckerman Global Voices: es.globalvoices.org
El libro Rewire: www.goodreads.com/book/show/16233761-rewire

Imagen: www.flickr.com/photos/brewbooks/