Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Exprimiendo el ORM de Django

924 views

Published on

¿No consigues expresar tus consultas con el ORM de Django? Si ves la necesidad de usar el método extra() casi cada vez que tienes que expresar una query, si aún no tienes claro para qué sirven Q() y F() o si, una vez te has desesperado, acabas escribiendo tus consultas en SQL plano y usando cursores, es probable que esto te interese.

Published in: Technology
  • Be the first to comment

Exprimiendo el ORM de Django

  1. 1. Exprimiendo el ORM de Django Héctor Pablos López PyconES 2017
  2. 2. #whoami Héctor Pablos López Frontend developer @ StyleSage www.stylesage.co hector@stylesage.co
  3. 3. Contenido • Introducción • Expresiones (Query expressions) • Joining • Cuando todo lo demás falla
  4. 4. Introducción
  5. 5. Introducción Why I hate the Django ORM: YouTube Video https://www.youtube.com/watch?v=GxL9MnWlCwo Rant alert
  6. 6. Introducción Why I hate the Django ORM: YouTube Video https://www.youtube.com/watch?v=GxL9MnWlCwo Alex Gaynor Desarrollador del ORM de Django
  7. 7. Introducción • Es agnóstico en cuanto al motor de BD • Escribimos consultas en alto nivel… Y en Python! • Completamente integrado en Django (Migraciones, modelos…) ¿Por qué usarlo?
  8. 8. Introducción • El ORM como a mi me hubiera gustado que me lo explicaran • Ejemplos en Python Y SQL Objetivo de esta charla
  9. 9. Introducción Car.ojects.values(‘model_name’).filter(build_year__gt=1995) SELECT car.model_name FROM car WHERE car.build_year > 1995; Expresión básica
  10. 10. Expresiones
  11. 11. Expresiones ¿Qué son? Un valor (simple o calculado) que se puede usar en una orden de DML .filter(expression) .order_by(expression) .annotate(expression) .aggregate(expression)
  12. 12. Expresiones Objetos F() Representan el valor de un campo Car.ojects.filter(units_produced__gt=F(‘units_sold’)) SELECT * FROM car WHERE car.units_produced > car.units_sold
  13. 13. Expresiones Objetos Q() Encapsulan colecciones de argumentos con nombre Car.ojects.filter(Q(kilometers__gt=1000) | Q(build_year__lt=1990)) SELECT * FROM car WHERE car.kilometers > 1000 OR car.build_year < 1990
  14. 14. Expresiones annotate() Anotar (dar nombre) a una expresión Car.objects.annotate(plate=‘plate_number’) SELECT plate_number AS plate FROM car
  15. 15. Expresiones Func() y Aggregate() Funciones de nuestra base de datos… Las que queramos! class DaysBetween(Func): function = ‘DAYS_BETWEEN’ class Now(Func): function = ‘NOW' class Median(Aggregate): function = ‘MEDIAN'
  16. 16. Expresiones Combinar expresiones
  17. 17. Expresiones Combinar expresiones Funciones y más funciones Company.objects .annotate(number_of_days=DaysBetween(Now(), F(‘date_started’))) .filter(number_of_days__lt=100) SELECT DAYS_BETWEEN(NOW(), date_Started) AS number_of_days FROM company WHERE DAYS_BETWEEN(NOW(), date_Started) < 100;
  18. 18. Expresiones Combinar expresiones Funciones y más funciones… Agregadas! Company.objects.values(‘region_id’) .annotate(all_the_days=Sum(DaysBetween(Now(), F(‘date_started’)))) SELECT SUM(DAYS_BETWEEN(NOW(), date_Started)) AS all_the_days FROM company GROUP BY region_id;
  19. 19. Expresiones Combinar expresiones Expresiones condicionales Product.objects.annotate( discounted_product_count=Count( Case( When(is_discounted=True, sold_out=False, then=F('id')), default=None, output_field=IntegerField() ), distinct=True ) )
  20. 20. Expresiones Combinar expresiones Product.objects.annotate( discounted_product_count=Count( [ … ] ), avg_discount=Sum( Case( When(is_discounted=True, sold_out=True, then=F('discount_percent')), default=0, output_field=FloatField() ) ) / Case( When(Q(discounted_product_count__gt=0), then=F('discounted_product_count')), default=1, output_field=IntegerField() ) / 100 )
  21. 21. Expresiones Combinar expresiones
  22. 22. Joining
  23. 23. Joining Con Foreign Key class Parameter(models.Model): name = models.CharField(max_length=30, unique=True) parameter_category = models.ForeignKey(ParameterCategory) affected_entities = models.ManyToManyField( Entity, blank=False, help_text=‘Entities affected by this’, ) Parameter.objects .values(‘name', 'parameter_category__name') SELECT "parameter"."name", "parametercategory"."name" FROM "parameter" INNER JOIN "parametercategory" ON ("parameter"."parameter_category_id" = "parametercategory"."id");
  24. 24. Joining Con Foreign Key class Parameter(models.Model): name = models.CharField(max_length=30, unique=True) parameter_category = models.ForeignKey(ParameterCategory) affected_entities = models.ManyToManyField( Entity, blank=False, help_text=‘Entities affected by this’, ) Parameter.objects.values('name', ‘affected_entities__name') SELECT "parameter"."name", “entity"."name" FROM "parameter" LEFT OUTER JOIN “affected_entities" ON ("parameter"."id" = "affected_entities"."parameter_id") LEFT OUTER JOIN “entity" ON ("affected_entites"."entity_id" = "entity"."id")
  25. 25. Joining Foreign keys: select_related() >> parameters = list( Parameter.objects.values(‘name', ‘parameter_category__name’) ) >> # Lista de diccionarios >> parameters[0][‘parameter_category__name’] u’Name of the category’ >> parameters = list( Parameter.objects.select_related(‘parameter_category’) ) >> # Lista de modelos. Accedemos a parameter_category sin consulta >> # adicional >> parameters[0].parameter_category.name u’Name of the category’
  26. 26. Joining ManyToMany fields: prefetch_related() >> parameters = list( Parameter.objects.values(‘name', ‘affected_entities__name’) ) >> # Lista de diccionarios >> parameters[0][‘affected_entities__name’] u’Name of the entity’ >> parameters = list( Parameter.objects.prefetch_related(‘affected_entities’) ) >> # Lista de modelos. Accedemos a la lista de entidades sin consulta >> # adicional >> parameters[0].affected_entities.all()[0].name u’Name of the category’
  27. 27. Joining Sin Foreign Key ¯_(ツ)_/¯ Método extra()
  28. 28. Joining Sin Foreign Key… Pero con modelos no gestionados por Django class TableWithoutForeignKey(models.Model): not_real = models.ForeignKey(OtherTable, db_column=‘other_column') foo = models.CharField(max_length=50) bar = models.IntegerField() class Meta: db_table = ‘table_without_foreign_key’ managed = False
  29. 29. Si todo lo demás falla
  30. 30. Si todo lo demás falla RawSQL() queryset.annotate( val=RawSQL("select col from sometable where othercol = %s", (someparam,)) ) Añadir subqueries dentro del select
  31. 31. Si todo lo demás falla extra() Joins sin Foreign Key QuerysetTable.extra( where=[ 'table_to_join_with.id = queryset_table.table_to_join_with_id' ], tables = ['table_to_join_with'], )
  32. 32. Si todo lo demás falla Cursores connection = connections[‘connnection_in_settings'] cursor = connection.cursor() sql = """ SELECT some_data, some_other_data, a_thing, footer FROM THE_TABLE WHERE id=%s AND code=%s ORDER BY column_to_order_by; """ cursor.execute(sql, [table_id_to_get, table_code_to_get]) desc = cursor.description the_data_list = [ dict(zip([col[0].lower() for col in desc], row)) for row in cursor.fetchall() ]
  33. 33. #whoami Héctor Pablos López Frontend developer @ StyleSage www.stylesage.co hector@stylesage.co

×