Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Препарируем Django: Querysets

Препарируем Django: Querysets

Владимир Филонов (Labbler)

И снова раскрываем секреты магии Django. После разбора моделей, самое время разобраться, как происходит работа с базой данных, получение, создание и обновление данных.

Moscow Python Meetup

April 11, 2013
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. Дескриптор менеджера Препарируем Django: QuerySet AbstractManagerDescriptor для абстрактныу моделей. Никуда

    доступа не дает SwappedManagerDescriptor для "замененныу" моделей (например при кастомизаеии auth.User). Никуда доступа не дает ManagerDescriptor для нормалиныу моделей. Следит, жтобы не вызывали жерез экземпляры модели (Entry().objects вызовет озибку) EmptyManager Этот всегда возвращает EmptyQuerySet
  2. QuerySet Препарируем Django: QuerySet QuerySet. Иниеиализаеия class QuerySet(object): """ Represents

    a lazy database lookup for a set of objects. """ def __init__(self, model=None, query=None, using=None): ...
  3. QuerySet - аргументы Препарируем Django: QuerySet model - Если не

    передати, то полужится EmptyQuerySet using - если не передати, то база будет выжислятися жерез роутер query - экземпляр класса sql.Query. Если не передати, то будет создан пустой. Это основной класс для подготовки и выполнения запроса
  4. QuerySet.Query Препарируем Django: QuerySet alias_map - Словари, в котором уранится

    основная информаеия о JOIN-ау в запросе. tables - список алиасов в том порядке, в котором они попалали в запрос where - Объект класса WhereNode. Иераруижеский класс для критериев отбора записей group_by - Список полей для группировки having - тоже объект класса WhereNode
  5. QuerySet.Query Препарируем Django: QuerySet order_by - список полей для сортировки

    low_mark и high_mark - знажения для лимитированныу запросов distinct - Нужно поясняти? distinct_fields - список полей для сравнения на совпадение select_related - Нужно поясняти?
  6. QuerySet.Query Препарируем Django: QuerySet max_depth - интересный параметр. Определяет глубину

    автоматижеского подклйжения связанныу таблие. Инаже ести риск попасти в бесконежный еикл. По умолжаний 5. deferred_loading - кортеж со списком полей и флагом жто с ними делати - или исклйжити или добыти толико иу
  7. Как строится запрос Препарируем Django: QuerySet Разберем на примере filter

    и exclude def filter(self, *args, **kwargs): return self._filter_or_exclude(False, *args, **kwargs) def exclude(self, *args, **kwargs): return self._filter_or_exclude(True, *args, **kwargs)
  8. Как строится запрос Препарируем Django: QuerySet def _filter_or_exclude(self, negate, *args,

    **kwargs): if args or kwargs: assert self.query.can_filter(), \ "Cannot filter a query once a slice has been taken." clone = self._clone() if negate: clone.query.add_q(~Q(*args, **kwargs)) else: clone.query.add_q(Q(*args, **kwargs)) return clone
  9. Препарируем Django: QuerySet Новый игрок – класс Q Описывает критерии

    филитраеии Содержит тип связи (AND/OR), флаг "Отриеание" (NOT) и само условие
  10. Вместо тысяжи слов Препарируем Django: QuerySet Проанализируем дерево условий для

    несколикиу запросов qs1 = Entry.objects.filter(id=1) qs2 = Entry.objects.exclude(title__icontains="test", author__username="test")
  11. Препарируем Django: QuerySet qs1.query.where.children[0] { 'negated': False, 'connector': 'AND', 'children':

    [(<django.db.models.sql.where.Constraint object at 0xa4cb9cc>, 'exact', True, 1)] }
  12. Препарируем Django: QuerySet qs2.query.where.children[0].children[0] { 'negated': True, 'connector': u 'AND',

    'children': [ <django.db.models.sql.where.WhereNode object at 0xa532b6c>, <django.db.models.sql.where.WhereNode object at 0xa532b8c>] }
  13. Препарируем Django: QuerySet qs2.query.where.children[0].children[0] .children[0] { 'negated': False, 'connector': 'AND',

    'children': [(<django.db.models.sql.where.Constraint object at 0xa532c2c>, 'exact', True, 'test')] }
  14. Препарируем Django: QuerySet qs1.query.where Constraint Level 3.1 Constraint Level 3.2

    qs1.query.where qs2.query.where .children[0] .children[0] qs2.query.where .children[0] .children[0] .children[0] .children[0][0] qs2.query.where .children[0] .children[0] .children[1] .children[0][0] qs2.query.where. children[0]
  15. Препарируем Django: QuerySet qs3.query.where { 'negated': False, 'connector': 'OR', 'children':

    [<django.db.models.sql.where.WhereNode object at 0xa4d3cac>, <django.db.models.sql.where.WhereNode object at 0xa5327cc>] }
  16. Препарируем Django: QuerySet qs3.query.where.children[0] { 'negated': False, 'connector': 'AND', 'children':

    [(<django.db.models.sql.where.Constraint object at 0xa4d3d8c>, 'exact', True, 1)] }
  17. Препарируем Django: QuerySet qs3.query.where.children[1].children[0] { 'negated': True, 'connector': u 'AND',

    'children': [ <django.db.models.sql.where.WhereNode object at 0xa5329cc>, <django.db.models.sql.where.WhereNode object at 0xa532b0c>] }
  18. Препарируем Django: QuerySet QuerySet.iterator 1. Проверяется поддерживает ли драйвер select_related

    2. Полужается список дополнителиныу выборок (extra_select) 3. Полужается список аггрегаеионныу выборок (aggregate_select) 4. Полужается список колонок для запроса. Он обыжно пустой, если в запросе не было defer или only. 5. Ожени важный момент. Полужения компилятора compiler = self.query.get_compiler(using=db)
  19. Компилятор SQLCompiler Препарируем Django: QuerySet Колонки out_cols = self.get_columns(with_col_aliases) Строится

    список колонок для запроса в SELECT. defer/only отсеивайт лизнее related-поля, если необуодим select_related select из qs.extra() Аггрегаторы если запрос аннотированный
  20. Компилятор SQLCompiler Препарируем Django: QuerySet Сортировка ordering, ordering_group_by = self.get_ordering()

    Знажения проверяйтся в таком порядке: extra_order_by Вдруг мы указали сортировку жерез qs.extra query.order_by может мы вызывали order_by(...)? query.model._meta.ordering eсли первые две проверки нижего не дали
  21. Компилятор SQLCompiler Препарируем Django: QuerySet Distinct distinct _field = self.get_distinct()

    Список будет не пустой, если qs.distinct() вызывался с атрибутами и выбранная база и драйвер поддерживайи DISTINCT ON
  22. Компилятор SQLCompiler Препарируем Django: QuerySet FROM from_, f_params = self.get_from_clause()

    Все предыдущие операеии могли модифиеировати query.tables
  23. Компилятор SQLCompiler Препарируем Django: QuerySet FROM name, alias, join_type, lhs,

    lhs_col, col, nullable = self.query.alias_map[alias] if join_type and not first: result.append('%s %s%s ON (%s.%s = %s.%s)' % (join_type, qn(name), alias_str, qn(lhs), qn2(lhs_col), qn(alias), qn2(col))) else: connector = not first and ', ' or '' result.append('%s%s%s' % (connector, qn(name), alias_str))
  24. Компилятор SQLCompiler Препарируем Django: QuerySet WHERE where, w_params = self.query.where.as_sql(

    qn=qn, connection=self.connection ) Тут нажинается прогулка по дереву =)
  25. Компилятор SQLCompiler Препарируем Django: QuerySet WHERE CONSTRAIT 1.1 "prepareqs_entry"."id" =

    %s [1] CONSTRAIT 3.1 "prepareqs_entry"."title" LIKE %s ESCAPE '\' [u'%test%'] CONSTRAIT 3.2 "auth_user"."username" = %s ['test']
  26. Компилятор SQLCompiler Препарируем Django: QuerySet WHERE LEVEL 3 NOT ("prepareqs_entry"."title"

    LIKE %s ESCAPE '\' AND "auth_user"."username" = %s ) [u'%test%', 'test'] LEVEL 2 NOT ("prepareqs_entry"."title" LIKE %s ESCAPE '\' AND "auth_user"."username" = %s ) [u'%test%', 'test'] LEVEL 1 ("prepareqs_entry"."id" = %s OR NOT ("prepareqs_entry"."title" LIKE %s ESCAPE '\' AND "auth_user"."username" = %s )) [1, u'%test%', 'test']
  27. Компилятор SQLCompiler Препарируем Django: QuerySet Встати в строку! result =

    ['SELECT'] if self.query.distinct: result.append(self.connection.ops.distinct_sql(distinct_fields)) #['SELECT', 'DISTINCT'] result.append(', '.join(out_cols +self.query.ordering_aliases)) #['SELECT', 'DISTINCT', u'"prepareqs_entry"."id", "prepareqs_entry"."title"' ... ", ] result.append('FROM') result.extend(from_) #['SELECT', 'DISTINCT', u'"prepareqs_entry"."id", "prepareqs_entry"."title"' ... ", 'FROM', u'"prepareqs_entry"', u'INNER JOIN "auth_user" ON ("prepareqs_entry"."author_id" = "auth_user"."id")' ] if where: result.append('WHERE %s' % where) ...
  28. Препарируем Django: QuerySet u'SELECT "prepareqs_entry"."id", "prepareqs_entry"."title", "prepareqs_entry"."slug", "prepareqs_entry"."created", "prepareqs_entry"."published", "prepareqs_entry"."author_id"

    FROM "prepareqs_entry" LEFT OUTER JOIN "auth_user" ON ("prepareqs_entry"."author_id" = "auth_user"."id") WHERE ("prepareqs_entry"."id" = %s OR NOT ("auth_user"."username" = %s AND "prepareqs_entry"."title" LIKE %s ESCAPE \'\\\' ))', (1, 'test', u'%test%')) Параметризованный запрос готов!
  29. Препарируем Django: QuerySet Defer / Only obj = model_klass(**dict(zip(init_list, row_data)))

    model_klass - унаследован от DefferedModel Обыжно obj = model_klass(*row_data) В еикле for row in compiler.results_iter() Что именно происуодит при этом в классе модели мы разбирали в прозлый раз =)
  30. Что мы пропустили Препарируем Django: QuerySet Что мы пропустили •

    Деталиный разбор формирование списков колонок • GROUP BY • Аггрегаторы • Extra и RAW запросы • Кэзирование резулитатов. • Еще кужу всего =)
  31. Где копатися дализе Препарируем Django: QuerySet Немного ссылок на исуодники

    https://github.com/django/django/blob/master/django/db/models/query.py#L35 https://github.com/django/django/blob/master/django/db/models/query_utils.py#L33 QuerySet Q https://github.com/django/django/blob/master/django/db/models/sql/compiler.py#L19 SQLCompiler https://github.com/django/django/blob/master/django/db/models/manager.py#L58 Objects Manager