sábado, 28 de agosto de 2010

Ocultando el label para los campos hidden en el admin de django

Recientemente me enfrente a una de las pocas piedras que le aparecen en el camino a los desarrolladores que usamos django. ;-)

Queria registrar un modelo en el admin de django, y definir uno de sus campos como hidden.

Para mi sorpresa el label de dicho campo no me hacia caso.
creo que resulta traumatico que un campo hidden de un formulario, le acompane inseparablemente su label no ;-)


Comence a escarbar las plantillas del admin que estuvieran implicadas (change_form.html e includes/fieldset.html) pero demasiado desbarajuste habia que armar.

Después de almuerzo se me ocurrio apoyarme en jquery momentaneamente ya que al final de todas pienso que pronto, esto tenga una solucion a lo django.

Aqui les va mi solución:


extendi la plantilla change_form.html para la aplicacion que necesitaba de la siguiente forma:

{% extends "admin/change_form.html" %}

{% block extrahead %}
{{ block.super }}
<script>
$(document).ready(function(){
$('.hidden').parent().parent().hide();
});
</script>
{% endblock %}


y en la definicion del campo hidden le agregue como atributo lo siguiente:

attr_hidden = {'class' : 'hidden'}

class AdminForm(forms.ModelForm)
hidden_field = forms.CharField(widget=HiddenInput(attr_hidden))

class Meta:
model = MyModel


para mi sigue siendo una solucion indecorosa, asi que cualquier idea, sera bienvenida.

animo djangueros!!!!!

viernes, 11 de junio de 2010

Evitando accesos innecesarios a la Base de Datos mientras usamos la API de QuerySet de Django

Recientemente tuve que realizar algunas mejoras de desempeño a un sitio web hecho en django, y me encontre un mal uso de la API del QuerySet, que me gustaria compartir con ustedes

Es muy comun que dados 2 tablas, digase Categoria y Subcategoria, donde Subcategoria tiene como llave foranea a Categoria. Necesitemos listar las Subcategorias, y para cada subcategoria mostrar a que categoria pertenece.

[any_view.py]

def foobar_view(request):
data_template = {
'subcategorias' : Subcategoria.objects.all()
}
return render_to_response('template.html', data_template)

[template.html]

<table>
<tr>
<td>Subcategoria</td>
<td>Categoria</td>
</tr>
{% for subcategoria in subcategorias %}
<tr>
<td>{{ subcategoria.nombre }}</td>
<td>{{ subcategoria.categoria.nombre }}</td>
</tr>
{% endfor %}
</table>


Ahora donde esta el error aqui.

Primero dejemos bien claro dos cosas:

1-los QuerySet tiene un comportamiento "retardado", es decir, que el acceso a la base de datos no ocurre hasta que no se accesa al objeto como tal

2-Cuando nos referimos a un campo ForeignKey, estamos haciendo un "acceso" a la base para obtener el elemento al que hace referencia este campo a menos que lo hayamos cargado con anterioridad

Abiendo aclarado estos 2 puntos, en el ejemplo anterior, estamos accediendo a la BD para obtener las subcategorias, y luego por cada iteracion, estamos accediendo a la BD para obtener la categoria a la que pertenece la subcategoria.

Como quedaría resuelto este gravísimo problema:

haciendo uso del metodo "select_related" de la API de QuerySet

[any_view.py]

def foobar_view(request):
data_template = {
'subcategorias' : Subcategoria.objects.select_related()
}
return render_to_response('template.html', data_template)


3 Maneras de invocar select_realted

1-selected_related()
Profundiza solo en los campos de tipo ForeignKey que no puedan ser nulos
Tiene un comportamiento recursivo, si en la relacion, a su vez tiene relacion con otro modelo

2-selected_related(depth = 2)
Profundiza solo en los campos de tipo ForeignKey que no puedan ser nulos
Avanza tanto niveles se especifique en el parametro

3-selected_related(field_name_list)
Profundiza en los campos especificados, independientemente que puedan ser nulos

Reglas a la hora de usar select_related
-No admite combinar el uso del parametro depth con la lista de campos
-La variante 2 y 3, son incluidas en la API de Query a partir de django 1.0


