Slide 1

Slide 1 text

Препарируем Django: QuerySet Владимир Филонов

Slide 2

Slide 2 text

QuerySet нажинается с менеджера Препарируем Django: QuerySet models.Manager - просто proxy

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Все осталиные методы возвращайт QuerySet Препарируем Django: QuerySet class Manager(object): ... def get_query_set(self): return QuerySet(self.model, using=self._db)

Slide 5

Slide 5 text

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): ...

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Entry.objects.filter(...).exclude(...).select_related(...).prefetch_ related(...).annotate(...).order_by(...).distinct() Построение еепожек Препарируем Django: QuerySet Привыжная конструкеия?

Slide 8

Slide 8 text

Построение еепожек Препарируем Django: QuerySet Методы QuerySet возвращайт модифиеированнуй копий qs = Entry.objects.filter(...) qs1 = qs.exclude(...) qs2 = qs1.select_related(...) ...

Slide 9

Slide 9 text

Препарируем Django: QuerySet QuerySet.Query Вот тут нажинается самое интересное

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Препарируем Django: QuerySet Как сроится запрос Немного примеров и анализа

Slide 14

Slide 14 text

Как строится запрос Препарируем 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)

Slide 15

Slide 15 text

Как строится запрос Препарируем 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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Препарируем Django: QuerySet ~Q вызывает метод __invert__ Который устанавливает флаг "Отриеание"

Slide 18

Slide 18 text

Препарируем Django: QuerySet Обновляет дерево условий, живущее в QuerySet.query.where clone.query.add_q

Slide 19

Slide 19 text

Вместо тысяжи слов Препарируем Django: QuerySet Проанализируем дерево условий для несколикиу запросов qs1 = Entry.objects.filter(id=1) qs2 = Entry.objects.exclude(title__icontains="test", author__username="test")

Slide 20

Slide 20 text

Препарируем Django: QuerySet qs1.query.where { 'negated': False, 'connector': 'AND', 'children': [] }

Slide 21

Slide 21 text

Препарируем Django: QuerySet qs1.query.where.children[0] { 'negated': False, 'connector': 'AND', 'children': [(, 'exact', True, 1)] }

Slide 22

Slide 22 text

Препарируем Django: QuerySet qs1.query.where.children[0].children[0][0] { 'alias': u 'prepareqs_entry', 'field': , 'col': u 'id' }

Slide 23

Slide 23 text

Препарируем Django: QuerySet Дерево!

Slide 24

Slide 24 text

Препарируем Django: QuerySet qs1.query.where Constraint Level 1.1 qs1.query.where qs1.query.where.children[0] qs1.query.where .children[0] .children[0][0]

Slide 25

Slide 25 text

Препарируем Django: QuerySet WHERE "prepareqs_entry"."id" = 1 qs1.query

Slide 26

Slide 26 text

Препарируем Django: QuerySet qs2.query.where { 'negated': False, 'connector': 'AND', 'children': [] }

Slide 27

Slide 27 text

Препарируем Django: QuerySet qs2.query.where.children[0] { 'negated': False, 'connector': 'AND', 'children': [] }

Slide 28

Slide 28 text

Препарируем Django: QuerySet qs2.query.where.children[0].children[0] { 'negated': True, 'connector': u 'AND', 'children': [ , ] }

Slide 29

Slide 29 text

Препарируем Django: QuerySet qs2.query.where.children[0].children[0] .children[0] { 'negated': False, 'connector': 'AND', 'children': [(, 'exact', True, 'test')] }

Slide 30

Slide 30 text

Препарируем Django: QuerySet qs2.query.where.children[0].children[0] .children[0] .children[0][0] { 'alias': u'auth_user', 'field': , 'col': 'username' }

Slide 31

Slide 31 text

Препарируем Django: QuerySet Еще дерево!

Slide 32

Slide 32 text

Препарируем 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]

Slide 33

Slide 33 text

Препарируем Django: QuerySet WHERE NOT ("auth_user"."username" = 'test' AND "prepareqs_entry"."title" LIKE %test% ) qs2.query

Slide 34

Slide 34 text

Препарируем Django: QuerySet Устали?

Slide 35

Slide 35 text

