Як обробляє запити Django
Уявлення та маршрутизація
Центральним моментом будь-якої веб-програми є обробка запиту, який надсилає користувач. Django за обробку запиту відповідають подання або views . По суті представлення представляють функції обробки, які приймають дані запиту у вигляді об'єкта HttpRequest з пакету django.http і генерують деякий результат, який потім надсилається користувачеві.
За промовчанням подання розміщуються у програмі у файлі views.py .
Наприклад, візьмемо стандартний проект, до якого додано програму (наприклад, проект створений у минулому розділі).
При створенні нового проекту файл views.py має такий вміст:
from django.shortcuts import render # Create your views here.
Цей код поки що ніяк не обробляє запити, він лише імпортує функцію render() , яка може використовуватися для обробки.
Генерувати результат можна у різний спосіб. Один із них представляє використання класу HttpResponse з пакету django.http, який дозволяє надіслати текстовий вміст.
Так, змінимо файл views.py наступним чином:
from django.http import HttpResponse def index(request): return HttpResponse("Головна") def about(request): return HttpResponse("Про сайт") def contact(request): return HttpResponse("Контакти")
У цьому випадку визначено три функції, які будуть опрацьовувати запити. Кожна функція приймає як параметр request об'єкт HttpRequest , який зберігає інформацію про запит. Однак у цьому випадку вона нам не потрібна, тому параметр ніяк не використовується. Для генерації відповіді конструктор об'єкта HttpResponse передається деякий рядок. Це може бути навіть код html у вигляді рядка.
Щоб ці функції зіставлялися із запитами, треба визначити для них маршрути у проекті у файлі urls.py . Зокрема, змінимо цей файл так:
з django.urls import path з hello import views urlpatterns = [ path('', views.index), path('about', views.about), path('contact', views.contact), ]
Змінна urlpatterns визначає набір зіставлень функцій обробки з певними рядками запиту. Наприклад, запит до кореня веб-сайту оброблятиметься функцією index, запит на адресу "about" оброблятиметься функцією about, а запит "contact" - функцією contact.
Запустимо проект і звернемося на деякі з цих адрес.
При цьому ми можемо відправляти не простий текст, а, наприклад, код HTML, який потім інтерпретується браузеромю. Так, змінимо файл views.py наступним чином:
from django.http import HttpResponse def index(request): return HttpResponse("Головна
") def about(request): return HttpResponse("Про сайт
") def contact(request): return HttpResponse("Контакти
")
Відповідно тепер браузер отримає код html:
🔧 Як Django обробляє запити: докладний огляд та найкращі практики
Веб-фреймворк Django є одним із найпопулярніших інструментів для розробки веб-додатків мовою Python. Він надає зручні засоби для обробки запитів від клієнтів та виконання відповідних дій. Давайте розглянемо детально, як Django обробляє запити.
Коли клієнт надсилає HTTP-запит на веб-сервер, Django використовує маршрутизацію URL для визначення, якому представленню (view) слід надіслати запит на обробку. Маршрутизація URL здійснюється на основі вмісту файлу urls.py в кожній програмі Django.
URL-шляхи Django складаються з двох основних компонентів: шляху (paths) і уявлення (views). Шляхи визначають структуру URL-адрес, а уявлення визначають функції або класи, які обробляють запити. Наприклад, у файлі urls.py може бути записаний наступний шлях:
path('articles/', views.article_list, name='article_list')
У цьому прикладі, якщо клієнт звертається до URL-адреси "articles/", запит передається функції article_list в модулі views.Цей модуль містить визначення уявлень, що виконують потрібні дії.
Після визначення подання Django викликає відповідну функцію з об'єктом запиту (request) як аргумент. Запит містить інформацію про вхідний запит, наприклад, URL-адресу, параметри запиту та тіло запиту.
У поданні може бути бізнес-логіка, яка обробляє запит і формує відповідь. Наприклад, подання може отримувати дані з бази даних, проводити розрахунки, взаємодіяти з іншими сервісами та генерувати HTML-сторінки як відповідь.
Після виконання подання Django генерує HTTP-відповідь, який включає статусний код, заголовки і тіло відповіді. Відповідь може бути сторінкою HTML або іншим типом даних, таким як JSON або зображення.
Отримана HTTP-відповідь відправляється назад клієнту, який ініціював запит, і він може обробити його відповідним чином. Наприклад, браузер може відобразити отриману HTML-сторінку або іншу програму може використовувати дані з відповіді.
Весь цей процес обробки запитів у Django відбувається автоматично завдяки вбудованим механізмам фреймворку. Розробнику необхідно правильно налаштувати URL-адреси та визначити уявлення для обробки запитів відповідним чином.
Насамкінець, Django є потужним веб-фреймворком, який полегшує обробку запитів від клієнтів і дозволяє розробникам створювати складні веб-програми. Розуміння того, як Django обробляє запити, допоможе вам розробляти ефективні та безпечні веб-програми з використанням цього фреймворку.
Django ORM для початківців | Оптимізуємо запити
Я буду використовувати django-extentions, щоб отримати корисну інформацію за допомогою:
python manage.py shell_plus --print-sql
>>> post = Post.objects.all() >>> post SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content", "blog_post"."blog_id" FROM " blog_post" LIMIT 21 Execution time: 0.000172s [Database: default] ]>
1.Використовуємо ForeignKey значення безпосередньо
>>> Post.objects.first().blog.id SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content", "blog_post"."blog_id" FROM "blog_post" ORDER BY "blog_post"."id" ASC LIMIT 1 Час виконання: 0.000225s [Database: default] SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."url" FROM "blog_blog" WHERE "blog_blog". [Database: default] 1
А так отримуємо 1 запит у БД:
>>> Post.objects.first().blog_id SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content", "blog_post"."blog_id" FROM "blog_post" ORDER BY "blog_post"."id" ASC LIMIT 1 Час виконання: 0.000155s [Database: default] 1
2. OneToMany Relations
Якщо ми використовуємо OneToMany відносини ми використовуємо ForeignKey поля і запит виглядає приблизно так:
>>> post = Post.objects.get(id=1) SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content", "blog_post"."blog_id" FROM "blog_post " WHERE "blog_post"."id" = 1 LIMIT 21 Execution time: 0.000161s [Database: default]
І якщо ми хочемо отримати доступ до об'єкта блогу з об'єкта посту, ми можемо зробити:
>>> post.blog SELECT "blog_blog"."id", "blog_blog"."name", "blog_blog"."url" FROM "blog_blog" WHERE "blog_blog". s [Database: default]
Тим не менш, це викликало новий запит, щоб отримати інформацію із блогу. Тож використовуйте select_related, щоб уникнути цього. Щоб використати його, ми можемо оновити наш оригінальний запит:
>>> post = Post.objects.select_related("blog").get(id=1) SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content", "blog_post" ."blog_id", "blog_blog"."id", "blog_blog"."name", "blog_blog"."url" FROM "blog_post" INNER JOIN "blog_blog" ON ("blog_post"."blog_id" = "blog_blog"."id") WHERE "blog_post"."id" = 1 LIMIT 21 Execution time: 0.000159s [Database: default]
Зверніть увагу, що Django використовує JOIN зараз! І час виконання запиту менший, ніж раніше.Крім того, тепер post.blog буде кешовано!
select_related так само працює з QurySets:
>>> posts = Post.objects.select_related("blog").all() >>> for post in posts: . post.blog. SELECT "blog_post"."id", "blog_post"."title", "blog_post"."content", "blog_post"."blog_id", "blog_blog"."id", "blog_blog"."name", " blog_blog"."url" FROM "blog_post" INNER JOIN "blog_blog" ON ("blog_post"."blog_id" = "blog_blog"."id") Execution time: 0.000241s [Database: default]
3. ManyToMany Relations
Щоб отримати авторів постів ми використовуємо щось на зразок цього:
>>> for post in Post.objects.all(): . post.authors.all() . SELECT "blog_post". "id", "blog_post". "title", "blog_post". ."id", "blog_author"."name" FROM "blog_author" INNER JOIN "blog_post_authors" ON ("blog_author"."id" = "blog_post_authors"."author_id") WHERE "blog_post_authors"."post_id" = 1 21 Execution time: 0.000125s [Database: default] , , ]> SELECT "blog_author"."id", "blog_author"."name" FROM "blog_author" INNER JOIN "blog_post_authors" ON ("blog_author". "blog_post_authors"."author_id") WHERE "blog_post_authors"."post_id" = 2 LIMIT 21 Час виконання: 0.000109s [Database: default] , ]>
Схоже, ми отримали запит для кожного постового об'єкта. Тому ми повинні використовувати prefetch_related. Це схоже на select_related але використовується з ManyToMany Fields:
>>> for post in Post.objects.prefetch_related("authors").all(): . post.authors.all() . SELECT "blog_post"."id", "blog_post". "title", "blog_post"."content", "blog_post". "."post_id") AS "_prefetch_related_val_post_id", "blog_author"."id", "blog_author"."name" FROM "blog_author" INNER JOIN "blog_post_authors" ON ("blog_author"."id" = "blog_post_authors"."author_id" ."post_id" IN (1, 2) Execution time: 0.000379s [Database: default] , , ]> , ]>
Що тільки-но сталося. Ми скоротили кількість запитів з 2 до 1, щоб отримати 2 QuerySet!
4. Prefetch object
prefetch_related достатньо для більшості випадків, але це не завжди допомагає уникнути додаткових запитів. Наприклад, якщо ми використовуємо фільтрацію Django не може використовувати наші кешовані postsтому що вони не були відфільтровані, коли вони були запитані в першому запиті. І ми отримаємо:
>>> authors = Author.objects.prefetch_related("posts").all() >>> for author in authors: . print(author.posts.filter(published=True)) . SELECT "blog_author"."id", "blog_author"."name" FROM "blog_author" Час виконання: 0.000580s [Database: default] SELECT ("blog_post_authors"."author_id") AS "_prefetch_related_val_ id", "blog_post"."title", "blog_post"."content", "blog_post"."published", "blog_post". id" = "blog_post_authors"."post_id") WHERE "blog_post_authors"."author_id" IN (1, 2, 3) Визначення часу: 0.000759s [Database: default] SELECT "blog_post"."id", "blog_post". 'title', 'blog_post'. "post_id") WHERE ("blog_post_authors"."author_id" = 1 AND "blog_post"."published" = 1) LIMIT 21 Execution time: 0.000299s [Database: default] , ]> SELECT "blog_post"." "blog_post"."title", "blog_post"."content", "blog_post"."published", "blog_post"."blog_id" FROM "blog_post" INNER JOIN "blog_post_authors" ON ("blog_post"."id" = "blog_post_authors"."post_id") WHERE ("blog_post_authors"."author_id" = 2 AND "blog_post"."published" = 1) LIMIT 21 Час виконання: 0.000336s [Database: default] , ]> "id", "blog_post"."title", "blog_post"."content", "blog_post"."published", "blog_post". "id" = "blog_post_authors"."post_id") WHERE ("blog_post_authors"."author_id" = 3 AND "blog_post"."published" = 1) LIMIT 21 Execution time: 0.000412s [Database: default]]
Тобто ми використовували prefetch_related, щоб зменшити кількість запитів, але ми фактично збільшили його. Щоб уникнути цього, ми можемо налаштувати запит за допомогою об'єкта Prefetch:
>>> authors = Author.objects.prefetch_related( . Prefetch( . "posts", . queryset=Post.objects.filter(published=True), . to_attr="published_posts", . ) . ) ) >>> for author in authors: . print(author.published_posts) . SELECT "blog_author"."id", "blog_author"."name" FROM "blog_author" Довідка: 0.000183s [Database: default] SELECT ("blog_post_authors"."author_id") AS "_prefetch_related_val_ id", "blog_post"."title", "blog_post"."content", "blog_post"."published", "blog_post"."blog_id" FROM "blog_post" INNER JOIN "blog_post_authors" ON ("blog_post"."id" = "blog_post_authors"."post_id") WHERE ("blog_post"."published" = 1 AND "blog_post_authors"."author_id" IN (1, 2, 3)) Execution time: 0.000404s [Database: default] [, ] [, ] []
Ми використовували певний запит для отримання постів через параметр запиту та зберегли відфільтровані повідомлення у новому атрибуті. Як ми бачимо, тепер у нас є лише 2 запити до бази даних.