Espero esto les pueda ser util para mejorar el rendimiento de sus aplicaciones hechas en django. Como siempre, me despido de ustedes hasta la proxima vez que encuentre algo interesante que aportar a la comunidad, por mas sencillo que sea.

Bibliografia consultada
http://docs.djangoproject.com/en/dev/ref/models/querysets/#id4

viernes, 28 de mayo de 2010

Escogiendo el nombre adecuado para liberar un paquete de software

Como sabemos por el nombre, que liberacion de un proyecto es mas reciente? Existe algun estandar para la tecnica de asignarle la version a un proyecto?


Resulta super util dominar la nomenclatura para nombrar las liberaciones de los proyectos a la hora de distribuirlo. Y mas aun, cuando de ese proyecto se estan liberando constantemente versiones, que queremos clasificar en estable, en desarrollo, candidatas, nocturnas, etc., de manera que la comunidad pueda guiarse por ella y saber que esta descargando.

Te has preguntado cual de las siguientes versiones es mas reciente?

version 1.2b1 o version 1.2 ?
version 0.9.1 o 0.9.1-r1234 ?
version 0.9 o 0.9pre ?
version 5.2g o 5.2 ?

Bien antes de dar respuesta, empezemos por definir que la version de un proyecto consiste en una serie alternada de "numeros de liberación" y marcas pre o post liberación.

A su vez, un "numero de liberacion" es una serie de digitos separados por ".". Donde cada serie es tratada numericamente. Con esto ultimo quiero decir que "2.1.0 equivale a 2.1"

A continuación de un "numero de liberacion", es posible poner una marca "pre-release" o "post-release".

