Implementamos tecnologia y es asi como lo hacemos

Paginacion en Django estilo Digg

Enero 29, 2010 - 12:20 pm - Posted by Camilo Nova

La Paginación en django es excelente, permite una flexibilidad importante para solucionar muchos problemas que se presentan al paginar resultados, por ejemplo el problema del cacheo, que se presenta al realizar una consulta que pide todos los datos sabiendo que solo vamos a mostrar unos pocos.

Gracias a la excelente documentación podemos encontrar toda la información aquí: http://docs.djangoproject.com/en/1.1/topics/pagination/#topics-pagination

Sin embargo, cuando se trabajan volúmenes grandes de información, digamos mas de 50 paginas, se hace dispendioso pasar entre paginas hasta llegar a la que buscamos, por eso es muy útil tener una paginación al estilo Digg que muestra algunas paginas adicionales y no solo el enlace a la anterior y la siguiente.

Tomando como base este excelente trabajo: http://krisje8.com/blog/2009/jul/02/django-pagination-template-tag-digg-style/ realice algunas modificaciones para que muestre ‘…’ entre las paginas iniciales y la pagina actual, para darle una mejor ubicación al usuario sobre donde se encuentra.

Tenemos el siguiente template_tag:

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
40
41
42
43
44
45
46
47
#! /usr/bin/env python
# -*- coding: utf8 -*-
# render_paginator.py
from django.template import Library
 
register = Library()
 
def render_paginator(context, first_last_amount=2, before_after_amount=4):
    page_obj = context['page_obj']
    paginator = context['paginator']
    page_numbers = []
 
    # Pages before current page
    if page_obj.number > first_last_amount + before_after_amount:
        for i in range(1, first_last_amount + 1):
            page_numbers.append(i)
 
        page_numbers.append(None)
 
        for i in range(page_obj.number - before_after_amount, page_obj.number):
            page_numbers.append(i)
 
    else:
        for i in range(1, page_obj.number):
            page_numbers.append(i)
 
    # Current page and pages after current page
    if page_obj.number + first_last_amount + before_after_amount < paginator.num_pages:
        for i in range(page_obj.number, page_obj.number + before_after_amount + 1):
            page_numbers.append(i)
 
        page_numbers.append(None)
 
        for i in range(paginator.num_pages - first_last_amount + 1, paginator.num_pages + 1):
            page_numbers.append(i)
 
    else:
        for i in range(page_obj.number, paginator.num_pages + 1):
            page_numbers.append(i)
 
    return {
        'paginator': paginator,
        'page_obj': page_obj,
        'page_numbers': page_numbers
    }
 
register.inclusion_tag('layout/pagination.html', takes_context=True)(render_paginator)

Con la siguiente plantilla:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% if page_obj.has_previous %}
  <a href="?page={{ page_obj.previous_page_number }}">Previous</a>
{% endif %}
{% for page in page_numbers %}
  {% if page %}
    {% ifequal page page_obj.number %}
      <b>{{ page }}</b>
    {% else %}
      <a href="?page={{ page }}">{{ page }}</a>
    {% endifequal %}
  {% else %}
    ...
  {% endif %}
{% endfor %}
{% if page_obj.has_next %}
  <a href="?page={{ page_obj.next_page_number }}">Next</a>
{% endif %}

Para usarlo se coloca el siguiente codigo en cualquiera de las plantillas que queramos paginar:

1
2
3
4
    {% if is_paginated %}
      {% load render_paginator %}
      {% render_paginator 2 3 %}
    {% endif %}

Lo que genera un código como:

1
Previous  1  2  ... 5  6  7  8  9  10  11  ... 25  26  Next

Lo mejor de todo es que no necesita ningún componente adicional ni interfiere con la paginación por defecto que traen las vistas genéricas en django.

Etiquetas: , , | 2 Comentarios »

Trabajando con Symfony en forma interactiva

Noviembre 9, 2009 - 1:23 am - Posted by Juan Pablo Romero Bernal

Para algunos desarrolladores nos es muy cómodo trabajar desde la línea de comandos, ya que diez dedos hacen más que dos. De hecho cuando se desarrollan aplicaciones con Symfony, el uso de la terminal de comandos es fundamental para muchas tareas (generación del modelo, creación de esquemas de base de datos, limpiar la cache, etc,.) que simplifican el trabajo significativamente.

