El Blog de Trespams

Blog personal sobre tecnologia, gestió de projectes i coses que se me passen pel cap

Solapament d'intervals ara amb Django

Segueixo amb la vena friki, ahir damunt el solapament d'intervals de mode genèric i avui veurem la implementació d'aquesta idea a un model Django.

class Oferta(models.Model):
"""Una oferta que va per rang"""
nom = models.CharField(max_length=200)
inici = models.DateField()
fi = models.DateField()
activa = models.BooleanField(default=True)

class Meta:
verbose_name = "Oferta"
verbose_name_plural = "Ofertes"

def __unicode__(self): return self.nom

El model com veis és molt senzill, una oferta pot estar activa o no i pertany sempre a un rang de dates. Queda fora de l'exemple la determinació de si admetem solapament de dates per una oferta, que es fareia de manera semblant al la cerca que ara farem.

La idea és que hem d'aconseguir que amb l'ORM de Django nodelar una consulta del tipus not ((x1<y0) or (y1<x0)).

Els filtres per defecte el que fan es un and, per la qual cosa no ens serveixen directament. El que farem és utilitzar una característica de l'ORM anomenada funció Q que ens permete afegir condicons de filtratge múltiples i fer que vaign per OR enlloc de per AND.

from django.db.models import Q

Ara sols queda fer la consulta. Particularment si són consultes que poden ser susceptibles de fer-se servir a varis llocs o que estan molt relacionades amb el model, m'agrada lligar-les al model amb un manager ad-hoc o bé amb un mètode estàtic, que és el que farme ara. Així:

@staticmethod
def ofertes_entre(pInici, pFi):
return Oferta.objects.filter(activa=True). \ exclude(Q(fi__lt=pInici) | Q(inici__gt=pFi)).order_by('-inici')

Fitxem-nos que el NOT de la consulta s'aconsegueix amb l'exclude i que feim servir la fució Q per afegir les dues condicions de comparació dins aquest filtre i les lligam per OR (|).

He afegit un grapat de dades d'exemple, i executat la consulta, que queda com

Oferta.ofertes_entre(f1, f2)

Ho he fet des de línia de comandes, de manera que ara puc veure la query que es genera:

>>from django.db import connection 

>>>connection.queries

'sql': u'SELECT "oferta_oferta"."id", "oferta_oferta"."nom",
"oferta_oferta"."inici", "oferta_oferta"."fi", "oferta_oferta"."activa" FROM "oferta_oferta" WHERE ("oferta_oferta"."activa" = True AND NOT (("oferta_oferta"."fi" < 2011-03-20 OR "oferta_oferta"."inici" > 2011-04-20 ))) ORDER BY "oferta_oferta"."inici"

Com m'agrada Django!

blog comments powered by Disqus