Ejemplo:
2.1c1 pre-lease (liberacion candidata #1 a la version 2.1)
2.1-r3421 post-release (liberacion #3421 del subversion de la version 2.1)


Empezemos por los pre-release:
Consiste en una serie de letras alfabeticamente menores que la cadena "final". Ejemplos mas comunes son: alpha, beta, a, c, dev, entre otros.
No es necesario anteponer un "." despues de un numero, pues es equivalente. Esto quiere decir que:

2.3.c1 equivale a 2.3c1, y ambos significan una version previa a la 2.3

por lo tanto "2.3c1 < 2.3"

Nota: Hay tres cadenas excepcionales que son tratadas como la cadena "c". Estas son "pre", "preview", "rc".

Esto quiere decir que "5.2rc1", "5.2pre1", "5.2preview1" son equivalente a "5.2c1" y son tratadas de igual manera por setuptools.

Se usa para distinguir las ediciones previas (liberacion candidata, desarrollo, nocturna) a la liberacion estable de una version de un producto.


Y ahora con los post-release:
Consiste en una serie de letras alfabeticamente mayor o igual que la cadena "final" o "-". Es mas comun encontrarselo con la segunda variante.

Ejemplos:

3.2g1
3.2-r4155

Se usa para distinguir las versiones posteriores a partir de revisiones, compilaciones, parches, entre otros.


Mezclando Pre y Pos Release:
Despues que cada marca pre o post release, y seguido de un numero de version, puedes a su vez especificar otra marca pre o post release

Ejemplo:

1.2rc1-r1234

significa que es una version posterior, a la version candidata 1 de la version 1.2. O sea que "1.2rc1-r1234 < 1.2" pero "1.2rc1-r1234 < 1.2rc1"


COMO VERIFICAR POR CODIGO, SI UN NOMBRE DE VERSION ES MAS O MENOS RECIENTE QUE OTRO?

Si has llegado hasta este punto del articulo, te mereces que te comente como puedes probar si exactamente es una version mayor o menor que otra, usando el paquete setuotools de python


1-Instala setuptools
2-Abre una consola de python
3-Introduce el siguiente codigo de prueba


from pkg_resources import parse_version
parse_version('1.2b1') > parse_version('1.2')


Esto devuelve False, de esta manera puedes verificar cada una de las preguntas del inicio.

Interesante la funcion parse_version() no? de manera resumida te dire que a partir de la version representada como cadena, devuelve una tupla ordenable cronologicamente, de manera que al compararla con otra permita obtener la precedencia de una version sobre otra. Para mas detalles teclea:


parse_version.__doc__

Bibliografia utilizada:

http://peak.telecommunity.com/DevCenter/setuptools

lunes, 24 de mayo de 2010

C# 3.0 y los métodos de extension

Si estas incursionando en el uso de linq a estas alturas y no has seguido de cerca las modificaciones que se le han hecho a c# a partir de su version 3.0 (me consta que ha llovido bastante, pero nunca es tarde para meditar sobre cosas del pasado), este articulo puede resultarle util.


Seguramente has presionado Ctrl + Spacio, seguido de una instancia de un tipo de datos que me represente una coleccion, pudiesemos escoger cualquiera de los siguiente namespace:

System.Collections,
System.Colleciotns.Generic,
System.Linq


asi que la siguiente imagen te puede resultar familiar

Fig con metodos de extension para mostrar el icono distintivo de estos metodos


Pues la flechita azul, indica que se trata de un metodo de extension. ahora bien empecemos por dar una definicion.

Metodo de extension: es un metodo estatico de una clase estatica, que usted puede llamar como si fuese un metodo de instancia de una clase diferente a donde se definio.

interesante no!!!!


SITUACION PROBLEMICA

Para entender bien esto, creo que lo mejor es a partir de un problema concreto, tratar de resolverlo con lo que existia antes y luego ver como los metodos de extension permiten resolverlo.


Supongamos que queremos incorporle a los datos de tipo String, la funcionalidad de saber si la cadena tiene espacio en blancos.


lo primero que nos viene a la mente es un metodo estatico, que reciba como parametros un string devolveria true/false.


namespace string_utiles
{
public class Utils
{
public static bool tiene_espacios(string input_str)
{
return input_str.Contains(" ");
}
}
}



de manera que un ejemplo de su uso seria:


using string_utiles;
...
...

String s = "cadena con espacios en blanco";
bool tiene = Utils.tiene_espacios(s);


perooooo....
no seria mas elegante que este metodo se pudiese invocar como un metodo de la propia instancia, veamos


String s = "cadena con espacios en blanco";
bool tiene = s.tiene_espacios();


porque al final de cuentas, es responsailidad de la cadena, saber si tiene o no, espacios en blanco


desafortunadamente String, es un clase sealed (sellada), lo que en español quiere decir que no se puede heredar de ella, o que en ella murio el cuento ;)....

porque si no fuese asi, podriamos hacer algo como esto,


class CustomString : String
{
public bool tiene_espacios()
{
return this.Contains(" ");
}
}


pero entonces tendriamos un problema, que nuestros tipos de datos string, tendrian que pasar a ser de tipo CustomString, y esto implica mucho cambio de codigo... ufff... esto no tiene nada de buenas practicas eh....


pero bueno, la solucion ideal esta por ahi, incluirle un metodo a los tipos de datos string, de manera que no haya que heredar de la clase String, pero se invoque como si fuese un metodo de instancia... mmmmm ... esto me recuerda al principio de la programacion orientada a objetos, "abierto a la extension y cerrado a la modificacion".

y esto es posible gracias a los metodos de extension.

como quedaria...



namespace string_utiles
{
public static class Utils
{
public static bool tiene_espacios(this string s)
{
return s.Contains(" ");
}
}
}


using string_utiles;

String s = "cadena con espacios en blanco";
bool tiene = s.tiene_espacios();



DEFINIENDO E INVOCANDO LOS METODOS DE EXTENSION

definiendo.....


namespace [nombre_namespace]{
public class static [nombre_clase]
{
public static [return type] [nombre_metodo] (this [tipo instancia] nombre_instancia ,param1, param2, ..., param n)
{
//statement.....
}
}

}



reglas para la definicion:
-los metodos deben definirse dentro de una clase estatica
-los metodos deben ser estaticos
-el primer parametro del metodo, debe estar precedido con la palabra clave this, y debe ser del tipo al que le queremos incluir este metodo de extension.


invocando....


using [nombre_namespace];
...

[tipo instancia] instancia = [valor];

instancia.[nombre_metodo](param1, param2,..., param n);
...


-basta con incluir el namespace, donde se encuentre la clase estatica, que contenga los metodos de extension, automaticamente, seran reconocidos.
-los metodos de instancia tiene tienen precedencia sobre los metodos de extension, en caso de que coincida en ombre y parametros.



Espero ahora ya sepas cuando estas usando un metodo de extension que hay detras de ello, y ademas puedas encontrarle alguna utilidad en tus desarrollos y te atrevas a definir los tuyos propios, como comente anteriormente, son un elemento que favorece el principio de abierto a la extension, cerrado a la modificacion.

martes, 18 de mayo de 2010

Hablando de metaclases en python

De manera super resumida y algo recursiva, una metaclase es para una clase, lo que una clase es para un objeto.

pero la metaclase la podemos tratar como si fuese una clase

veamos el siguiente ejemplo


class MetaPersona(type):
pass


class Persona(object):
__metaclass__ = MetaPersona

p = Persona()


Decifrando el trabalengua anterior, si para construir objeto(p) se necesita una clase(Persona), para construir la clase "Persona", python usa la metaclase "MetaPersona", que se ha especificado en la linea 6. Si obviamos esta linea, como es lo habitual, se usaria "type", que es la metaclase por defecto.

El ejemplo anterior tambien podria haberse especificado la metaclase de la siguiente manera

Persona = MetaPersona('Persona', (), {})

puesto que una metaclase es una clase, lo que hemos hecho es constriur una instancia de la metaclase, el contructor de la metaclase tiene la forma:

type(name, bases, dct)

name: nombre de la nueva clase
bases: clases bases de la nueva clase
dct: diccionario con las definiciones para la nueva claseclass.

Ahora veamos "in a nutshell" como ocurre el proceso de creacion de una instancia, para luego entender mejor el proceso de creacion de una clase.

En que orden ocurre el proceso de creacion de la instancia?

1* Se invoca la clase con los argumentos requeridos
2* Se ejecuta el método __new__ pasándose la clase a sí misma como primer argumento, y a continuación los argumentos que indicó el usuario en la “invocación” original. Este metodo debe retornar una nueva instancia.
3* Se ejecuta el método __init__ pasando como primer argumento la instancia creada por __new__ y también todos los argumentos de la invocación original.

El siguiente codigo sirve de practica para comprobar lo anterior:

class Persona(object):
def __new__(cls, *args, **kwargs):
print "creacion de la instancia"
return super(Persona, cls).__new__(cls, args, kwargs)
def __init__(self, *args, **kwargs):
print "inicializando la instancia"
super(Persona, self).__init__(self, args, kwargs)

compruebelo usted mismo usando un shell y obtendra lo siguiente


>>>p = Persona()
creacion de la instancia
inicializando la instancia

TRASPOLANDO EL PROCESO DE CREACION DE UNA INSTANCIA AL PROCESO DE CREACION DE UNA CLASE

Por suerte para todos, el mismo proceso de creacion de instancias, tiene lugar cuando se crea una clase, de ahi que la metaclase tambien tiene un metodo __new__ y un metodo __init__, con la diferencia, de que, el metodo __new__ y el metodo __init__ reciben como primer parametro, la metaclase y la clase creada por el metodo __new__ respectivamente.


class MetaPersona(type):
def __new__(meta, name, base, dct):
return super(MetaPersona, meta).__new__(meta, name, base, dct)

def __init__(cls, name, bases, dct):
super(MetaPersona, cls).__init__(name, bases, dct)


class Persona(object):
__metaclass__ = MetaPersona

>>creando la clase
>>inicializacion de la clase


DEFINIENDO METODOS EN LA METACLASE

Normalmente para definir metodos de clase, lo hacemos dentro de la clase como se refleja a continuacion


class Persona(object):

@classmethod
def prefix_nombre_clase(cls, prefix):
return prefix + cls.__name__

Persona.prefix_nombre_clase("esta es la clase ")


vale aclarar que @classmethod es un decorador en python y es equivalente a decir


class Persona(object):

def prefix_nombre_clase(cls, prefix):
return prefix + cls.__name__

prefix_nombre_clase = classmethod(prefix_nombre_clase)


pero tambien pudiesemos hacerlo como metodo de la metaclase


class MetaPersona(type):

def prefix_nombre_clase(cls, prefix):
return prefix + cls.__name__

class Persona(objet):
__metaclass__ = MetaPersona

Persona.prefix_nombre_clase("esta es la clase ")


Hay realmente alguna diferencia??

Pues sip, una de ellas es que usando @classmethod, el metodo aun pudiese llamarse a partir de una instancia de la clase. O sea:


p = Persona()
p.prefix_nombre_clase("prefijo")


equivaldria a llamar al metodo desde la clase a la que pertenece el objeto. Sin embargo si definimos el metodo dentro de la metaclase, solo pudiesemos llamarlo desde la clase, de lo contrario obtendriamos el siguiente mensaje de error:

AttributeError: 'Persona' object has no attribute 'prefix_nombre_clase'

DONDE NOS PODRIAN AYUDAR EL USO DE LAS METACLASES

Supongamos que queremos abtraernos en la clase BasePersona, las caracteristicas comunes a todas las personas, para que luego otros programadores, hereden de esta y definar especializaciones como Estudiante, Trabajador, etc. Como mecanismo de validacion, se quiere que dado una lista fija de keywords, se valide, que ninguna clase que herede de BasePersona, pueda definir un atributo, que coincida con alguna de los keywords.



RESERVED_WORDS = ['departamento', 'sancionado']

class Meta(type):
def __init__(cls, name, bases, dct):
for attr_name in dct.keys():
if attr_name in RESERVED_WORDS:
raise Exception

class BasePersona(object):
__metaclass__ = Meta
nombre = ''
apellidos = ''

class Estudiante(BasePersona):
curso = ''
aula = ''

class Trabajador(BasePersona):
especialidad = ''
departamento = ''


LAS METACLASES Y LOS MODELOS EN DJANGO

Justamente antes de decidir escribir estas lineas, me invadia constantemente una pregunta, cuando definimos un modelo en


from django.db import models

class Persona(models.Model)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)