Desde luego, todo esto es gracias a la interfaz en línea de comandos del ínterprete php (más conocido como php-cli), que en algunos casos se queda corto para ejecutar algunas pruebas: ver el estado de un objeto, insertar datos en la base de datos,  probar métodos de manera interactiva, etc,. Sé que algunos dirán que php-cli tiene un modo interactivo, pero en realidad desde mi punto de vista es muy limitado.  Sería muy interesante contar con algo similar a la terminal interactiva de python y desde luego que se pueda trabajar con nuestros proyectos en Symfony.

Lo mejor de todo, es que la gente de facebook liberó bajo licencia BSD una shell interactiva para php y como sus mismos credadores dicen: “… irónicamente escrita en python ….”; se llama phpsh y realmente es un gran trabajo. He realizado algunas pruebas de integración con Symfony y creo que puede ser de gran apoyo para pruebas y aún más para las personas que están aprendiendo a trabajar con el framework.

A continuación van algunos de los sencillos experimentos realizados:

Una vez se haya instalado phpsh (leer el README), podemos trabajar  con sentencias de php y usar las funciones:

test$ phpsh
Starting php
type 'h' or 'help' to see instructions &amp; features
php&gt; echo "Hola mundo"
Hola mundo
php&gt; $a = 2+3; echo $a
5
php&gt; $f = array('uno' =&gt; 1, 'dos' =&gt; 2, 'tres' =&gt; 3);
php&gt; foreach ($f as $n) { echo $n; }
123
php&gt; foreach ($f as $n) { echo $n."\n"; }
1
2
3
php&gt; foreach ($f as $n) {
... echo $n."\n";
... }
1
2
3
php&gt; echo count($f);
3

Bien, pero ahora viene algo un poco más interesante: usar a través del shell interactivo nuestra aplicación hecha en Symfony.

No requiere ninguna configuración especial, simplemente cargar un archivo y listo; se trata del controlador frontal de nuestra aplicación (alguno de los que se encuentra en web), así:

test$ phpsh web/index.php
Starting php with extra includes: ['web/index.php']
 
  .......................
 
php&gt;

Para obviar la salida html, podemos crear una copia de ese archivo y evitar la llamada al método dispatch(), así:

require_once(dirname(__FILE__).'/config/ProjectConfiguration.class.php');
 
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend','prod', false);
sfContext::createInstance($configuration);

Ahora, de nuevo lo cargamos (ese archivo lo he llamado shell.php):

test$ phpsh shell.php
Starting php with extra includes: ['shell.php']
type 'h' or 'help' to see instructions &amp; features
php&gt; $c = CiudadPeer::doSelect(new Criteria);
 
php&gt; foreach ($c as $ciudad) {
 ... echo $ciudad."\n";
 ... }
Barranquilla
Bogota
Constantinopla
Medellin
 
php&gt;

Como se puede ver, puedo acceder a las clases de mi modelo (disculpas a los usuarios de Doctrine) y desde luego a las clases que nos proporciona Symfony, por ejemplo si mi archivo apps/frontend/config/app.yml es:

all:
 resultados_por_pagina: 15
 sf_phpmailer:
   mailer:                    true
   smtp_auth:                 true
   smtp_secure:               "ssl"
   host:                      "smtp.mihost"
   port:                      600
   username:                  "miusuario"
   password:                  "miclave"
   from:                      "nosesabe@example.com"
   from_name:                 "Anonimo"
test$ phpsh shell.php
Starting php with extra includes: ['shell.php']
type 'h' or 'help' to see instructions &amp; features
php&gt; $conf = sfConfig::get('app_sf_phpmailer_port');
 
php&gt; echo $conf;
600
php&gt; echo sfConfig::get('app_resultados_por_pagina');
15
php&gt;

Bueno, esos han sido los experimentos que he alcanzado a realizar. Espero trabajar más en detalle y publicar los resultados. Hasta una próxima.

Etiquetas: , , , | 2 Comentarios »

Mostrar numero de version en django

Noviembre 3, 2009 - 8:31 am - Posted by Camilo Nova

En AxiaCore utilizamos subversion para llevar el control de versiones de los proyectos, junto a nuestro esquema de desarrollo ágil manejamos ciclos cortos de lanzamiento de nuevas funcionalidades, por eso para nosotros es necesario conocer el numero de revisión del SVN y publicarlo en un lugar fácilmente accesible para los usuarios, de tal forma que rápidamente nos puedan indicar la versión que están utilizando.

