Descubrimientos

Consultas Django – usando todo

Usando varios elementos de las consultas para hacer una consulta compleja

qs = Person.objects.annotate(birth_year_int=Cast(
    ExtractYear("birthdate"),
    output_field=IntegerField(),),
    birth_year_modulus_4=F("bith_year_int") % 4,
    birth_year_modulus_100=F("birth_year_int") % 100,
    birth_year_modulus_400=F("birth_year_int") % 400,
).annotate(born_in_leap_year=Case(
    When(Q(Q(birth_year_modulus_4=0) & 
        Q(~Q(birth_year_modulus_100=0) | Q(birth_year_modulus_400=0))
    ),then=True,),
    default=False,
    output_field=BooleanField(),
).filter(born_in_leap_year=True)
.values_list("birthdate__date", flat=True)
)

for year in years:
   assert calendar.isleap(year), (f"{year} ins't a leap year")
     

Extraído de

DjangoCon 2019 – Building effective Django queries with expressions by Vanessa Barreiros – YouTube

Consultas Django Conditionals

class Order(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    customer = models.ForeignKey(Person, related_name="orders",on_delete=models.CASCADE)
    total = models.PositiveIntegerField()

aplicando un descuento a los clientes antiguos

from django.utils import timezone

orders = Order.bjects.filter(created_at__date=timezone.now().date()

for order in orders():
    customer_joined_year = order.customer.joined_on.year
    if(customer_joined_year >= 2010 and customer_joined_year <2019):
        order.total *=0.8
        order.save()
    elif(customer_joined_year <= 2000 and cutomer_joined_year >= 1995):
        order.total *= 0.6
        order.save()

ahora la versión con Conditionals

from django.db.models import Case, When

orders.annotate(discounted_total=
    Case(
        When(customer__joined_on__year__range=(1995, 2018),
            then=F("total") * 0.8,), 
        When(customer__joined_on__year__range=(1995, 2000),
            then=F("total") * 0.6,),
        default="total",)
)
      

Extraído de

DjangoCon 2019 – Building effective Django queries with expressions by Vanessa Barreiros – YouTube

Consultas Django Coalesce

No se bien su uso

from django.db.models.functions import Coalesce, NullIf
class Person(models.Model):
    first_name = models.Charfield(max_length=255)
    nickname = models.Charfield(max_length=255, null=True, blank=True)
    ...
Person.objects.annotate(
    goes_by=Coalesce("nickname", "first_name")
).values_list("goes_by")

None si la cadena es vacía «»

Person.objects.annotate(
    goes_by=Coalesce(
        NullIf("nickname", Value(""))
        "first_name"
    )
).values_list("goes_by")

Consultas Django Concat

No se puede hacer una consulta de una @property pero puedes crear un valor personalizado concatenando

Ejemplo fullname:

from django.db.models import Value
from django.db.models.functions import Concat

Person.objects.annotate(
    fullname=Concat("fist_name", Value(" "), "last_name")
    .filter(fullname__icontains="pedro perez")
    .values_list("fullname")
) 

Consultas Django

Consultar todos los elementos de un modelo.

all_entries = Entry.objects.all()

Filtrar por uno o varios atributos, se puede filtrar por fecha(year, month, day, …), si es mayor, mayor igual (gt, gte, lt, lte, …) leer más.

Entry.objects.filter(pub_date__year=2006)

Buscar un elemento con get().

entry = Entry.objects.get(pk=1)

«Anotaciones»

  • numero de mascotas por tipo, accedes con doble barra baja __count
  • número de entradas de diferentes blogs, tomas la función de agregación (Count()) y la igualas a un nombre que gustes (number_of_entries), y con ese nombre accedes al valor que querías calcular por cada elemento.
from django.db.models import Count

qs1 = Pet.objects.values('type').annotate(Count("type"))
#[(type: fish, type__count: 354), (type: dog, type__count: 375) ...]


qs2 = Blog.objects.annotate(number_of_entries=Count('entry'))
qs2[0].name
#'primer blog'
qs2[0].number_of_entries
#40 "publicaciones del primer blog"

Consultas Django «F objects»

class Persona(models.Model):
    #...
    last_donate = models.DateTimeField()
    can_donate_on = models.DateTimeField()

filtro sencillo, personas con fechas anteriores o iguales a «ahora (now)»

from django.utils import timezone

qs = Persona.objects.filter(can_donate_on__lte=timezone.now())

Filtrar operando valores del objeto

from datetime import timedelta
from django.db.models import F


qs = Persona.objects.annotate(can_donate_on=F("last_donate") + timedelta(day=56)).

filter(can_donate_on__lt=timezone.now())

otro ejemplo con conteos y operaciones (Banco de sangre; meta de bolsas; bolsas actuales; cuantas faltan)

qs = BloodBank.objects.annotate(
    existent_amount=Count("blood_bag_set"),
    remaining=F("goal")-F("existent_amount"),
).value_list("goal", "existent_amount", "remaining")
#1500 1000 500

Operar tipos distintos

from django.db.models import ExpressionWrapper, DateTimeField

qs = Event.objects.annotate(
    ends_on=ExpressionWrapper(F("start_at")+F("duration"), 
    output_field=DateTimeField())
    )
)

Extraído de

DjangoCon 2019 – Building effective Django queries with expressions by Vanessa Barreiros – YouTube

Consultas Django Q objects

Combinar condiciones & para AND, | para OR, ~ para negación

from django.db.models import Q

Person.objects.filter(Q(
    Q(birthdate__year__range=(1981, 1996))
    | Q(birthday__year__range=(1946,1964))
    ) & ~Q(location__postal_code="NY")
)

agregar filtros dinámicamente

...
value = params["value"]
user_filter = Q(is_active=True)
if "name" in params:
    user_filter.add(Q(first_name__icontains=value) | Q(last_name__icontains=value), Q.OR)

if "job" in params:
    user_filter.add(Q(job_name__icontains=value), Q.OR)

if "email" in params:
    user_filter.add(Q(email__icontains=value), Q.OR)

qs = Person.objects.filter(user_filter)
...

Extraido de

DjangoCon 2019 – Building effective Django queries with expressions by Vanessa Barreiros – YouTube