class Meta:
db_table = 'd_personas'
verbose_name_plural = "personas"


Es la clase "Meta" una metaclase?, si no, como se entiende, que permita definir meta opciones para un modelo especifico?

Realmente la clase Meta, no es mas que una clase interna que se usa para contener descriptores que se usaran en el mecanismo de creacion de la clase en si. Como logran hacer esto? pues usando metaclases. A modo de informacion, la clase Model, de django, usa como metaclase ModelBase, a continuacion un fragmento del codigo:


class ModelBase(type):
"""
Metaclass for all models.
"""
def __new__(cls, name, bases, attrs):
"""
MIS COMENTARIOS
intercepta el mecanismo de creacion de la clase
leer la clase interna Meta, y basado en los atributos que
contenga modifica la creacion de la clase del que hereda de Model

agrega el atributo "_meta", a la clase del modelo, para acceder
a todos los atributos de la clase interna Meta
si lo desea puede remitirse a django.db.models.base.py line 25

...
...
...
"""

class Model(object):
__metaclass__ = ModelBase

...
...

Vemos como en esta situacion, se aprovecha el proceso de creacion de la clase para interceptar la forma en que esta se crea

LAS METACLASES Y LOS MODELOS EN APPENGINE


from google.appengine.ext import db

class Persona(db.Model):
first_name = db.StringProperty()
last_name = db.StringProperty()

esta clase Model, hace uso de las metaclases para intervenir el proceso de inicializacion de las propiedades del modelo y hacer algunas validaciones como no permitir usar determinadas keywords como atributos, y evitar duplicados en los atributos de los modelos.


class PropertiedClass(type):

def __init__(cls, name, bases, dct, map_kind=True):
super(PropertiedClass, cls).__init__(name, bases, dct)

_initialize_properties(cls, name, bases, dct)

if map_kind:
_kind_map[cls.kind()] = cls

class Model(object):
__metaclass__ = PropertiedClass

Vemos como en este ejemplo, se aprovecha el metodo __init__ de la metaclase, para intervenir en la inicializacion