python - from - foreign key django example
AnotaciĆ³n de consulta Django con campo booleano. (5)
Cuando tenga que anotar la existencia con algunos filtros, se puede utilizar la anotación de Sum
. Por ejemplo, las siguientes anotaciones si hay GIF en las images
:
Product.objects.filter(
).annotate(
animated_images=Sum(
Case(
When(images__image_file__endswith=''gif'', then=Value(1)),
default=Value(0),
output_field=IntegerField()
)
)
)
Esto realmente los contará, pero cualquier pythonic if product.animated_images:
funcionará igual que booleano.
Digamos que tengo un modelo de producto con productos en un escaparate y una tabla de imágenes de producto con imágenes del producto, que pueden tener cero o más imágenes. Aquí hay un ejemplo simplificado:
class Product(models.Model):
product_name = models.CharField(max_length=255)
# ...
class ProductImage(models.Model):
product = models.ForeignKey(Product, related_name=''images'')
image_file = models.CharField(max_length=255)
# ...
Al mostrar los resultados de búsqueda de productos, quiero priorizar los productos que tienen imágenes asociadas a ellos. Puedo obtener fácilmente el número de imágenes:
from django.db.models import Count
Product.objects.annotate(image_count=Count(''images''))
Pero eso no es realmente lo que quiero. Me gustaría anotarlo con un campo booleano, have_images
, que indica si el producto tiene una o más imágenes, de modo que pueda ordenar por eso:
Product.objects.annotate(have_images=(?????)).order_by(''-have_images'', ''product_name'')
¿Cómo puedo hacer eso? ¡Gracias!
Lea la documentación sobre extra
qs = Product.objects.extra(select={''has_images'': ''CASE WHEN images IS NOT NULL THEN 1 ELSE 0 END'' })
Probado funciona
Pero order_by
o where
(filter) en este campo no es para mí (Django 1.8) 0o:
Si necesita ordenar el conjunto de consultas resultante usando algunos de los nuevos campos o tablas que ha incluido a través de extra () use el parámetro order_by para extra () y pase una secuencia de cadenas. Estas cadenas deben ser campos de modelo (como en el método order_by () normal en querysets), de la forma nombre_tabla.nombre_columna o un alias para una columna que especificó en el parámetro select para extra ().
qs = qs.extra(order_by = [''-has_images''])
qs = qs.extra(where = [''has_images=1''])
FieldError: no se puede resolver la palabra clave ''has_images'' en el campo.
He encontrado https://code.djangoproject.com/ticket/19434 aún abierto.
Así que si tienes problemas como yo, puedes usar raw
Si el rendimiento importa, mi sugerencia es agregar el campo booleano hasPictures
(como editable=False
)
Luego, mantenga el valor correcto a través de las señales del modelo ProductImage
(o sobrescriba los métodos de save
y delete
)
Ventajas:
- Índice amigable.
- Mejor presentación. Evitar las uniones.
- Base de datos agnóstica.
- Codificarlo elevará tus habilidades de django al siguiente nivel.
Use expresiones condicionales y lance el campo de salida a BooleanField
Product.objects.annotate(image_count=Count(''images'')).annotate(has_image=Case(When(image_count=0, then=Value(False)), default=Value(True), output_field=BooleanField())).order_by(''-has_image'')
Finalmente encontré una manera de hacer esto usando las nuevas expresiones condicionales de django 1.8:
from django.db.models import Case, When, Value, IntegerField
q = (
Product.objects
.filter(...)
.annotate(image_count=Count(''images''))
.annotate(
have_images=Case(
When(image_count__gt=0,
then=Value(1)),
default=Value(0),
output_field=IntegerField()))
.order_by(''-have_images'')
)
Y así es como finalmente encontré un incentivo para actualizar a 1.8 desde 1.7.