Препарируем Django: QuerySet Объединим запросы qs3 = qs1 | qs2

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Препарируем Django: QuerySet qs3.query.where.children[0] { 'negated': False, 'connector': 'AND', 'children': [(, 'exact', True, 1)] }

Slide 38

Slide 38 text

Препарируем Django: QuerySet qs3.query.where.children[1] { 'negated': False, 'connector': 'AND', 'children': [] }

Slide 39

Slide 39 text

Препарируем Django: QuerySet qs3.query.where.children[1].children[0] { 'negated': True, 'connector': u 'AND', 'children': [ , ] }

Slide 40

Slide 40 text

Препарируем Django: QuerySet Болизое дерево!

Slide 41

Slide 41 text

Препарируем Django: QuerySet qs3.query.where Constraint Level 3.1 Constraint Level 3.2 Constraint Level 1.1

Slide 42

Slide 42 text

Препарируем Django: QuerySet WHERE ("prepareqs_entry"."id" = 1 OR NOT ("auth_user"."username" = 'test' AND "prepareqs_entry"."title" LIKE %test% )) qs3.query

Slide 43

Slide 43 text

Препарируем Django: QuerySet qs3.query.where Constraint Level 3.1 Constraint Level 3.2 Constraint Level 1.1

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Препарируем Django: QuerySet Но как же все-таки стоится запрос

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Препарируем Django: QuerySet QuerySet.iterator() compiler.results_iter() compiler.execute_sql() compiler.as_sql()

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

Компилятор 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сли первые две проверки нижего не дали

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Компилятор SQLCompiler Препарируем Django: QuerySet FROM from_, f_params = self.get_from_clause() Все предыдущие операеии могли модифиеировати query.tables

Slide 52

Slide 52 text

Компилятор 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))

Slide 53

Slide 53 text

Компилятор SQLCompiler Препарируем Django: QuerySet FROM [ u'"prepareqs_entry"', u'INNER JOIN "auth_user" ON ("prepareqs_entry"."author_id" = "auth_user"."id")т ]

Slide 54

Slide 54 text

Компилятор SQLCompiler Препарируем Django: QuerySet WHERE where, w_params = self.query.where.as_sql( qn=qn, connection=self.connection ) Тут нажинается прогулка по дереву =)

Slide 55

Slide 55 text

Препарируем Django: QuerySet qs3.query.where Constraint Level 3.1 Constraint Level 3.2 Constraint Level 1.1

Slide 56

Slide 56 text

Компилятор 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']

Slide 57

Slide 57 text

Компилятор 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']

Slide 58

Slide 58 text

Препарируем Django: QuerySet Та-да! Пора собирати строку! И кормити ей драйвер

Slide 59

Slide 59 text

Компилятор 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) ...

Slide 60

Slide 60 text

Препарируем Django: QuerySet И так далее для GROUP BY, HAVING, ORDER BY, OFFSET/LIMIT

Slide 61

Slide 61 text

Препарируем 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%')) Параметризованный запрос готов!

Slide 62

Slide 62 text

Препарируем Django: QuerySet QuerySet.iterator() compiler.results_iter() compiler.execute_sql() compiler.as_sql()

Slide 63

Slide 63 text

Препарируем Django: QuerySet compiler.execute_sql() cursor = self.connection.cursor() cursor.execute(sql, params) …... cursor.fetchone() / cursor.fetchmany()

Slide 64

Slide 64 text

Препарируем Django: QuerySet QuerySet.iterator() compiler.results_iter() compiler.execute_sql() compiler.as_sql()

Slide 65

Slide 65 text

Препарируем 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() Что именно происуодит при этом в классе модели мы разбирали в прозлый раз =)

Slide 66

Slide 66 text

Препарируем Django: QuerySet Если еще ести силы Осталаси последняя ситуаеия. qs.select_related()

Slide 67

Slide 67 text

Препарируем Django: QuerySet get_cached_row Основная модели Аггрегаторы какие-нибуди Related model 1 Related model 2 Строка ответа от БД

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

Где копатися дализе Препарируем 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

Slide 70

Slide 70 text

It’s all about music business ВОПОСЫ? – [email protected] FACEBOOK - facebook.com/pyhoster Спасибо!