La aproximación inicial es tener un parámetro donde se indique un numero de versión de la aplicación, pero esta fue rápidamente descartada porque no es flexible y se tendría que cambiar la versión manualmente en cada nuevo cambio, así que decidimos manejar el numero de revisión del repositorio como el indicador de la versión. Ahora bien, se necesita una manera automática de obtener dicho numero y publicarlo en una plantilla HTML para verlo en la interfaz de usuario.

Lo resolvimos así:

Partimos de la plantilla donde básicamente django nos permite lo siguiente:

1
2
{% load version_tag %}
<p>Version: {% get_version %}</p>

Entonces cargamos un ‘custom tag’ que nos retorna la versión actual de la aplicación, el cual es:

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
#! /usr/bin/env python
# -*- coding: utf8 -*-
import subprocess
from django import template
from django.core.cache import cache
 
register = template.Library()
 
@register.simple_tag
def get_version():
    """
    Retorna el numero de version para la aplicacion y lo almacena en
    cache para evitar ser llamado multiples veces y mejorar el rendimiento
    de la aplicacion.
    """
    if not cache.get('version'):
        comando = 'svn info | grep Rev | head -1'
        try:
            proc = subprocess.Popen(comando, shell=True, 
                stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )
            line = proc.communicate()[0]
            version = line[line.find(" ")+1:].rstrip("\n")
        except:
            version = '---'
 
        tiempo = 24 * 60 * 60   #Tiempo en segundos de un dia
        cache.set('version', version, tiempo)
 
    return cache.get('version')

Este archivo (version_tag.py) debe estar dentro de una carpeta llamada ‘templatetags’ de alguna aplicación del proyecto.

Lo interesante de esta solución es:

  1. Obtiene el numero de versión por el comando ’svn info’
  2. Reduce las llamadas al comando ubicando la información en cache durante un día
  3. Es totalmente desacoplado del proyecto y se puede reutilizar fácilmente
  4. Se puede adaptar para otros sistemas de control de versiones

Que les parece?

Etiquetas: , , | 2 Comentarios »

Cambiar el QuerySet de un ForeingKey de un modelo en Django

Enero 21, 2009 - 12:07 pm - Posted by Camilo Nova

Es posible declarar tipos ForeignKey en un modelo de datos de Django, pero puede que necesitemos filtrar los valores de esta relación, para efectuar tal cambio necesitamos recurrir al Form que muestra ese modelo y modificar el método __init__ de la siguiente manera:

1
2
3
4
class MyModelForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super(MyModelForm, self).__init__(*args, **kwargs)
        self.fields["myFKField"].queryset = MyModel.objects.all()

Esto permite cambiar los datos que son mostrados por el campo en el Form por unos filtrados que nosotros queramos, existe tambien la posibilidad de trabajar con limit_choices_to de ForeignKey pero esta solución me funciono de inmediato.

Etiquetas: , , | Comente »

Adicionar o Sustraer Dias en Python

Enero 20, 2009 - 3:22 pm - Posted by Camilo Nova

Para agregar o sustraer días a una fecha determinada en python lo mejor es hacerlo así:

1
2
3
4
5
from datetime import date, timedelta
#Agregar
d=date.today()+timedelta(days=dias)
#Sustraer
d=date.today()-timedelta(days=dias)

La operación respeta los días al cambiar de mes y funciona perfecto.

Etiquetas: , | Comente »

AxiaCore Blog

Publicidad

Etiquetas

Nosotros Leemos

Comentarios Recientes:

  • Jorge Chávez: Algo que me ha interesado en los últimos días es intentar agregar nuevos widgets en el filtro, que...
  • Jorge Chávez: Excelente post! Sin duda los filtros son un problema con la falta de documentación oficial, pero en lo...
  • CBTIS_102: pzz la vdd python es un programa muy completo y facil, pero a veces los que enseñan python son pesimos,...
  • katerine: CORIDAL SALUDO, ES HERMOSA ESTA LABOR. ME ENCANTARIA SABER LOS DATOS DE LA FUNDACION PARA ACERCARME A...
  • Camilo Nova: Copyright © 2008 AxiaCore S.A.S. – info@axiacore.com – http://axiacore.com

Enlaces Recientes:

Archivo

Admin