From c841d88eb423566645df3aff90ce693890850262 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Tue, 18 Apr 2023 17:58:13 +0300 Subject: [PATCH 01/23] edit grammar and style --- lesson28.md | 371 ++++++++++++++++++++++++++-------------------------- 1 file changed, 184 insertions(+), 187 deletions(-) diff --git a/lesson28.md b/lesson28.md index fc2ee7941..6c4c4f172 100644 --- a/lesson28.md +++ b/lesson28.md @@ -2,47 +2,47 @@ ![](https://the-flow.ru/uploads/images/resize/830x0/adaptiveResize/06/04/56/59/17/253344b40cd7.jpg) -## Как работает интернет и что именно мы будем изучать? +## Как работает интернет, и что именно мы будем изучать? -Мы, в рамках этого курса изучим то как можно создавать сайты и приложения. Я надеюсь ни для кого не окажется секретом, +В рамках этого курса мы изучим то, как можно создавать сайты и приложения. Я надеюсь, ни для кого не окажется секретом, что любой сайт или приложение работают в сети интернет. Но как именно? -На самом деле, большая часть работы это результат выполнения различных запросов, и в первую очередь запросов по -протоколу `http` (Ну скорее `https`, о чём поговорим сильно позже). +На самом деле большая часть работы это результат выполнения различных запросов, и в первую очередь - запросов по +протоколу `http` (скорее, `https`, о чём мы поговорим значительно позже). -Когда вы набираете в браузере url, или просто переходите по закладкам или ссылкам, на самом деле вы заставляете браузер -создавать и отправлять `GET http` запрос. А когда вводите юзернейм и пароль, чаще всего после кнопки, например "Войти", -вы формируете и отправляете `POST http` запрос +Когда вы набираете в браузере url или просто переходите по закладкам или ссылкам, на самом деле, вы заставляете браузер +создавать и отправлять `GET http` запрос. А когда вводите юзернейм и пароль, чаще всего после кнопки, например, "Войти", +вы формируете и отправляете `POST http` запрос. -## Request-response, Он же клиент-сервер +## Request-response, он же клиент-сервер -При работе с веб-ресурсами, в самом распространённом случае мы будем иметь дело с так называемым -взаимодействием `запрос-ответ`, что на английском `request-response`. +Чаще всего при работе с веб-ресурсами мы будем иметь дело с так называемым +взаимодействием `запрос-ответ`, что на английском звучит как `request-response`. -А это значит, что у нас есть кто-то кто "спрашивает", и тот кто "отвечает". +А это значит, что у нас есть кто-то, кто "спрашивает", и тот, кто "отвечает". ### Кто может спрашивать? -На самом деле спрашивать может любое устройство или приложение которое способно -сформировать `http` запрос, а в современном мире это уже почти любое устройство включая, чайники, часы и стиральные +На самом деле спрашивать может любое устройство или приложение, которое способно +сформировать `http` запрос, а в современном мире это уже почти любое устройство, включая чайники, часы и стиральные машины. -Но в самом распространённом варианте, это всё-таки будут: +Но в самом распространённом варианте это всё-таки будут: -- Веб-браузеры (Chrome, mozilla, opera, их же мобильные аналоги, итд.) -- Приложения (Instagram, telegram итд. приложения бывают не только мобильные, но и для компьютера или планшета, и мы - даже будем таким в рамках курса пользоваться) +- Веб-браузеры (Chrome, Mozilla, Opera, их мобильные аналоги и т. п.) +- Приложения (Instagram, Telegram и т. п. Приложения бывают не только мобильные, но и для компьютера или планшета, и мы + даже будем ими пользоваться в рамках курса) ![](https://www.iguides.ru/upload/medialibrary/a60/a60857ccc094b4bfd975d0fa842bcb1c.png) ### Кто может отвечать? -Отвечать может только сервер. В отличие от задающего вопрос, отвечать может только специально подготовленный. Сервером -может являться по сути любое электронное устройство, на котором возможно запустить необходимый набор программ или -команд. В рамках обучения, вы будете запускать сервер, прямо на ваших компьютерах, при разработке так обычно и делается, -но реальные сайты и приложения обычно располагаются на специально подготовленных, изолированных, или облачных -компьютерах, и в случае в веб-приложения на python, это практически всегда будет компьютер с операционной системой -Linux, зачем и как это настроить мы поговорим ближе к концу нашего курса. +Отвечать может только сервер. В отличие от задающего вопрос, отвечать может только специально подготовленный сервер. Сервером +может являться по сути любое электронное устройство, на котором можно запустить необходимый набор программ или +команд. В рамках обучения вы будете запускать сервер прямо на ваших компьютерах, при разработке так обычно и делается, +но реальные сайты и приложения обычно располагаются на специально подготовленных, изолированных или облачных +компьютерах, и в случае веб-приложения на python это практически всегда будет компьютер с операционной системой +Linux, зачем и как это настроить, мы поговорим ближе к концу нашего курса. #### А бывает ли не клиент-сервер? @@ -50,56 +50,55 @@ Linux, зачем и как это настроить мы поговорим б ### Краткое описание http запросов -Из чего состоит http запрос можно почитать [тут](https://developer.mozilla.org/ru/docs/Web/HTTP/Messages), но если +Из чего состоит http запрос можно почитать [тут](https://developer.mozilla.org/ru/docs/Web/HTTP/Messages), но, если вкратце, то из тела, где хранятся данные, и хедеров, где хранится служебная информация. #### Метод GET Используется для запроса содержимого указанного ресурса. Например, получить данные, файл или любую другую информацию, -браузер (Chrome, Mozzila, etc.) при наборе url (https://ru.wikipedia.org/, +браузер (Chrome, Mozilla, etc.) при вводе url (https://ru.wikipedia.org/, https://www.youtube.com/watch?v=WdZJ-QUItHw&t=7974s) использует именно GET запрос, может передавать переменные в query -параметре (в примере с ютубом ?v=WdZJ-QUItHw&t=7974s это query параметры, начинается с символа ?, следующий параметр -добавляется при помощи символа & в данном примере, параметр {'v': 'WdZJ-QUItHw', 't': '7974s'}) +параметре (в примере с ютубом ?v=WdZJ-QUItHw&t=7974s - это query параметры, первый параметр начинается с символа ?, следующий параметр +добавляется при помощи символа &, в данном примере параметр - это словарь {'v': 'WdZJ-QUItHw', 't': '7974s'}). Обычно не используется для отправки данных (query параметры чаще используются для уточнения того, что вы хотите -получить, например значение фильтров, или каких-то системных параметров) +получить, например, значения фильтров или каких-то системных параметров). Например, получение комментариев к посту в блоге, или списка рекомендаций на ютубе, открытие любого сайта это уже `GET` запрос. #### Метод POST -Применяется для передачи пользовательских данных заданному ресурсу. Если мы хотим, ввести юзернейм и пароль, обычно мы -используем `POST`, потому что это передача пользовательских данных. Хотим оставить комментарий, `POST`, заказать товар -из интернет-магазина, `POST`, итд. Практически всегда когда мы отправляем пользовательские данные, это будет `POST`. +Применяется для передачи пользовательских данных заданному ресурсу. Если мы хотим ввести юзернейм и пароль, обычно мы +используем `POST`, потому что это передача пользовательских данных. Хотим оставить комментарий - `POST`, заказать товар +из интернет-магазина - `POST`, и т. д. Практически всегда, когда мы отправляем пользовательские данные, это будет `POST`. #### Методы PUT и PATCH -Применяется для обновления данных. Например, изменить текст сообщения, или пароль пользователя. Подробно будем -разбираться как и с методом `DELETE` в следующем блоке этого курса. +Применяются для обновления данных. Например, изменить текст сообщения или пароль пользователя. Подробно будем +разбираться с ними и с методом `DELETE` в следующем блоке этого курса. #### Метод DELETE Используется для удаления объектов. Подробно рассмотрим в следующем блоке. -#### Методы TRACE, HEAD, OPTIONS итд. +#### Методы TRACE, HEAD, OPTIONS и т. д. -Существуют и другие запросы, которые используют реже и с другими назначениями, подробно изучать не будем, хотя наверняка -расскажу несколько практических примеров использования в дальнейшем +Существуют и другие запросы, которые используются реже и с другими назначениями, подробно их изучать не будем, хотя я +расскажу несколько практических примеров использования их в дальнейшем. ## Шаблон проектирования MVC -Для разработки любых сложных решений используются шаблоны проектирования (`patterns`). +Для разработки сложных решений используются шаблоны проектирования (`patterns`). Если вы собираетесь строить дом, я сомневаюсь, что нужно придумывать собственный способ его постройки. Залить -фундамент, на него поставить каркас и в каркасе провести коммуникации и сделать отделку. Придумывание своих путей -постройки дома, скорее всего приведёт к плохому результату, либо не возможности достичь результата вообще. +фундамент, на него поставить каркас, в каркасе провести коммуникации и сделать отделку. Придумывание своих путей +постройки дома, скорее всего, приведёт либо к неудовлетворительному результату, либо к невозможности достичь результата вообще. Так вот, в программировании тоже все стандартные решения придумали за нас. Называются они паттерны, и вы уже слышали -этот -термин, когда обсуждали такие вещи как ООА\ООД\ООП. +этот термин, когда обсуждали такие вещи как ООА\ООД\ООП. -Для нас, в данный момент, с точки зрения создания веб ресурсов самым важных из них является `MVC` **(Model - View - +В данный момент для нас с точки зрения создания веб-ресурсов самым важным из них является `MVC` **(Model - View - Controller) (Модель - Отображение - Контроллер)** ### Что же это такое? @@ -108,48 +107,47 @@ Controller) (Модель - Отображение - Контроллер)** Давайте посмотрим на работу обычного ресторана. -Клиент приходит, садится за столик и видит только меню и официанта. Выбрав, что-то из меню, через официанта он -отправляет запрос на кухню, на приготовление его блюда. Официант в свою очередь идёт к поварам, что бы сообщить о -заказе, а повар получив заказ, должен сначала понять есть ли у него необходимые продукты, и работает ли плита или -духовка. В случае если всё хорошо, то готовит блюдо, и после отдаёт его официанту, который и выносит блюдо. +Клиент приходит, садится за столик и видит только меню и официанта. Выбрав что-то из меню, через официанта он +отправляет запрос на кухню на приготовление его блюда. Официант в свою очередь идёт к поварам, чтобы сообщить о +заказе, а повар, получив заказ, должен сначала понять, есть ли у него необходимые продукты и работает ли плита или +духовка. В том случае, если всё хорошо, то повар готовит блюдо и после отдаёт его официанту, который и выносит блюдо клиенту. -Получается, что клиент, видел только меню и конечное блюдо, но не видел повара, или тем более склад с продуктами, но -ожидаемые результат всё-таки получил. +Получается, что клиент видел только меню и конечное блюдо, но не видел повара или, тем более, склад с продуктами, но +ожидаемый результат всё-таки получил. Большая часть сайтов или приложений так же делится на три компонента, `Model` - `View` - `Controller` -Если рассматривать пример с рестораном, то зал в котором находится клиент, его столик и его тарелка будут +Если рассматривать пример с рестораном, то зал, в котором находится клиент, его столик и его тарелка будут являться `Отображением`, оно же `View`, В случае с сайтом или приложением, этим элементом является всё что может увидеть пользователь. -Официант, который принимает заказ и выносит блюдо, является в этой аналогии, `http` запросами и ответами. И общается +Официант, который принимает заказ и выносит блюдо, является в этой аналогии `http` запросами и ответами. И общается только с `Контроллером`, он же `Controller` -Кухня и повара на ней, будут являться `Контролером`, так как они могут принимать запросы от официантов, но при этом -могут сходить и на склад, посмотреть, например, на наличие продуктов, а склад в нашем случае будет являться -моделью, `Model`. При написании веб-приложения, контролером будет являться та часть кода, которая отвечает за обработку +Кухня и повара на ней будут являться `Контроллером`, так как они могут принимать запросы от официантов, но при этом +могут сходить и на склад посмотреть, например, на наличие продуктов, а склад в нашем случае будет являться +моделью, `Model`. При написании веб-приложения, контроллером будет являться та часть кода, которая отвечает за обработку запросов. И так же выполнять действия и с отображением, и с моделью. -Склад в этой конструкции, будет являться моделью, `Model`, официант или клиент, не могут пойти на склад и взять себе +Склад в этой конструкции будет являться моделью, `Model`, официант или клиент, не могут пойти на склад и взять себе продуктов, это может сделать только повар, так же только повар может принять продукты у поставщика и добавить их на -склад. В случае с веб-приложениями, моделью является `база данных`, и код который отвечает за взаимодействия с ней. +склад. В случае с веб-приложениями моделью является `база данных`, и код, который отвечает за взаимодействие с ней. -При этом клиент не может заказать, всё что ему захочется, ему нужно будет выбрать из `меню` +При этом клиент не может заказать всё, что ему захочется, ему нужно будет выбрать из `меню` -Сегодня мы изучим как именно формируется "меню" для django, и что это вообще. +Сегодня мы изучим, как именно формируется "меню" для django, и что это вообще. ![](https://miro.medium.com/max/1304/1*la8KCs0AKSzVGShoLQo2oQ.png) -По этому же принципу работает и Django, но с немного другими названиями, паттерн называется `MVT` (`Model` - `View` +По этому же принципу работает и Django, но с немного другими названиями, паттерн называется `MVT` (`Model` - `View` - `Template`) -- `Template`), - где `View` играет роль `Controller`, а `Template` роль `View`, но это точно такая-же идея, изменились только названия +- где `View` играет роль `Controller`, а `Template` роль `View`, но это точно такая же идея, изменились только названия блоков. -## Что же такое все таки Django? +## Что же такое все-таки Django? -**Django** - веб-фреймворк, а фреймворк это по своей сути конструктор, который помогает нам собирать блоки, часто даже -не задумываясь как именно это работает под капотом. +**Django** - это веб-фреймворк, а фреймворк - это конструктор, который помогает нам собирать блоки, часто даже +не задумываясь, как именно это работает под капотом. ![](https://www.meme-arsenal.com/memes/07d78e429e60f57b09f8afb5e4446bd1.jpg) @@ -159,25 +157,25 @@ Controller) (Модель - Отображение - Контроллер)** ### Виртуальное окружение -Когда мы разрабатываем какой-либо проект, обычно мы не хотим, что-бы пакеты которые были установлены для него, как -либо пересекались с теми, которые установлены, для другого проекта. А у разработчика вполне может быть большое +Когда мы разрабатываем какой-либо проект, обычно мы не хотим, чтобы установленные для него пакеты как-либо пересекались +с пакетами, установленными для другого проекта. А у разработчика вполне может быть большое количество проектов на рабочем компьютере. -Для того, что бы разные проекты не пересекались, можно использовать разные компьютеры, для разных проектов, но не надо +Чтобы разные проекты не пересекались, можно использовать разные компьютеры для разных проектов, но не надо так делать :) Существует возможность создать `виртуальное окружение` для каждого отдельного проекта. По сути, создаётся папка, в -которую копируется сам интерпретатор питона, вспомогательные системные файлы, и создаётся папка, куда будут +которую копируется сам интерпретатор python, вспомогательные системные файлы, и создаётся папка, куда будут устанавливаться все необходимые для конкретного проекта пакеты. #### Консоль Когда мы занимаемся программированием, нам очень часто необходимо использовать консоль, будь то Windows или Linux -В Windows, что бы открыть терминал, необходимо нажать Win+R, и во всплывшем окне набрать `cmd` +В Windows, чтобы открыть терминал, необходимо нажать Win+R, и во всплывшем окне набрать `cmd`. В Linux/Mac многое зависит от дистрибутива, но часто можно найти по поиску, по слову Terminal, или по клавишам Alt+T ( -Далеко не везде будет работать) +далеко не везде будет работать) #### cd @@ -190,54 +188,54 @@ Controller) (Модель - Отображение - Контроллер)** #### Создание виртуального окружения -Для, того, что бы создать виртуальное окружение, вам необходим компьютер с установленным python и открытая консоль +Чтобы создать виртуальное окружение, вам необходим компьютер с установленным python и открытая консоль. В консоли необходимо указать ```python -m venv /path/to/new/virtual/environment``` -где `/path/to/new/virtual/environment` путь к вашему виртуальному окружению (Я лично создал себе папку которую назвал -enviroments, и внутри неё уже создаю новые виртуальные окружения) +где `/path/to/new/virtual/environment` - путь к вашему виртуальному окружению (я создал себе папку, которую назвал +environments, и уже непосредственно в ней создаю новые виртуальные окружения) #### Активация и деактивация виртуального окружения -После создания, что бы использовать виртуальное окружение его необходимо активировать, так же через консоль. +После создания, чтобы использовать виртуальное окружение, его необходимо активировать, так же через консоль. -Для активации, необходимо запустить активационный скрипт. +Для активации необходимо запустить активационный скрипт. Windows Для windows внутри папки с виртуальным окружением нам необходим файл `activate` в папке `Scripts` Допустим вы находились в папке `C:\Users\black>`, и прописали команду `python -m venv test_env`, это создаст -папку `C:\Users\black\test_env`, значит что бы запустить виртуальное окружение, нужно выполнить +папку `C:\Users\black\test_env`, значит, чтобы запустить виртуальное окружение, нужно выполнить команду `test_env\Scripts\activate` Linux/Mac -В этих операционных системах необходимо использовать команду `source` применяя её к файлу `activate` в папке `bin`. +В этих операционных системах необходимо использовать команду `source`, применяя её к файлу `activate` в папке `bin`. -Допустим вы находились в папке `/home/black/`, и прописали команду `python -m venv test_env`, это создаст -папку `/home/black/test_env`, значит что бы запустить виртуальное окружение, нужно выполнить +Допустим вы находились в папке `/home/black/` и прописали команду `python -m venv test_env`, это создаст +папку `/home/black/test_env`, значит, чтобы запустить виртуальное окружение, нужно выполнить команду `source test_env/bin/activate` -После чего в консоли перед курсором появится указание виртуального окружения `(test_env)`, в наших примерах. +После чего в консоли перед курсором появится название виртуального окружения `(test_env)`(в наших примерах). -Что бы деактивировать, для любых систем необходимо использовать команду `deactivate` +Чтобы деактивировать виртуальное окружение, для любых систем необходимо использовать команду `deactivate` ### Создадим новый проект -Для создания проекта, создаём виртуальное окружение, и активируем его (описание выше). +Для создания проекта создаём виртуальное окружение и активируем его (описание выше). После чего необходимо выполнить два действия: -- Установить джанго -- Перейти в папку где мы собираемся создать проект, в котором будем писать код. +- Установить django +- Перейти в папку, где мы собираемся создать проект, в котором будем писать код. -~Порядок этих двух действий не имеет значения, ведь установка пакета через `pip` установит пакет в папку виртуального -окружения, где находитесь вы, совершенно не имеет значения. +Порядок этих двух действий не имеет значения, ведь установка пакета django через `pip` установит пакет в папку виртуального +окружения, независимо от того, где вы находитесь. -Что бы установить последнюю версию django необходимо выполнить команду: +Чтобы установить последнюю версию django, необходимо выполнить команду: ```pip install django``` @@ -267,27 +265,27 @@ mysite / asgi.py ``` -**manage.py** - файл точка входа, при помощи которого мы будем взаимодействовать со многими частями Django из консоли +**manage.py** - файл точки входа, при помощи которого мы будем взаимодействовать со многими частями Django из консоли Внутри папки `mysite`: -**\_\_init__.py** - Пустой файл, который говорит Python-у, что этот каталог должен рассматриваться как пакет Python-а +**\_\_init__.py** - пустой файл, который говорит Python-у, что этот каталог должен рассматриваться как пакет Python-а -**settings.py** - Настройки и конфигурации проекта. +**settings.py** - настройки и конфигурации проекта. **urls.py** - URL-ы для этого проекта; «меню» вашего сайта на платформе Django. -**wsgi.py** - Файл отвечающий за входную точку сервера (Позволяет запускать код как сайт), разберём почти в самом конце +**wsgi.py** - файл, отвечающий за входную точку сервера (позволяет запускать код как сайт), разберём почти в самом конце курса -**asgi.py** - Файл отвечающий за входную точку сервера асинхронно (Позволяет запускать код как сайт), разберём почти в +**asgi.py** - файл, отвечающий за входную точку сервера асинхронно (позволяет запускать код как сайт), разберём почти в самом конце курса Проверим работоспособность. В консоли запустим локальный "сервер" из папки с нашим "сайтом" (в моём примере `mysite`) -**Необходимо находится в консоли в той же папке где находится `manage.py` с включённым виртуальным окружением. +**Необходимо находиться в консоли в той же папке, где находится `manage.py` с включённым виртуальным окружением. ```python manage.py runserver``` @@ -297,17 +295,17 @@ mysite / Не закрывая консоль (сервер должен работать), открываем в браузере http://127.0.0.1:8000/ -В этом случае скрипт запускает сервер, он должен работать, что бы запросы вообще могли выполняться. +В этом случае скрипт запускает сервер, он должен работать, чтобы запросы вообще могли выполняться. -А браузер будет клиентом (Помним о взаимодействии `клиент-сервер`? ) +А браузер будет клиентом (помним о взаимодействии `клиент-сервер`? ) -При таком подходе наш компьютер сразу является и клиентом (браузер) и сервером (скрипт запущенный в консоли) +При таком подходе наш компьютер сразу является и клиентом (браузер), и сервером (скрипт запущенный в консоли). -`127.0.0.1` это локальный хост (обращаться к вашему же собственному компьютеру) а `:8000` это номер порта на котором -запущен процесс (Если не указывать порт, то по умолчанию будет использоваться 80 порт, для `http` и 443 для `https`, -когда вы открываете любой сайт, на самом деле вы скрытым образом указываете порт. Для разработки, часто используют те +`127.0.0.1` - это локальный хост (обращаться к вашему же собственному компьютеру), а `:8000` - это номер порта, на котором +запущен процесс (если не указывать порт, то по умолчанию будет использоваться 80 порт для `http` и 443 для `https`). +Когда вы открываете любой сайт, на самом деле, вы скрытым образом указываете порт. Для разработки часто используют те порты, на которых точно не будет ничего полезного (других программ работающих на вашем компьютере), для django по -умолчанию используется 8000, если нужно его можно изменить, но обычно в этом нет необходимости. `127.0.0.1` можно +умолчанию используется порт 8000, если нужно, его можно изменить, но обычно в этом нет необходимости. `127.0.0.1` можно заменить на `0.0.0.0` или слово `localhost`, все три варианта практически взаимозаменяемы. Если вы всё сделали правильно, то в браузере должны увидеть, что-то такое: @@ -316,15 +314,15 @@ mysite / ## Создаём приложение -Настройки и конфигурации это замечательно, но разработка подразумевает использование отдельных +Настройки и конфигурации - это замечательно, но разработка подразумевает использование отдельных модулей как части сайта (бывает один на весь сайт, а бывают и тысячи), такие модули называются приложениями, и именно в них пишется "суть" сайта. -Через консоль, и уже известную нам `manage.py` создадим приложение +Через консоль и уже известную нам `manage.py` создадим приложение ```python manage.py startapp myapp``` -Команда создаст вам папку с вашим приложением, давайте разберем её подробнее +Команда создаст вам папку с вашим приложением, давайте разберем её подробнее. ``` myapp/ @@ -338,36 +336,36 @@ myapp/ views.py ``` -**\_\_init__.py** - Пустой файл, который говорит Python-у, что этот каталог должен рассматриваться как пакет Python-а +**\_\_init__.py** - пустой файл, который говорит Python-у, что этот каталог должен рассматриваться как пакет Python-а -**admin.py** - заготовка под страницу для администратора, удобный встроенные инструмент, разберём на следующих занятиях +**admin.py** - заготовка под страницу для администратора, удобный встроенный инструмент, разберём на следующих занятиях **apps.py** - информация о приложении, просто смиритесь, что этот файл есть. -**папка migrations** - Тут будут миграции базы данных. +**папка migrations** - тут будут миграции базы данных. -**models.py** - Модели. +**models.py** - модели. -**tests.py** - Тесты. +**tests.py** - тесты. -**views.py** - Контроллер приложения. +**views.py** - контроллер приложения. ## Наш первый URL -Теперь у нас всё готово для того, что бы начать создавать наш сайт. Сегодня мы будем разбираться, как работают urls. +Теперь у нас всё готово для того, чтобы начать создавать наш сайт. Сегодня мы будем разбираться, как работают urls. -При любом запросе к запущенному Django приложению запущенный сервер при обработке в первую очередь заходит в -файл `settings.py` и ищет там переменную `URL_CONF`, по умолчанию там будет указан файл `urls.py` который находится в -той же папке где и `settings.py`. +При любом запросе к запущенному Django приложению при обработке запроса запущенный сервер в первую очередь заходит в +файл `settings.py` и ищет там переменную `URL_CONF`, по умолчанию там будет указан файл `urls.py`, который находится в +той же папке, где и `settings.py`. -Внутри этого файла Django ожидает наличие переменной `urlpatterns`, которая содержит коллекцию (например список) +Внутри этого файла Django ожидает наличие переменной `urlpatterns`, которая содержит коллекцию (например, список), состоящую из специальных объектов `path` или `re_path`. -На самом деле эти объекты появились в Django только после версии, 2.0, сейчас актуальная версия это 4.0, но потенциально -вы можете встретить и версии ниже, например 1.11, поэтому ниже разберем и более старые варианты урлов. До 2.0 там были +На самом деле эти объекты появились в Django только после версии, 2.0, сейчас актуальная версия это 4.2, но потенциально +вы можете встретить и версии ниже, например, 1.11, поэтому ниже мы разберем и более старые варианты урлов. До 2.0 там были коллекции из объектов `url`, и все они работали на регулярных выражениях, о них позже. -Всё основная логика обработки запросов пишется в приложениях, в файлах `views.py`, у нас всего одно приложение поэтому +Вся основная логика обработки запросов пишется в приложениях в файлах `views.py`, у нас всего одно приложение, поэтому всю логику (сегодня она будет простейшая) мы будем писать в файл `myapp/views.py`. Для начала изменим файл `views.py` @@ -377,22 +375,22 @@ myapp/ from django.http import HttpResponse -# Поздравляю, это ваш первый контроллер, который может, принять запрос, и отдать ответ с текстом, больше ничего +# Поздравляю, это ваш первый контроллер, который может принять запрос и отдать ответ с текстом, больше ничего def main(request): return HttpResponse("Hey! It's your main view!!") ``` Мы создали функцию, которая принимает один параметр `request` и возвращает объект `HttpResponse`. -Это нужно потому что вся веб-инфраструктура работает на `request-response` системе, мы тоже не будем её нарушать, и -будем принимать запросы, и возвращать ответы. +Это нужно, потому что вся веб-инфраструктура работает на `request-response` системе, мы тоже не будем её нарушать, и +будем принимать запросы и возвращать ответы. -Объект `HttpResponse` мы импортировали из Django, и на данном этапе нас совершенно не интересует как он работает, как и -то что именно находится в переменной `request`. +Объект `HttpResponse` мы импортировали из Django, и на данном этапе нас совершенно не интересует, как он работает, как и +то, что именно находится в переменной `request`. -По сути эта функция является контролером, но не спешите, контролеры мы будем изучать детально далее. +По сути, эта функция является контроллером, но не спешите, контроллеры мы будем подробно изучать дальше. -Для использования этого контролера в качестве логики, он должен быть описан в файле `urls.py`: +Для использования этого контроллера в качестве логики, он должен быть описан в файле `urls.py`: ```python # mysite/mysite/urls.py @@ -406,37 +404,37 @@ urlpatterns = [ Мы импортировали `path` из django и метод `main` из нашего `views.py` -Объект `path`, принимает два обязательных параметра: +Объект `path` принимает два обязательных параметра: - Строка, которая содержит `путь` - Обработчик, что именно должно происходить при запросе по такому пути. ### Строка пути -Строка, которая указывает как именно можно выполнить запрос к данному `url`. +Строка, которая указывает, как именно можно выполнить запрос к данному `url`. -Например, если указать там пустую строку, и запустим сервер на локалхосте на 8000 порту, то запросы нужно выполнять по +Например, если указать там пустую строку и запустить сервер на локалхосте на 8000 порту, то запросы нужно выполнять по адресу `127.0.0.1:8000/` -Если указать там, например `super_cool_path`, то обращаться нужно было бы к `127.0.0.1:8000/super_cool_path/` +Если указать там, например, `super_cool_path`, то обращаться нужно было бы к `127.0.0.1:8000/super_cool_path/` Не может содержать пробелов, может содержать `/` ### Обработчик -Что угодно что можно вызвать `()`, на данном этапе это функции, дальше мы будем использовать классы. При вызове, -добавляет в качестве аргумента переменную `request`. +Что угодно, что можно вызвать `()`, на данном этапе это функции, дальше мы будем использовать классы. При вызове +добавляем в качестве аргумента переменную `request`. Объект `path` так же может принимать атрибут `name=`, который нужен дня автоматической генерации адресов, будем изучать на следующем -занятии, и некоторые другие не обязательные атрибуты. +занятии, и некоторые другие необязательные атрибуты. Если мы перезапустим (ну или запустим) сервер и откроем страницу, то мы увидим, что-то такое: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson28/main_home.png) -Список урлов проекта называется роутинг. Роутинг можно строить так как вам удобно, или как того требует задание. +Список урлов проекта называется роутинг. Роутинг можно строить так, как вам удобно, или как того требует задание. -Напишем еще один контролер, для "другой" страницы. В файле `myapp/views.py` +Напишем еще один контроллер, для "другой" страницы. В файле `myapp/views.py` ```python from django.http import HttpResponse @@ -451,7 +449,7 @@ def another(request): ``` -А в файле `mysite/urls.py` импортируем эту функции и добавим маршрут в список, что бы получилось так: +А в файле `mysite/urls.py` импортируем эти функции и добавим маршрут в список, чтобы получилось так: ```python from django.urls import path @@ -463,7 +461,7 @@ urlpatterns = [ ] ``` -То что написано в строке до контролера это путь к урлу, а значит, что бы увидеть обработку этого контролера нужно +То, что написано в строке до контроллера - это путь к урлу, а значит, чтобы увидеть обработку этого контроллера, нужно открыть страницу http:/127.0.0.1:8000/some_url/ Открываем [эту](http:/127.0.0.1:8000/some_url/) страницу и видим результат: @@ -472,7 +470,7 @@ urlpatterns = [ ## Урлы с параметрами -Урл может заведомо принимать параметры, самый простой способ, это описание параметра через синтаксис `` +Урл может заведомо принимать параметры, самый простой способ - это описание параметра через синтаксис `` Обновим наш `urls.py`: @@ -483,34 +481,33 @@ from myapp.views import main, another, main_article, uniq_article, article urlpatterns = [ path('', main), path('some_url/', another), - path('article/', main_article, name='mail_article'), - path('article/33/', uniq_article, name='uniq_article'), + path('article/', main_article, name='main_article'), + path('article/33/', uniq_article, name='unique_article'), path('article//', article, name='article'), ] ``` -Обратите внимание, часть урлов теперь имеет параметр `name`, он понадобится на следующем занятии +Обратите внимание, часть урлов теперь имеет параметр `name`, он понадобится на следующем занятии. -А в последнем url мы указали параметр , значение до двоеточия, это тип данных который принимает -параметр, а после это имя параметра который можно будет использовать в контролере, что бы получить значение этого +А в последнем url мы указали параметр , значение до двоеточия - это тип данных, принимаемых параметром, а после двоеточия - это имя параметра, который можно будет использовать в контроллере, чтобы получить значение этого параметра. Всего существует 5 таких типов: -- str - Ищет не пустую строку, без символа `/` +- str - ищет непустую строку, без символа `/` -- int - Ноль или любое положительное число. +- int - ноль или любое положительное число. -- slug - по сути это тоже строка, которая состоит из букв, цифр, нижних подчеркиваний, дефисов, символов плюс, - например `building-your-1st-django-site` отличается от обычной строки тем, что обычно этот урл берет данные из уже - существующих данных, название статьи, уникальный номер товара итд. +- slug - по сути, это тоже строка, которая состоит из букв, цифр, нижних подчеркиваний, дефисов, символов плюс, + например, `building-your-1st-django-site` отличается от обычной строки тем, что обычно этот урл берет данные из уже + существующих данных. Слагом может быть название статьи, уникальный номер товара и т. д. -- uuid - Ищет соответствие UUID, это специальный формат состоящий из шестнадцатиричных цифр и букв и дефисов, дефисы - должны быть обязательно, буквы маленькие. Например, 075194d3-6885-417e-a8a8-6c931e272f00. +- uuid - ищет соответствие UUID, это специальный формат, состоящий из шестнадцатеричных цифр, букв и дефисов, дефисы + должны быть обязательно, буквы в нижнем регистре. Например, 075194d3-6885-417e-a8a8-6c931e272f00. -- path - Строка с символом/лами `/` +- path - строка с одним или несколькими символами `/` -```Обратите внимание что url `article/33/` попадает так же и под `article//` потому что 33 это тоже цифра, в этом случае будет использован тот который находится в списке первым. Урлы считываются сверху вниз ``` +```Обратите внимание, что url `article/33/` попадает так же и под `article//`, потому что 33 - это тоже цифра, в этом случае будет использован тот, который находится в списке первым. Урлы считываются сверху вниз ``` Изменим `myapp/views.py`: @@ -531,16 +528,16 @@ def main_article(request): def uniq_article(request): - return HttpResponse('This is uniq answer for uniq value') + return HttpResponse('This is a unique answer for a unique value') def article(request, article_id): - return HttpResponse(f"This is an article #{article_id}.") + return HttpResponse(f"This is article #{article_id}.") ``` -Обратите внимание в функцию `article` я добавил параметр `article_id` для получения значения в контролере. +Обратите внимание, в функцию `article` я добавил параметр `article_id` для получения значения в контроллере. -Давайте посмотрим на результат при заходе на разные страницы. +Давайте посмотрим на результат при переходе на разные страницы. ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson33/static_url.png) @@ -550,8 +547,8 @@ def article(request, article_id): ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson28/page_article_with_str.png) -Последнее это пример работы если урл не был описан (Всем известная страница 404, в дебаг режиме, она может рассказать -много подробностей об ошибке) В данном примере, ошибка говорит, о том, что контролера для указанного запроса не +Последнее - это пример работы, если урл не был описан (всем известная страница 404, в дебаг режиме, она может рассказать +много подробностей об ошибке). В данном примере ошибка говорит о том, что контроллера для указанного запроса не существует. Так как параметры в функции могут быть не обязательными, мы можем воспользоваться и этим свойством тоже. @@ -575,7 +572,7 @@ def main_article(request): def uniq_article(request): - return HttpResponse('This is uniq answer for uniq value') + return HttpResponse('This is a unique answer for a unique value') def article(request, article_id, name=''): @@ -593,8 +590,8 @@ from myapp.views import main, another, main_article, uniq_article, article urlpatterns = [ path('', main), path('some_url/', another), - path('article/', main_article, name='mail_article'), - path('article/33/', uniq_article, name='uniq_article'), + path('article/', main_article, name='main_article'), + path('article/33/', uniq_article, name='unique_article'), path('article//', article, name='article'), path('article//', article, name='article_name'), ] @@ -608,18 +605,18 @@ urlpatterns = [ ### include -На самом деле path, может принимать не только обработчик, но и специальный параметр `include`, который позволяет +На самом деле path может принимать не только обработчик, но и специальный параметр `include`, который позволяет добавить урлы из другого файла к основному. -Например, вы видите, что у нас есть 4 урла которые начинаются на `article/`, а значит что удобным вариантом было бы -вынести урлы в отдельный файл, обычно такие файлы создаются на уровне приложений, а основной файл с урлами содержит -только инклюды +Например, вы видите, что у нас есть 4 урла, которые начинаются на `article/`, а значит, что удобным вариантом было бы +вынести урлы в отдельный файл. Обычно такие файлы создаются на уровне приложений, а основной файл с урлами содержит +только команды `include`. -Для того, что бы перенести часть урлов, нам необходимо создать файл `urls.py` в приложении `myapp` (на самом деле мы +Чтобы перенести часть урлов, нам необходимо создать файл `urls.py` в приложении `myapp` (на самом деле мы могли и назвать его как угодно, и сложить его куда угодно, но так просто удобнее и читаемее), и обязательно в этом файле -создать переменную `urlpatterns` содержащую коллекцию урлов. +создать переменную `urlpatterns`, содержащую коллекцию урлов. -Посмотрим на измененные\добавленные файлы: +Посмотрим на измененные и добавленные файлы: `myapp/urls.py` @@ -628,8 +625,8 @@ from django.urls import path from .views import main_article, uniq_article, article urlpatterns = [ - path('', main_article, name='mail_article'), - path('33/', uniq_article, name='uniq_article'), + path('', main_article, name='main_article'), + path('33/', uniq_article, name='unique_article'), path('/', article, name='article'), path('/', article, name='article_name'), ] @@ -649,10 +646,10 @@ urlpatterns = [ ``` **После этого изменения ни один урл не изменился** - они просто стали по другому располагаться, в случае с 4 урлами не -до конца понятно зачем это может быть нужно, но когда у нас существует 20 приложений и в них 5000+ урлов, структура -становится очень важна, что бы ничего не потерять, поэтому правилом хорошего тона для django кода считается создание -файла с урлами в каждом приложении, а в основном хранить только инклюды на них (Если сделать `url` path('', include(' -app.urls')), то можно перенести и те 2 урла которые мы сейчас оставили в основных урлах.) +до конца понятно, зачем это может быть нужно, но когда у нас существует 20 приложений и в них 5000+ урлов, структура +становится очень важна, чтобы ничего не потерять, поэтому правилом хорошего тона для django кода считается создание +файла с урлами в каждом приложении, а в основном файле хранить только команды `include` на них. (Если сделать `url` path('', include(' +app.urls')), то можно перенести и те 2 урла, которые мы сейчас оставили в основных урлах.) ## re_path @@ -662,25 +659,25 @@ app.urls')), то можно перенести и те 2 урла которы Как говорит нам википедия [Регуля́рные выраже́ния](https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%80%D0%BD%D1%8B%D0%B5_%D0%B2%D1%8B%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F) ( -англ. regular expressions) — используемый в компьютерных программах, работающих с текстом, формальный язык поиска и +англ. regular expressions) — это используемый в компьютерных программах, работающих с текстом, формальный язык поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов (символов-джокеров, англ. wildcard characters). Для поиска используется строка-образец (англ. pattern, по-русски её часто называют «шаблоном», «маской»), состоящая из символов и метасимволов и задающая правило поиска. Для манипуляций с текстом дополнительно задаётся строка замены, которая также может содержать в себе специальные символы. -По факту это механизм который позволяет понять, соответствует ли наша строка заданному шаблону. +По факту это механизм, который позволяет понять, соответствует ли наша строка заданному шаблону. ![](https://python-school.ru/wp-content/uploads/2020/12/2.jpg) -Например. Бывают случаи когда нам не подходит просто строка или просто число, а нужно указывать более сложные параметры. +Например, бывают случаи, когда нам не подходит просто строка или просто число, а нужно указывать более сложные параметры. Предположим, что нам нужно указать в качестве части урла три цифры и хотя бы 2 буквы (`123blabla`, `432fo`,`111aaaaa` -итд. но `12asd` не подходило) +и т. д., но `12asd` не подходило) ![](https://miro.medium.com/max/660/0*NfcqRr1hjlXdl2lZ.jpg) -Для того, что бы собрать такую конструкцию нужно воспользоваться синтаксисом регулярных выражений, мне лично нравится -вот этот [сайт](https://regex101.com/), на неё можно изучить базовые принципы использования регулярных выражений. +Чтобы собрать такую конструкцию, нужно воспользоваться синтаксисом регулярных выражений, мне лично нравится +вот этот [сайт](https://regex101.com/), на нем можно изучить базовые принципы использования регулярных выражений. Получаем необходимое регулярное выражение: @@ -721,7 +718,7 @@ def main_article(request): def uniq_article(request): - return HttpResponse('This is uniq answer for uniq value') + return HttpResponse('This is a unique answer for a unique value') def article(request, article_id, name=''): @@ -731,11 +728,11 @@ def article(request, article_id, name=''): def regex(request): - return HttpResponse("it's regexp") + return HttpResponse("It's regexp") ``` -Обратите внимание мы используем не `path`, a `re_path`. +Обратите внимание, мы используем не `path`, a `re_path`. Такой url уже будет работать. @@ -744,7 +741,7 @@ def regex(request): Но как нам получить значение именно этого регулярного выражения? Как бы не было ужасно, но завернуть его в другое регулярное выражение. -И после этого не забыть получать его в контролере +И после этого не забыть получить его в контроллере. `mysite/urls.py` @@ -780,7 +777,7 @@ def main_article(request): def uniq_article(request): - return HttpResponse('This is uniq answer for uniq value') + return HttpResponse('This is a unique answer for a unique value') def article(request, article_id, name=''): @@ -790,7 +787,7 @@ def article(request, article_id, name=''): def regex(request, text): - return HttpResponse(f"it's regexp with text: {text}") + return HttpResponse(f"It's regexp with text: {text}") ``` @@ -809,7 +806,7 @@ def regex(request, text): https://edu-python-course.github.io/_build/html/uk/appx/blog.html#challenge-site-views -Там прописаны все задачи разбитые на подзадачи, что бы собрать их в один цельный проект +Там прописаны все задачи, разбитые на подзадачи, чтобы собрать их в один цельный проект Задание на это занятие: @@ -825,13 +822,13 @@ https://edu-python-course.github.io/_build/html/uk/appx/blog.html#challenge-site - `//`: Потенциальная страница для просмотра одного блога. Динамический контент, который потенциально будет ходить в базу данных -- `//comment/`: Урл для добавления коментария к посту. +- `//comment/`: Урл для добавления комментария к посту. - `/create/`: Создание нового поста - `//update/`: Обновление существующего поста -- `//delete/`: Удаление поста +- `//delete/`: Удаление поста - `/profile//`: Личная страница пользователя @@ -841,4 +838,4 @@ https://edu-python-course.github.io/_build/html/uk/appx/blog.html#challenge-site - `/login/`: Логин -- `/logout/`: Логаут \ No newline at end of file +- `/logout/`: Логаут From 26b3bb4907a33c7e25b5220d52bc7f1ab2824d55 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Thu, 20 Jul 2023 18:15:52 +0300 Subject: [PATCH 02/23] edit grammar and style --- lesson29.md | 137 +++++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 67 deletions(-) diff --git a/lesson29.md b/lesson29.md index 2e8902db7..0f29f7922 100644 --- a/lesson29.md +++ b/lesson29.md @@ -4,17 +4,17 @@ ![](https://imgflip.com/s/meme/Boardroom-Meeting-Suggestion.jpg) -Что же такое шаблон? В бытовом понимании это заготовка под что-то, что потом будет использоваться, в Django это почти +Что же такое шаблон? В бытовом понимании - это заготовка под что-то, что потом будет использоваться, в Django это почти также. -Шаблонами мы называем заготовленные html страницы в которые мы можем добавить необходимые нам данные и логику. +Шаблонами мы называем заготовленные html страницы, в которые мы можем добавить необходимые нам данные и логику. Но как это работает? Откроем начатый проект с прошлого занятия. -Создадим новую папку на уровне корня проекта и назовём её `templates` (Название может быть любым, но принято называть -именно так.) Что бы получилась вот такая структура +Создадим новую папку на уровне корня проекта и назовём её `templates` (название может быть любым, но принято называть +именно так.) Чтобы получилась вот такая структура: ``` mysite/ @@ -23,35 +23,35 @@ templates/ manage.py ``` -Для того, что бы обрабатывать шаблоны мы должны "рассказать" Django где именно искать эти самые шаблоны, для этого нужно +Чтобы обрабатывать шаблоны, мы должны "рассказать" Django, где именно искать эти самые шаблоны. Для этого нужно открыть `mysite/settings.py` и отредактировать его. -В данный момент нас интересует переменная `TEMPLATES`, выглядит примерно так: +В данный момент нас интересует переменная `TEMPLATES`, выглядит она примерно так: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/templates_var.png) -В ключ `DIRS` добавим нашу папку с шаблонами, что бы получилось так: +В ключ `DIRS` добавим нашу папку с шаблонами, чтобы получилось так: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/templates_filled.png) Ключи: -`BACKEND`: Путь к классу который отвечает за обработку данных и логику. (Замена требуется очень редко) +`BACKEND`: путь к классу, который отвечает за обработку данных и логику. (Замена требуется очень редко.) -`DIRS`: Список папок в которых Django будет искать шаблоны +`DIRS`: список папок, в которых Django будет искать шаблоны. -`APP_DIRS`: Булевое поле которое отвечает за то, нужно ли искать папки с шаблонами внутри папки с приложениями, например +`APP_DIRS`: булевое поле, которое отвечает за то, нужно ли искать папки с шаблонами внутри папки с приложениями, например, в нашей структуре, если значение `False`, то поиск будет только в папке `templates` на уровне файла `manage.py`, а если -значение `True` то в папках `/templates` и `/myapp/templates/` +значение `True`, то в папках `/templates` и `/myapp/templates/`. -`OPTIONS`: Дополнительные настройки, подробнее будем рассматривать позже. +`OPTIONS`: дополнительные настройки, подробнее будем рассматривать позже. -Для применения любых изменений нужно перезапускать сервер (команда manage.py runserver из прошлого занятия) +Для применения любых изменений нужно перезапускать сервер (команда `python manage.py runserver`). -Мы "рассказали" Django где именно искать шаблоны, но пока ни одного не создали, давайте сделаем это! +Мы "рассказали" Django, где именно искать шаблоны, но пока ни одного не создали. Давайте сделаем это! -В папке `templates` нужно создать html файл, назовём его `index.html` (Название не имеет значения, главное, что бы -формат был `html`) +В папке `templates` нужно создать html файл, назовём его `index.html` (название не имеет значения, главное, чтобы +формат был `html`). ``` mysite/ @@ -61,7 +61,7 @@ templates/ manage.py ``` -С таким содержанием: +Содержимое файла `index.html`: ```html @@ -78,9 +78,9 @@ That's template! ``` -Итак, теперь у нас есть один шаблон, но мы его не используем, давайте переделаем нашу `view` для обработки шаблонов +Итак, теперь у нас есть один шаблон, но мы его не используем, давайте переделаем нашу `view` для обработки шаблонов. -В файле `myapp/views.py` нужно импортировать обработчик шаблонов, в начале файла добавляем +В файле `myapp/views.py` нужно импортировать обработчик шаблонов, в начало файла добавляем ```python from django.shortcuts import render @@ -93,14 +93,14 @@ def index(request): return render(request, 'index.html') ``` -Перезапустим сервер, и увидим результат на главной странице `http://127.0.0.1:8000/` +Перезапустим сервер и увидим результат на главной странице `http://127.0.0.1:8000/` ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/render_template.png) Мы отрендерили шаблон! Но в нём нет никаких переданных данных, для передачи данных нужно в метод render добавить третий -аргумент, в виде словаря. +аргумент в виде словаря. -Для демонстрации основных типов данных, я допишу функцию `index` и передам большое кол-во значений в словаре: +Для демонстрации основных типов данных я допишу функцию `index` и передам большое количество значений в словаре: ```python class MyClass: @@ -119,7 +119,7 @@ def index(request): my_tuple = ('tuple_first_item', 'tuple_second_item', 'tuple_third_item') my_class = MyClass('class string') return render(request, 'index.html', { - 'my_num': my_num, + 'my_num': my_num, 'my_str': my_str, 'my_dict': my_dict, 'my_list': my_list, @@ -129,7 +129,7 @@ def index(request): }) ``` -Значения переданы, но пока они никак не используются, давай те же посмотрим, как отобразить переменные в шаблоне! +Значения переданы, но пока они никак не используются, давайте же посмотрим, как отобразить переменные в шаблоне! Для вывода данных в Django темплейте используются фигурные скобки `{{ }}` @@ -146,7 +146,7 @@ def index(request): {{my_list.0}} ``` -Изменим наш **index.html**: +Изменим наш `index.html`: ```html @@ -192,13 +192,15 @@ def index(request): ![](http://risovach.ru/upload/2013/01/mem/kakoy-pacan_7772237_orig_.jpeg) -Все логические операторы и циклы в шаблонах нужно закрывать! +После всех логических операторов и циклов в шаблонах нужно ставить соответствующий закрывающий тег! +Например, `{% for ...%} {% endfor %}`, `{% if ... %} {% endif %}`. + #### Логические операторы -В шаблонах можно оперировать не только переменными, но и не сложной логикой, такой как логические операторы и циклы. +В шаблонах можно оперировать не только переменными, но и несложной логикой, такой как логические операторы и циклы. -Давайте добавим в наши параметры переменную 'display_num' и назначим ей False +Давайте добавим в наши параметры переменную 'display_num' и назначим ей значение `False`. `myapp/views.py` @@ -242,7 +244,7 @@ def index(request): ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/If_statement.png) -А если изменим только во view переменную с False на True: +А если в файле `views.py` изменим переменную `display_num` с False на True: `myapp/views.py` @@ -263,11 +265,11 @@ def index(request): ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/if_true.png) -Т.к. переменная True, то мы видим значение переменной `my_num` +Т.к. значение переменной `display_num` True, то мы видим значение переменной `my_num` #### Циклы -Так же как и в python мы можем использовать циклы в шаблонах, но только цикл for +Так же как и в python мы можем использовать циклы в шаблонах, но только цикл for. Изменим наш `index.html`: @@ -297,9 +299,9 @@ def index(request): Давайте скомбинируем! -Внутри цикла `for` Джанго уже генерирует некоторые переменные, например переменную `{{ forloop.counter0 }}` +Внутри цикла `for` Джанго уже генерирует некоторые переменные, например, переменную `{{ forloop.counter0 }}`, -В которой хранится индекс текущей итерации, давайте не будем выводить в цикле второй элемент +в которой хранится индекс текущей итерации, давайте не будем выводить в цикле второй элемент. ```html @@ -321,9 +323,9 @@ def index(request): ``` -{% if forloop.counter0 != 1 %} +`{% if forloop.counter0 != 1 %}` -Символ != это не равно, а значение 1, потому что индекс начинается с 0 +Символ != это не равно, а значение 1, потому что индекс начинается с 0. Обновляем страницу и видим: @@ -336,13 +338,13 @@ def index(request): ![](http://memesmix.net/media/created/gmkbf5.jpg) -[Ссылка на доку](https://docs.djangoproject.com/en/2.2/ref/templates/builtins/) +[Ссылка на доку](https://docs.djangoproject.com/en/4.2/ref/templates/builtins/) -Их очень много, часть мы рассмотрим, часть вам прийдется изучить самостоятельно. +Их очень много, часть мы рассмотрим, часть вам придется изучить самостоятельно. ## Tag URL -Тег url позволяет нам сгенерировать урл по его имени. Это очень удобно если адрес меняется, а его имя нет. +Тег url позволяет нам сгенерировать урл по его имени. Это очень удобно, если адрес меняется, а его имя нет. Давайте сгенерируем две ссылки на два наших урла. @@ -397,8 +399,8 @@ path('', first, name='first'), Зачем нам это надо? -Наследование шаблонов нужно, что бы не писать одно и то же отображение много раз, как часто вы видели сайты где сверху, -снизу, слева, справа итд. всегда одно и тоже, а меняется только "середина", самый простой способ так сделать это +Наследование шаблонов нужно, чтобы не писать одно и то же отображение много раз. Как часто вы видели сайты, где сверху, +снизу, слева, справа и т. д. всегда одно и тоже, а меняется только "середина"? Самый простой способ так сделать - это наследование. Тут нам и помогут наши волшебные теги! @@ -465,19 +467,18 @@ def first(request): Что произошло? -Мы создали базовый шаблон `base.html` в котором описали то что будет во всех шаблонах (И покрасили в голубенький, для -наглядности) которые от него наследуются, и обозначили `{% block content %}` (слово контент взято из головы, назвать -можно как угодно) вся нужная нам информация будет наследоваться именно в указанный блок, на странице разных блоков может +Мы создали базовый шаблон `base.html`, в котором описали то, что будет во всех шаблонах (и покрасили в голубенький, для +наглядности), которые от него наследуются, и обозначили `{% block content %}` (здесь block - это тэг, а content - присвоенное ему название, его можно выбрать произвольно) вся нужная нам информация будет наследоваться именно в указанный блок, на странице разных блоков может быть сколько угодно главное, что бы они имели разные названия. -Наш индекс пейдж рендерит страницу `index.html` в ней мы отнаследовались от нашей `base.html` вписали такой же -блок `content` что бы в него вписать нужные нам данные, в нашем случае это просто текст и ссылка, текст мы перекрасили, -что бы было видно что это данные из нового файла, а ссылку нет, что бы было видно, что цвет из `base.html` -отнаследовался, то же самое произошло и с `first.html` +Наш индекс пейдж рендерит страницу `index.html`, в ней мы отнаследовались от нашей `base.html`, вписали такой же +блок `content`, чтобы передать в него нужные нам данные, в нашем случае это просто текст и ссылка, текст мы перекрасили, +чтобы было видно, что это данные из нового файла, а ссылку нет, чтобы было видно, что цвет из `base.html` +отнаследовался, то же самое произошло и с `first.html`. #### Include -А теперь представим обратную ситуацию, нам нужно в разные части сайта "засунуть" один и тот же блок (рекламу например) +А теперь представим обратную ситуацию: нам нужно в разные части сайта "засунуть" один и тот же блок (рекламу, например) Тут нас спасает тег `include` который позволяет "внедрить" нужную часть страницы куда угодно @@ -487,10 +488,10 @@ def first(request): ```html -
That's included html!!!
+
That's included html!
``` -И теперь добавим этот файл к страницам `index.html` и `first.html` но в разные места, что бы получилось +И теперь добавим этот файл к страницам `index.html` и `first.html`, но в разные места, чтобы получилось `template/index.html` @@ -518,15 +519,15 @@ def first(request): Смотрим на результат: -index: +`index.html`: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/include_index.png) -first: +`first.html`: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/include_first.png) -В добавленную станицу можно передать переменные, при помощи тега `with` +В добавленную станицу можно передать переменные при помощи тега `with` Изменим файл `templates/add.html` @@ -549,15 +550,15 @@ first: Смотрим на результат: -index +`index.html` ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/include_var.png) -first +`first.html` ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson34/include_non_var.png) -В первом случае, мы видим добавлена переменная, во втором ничего так как мы ничего не передавали. +В первом случае мы видим добавленную переменную, во втором - ничего, так как мы ничего не передавали. Давайте добавим проверку на наличие переменной! @@ -565,7 +566,7 @@ first ```html -
{% if name %} Hello {{ name }} ! {% else %} Sorry I don't know +
{% if name %} Hello {{ name }} ! {% else %} Sorry, I don't know your name {% endif %}
``` @@ -584,7 +585,7 @@ first Что это такое, и зачем это нужно? -Фильтры это возможность видоизменить данные перед их отображением, давайте попробуем ими воспользоваться, для этого +Фильтры - это возможность видоизменить данные перед их отображением. Давайте попробуем ими воспользоваться, для этого во `view` добавим сверху файла ```python @@ -635,15 +636,15 @@ def index(request): Каждый фильтр имеет свои особенности и правила написания, подробнее можно посмотреть по ссылке выше. -Так же для данных существуют встроенные фильтры +Кроме того, для данных существуют встроенные фильтры. -Например `date`, `default`, `join`, `capfirst` на самом деле их огромное кол-во весь список встроенных фильтров можно -посмотреть [Тыц](https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#add) +Например `date`, `default`, `join`, `capfirst`. На самом деле их огромное количество, весь список встроенных фильтров можно +посмотреть [Тыц](https://docs.djangoproject.com/en/4.2/ref/templates/builtins/) ## Кастомные темплейт теги и фильтры -На самом деле, стандартным набором дело не ограничивается и если нужно можно дописать свои теги и фильтры, почитать об -этом можно вот [тут](https://docs.djangoproject.com/en/3.1/howto/custom-template-tags/) +На самом деле, стандартным набором дело не ограничивается, и в случае необходимости можно дописать свои теги и фильтры, почитать об +этом можно вот [тут](https://docs.djangoproject.com/en/4.2/howto/custom-template-tags/) ![](https://i.ytimg.com/vi/gF060AIFiB8/hqdefault.jpg) @@ -653,9 +654,8 @@ def index(request): https://edu-python-course.github.io/_build/html/uk/appx/blog.html#challenge-templates -На данном этапе мы пока не можем реализовать все что указанно в спецификации поэтому на текущем моменте надо реализовать такие страницы как: - -Все страницы должны быть отнаследованы от базового шаблона, сверху должна быть навигация между указанными ниже страницами (для детального просмотра, впишите любой стационарный слаг): +На данном этапе мы пока не можем реализовать все, что указанно в спецификации, поэтому на текущем моменте +надо реализовать следующие страницы: - Логин - Регистрация @@ -663,4 +663,7 @@ https://edu-python-course.github.io/_build/html/uk/appx/blog.html#challenge-temp - Страница с деталями блога (при любом слаге пока одна и та же) - Страница создания нового блога -Пока что ничего из этого кроме блока навигации не будет кликабельным :) \ No newline at end of file +Все страницы должны быть отнаследованы от базового шаблона, сверху должна быть навигация между указанными страницами +(для детального просмотра впишите любой стационарный слаг): + +Пока что ничего из этого кроме блока навигации не будет кликабельным :) From dcd1e52a78fc15a544f7a17b221d476f959c6bf9 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Thu, 20 Jul 2023 19:59:10 +0300 Subject: [PATCH 03/23] edit grammar and style --- lesson30.md | 196 +++++++++++++++++++++++++--------------------------- 1 file changed, 96 insertions(+), 100 deletions(-) diff --git a/lesson30.md b/lesson30.md index 4b35ec3ca..b7153097c 100644 --- a/lesson30.md +++ b/lesson30.md @@ -6,11 +6,11 @@ ## Установка базы -В наших примерах, мы будем использовать PostgreSQL, для этого предварительно нужно эту базу установить. +В наших примерах мы будем использовать PostgreSQL, для этого предварительно нужно эту базу установить. Скачать, если не установлена: [Тут](https://www.postgresql.org/download/) -## Cоздание базы и пользователя базы +## Создание базы и пользователя базы [Прекрасная статья по этому поводу под Linux](https://medium.com/coding-blocks/creating-user-database-and-adding-access-on-postgresql-8bfcd2f4a91e) @@ -30,7 +30,7 @@ ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/psql.png) -Создаём базу с кодировкой 'UTF8', что бы избежать проблем с русским и другими языками в базе. +Создаём базу с кодировкой 'UTF8', чтобы избежать проблем с русским и другими языками в базе. `create database mydb with encoding 'UTF8';` @@ -38,7 +38,7 @@ `create user myuser with password 'mypass';` -Даём новому пользователю права для использования новой базой. +Даём новому пользователю права на использование новой базы. `grant all on database mydb to myuser;` @@ -49,7 +49,7 @@ **Если вы используете Postgres 15 и новее** -Необходимо выполнить дополнительное действие, после предоставления прав к базе данных. +Необходимо выполнить дополнительное действие после предоставления прав к базе данных. Нужно предоставить вашему пользователю права к схеме `public` в новой базе данных. @@ -65,11 +65,11 @@ ## Конфигурация Django -Открываем проект, и в нём файл `settings.py` +Открываем проект и в нём файл `settings.py` -И находим строку `DATABASES` +Находим строку `DATABASES` -Если вы ничего не меняли, то выглядеть должна так: +Если вы ничего не меняли, то выглядеть она должна так: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/sqllitedb.png) @@ -88,27 +88,25 @@ DATABASES = { } ``` -Где `ENGINE` это "движок" он же модуль отвечающий за работу базы данных, +Где `ENGINE` - это "движок", он же модуль, отвечающий за работу базы данных. -`NAME` это имя базы, +`NAME` - это имя базы, -`USER` это имя пользователя, +`USER` - это имя пользователя, -`PASSWORD` это пароль пользователя, +`PASSWORD` - это пароль пользователя, -`HOST` это хост(урл, расположение базы), +`HOST` - это хост (урл, расположение базы), -`PORT` это порт (5432 стандартный порт для postgres, если вы его изменили при установке, укажите свой). +`PORT` - это порт (5432 стандартный порт для PostgreSQL, если вы его изменили при установке, укажите свой). -Для того, что бы это работало, нужно установить тот самый "движок". +Чтобы это работало, нужно установить тот самый "движок", соответствующий вашей операционной системе: ``` pip install psycopg2 # windows pip install psycopg2-binary # unix ``` -в зависимости от вашей операционной системы - **Не забывайте про venv** Если вы всё сделали правильно, то при запуске сервера (`python manage.py runserver`) вы должны увидеть, что-то такое: @@ -121,14 +119,14 @@ pip install psycopg2-binary # unix ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/migrate_text.png) -Когда мы создаём Django проект, мы создаём приложения для своих нужд, но на самом деле, внутри уже есть несколько +Когда мы создаём Django проект, мы создаём приложения для своих нужд, но на самом деле внутри уже есть несколько приложений для общих нужд, `admin`, `auth`, `contenttype`, `session`. -Все их мы разберем немного позже, в данный момент критичным является то, что в каждом из этих приложений находится -информация о том что должно храниться в базе, а наша свежая, только что созданная база, не имеет нужных таблиц, в -соответствии с моделями описанными в этих приложениях, описания того что должно быть в базе называются **Миграции**. +Все их мы разберем немного позже. В данный момент критичным является то, что в каждом из этих приложений находится +информация о том, что должно храниться в базе. А наша свежая, только что созданная база не имеет нужных таблиц, в +соответствии с моделями описанными в этих приложениях. Описания того, что должно быть в базе, называются **Миграции**. -При применении миграций, в базе создаются нужные таблицы, поля, связи итд. +При применении миграций в базе создаются нужные таблицы, поля, связи и т. д. Для применения нужно выполнить команду @@ -140,11 +138,9 @@ pip install psycopg2-binary # unix ## Приложения -Для того что бы Django увидела какие-либо изменения нужно добавлять каждое своё приложение в `settings.py` - -Находим в этом файле `INSTALLED_APPS` +Чтобы Django увидела какие-либо изменения, нужно добавлять каждое своё приложение в `settings.py` -и дописываем наше приложение, что бы получилось: +Находим в файле `settings.py` раздел `INSTALLED_APPS` и дописываем наше приложение, чтобы получилось: ```python INSTALLED_APPS = [ @@ -158,16 +154,16 @@ INSTALLED_APPS = [ ] ``` -Теперь всё готово, для того что бы начинать разработку собственных моделей!! +Теперь всё готово для того, чтобы начинать разработку собственных моделей. ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/start.jpg) ## Создание моделей -Всё еще лучше, чем официальная документация еще никто и ничего не придумал, офф -дока [Тут](https://docs.djangoproject.com/en/2.2/topics/db/models/) +Ничего лучше, чем официальная документация, никто не придумал, офф +дока [Тут](https://docs.djangoproject.com/en/4.2/topics/db/models/) -Что такое класс модели? Это таблица для базы данных, где атрибуты это её поля. +Что такое класс модели? Это таблица для базы данных, где атрибуты - это её поля. Давайте создадим модель! @@ -184,11 +180,11 @@ class Article(models.Model): text = models.TextField(null=True, blank=True) ``` -Мы создали нашу первую модель, состоящую из **3** полей, поля `name`, `text`, `id`, при чём id создался автоматически -без нашего участия, автоматически стало primary key. Поле `name` не может содержать более 100 символов Поле `text` может +Мы создали нашу первую модель, состоящую из **3** полей: `name`, `text` и `id`, причём поле `id` было создано автоматически +без нашего участия, и она автоматически стало primary key. Поле `name` не может содержать более 100 символов. Поле `text` может быть "пустым" или отсутствовать полностью. -Для того, что бы наши изменения попали в базу, нужно создать и применить миграцию. +Чтобы наши изменения попали в базу, нужно создать и применить миграцию. #### Команда makemigrations @@ -200,7 +196,7 @@ class Article(models.Model): ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/makemigrations.png) -Django радостно нам сообщает, что миграция была создана, давайте проверим, откроем папку `myapp/migrations` и увидим там +Django радостно нам сообщает, что миграция была создана. Давайте проверим, откроем папку `myapp/migrations` и увидим там новый файл `0001_initial.py` Выглядеть он будет вот так: @@ -211,7 +207,7 @@ Django радостно нам сообщает, что миграция был #### Команда showmigrations -Что бы убедиться, что миграция не применена/применена используется команда +Чтобы убедиться, что миграция не применена/применена, используется команда `python manage.py showmigrations` @@ -219,7 +215,7 @@ Django радостно нам сообщает, что миграция был ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/showmigrations.png) -Как мы можем видеть, наша миграция существует, но не применена, давайте применим её, при помощи `migrate` +Как мы можем видеть, наша миграция существует, но не применена, давайте применим её командой `python manage.py migrate` Результат: @@ -231,14 +227,14 @@ Django радостно нам сообщает, что миграция был ## Админка -Самый быстрый и удобный способ смотреть на объекты моделей это админка, для того, что бы ей пользоваться нужно сделать -две вещи, в урлы добавить встроенный урл админки, и создать суперпользователя. +Самый быстрый и удобный способ смотреть на объекты моделей - это админка. Чтобы ей пользоваться, нужно сделать +две вещи: в урлы добавить встроенный урл админки и создать суперпользователя. -Что бы создать пользователя нам поможет команда +Команда создания суперпользователя: `python manage.py createsuperuser` -Вводим всё что от нас требует консоль, и юзер будет создан: +Вводим всё, что от нас требует консоль, и юзер будет создан: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/createsuperuser.png) @@ -258,13 +254,13 @@ urlpatterns = [ ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/admin_login.png) -Вбиваем свои криды, и видим: +Вбиваем имя и пароль созданного пользователя и видим: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/admin_first_login.png) -По дефолту, у джанго сразу есть две модели из "коробки", User и Group, +По дефолту, у Django сразу есть две модели из "коробки" - User и Group. -Но нет нашей модели, почему же? Потому что нужные модели нужно регистрировать, что бы не заполнять админку не нужными +Но нет нашей модели, почему же? Потому что модели нужно регистрировать, чтобы не заполнять админку ненужными данными. Для этого нам нужно в файле `admin.py` в вашем приложении импортировать вашу модель и зарегистрировать её @@ -280,18 +276,18 @@ admin.site.register(Article) ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson35/add_model_to_admin.png) -Появилась наша модель, через админку мы можем добавлять, удалять, смотреть, редактировать наши модели. +Теперь наша модель появилась в админке, и мы можем добавлять, удалять, смотреть, редактировать наши модели. ## Кастомная админка -Возможности встроенной админки очень велики, но её можно дописывать, видоизменять, добавлять любые кастомные действия -итд. Подробно разберем на одном из следующих занятий, сейчас попробуйте создать/изменить/удалить несколько объектов. +Возможности встроенной админки очень велики, но её можно дописывать, видоизменять, добавлять кастомные действия +и т. д. Подробно разберем на одном из следующих занятий, сейчас попробуйте создать/изменить/удалить несколько объектов. -Админку можно кастомизировать, и изменить или добавить любое действие для это используются специальные классы. +Админку можно кастомизировать. Чтобы изменить или добавить любое действие, используются специальные классы. -Ссылка на оф доку [тут](https://docs.djangoproject.com/en/3.1/ref/contrib/admin/) +Ссылка на оф доку [тут](https://docs.djangoproject.com/en/4.2/ref/contrib/admin/) -Весь функционал, на самом деле, описан в приложении `django.contrib.admin`, о котором мы говорили выше, оно добавлено в +Весь функционал описан в приложении `django.contrib.admin`, о котором мы говорили выше, оно добавлено в наш проект по умолчанию. ```python @@ -311,19 +307,19 @@ class ArticleAdmin(admin.ModelAdmin): admin.site.register(Article, ArticleAdmin) ``` -## Основные типы филдов, и их стандартные атрибуты. +## Основные типы полей и их стандартные атрибуты. -[Ссылка на все существующие типы филдов](https://docs.djangoproject.com/en/3.0/ref/models/fields/) +[Ссылка на все существующие типы полей](https://docs.djangoproject.com/en/4.2/ref/models/fields/) -Когда вы создаёте любую модель, у неё автоматически появляется атрибут `id` он является праймари кеем по умолчанию, если -это не было переписано явно +Когда вы создаёте модель, у неё автоматически появляется атрибут `id`. Он является Primary Key по умолчанию, если +это не было переписано явно. -Id это очень удобное поле, т.к. оно является автоматическим, и когда вы создаёте новый объект ему сразу назнчается новый +`id` - это очень удобное поле, т. к. оно является автоматическим, и когда вы создаёте новый объект, ему сразу назначается новый идентификационный номер, на один больше предыдущего. -Так же все типы филдов имеют встроенный атрибут default, который заполняется если нужно указать значение по умолчанию +Так же все типы полей имеют встроенный атрибут `default`, который заполняется, если нужно указать значение по умолчанию -### Boolean field +### BooleanField Хранит True или False @@ -331,36 +327,36 @@ Id это очень удобное поле, т.к. оно является а my_flag = models.BooleanField() ``` -### Char field +### CharField -Строковое поле, принимает обязательный аргумент max_length - максимальное кол-во символов +Строковое поле, принимает обязательный аргумент max_length - максимальное количество символов -Часто используемые флаги null и blank, null=True, означает, что поле может быть None, black=True, означает, что поле -может быть пустой строкой '' +Часто используемые флаги `null` и `blank`, `null=True` означает, что поле может быть `None`, `black=True` означает, что поле +может быть пустой строкой `''`. ```python my_char = models.CharField(max_length=120, null=True, blank=False) ``` -### Date и DateTime field +### DateField и DateTimeField -Поля для хранения даты, и даты и времени +Поля для хранения даты и даты и времени -Принимают аргументы auto_now и auto_now_add, auto_now_add обозначает, что при создании объекта это поле будет -автоматически заполненное, текущей датой и временем. auto_now будет обновляться каждый раз когда объект сохраняется. ( -часто используют для сохранения даты создания и даты обновления объекта) +Принимают аргументы `auto_now` и `auto_now_add`. +`auto_now_add` обозначает, что при создании объекта это поле будет автоматически заполнено текущей датой и временем. +`auto_now` будет обновляться каждый раз, когда объект сохраняется. +(часто используют для сохранения даты создания и даты обновления объекта) ```python -created_at = models.DateField(auto_now=True) -updated_at = models.DateTimeField(auto_now_add=True) +created_at = models.DateField(auto_now_add=True) +updated_at = models.DateTimeField(auto_now=True) ``` -### Decimal Field +### DecimalField -Хранение float чисел +Хранение чисел типа Float -Обязательніе параметры max_digits, decimal_places. Первое максимальное кол-во символов, второе кол-во знаков после -запятой. +Обязательные параметры: `max_digits` - максимальное количество символов, `decimal_places` - количество знаков после запятой. ```python float_number = models.DecimalField(decimal_places=2, max_digits=12) @@ -368,20 +364,20 @@ updated_at = models.DateTimeField(auto_now_add=True) ### EmailField -Такой же текстовый как и CharField с проверкой на валидность имейла +Такой же текстовый, как и CharField, с проверкой на валидность имейла ### FileField -Для хранения файлов, можно указать upload_to - место для хранения файлов, если не указано, будет использованно, то, что -в settings.py +Для хранения файлов, можно указать `upload_to` - место для хранения файлов, если не указано, будет использовано, то, что +в `settings.py` ```python my_file = models.FileField() ``` -### Image Field +### ImageField -Тоже что и файл филд, с валидацией на картинку +Тоже что и FileField с валидацией изображения. ### IntegerField @@ -391,9 +387,9 @@ updated_at = models.DateTimeField(auto_now_add=True) int_number = models.IntegerField() ``` -### URL Field +### URLField -Текстовый тип, для хранения урлов. +Текстовый тип для хранения урлов. ```python my_url = models.URLField() @@ -405,20 +401,20 @@ updated_at = models.DateTimeField(auto_now_add=True) ![](http://memesmix.net/media/created/jb8uel.jpg) -Модели могут быть связанны между собой, для этого существует 3 типа связей +Модели могут быть связаны между собой, для этого существует 3 типа связей -OneToOne +OneToOne - связь один-к-одному -ForeignKey +ForeignKey - связь один-ко-многим -ManyToMany +ManyToMany - связь многие-ко-многим ### One to one -Связь один к одному, чаще всего используется для какого-либо однозначного определения разных моделей (Допустим -пользователя, и модели где хранятся его настройки) +Связь один к одному чаще всего используется для какого-либо однозначного определения разных моделей (Допустим, +пользователя и модели, где хранятся его настройки) -Для того, что бы сделать связь, нам нужно создать две модели и в одной из них указать зависимость +Чтобы установить связь, нам нужно создать две модели и в одной из них указать зависимость ```python from django.db import models @@ -434,11 +430,11 @@ class CustomerSettings(models.Model): customer = models.OneToOneField(Customer, on_delete=models.CASCADE, related_name='cus') ``` -Теперь эти модели связанны, что мы и увидим в админке. +Теперь эти модели связаны, что мы и увидим в админке. ### Foreign Key -Самая распространенная связь. Один ко многим. Допустим у нас есть книга, её написал конкретный автор, но это не значит, +Самая распространенная связь. Один ко многим. Допустим, у нас есть книга, её написал конкретный автор, но это не значит, что этот автор написал только эту книгу. ```python @@ -455,8 +451,8 @@ class Book(models.Model): ### Many to Many -Многие ко многим, допустим вы описываете базу для кинопоиска, один фильм может быть снят несколькими режиссерами, но при -этом каждый из режиссеров может снять больше одного фильма, такая связь называется ManyToMany Для построения таких +Многие ко многим, допустим, вы описываете базу для кинопоиска, один фильм может быть снят несколькими режиссерами, но при +этом каждый из режиссеров может снять больше одного фильма, такая связь называется ManyToMany. Для построения таких связей мы используем связь ManyToMany: ```python @@ -484,10 +480,10 @@ class Article(models.Model): return self.headline ``` -На самом деле "Под капотом" создаётся дополнительная таблица которая хранит информацию о связи между двумя видами +На самом деле, "под капотом" создаётся дополнительная таблица, которая хранит информацию о связи между двумя видами моделей. -Если нам нужно контролировать эту таблицу, мы можем делать это при помощи специального слова ```through``` +Если нам нужно контролировать эту таблицу, мы можем сделать это при помощи специального слова ```through``` ```python from django.db import models @@ -517,15 +513,15 @@ class Membership(models.Model): ## ContentTypes -На самом деле существуют более сложные конструкции, например фреймворк `ContentType`, который позволяет, делать -зависимость полей динамической (Допустим сделать лайк зависимым от динамического типа данных, например, хочешь ставить к +На самом деле, существуют более сложные конструкции, например, фреймворк `ContentType`, который позволяет делать +зависимость полей динамической (Допустим, сделать лайк зависимым от динамического типа данных, например, хочешь ставить к статье, а хочешь к комментарию, хотя это одна и та же модель) -Дока [Тут](https://docs.djangoproject.com/en/3.1/ref/contrib/contenttypes/) +Дока [Тут](https://docs.djangoproject.com/en/4.2/ref/contrib/contenttypes/) Подробно мы не будем рассматривать этот функционал, но я бы очень рекомендовал ознакомиться. -Работает основываясь на приложении `django.conrib.contenttype`, добавленное в наш проект по умолчанию. +Работает, основываясь на приложении `django.conrib.contenttype`, добавленное в наш проект по умолчанию. ![](https://lendiplompro.ru/images/praktica/praktica.jpg) @@ -533,12 +529,12 @@ class Membership(models.Model): https://edu-python-course.github.io/_build/html/uk/appx/blog.html#challenge-data-models -topic <--> blogpost = m2m: у blogpost повинен бути хоча б 1 topic +topic <--> blogpost = m2m: у blogpost должен быть хотя бы 1 topic -user <--> topic = m2m: 0 або більше з 2-х боків +user <--> topic = m2m: 0 или больше с 2-х сторон -blogpost <--> user = fk: у поста 1 автор, автор може створити скільки завгодно постів +blogpost <--> user = fk: у поста 1 автор, автор может создать любое количество постов -comment <--> user = fk: те саме, що й вище +comment <--> user = fk: то же самое, что и выше -comment <--> blogpost = fk: коментар прив'язаний до 1 поста, у поста скільки завгодно коментарів \ No newline at end of file +comment <--> blogpost = fk: комментарий привязан к 1 посту, у поста столько угодно комментариев From d40da967e97beed4df29c20a69f91502d980b95f Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Fri, 21 Jul 2023 00:15:21 +0300 Subject: [PATCH 04/23] edit grammar and style; update links --- lesson31.md | 401 +++++++++++++++++++++++++++------------------------- 1 file changed, 210 insertions(+), 191 deletions(-) diff --git a/lesson31.md b/lesson31.md index 40913bf70..9fb365a93 100644 --- a/lesson31.md +++ b/lesson31.md @@ -2,7 +2,7 @@ ![](https://cs8.pikabu.ru/post_img/2016/09/12/5/og_og_1473660997242355939.jpg) -Мы уже знаем про то как хранить данные, и как связать таблицы между собой, давайте научимся, извлекать, модифицировать и +Мы уже знаем про то, как хранить данные и как связать таблицы между собой. Давайте научимся извлекать, модифицировать и удалять данные при помощи кода. Допустим, что ваша модель выглядит так: @@ -23,7 +23,9 @@ GENRE_CHOICES = ( class Author(models.Model): - pseudonym = models.CharField(max_length=120, blank=True, null=True) + pseudonym = models.CharField(max_length=120, + blank=True, + null=True) name = models.CharField(max_length=120) def __str__(self): @@ -31,33 +33,45 @@ class Author(models.Model): class Article(models.Model): - author = models.ForeignKey(Author, on_delete=models.CASCADE, null=True, related_name='articles') - text = models.TextField(max_length=10000, null=True) + author = models.ForeignKey(Author, + on_delete=models.CASCADE, + null=True, + related_name='articles') + text = models.TextField(max_length=10000, + null=True) created_at = models.DateTimeField(default=timezone.now) updated_at = models.DateTimeField(default=timezone.now) - genre = models.IntegerField(choices=GENRE_CHOICES, default=1) + genre = models.IntegerField(choices=GENRE_CHOICES, + default=1) def __str__(self): - return "Author - {}, genre - {}, id - {}".format(self.author.name, self.genre, self.id) + return f"Author - {self.author.name}, genre - {self.genre}, id - {self.id}" class Comment(models.Model): text = models.CharField(max_length=1000) - article = models.ForeignKey(Article, on_delete=models.DO_NOTHING) - comment = models.ForeignKey('myapp.Comment', null=True, blank=True, on_delete=models.DO_NOTHING, + article = models.ForeignKey(Article, + on_delete=models.DO_NOTHING) + comment = models.ForeignKey('myapp.Comment', + null=True, + blank=True, + on_delete=models.DO_NOTHING, related_name='comments') - user = models.ForeignKey(User, on_delete=models.DO_NOTHING) + user = models.ForeignKey(User, + on_delete=models.DO_NOTHING) def __str__(self): - return "{} by {}".format(self.text, self.user.username) + return f"{self.text} by {self.user.username}" class Like(models.Model): - user = models.ForeignKey(User, on_delete=models.DO_NOTHING) - article = models.ForeignKey(Article, on_delete=models.DO_NOTHING) + user = models.ForeignKey(User, + on_delete=models.DO_NOTHING) + article = models.ForeignKey(Article, + on_delete=models.DO_NOTHING) def __str__(self): - return "By user {} to article {}".format(self.user.username, self.article.id) + return f"By user {self.user.username} to article {self.article.id}" ``` @@ -73,9 +87,9 @@ from django.contrib.auth.models import User from django.utils.translation import gettext as _ ``` -Стандартная функция перевода языка для Django, допустим что ваш сайт имеет функцию переключения языка, с русского, -украинского и английского, эта функция поможет нам в будущем указать значения для всех трех языков. Подробнейшая -информация по переводам [Тут](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/) +Стандартная функция перевода языка для Django. Допустим, что ваш сайт имеет функцию переключения языка, при которой текст +может отображаться на русском, украинском и английском. Именно эта функция поможет нам в будущем указать значения +для всех трех языков. Подробнейшая информация по переводам [Тут](https://docs.djangoproject.com/en/4.2/topics/i18n/translation/) ```python GENRE_CHOICES = ( @@ -87,9 +101,9 @@ GENRE_CHOICES = ( ) ``` -Переменная состоящая из тупла туплов (могла быть, любая коллекция коллекций), нужна для использования choices значений, -используется для хранения выбора чего-либо, в нашем случае жанра, в базе будет храниться только число, а пользователю -будет выводиться уже текст. +Переменная, состоящая из кортежа кортежей (могла быть любая коллекция коллекций), которая нужна для использования +choices значений, используется для хранения выбора чего-либо (в нашем случае жанра). То есть в базе будет храниться +только число, а пользователю будет выводиться уже текст. Используем это вот тут: @@ -100,35 +114,38 @@ genre = models.IntegerField(choices=GENRE_CHOICES, default=1) Рассмотрим вот эту строку ```python -return "Author - {}, genre - {}, id - {}".format(self.author.name, self.genre, self.id) +return f"Author - {self.author.name}, genre - {self.genre}, id - {self.id}" ``` -**self.author.name** - в базе по значению форейн кея хранится айди, но в коде мы можем получить доступ к значениям -связанной модели, конкретно в этой ситуации, мы берем значение поля **name** из связанной модели **author**. +**self.author.name** - в базе по значению поля ForeignKey хранится **id**, но в коде мы можем получить доступ к значениям +связанной модели, конкретно в этой ситуации мы берем значение поля **name** из связанной модели **author**. Рассмотрим вот эту строку: ```python -comment = models.ForeignKey('myapp.Comment', null=True, blank=True, on_delete=models.DO_NOTHING, +comment = models.ForeignKey('myapp.Comment', + null=True, + blank=True, + on_delete=models.DO_NOTHING, related_name='comments') ``` -Модель можно передать не только как класс, но и по имени модели указав приложение `appname.Modelname` (Да мне было лень -переименовывать приложение из myapp, во что-то читаемое) +Модель можно передать не только как класс, но и по имени модели указав приложение `appname.Modelname` (да, мне было лень +переименовывать приложение из myapp во что-то читаемое). При такой записи мы создаём связь один ко многим к самому себе, указав при этом black=True, null=True. Можно создать коммент без указания родительского комментария, а если создать комментарий со ссылкой на другой, это будет комментарий к комментарию, причем это можно сделать любой вложенности. -Кроме описания модели можно было бы использовать текст `self`, работает когда нужно сделать ссылку именно на самого себя +Кроме описания модели можно было бы использовать текст `self`. Это работает, когда нужно сделать ссылку именно на самого себя -`related_name` - в этой записи нужен для того, что бы получить выборку всех вложенных объектов, мы рассмотрим их немного -ниже. +`related_name` - в этой записи нужен для того, чтобы получить выборку всех вложенных объектов. Мы рассмотрим их немного +дальше. -## Meta моделей +## Класс Meta моделей -В некоторых ситуациях нам необходимо иметь возможность задать определённые условия на уровне модели, например порядок -сохранения объектов или другие особые условия, тут нам на помощь приходит встроенный класс `Meta` +В некоторых ситуациях нам необходимо иметь возможность задать определённые условия на уровне модели, например, порядок +сохранения объектов или другие особые условия. Тут нам на помощь приходит встроенный класс `Meta`. ```python from django.db import models @@ -144,22 +161,22 @@ class Ox(models.Model): Синтаксис такой конструкции нужно просто запомнить. -В мете может быть большое кол-во свойств моделей, давайте рассмотрим основные (полный -список [тут](https://docs.djangoproject.com/en/3.1/ref/models/options/)) +В классе Meta модели может быть большое количество свойств моделей, давайте рассмотрим основные (полный +список [тут](https://docs.djangoproject.com/en/4.2/ref/models/options/)) ### ordering -Содержит список из строк соответствующих названиям полей(атрибутов) могут быть указанны со знаком `-` что бы указать, -обратный порядок, в каком порядке указанны такой приоритет полей и будет, например, если -указан ``` ordering = ['name', '-age'] ``` то объекты будут расположены в базе по полю `name` и в случае совпадения -этого поля, по полю `age`, в обратном порядке. +Содержит список из строк, соответствующих названиям полей (атрибутов), которые могут быть указанны со знаком `-`, чтобы указать +обратный порядок сортировки. В каком порядке указаны поля, такой приоритет полей и будет, например, если +указан ``` ordering = ['name', '-age'] ```, то объекты будут расположены в базе по полю `name` и в случае совпадения +этого поля, по полю `age` в обратном порядке. -Может быть указана при помощи `F` объектов, о них позже. +Сортировки может быть указана при помощи `F-объектов`, о них позже. ### unique_together -Принимает коллекцию коллекций, например список списков, каждый список, должен содержать набор строк, с именами полей. -При указании этого набора, данные поля будут совместно уникальны (Если совместно уникальны имя и фамилия, то может быть +Принимает коллекцию коллекций, например, список списков. Каждый список должен содержать набор строк с именами полей. +При указании этого набора данные поля будут совместно уникальны (Если совместно уникальны имя и фамилия, то может быть сколько угодно объектов с именем `Мария`, и сколько угодно объектов с фамилией `Петрова`, но только один объект с такой комбинацией.) @@ -167,7 +184,7 @@ class Ox(models.Model): unique_together = [['driver', 'restaurant'], ['driver', 'phone_number']] ``` -Если есть только одно нужное значение может быть одним списком. +Если есть только одно нужное значение, может быть одним списком. ```python unique_together = ['driver', 'restaurant'] @@ -175,19 +192,19 @@ unique_together = ['driver', 'restaurant'] ### verbose_name и verbose_name_plural -Свойства содержащие строки и отвечающие за то какие имена будут описаны в админке и на уровне таблицы базы данных, в -единственном и множественно числе соответственно +Свойства, содержащие строки и отвечающие за то, какие имена будут описаны в админке и на уровне таблицы базы данных, в +единственном и множественно числе соответственно. ## Абстрактные и прокси модели ### Абстрактные модели -Абстрактные классы моделей, это `заготовки` под дальнейшие модели которые не создают дополнительных таблиц. Например +Абстрактные классы моделей - это `заготовки` под дальнейшие модели, которые не создают дополнительных таблиц. Например, некоторые из ваших моделей должны содержать поле `created_at`, в котором будет храниться информация о том, когда объект -создан, для этого можно в каждой модели прописать это поле, или один раз описать абстрактную модель с одним полем, и +создан. Для этого можно в каждой модели прописать это поле или один раз описать абстрактную модель с одним полем, и наследоваться уже от неё. -Модель как абстрактная указывается в мете. +Модель как абстрактная указывается во вложенном классе `Meta`. Синтаксис: @@ -207,11 +224,11 @@ class Student(CommonInfo): home_group = models.CharField(max_length=5) ``` -`Таблица для CommonInfo не будет созданна!!!` +`Таблица для CommonInfo не будет создана!!!` `Meta` не наследуется !! -Для наследования меты нужно использовать вот какой синтаксис (явное наследование меты): +Для наследования класса `Meta` нужно использовать вот какой синтаксис (явное наследование `Meta`): ```python from django.db import models @@ -241,8 +258,8 @@ class Student(CommonInfo, Unmanaged): ### Прокси модели -Модель, которая создаётся на уровне языка программирования, но не на уровне базы данных. Используется если нужно -добавить метод, изменить поведение менеджера итд. +Модель, которая создаётся на уровне языка программирования, но не на уровне базы данных. Используется, если нужно +добавить метод, изменить поведение менеджера и т. д. Синтаксис: @@ -264,82 +281,82 @@ class MyPerson(Person): pass ``` -В базе будет храниться одна таблица, в Django два класса. +В базе будет храниться одна таблица, в Django - два класса. ```python ->> > p = Person.objects.create(first_name="foobar") ->> > MyPerson.objects.get(first_name="foobar") -< MyPerson: foobar > +>> p = Person.objects.create(first_name="foobar") +>> MyPerson.objects.get(first_name="foobar") + ``` Часто используется для отображения в админке нескольких таблиц для одного объекта. ## objects и shell -Для доступа или модификации любых данных, у каждой модели есть аттрибут `objects`, который позволяет производить любые +Для доступа или модификации любых данных, у каждой модели есть атрибут `objects`, который позволяет производить манипуляции с данными. Он называется менеджер, и при желании его можно переопределить. Для интерактивного использования кода используется команда ```python manage.py shell``` -Эта команда открывает нам консоль с уже импортированными всеми стандартными, но не самописными модулями Django +Эта команда открывает нам консоль с уже импортированными стандартными, но не самописными модулями Django ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson36/clean_shell.png) Предварительно я создал несколько объектов через админку. -Для доступа к моделям, их нужно импортировать, я импортирую модель Comment +Для доступа к моделям их нужно импортировать, я импортирую модель Comment. Рассмотрим весь CRUD и дополнительные особенности. Очень подробная информация по всем возможным -операциям [Тут](https://docs.djangoproject.com/en/3.1/topics/db/queries/) +операциям [тут](https://docs.djangoproject.com/en/4.2/topics/db/queries/) ### R - retrieve -Функции для получения объектов в Django могут возвращать, два типа данных, **объект модели** или **queryset** +Функции для получения объектов в Django могут возвращать два типа данных, **объект модели** или **queryset** -Объект, это единичный объект, queryset это по сути список объектов, со своими встроенными методами. +Объект - это единичный объект, queryset - это список объектов со своими встроенными методами. -#### all +#### all() -Для получения всех данных используется метод `all()`, возвращает queryset со всеми существующими объектами этой модели. +Для получения всех данных используется метод `all()`, который возвращает queryset со всеми существующими объектами этой модели. ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson36/objects_all.png) -#### filter +#### filter() -Для получения отфильтрованных данных, мы используем метод `filter()` +Для получения отфильтрованных данных мы используем метод `filter()` -Если указать фильтр без параметров, то он сделает, то же самое что и all. +Если указать `filter()` без параметров, то он сделает то же самое, что и `all()`. -Какие у фильтра могу быть параметры? Да практически любые, мы можем указать любые поля для фильтрации. Например фильтр -по полю текст. +Какие у фильтра могу быть параметры? Да практически любые, мы можем указать любые поля для фильтрации. +Например, фильтр по полю текст: ```python Comment.objects.filter(text='Hey everyone') ``` -Фильтр по вложенным объектам, выполняется через двойное подчеркивание. +Фильтр по вложенным объектам выполняется через двойное подчеркивание. -Фильтр по жанру статьи комментария. +Фильтр по жанру статьи комментария: ```python Comment.objects.filter(article__genre=3) ``` -По псевдониму автора. +По псевдониму автора: ```python Comment.objects.filter(article__author__pseudonym='The king') ``` -По псевдониму автора и жанру (Через запятую можно указать логическое и). +По псевдониму автора и жанру (через запятую можно указать логическое И): ```python Comment.objects.filter(article__author__pseudonym='The king', article__genre=3) ``` -Так же у каждого поля существуют встроенные системы лукапов, пишутся с таким же синтаксисом как и доступ к вложенным +Кроме того, у каждого поля существуют встроенные системы лукапов. Синтаксис лукапов аналогичен синтаксису доступа к вложенным объектам `field__lookuptype=value` Стандартные лукапы: @@ -352,93 +369,93 @@ Comment.objects.filter(article__author__pseudonym='The king', article__genre=3) `gt` - больше -`startswith` - Начинается с +`startswith` - начинается с -`istartswith` - Начинается с, без учёта регистра +`istartswith` - начинается с, без учёта регистра -`endswith` - Заканчивается на +`endswith` - заканчивается на -`iendswith` - Заканчивается на, без учёта регистра +`iendswith` - заканчивается на, без учёта регистра -`range` - находится в рамках +`range` - находится в диапазоне `week_day` - день недели (для дат) `year` - год (для дат) -`isnull` - является наном +`isnull` - является `null` -`contains` - частично содержит учитывая регистр ("Всем привет, я Влад" содержит слово "Влад", но не содержит "влад") +`contains` - частично содержит с учетом регистра ("Всем привет, я - Влад" содержит слово "Влад", но не содержит "влад") -`icontains` - то же самое, но регистро независимо, теперь найдется и второй вариант. +`icontains` - то же самое, но без учета регистра, теперь найдется и второй вариант. -`exact` - совпадает (не обязательный лукап, делает то же, что и знак равно) +`exact` - совпадает (необязательный лукап, делает то же, что и знак равно) -`iexact` - совпадает регистро независимо (по запросу "привет" найдет и "Привет" и "прИвЕт") +`iexact` - совпадает без учета регистра (по запросу "привет" найдет и "Привет", и "прИвЕт") -`in` - содержится в каком то списке +`in` - содержится в каком-то списке -Их намного больше, читать [ТУТ](https://docs.djangoproject.com/en/2.2/ref/models/querysets/#field-lookups) +Их намного больше, читать [ТУТ](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#field-lookups) Примеры: -Псевдоним содержит слово 'king' +Псевдоним содержит слово 'king' без учета регистра: ```python Comment.objects.filter(article__author__pseudonym__icontains='king') ``` -Комменты к статье созданной не позднее чем вчера +Комменты к статье, созданной не позднее чем вчера: ```python Comment.objects.filter(article__created_at__gte=date.today() - timedelta(days=1)) ``` -Комменты к статьям с жанрами под номерами 2 и 3 +Комменты к статьям с жанрами, у которых `id = 2` и `id = 3`: ```python Comment.objects.filter(article__genre__in=[2, 3]) ``` -#### exclude +#### exclude() -Функция обратная функции `filter` вытащит всё что не попадает выборку +Функция обратная функции `filter` вытащит всё, что не попадает выборку. -Например все комменты к статьям у которых жанр не 2 и не 3 +Например, все комментарии к статьям, у которых жанр `id != 2` и `id != 3`: ```python Comment.objects.exclude(article__genre__in=[2, 3]) ``` -Фильтр и эксклюд можно совмещать. К любому кверисету можно применить фильтр или ексклюд еще раз. Например, все комменты -к статьям созданным не позже чем вчера, с жанрами не 2 и не 3 +`filter` и `exclude` можно совмещать. К любому queryset можно применить `filter` или `exclude` еще раз. Например, все комменты +к статьям, созданным не позже чем вчера, с жанрами не 2 и не 3 ```python Comment.objects.filter(article__created_at__gte=date.today() - timedelta(days=1)).exclude(article__genre__in=[2, 3]) ``` -Все эти функции возвращаю специальный объект называемый queryset, он является коллекцией записей базы (которые -называются в этой терминологии instance), к любому кверисету можно применить любой метод менджера модели (objects), -например к только что отфильтрованному кверисету, можно применить фильтр еще раз, итд. +Все эти функции возвращают специальный объект называемый queryset. Он является коллекцией записей базы (которые +называются в этой терминологии instance), к любому кверисету можно применить любой метод менеджера модели (objects). +Например, к только что отфильтрованному кверисету можно применить фильтр еще раз и т. д. -#### order_by +#### order_by() -По умолчанию все модели сортируются по айди, если явно не указанно иное, но часто нужно отсортировать данные специальным -образом для этого используется order_by(), при сортировке так же можно указывать вложенные объекты и знак `-`, что бы -указать сортировку в обратном порядке +По умолчанию все модели сортируются по полю `id`, если явно не указанно иное. Однако часто нужно отсортировать данные специальным +образом. Для этого используется метод order_by(). При сортировке можно указывать вложенные объекты и знак `-`, чтобы +указать сортировку в обратном порядке. ```python Comment.objects.filter(article__created_at__gte=date.today() - timedelta(days=1)).order_by('-article__created_at').all() ``` -Это далеко не всё что можно сделать с queryset +Это далеко не всё, что можно сделать с queryset. Все методы кверисетов -читаем [Тут](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#methods-that-return-new-querysets) +читаем [Тут](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#methods-that-return-new-querysets) -Особое внимание на методы `distinct`, `reverse`, `values`, `difference` +Особое внимание следует обратить на методы `distinct`, `reverse`, `values`, `difference` -Так же во все фильтры можно вставлять целые объекты, например +Помимо прочего во все фильтры можно вставлять целые объекты, например: ```python art = Article.objects.get(id=2) @@ -447,14 +464,14 @@ comments = Comment.object.exclude(article=art) ### Объектные методы -#### get +#### get() -В отличие от filter и exclude получает сразу объект, работает только если можно определить объект однозначно и он -существует. +В отличие от `filter` и `exclude` метод `get` получает сразу объект. Этот метод работает только в том случае, когда можно +определить объект однозначно и он существует. -Можно применять те же условия, что и для фильтра и эксклюда +Можно применять те же условия, что и для `filter` и `exclude` -Например получения объекта по айди +Например, получения объекта по `id` ```python Comment.objects.get(id=3) @@ -472,45 +489,45 @@ except Comment.MultipleObjectsReturned: return "More than one object" ``` -#### first и last +#### first() и last() -К кверисету можно применять методы first и last что бы получить первый или последний элемент кверисета +К кверисету можно применять методы `first` и `last`, чтобы получить первый или последний элемент кверисета -Например получить первый коммент написанный за вчера +Например, получить первый коммент, написанный за вчера: ```python Comment.objects.filter(article__created_at__gte=date.today() - timedelta(days=1)).first() ``` Информация по всем остальным -методам [Тут](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#methods-that-do-not-return-querysets) +методам [Тут](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#methods-that-do-not-return-querysets) ### related_name -Атрибут релейтед нейм, который указывается для полей связи, является обратной связью, и менеджером для объектов, -например в нашей модели у поля `author` модели `Article` есть related_name=`articles`: +Атрибут `related_name`, который указывается для полей связи, является обратной связью и менеджером для объектов, +например, в нашей модели у поля `author` модели `Article` есть `related_name=articles`: ```python a = Author.objects.first() -articles = a.articles.all() # тут будут все статьи конкретного автора в виде кверисета, т.к. all() возвращает кверисет +articles = a.articles.all() # Тут будут все статьи конкретного автора в виде кверисета, т.к. all() возвращает кверисет ``` -Можно ли получить объекты обратной связи без указания related_name? Можно. Связь появляется автоматически даже без +Можно ли получить объекты обратной связи без указания `related_name`? Можно. Связь появляется автоматически даже без указания этого атрибута. -Обратный менеджер формируется из названия модели и конструкции `_set`, допустим у поля `article` модели `Comment` не -указан related_name: +Обратный менеджер формируется из названия модели и конструкции `_set`. Допустим, у поля `article` модели `Comment` не +указано поле `related_name`: ```python a = Article.objects.first() -a.comment_set.all() # такой же менеджер как в прошлом примере, вернёт кверисет коментариев относящихся к этой статье. +a.comment_set.all() # такой же менеджер, как в прошлом примере, вернёт кверисет комментариев, относящихся к этой статье. ``` ### C - Create -Для создания новых объектов используется два возможных варианта, через метод, `create` или метод `save` +Для создания новых объектов используется два возможных варианта: метод `create()` или метод `save()` -Создадим двух новых авторов, при помощи разных методов +Создадим двух новых авторов при помощи разных методов: ```python Author.objects.create(name='New author by create', pseudonym="Awesome author") @@ -519,27 +536,27 @@ a = Author(name="Another new author", pseudonym="Gomer") a.save() ``` -В чём же разница? В том, что в первом случае, при выполнении этой строки запрос в базу будет отправлен сразу, во втором, +В чём же разница? В том, что в первом случае при выполнении этой строки запрос в базу будет отправлен сразу, во втором - только при вызове метода `save()` -Метод save так же является и методом для апдейта, если применяется для уже существующего объекта, рассмотрим его -подробнее немного дальше. +Метод `save()` также является и методом для обновления значения поля, если применяется для уже существующего объекта. Мы +рассмотрим его подробнее немного дальше. ### U - Update -Для апдейта используется метод `update()` +Для обновления значений полей используется метод `update()` **Применяется только к кверисетам, к объекту применить нельзя** -Например обновим текст в комментарии с айди = 3 +Например, обновим текст в комментарии с `id = 3`: ```python -Comment.objects.filter(id=3).update(text='bla-bla') +Comment.objects.filter(id=3).update(text='updated text') ИЛИ c = Comment.objects.get(id=3) -c.text = 'bla-bla' +c.text = 'updated text' c.save() ``` @@ -547,7 +564,7 @@ c.save() Как можно догадаться, выполняется методом `delete()`. -Удалить все комменты от пользователя с айди = 2 +Удалить все комменты от пользователя с `id = 2`: ```python Comment.objects.filter(user__id=2).delete() @@ -555,30 +572,30 @@ Comment.objects.filter(user__id=2).delete() ### Совмещенные методы -#### get_or_create() update_or_create() bulk_create() bulk_update() +#### get_or_create(), update_or_create(), bulk_create(), bulk_update() -get_or_create - Метод, который попытается создать новый объект если не сможет найти нужный в базе, возвращает сам объект -и булевое значение, которое обозначает, объект был создан или получен. +`get_or_create()` - это метод, который попытается создать новый объект. Если он не сможет найти нужный в базе, он +возвращает сам объект и булево значение, которое обозначает, что объект был создан или получен. -update_or_create Обновит если объект существует, создаст если не существует +`update_or_create()` - обновит, если объект существует, создаст, если не существует. -bulk_create - массовое создание, необходимо для того, что бы избежать большого кол-ва обращений в базу. +`bulk_create()` - массовое создание; необходимо для того, чтобы избежать большого количества обращений в базу. -bulk_update - массовые апдейт (отличие от обычного в том, что при обычном на каждый объект создается запрос, в этом -случае запрос делается массово) +`bulk_update()` - массовое обновление (отличие от обычного в том, что при обычном на каждый объект создается запрос, +в этом случае запрос делается массово) -Подробно почитать про них [Тут](https://docs.djangoproject.com/en/2.2/ref/models/querysets/#get-or-create) +Подробно почитать про них [Тут](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#get-or-create) -## Подробнее о методе save +## Подробнее о методе save() -Метод save() применяется при любых изменениях или создании данных, но очень часто нужно что-бы при сохранении данных -выполнялись еще какие-либо действия, переписывание данных или запись логов итд. Для этого используется переписывание -метода save(). По сути является способом написать аналог тригера в базе данных. +Метод `save()` применяется при любых изменениях или создании данных, но очень часто нужно, чтобы при сохранении данных +выполнялись еще какие-либо действия, переписывание данных или запись логов и т. д. Для этого используется переписывание +метода `save()`. По сути является способом написать аналог триггера в базе данных. -Метод сейв вызывают или явно, или не явно во время вызова методов создания или обновления, но без него запись в базу не +Метод `save()` вызывают явно или неявно во время вызова методов создания или обновления, но без него запись в базу не будет произведена. -Допустим мы хотим делать время создания статьи на один день раньше чем фактическая. Перепишем метод `save` для статьи +Допустим, мы хотим делать время создания статьи на один день раньше, чем фактическая. Перепишем метод `save()` для статьи: ```python class MyAwesomModel(models.Model): @@ -590,11 +607,11 @@ class MyAwesomModel(models.Model): super().save(**kwargs) ``` -Переопределяем значение, и вызываем оригинальный save, вуаля. +Переопределяем значение и вызываем оригинальный `save()`, вуаля. -Для того, что бы переопределить логику при создании, но не трогать при изменении, или наоборот, используется особенность -данных, у уже созданного объекта `id` существует, у нового нет. Так что фактически наш код сейчас обновляет это поле -всегда, и когда надо и когда не надо, допишем его. +Чтобы переопределить логику при создании, но не трогать при изменении, или наоборот, используется особенность +данных. У уже созданного объекта `id` существует, у нового - нет. Так что фактически наш код сейчас обновляет это поле +всегда, и когда надо, и когда не надо. Допишем его. ```python class MyAwesomModel(models.Model): @@ -609,8 +626,8 @@ class MyAwesomModel(models.Model): Теперь поле будет переписываться только в момент создания, но не будет трогаться при обновлении. -Метод delete(), при удалении объекта `save()` не вызывается, а вызывается `delete()` по аналогии мы можем его -переписать, например для отправки имейла перед удалением +Метод `delete()`, при удалении объекта `save()` не вызывается, а вызывается `delete()`. По аналогии мы можем его +переписать, например, для отправки имейла перед удалением. ```python class MyAwesomModel(models.Model): @@ -626,25 +643,25 @@ class MyAwesomModel(models.Model): Документация по этому разделу -[Тут](https://docs.djangoproject.com/en/3.1/ref/models/expressions/#django.db.models.F) -[Тут](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.Q) +[Тут](https://docs.djangoproject.com/en/4.2/ref/models/expressions/#django.db.models.F) +[Тут](https://docs.djangoproject.com/en/4.2/ref/models/querysets/#django.db.models.Q) -На самом деле мы не ограниченны стандартными конструкциями, мы можем применять предвычисления на уровне базы, добавлять -логические конструкции итд., давайте рассмотри подробнее. +На самом деле, мы не ограничены стандартными конструкциями. Мы можем применять предвычисления на уровне базы, добавлять +логические конструкции и т. д., давайте рассмотрим подробнее. ### Q объекты -Как вы могли заметить в случае фильтрации, мы можем выбрать объекты через логическое И, при помощи запятой. +Как вы могли заметить в случае фильтрации, мы можем выбрать объекты через логическое И при помощи запятой. ```python Comment.objects.filter(article__author__pseudonym='The king', article__genre=3) ``` -В этом случае у нас есть конструкция типа "выбрать объекты у которых псевдоним автора статьи это `The king` И жанр статьи это цифра 3" +В этом случае у нас есть конструкция типа "выбрать объекты, у которых псевдоним автора статьи `The king` И жанр статьи цифра 3" -Но что же нам делать есть нам нужно использовать логическое ИЛИ. +Но что же нам делать, если нам нужно использовать логическое ИЛИ? -В этом нам поможет использование Q объекта, на самом деле каждое из этих условий мы могли завернуть в такой объект: +В этом нам поможет использование Q объекта. На самом деле, каждое из этих условий мы могли завернуть в такой объект: ```python from django.db.models import Q @@ -653,7 +670,7 @@ q1 = Q(article__author__pseudonym='The king') q2 = Q(article__genre=3) ``` -Теперь мы можем явно использовать логические И и ИЛИ. +Теперь мы можем явно использовать логические `И` и `ИЛИ`. ```python Comment.objects.filter(q1 & q2) # И @@ -662,9 +679,9 @@ Comment.objects.filter(q1 | q2) # ИЛИ ### Aggregation -Агрегация в джанго это по сути предвычисления. +Агрегация в Django - это предвычисления. -Допустим что у нас есть модели: +Допустим, что у нас есть модели: ```python from django.db import models @@ -682,10 +699,12 @@ class Publisher(models.Model): class Book(models.Model): name = models.CharField(max_length=300) pages = models.IntegerField() - price = models.DecimalField(max_digits=10, decimal_places=2) + price = models.DecimalField(max_digits=10, + decimal_places=2) rating = models.FloatField() authors = models.ManyToManyField(Author) - publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE) + publisher = models.ForeignKey(Publisher, + on_delete=models.CASCADE) pubdate = models.DateField() @@ -694,7 +713,7 @@ class Store(models.Model): books = models.ManyToManyField(Book) ``` -Мы можем совершить предвычисления каких либо средних, минимальных, максимальных значений, вычислить сумму итд. +Мы можем совершить предвычисления каких-либо средних, минимальных, максимальных значений, вычислить сумму и т. д. ```python >> > from django.db.models import Avg @@ -702,21 +721,21 @@ class Store(models.Model): {'price__avg': 34.35} ``` -На самом деле all() не несёт пользы в этом примере +На самом деле, `all()` не несёт пользы в этом примере: ```python >> > Book.objects.aggregate(Avg('price')) {'price__avg': 34.35} ``` -Значение можно именовать +Значение можно именовать: ```python >> > Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} ``` -Можно вносить больше одной агрегации за раз +Можно вносить больше одной агрегации за раз: ```python >> > from django.db.models import Avg, Max, Min @@ -724,7 +743,7 @@ class Store(models.Model): {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')} ``` -Если нам нужно, что бы подсчитанное значение было у каждого объекта модели, мы используем метод `annotate` +Если нам нужно, чтобы подсчитанное значение было у каждого объекта модели, мы используем метод `annotate()` ```python # Build an annotated queryset @@ -743,7 +762,7 @@ q[1].authors__count 1 ``` -Их тоже может быть больше одного +Их тоже может быть больше одного: ```python book = Book.objects.first() @@ -758,37 +777,37 @@ q[0].store__count 6 ``` -Все эти вещи можно комбинировать +Все эти вещи можно комбинировать: ```python highly_rated = Count('book', filter=Q(book__rating__gte=7)) Author.objects.annotate(num_books=Count('book'), highly_rated_books=highly_rated) ``` -C ордерингом +c сортировкой (ordering): -``` ->>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors') +```python +Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors') ``` -## F - выражения +## F() выражения -F выражения нужны для получения значения поли, и оптимизации -записи. [Дока](https://docs.djangoproject.com/en/3.1/ref/models/expressions/#f-expressions) +F-выражения нужны для получения значения поля и оптимизации записи. +[Дока](https://docs.djangoproject.com/en/4.2/ref/models/expressions/#f-expressions) -Допустим нам нужно увеличить определенному объекту в базе значение какого либо поля на 1 +Допустим, нам нужно увеличить определенному объекту в базе значение какого-либо поля на 1 -``` +```python reporter = Reporters.objects.get(name='Tintin') reporter.stories_filed += 1 reporter.save() ``` -На самом деле в этот момент мы получаем значение из базы в память, обрабатываем, и записываем в базу +На самом деле, в этот момент мы получаем значение из базы в память, обрабатываем и записываем в базу. Есть другой путь: -``` +```python from django.db.models import F reporter = Reporters.objects.get(name='Tintin') @@ -796,30 +815,30 @@ reporter.stories_filed = F('stories_filed') + 1 reporter.save() ``` -Преимущества под капотом, но давайте предположим, что нам нужно сделать эту же операцию массово +Преимущества под капотом, но давайте предположим, что нам нужно сделать эту же операцию массово: -``` +```python reporter = Reporters.objects.filter(name='Tintin') reporter.update(stories_filed=F('stories_filed') + 1) ``` -Такие объекты можно использовать и в аннотации и в фильтрах и во многих других местах. +Такие объекты можно использовать и в `annotate()`, и в `filter()`, и во многих других местах. ## Домашка: -1) Создать два пользователя через createsuperuser -2) При помощи shell создать две категории постов, 5 постов в разных категориях (2 и 3, например), создать 5-7 комментариев к постам, хотя бы один пост оставить без коментариев -3) Реализовать такие страницы как: '/', /\/ +1) Создать два пользователя через createsuperuser. +2) При помощи shell создать две категории постов, 5 постов в разных категориях (2 и 3, например), +создать 5-7 комментариев к постам, хотя бы один пост оставить без комментариев. +3) Реализовать такие страницы как: '/', /\/. -На главной странице, должен отображаться список всех блогов, название каждого должно быть ссылкой на страницу с подробностями о блоге +На главной странице должен отображаться список всех блогов, название каждого должно быть ссылкой на страницу с подробностями о блоге. -На странице с подробностями, должны отображаться детали поста, и все комментарии написанные к этому посту +На странице с подробностями должны отображаться детали поста и все комментарии, написанные к этому посту. ### Дополнительная информация: -Если сложно разобраться со слагами, то реализуйте сначала урл который будет принимать id вместо слага, а только потом добавте еще один уже для слага - -Генерацию слага удобнее всего разместить в методе save. - +Если сложно разобраться со слагами, то реализуйте сначала урл, который будет принимать id вместо слага, а только потом +добавьте еще один уже для слага. +Генерацию слага удобнее всего разместить в методе save(). From fffa3defeb6eb44ec2d256fedd61c4f089cc0b15 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Sun, 23 Jul 2023 23:27:00 +0300 Subject: [PATCH 05/23] edit grammar and style; update links --- lesson32.md | 375 ++++++++++++++++++++++++++-------------------------- 1 file changed, 187 insertions(+), 188 deletions(-) diff --git a/lesson32.md b/lesson32.md index 8b4a3f50a..05a4de93f 100644 --- a/lesson32.md +++ b/lesson32.md @@ -4,21 +4,21 @@ ## HTML формы -Html форма, это специальный тег, который "сообщает" браузеру, что данные из этого тега нужно сгруппировать и подготовить +`HTML форма` - это специальный тег, который "сообщает" браузеру, что данные из этого тега нужно сгруппировать и подготовить к отправке на сервер. Принимает два параметра `action` и `method`. -`action` описывает, куда форма по результату должна обращаться, в нашем случае это будет `url`, если не указан явно, то +`action` описывает, куда форма по результату должна обращаться (в нашем случае это будет `url`), если не указан явно, то форма будет отправлена на тот же урл, на котором сейчас находится. -`method` - отвечает за метод отправки, варианты это `get` и `post`. `get` будет использован по умолчанию если не указан +`method` отвечает за метод отправки, варианты - `get` и `post`. `get` будет использован по умолчанию, если не указан явно. -Пост формы используются для передачи данных не подлежащих огласке, например, логин и пароль. Гет формы, используются для -общедоступной информации, например строка поиска. +Формы с методом `POST` используются для передачи данных, не подлежащих огласке, например, логин и пароль. +Формы с методом `GET` используются для общедоступной информации, например, строки поиска. -Внутри формы мы указываем нужное кол-во тегов `` с нужными типами. Именно эти данные будут в последствии переданы +Внутри формы мы указываем нужное количество тегов `` с нужными типами. Именно эти данные будут впоследствии переданы серверу. [Ссылка на общее описание форм](https://www.w3schools.com/html/html_forms.asp) @@ -33,11 +33,11 @@ Html форма, это специальный тег, который "сооб **radio** - радиобаттон (выбор только одного элемента из списка) -**button** - классическая кнопка (если в форме есть один такое элемент, но нет сабмита, браузер автоматически подсчитает +**button** - классическая кнопка (если в форме есть один такой элемент, но нет сабмита, браузер автоматически посчитает его сабмитом) -**hidden** - скрытое поле, чаще всего нужно для целей безопасности или добавления информации и данным не отображая её ( -Не отображается) +**hidden** - скрытое поле, чаще всего нужно для целей безопасности или добавления информации и данных, не отображая их +(не отображается) **submit** - отправка формы @@ -46,7 +46,7 @@ Html форма, это специальный тег, который "сооб ## GET форма -Создадим простейшую форму, для этого создадим, урл, функцию для обработки и хтмл страницу. +Создадим простейшую форму. Для этого создадим url, функцию для обработки и HTML страницу. В urls.py @@ -83,14 +83,14 @@ def form_view(request): {% endblock %} ``` -Обратите внимание, мы указали гет форму, экшеном является урл, на эту же страницу, который обрабатывается нашей же +Обратите внимание, мы указали `GET` форму, `action` - урл на эту же страницу, который обрабатывается нашей же функцией `form_view`. -В форме у нас один `input` которому мы указали 2 аттрибута `type` и `name`. +В форме у нас один `input`, которому мы указали 2 атрибута `type` и `name`. -Аттрибут `name` нам необходим для того, что бы мы смогли обработать данные во view. +Атрибут `name` нам необходим для того, чтобы мы смогли обработать данные во view. -Также у нас есть кнопка `submit`, необходима для того, что бы отправить запрос, на сервер. +Также у нас есть кнопка `submit`, она необходима для того, чтобы отправить запрос на сервер. При нажатии на кнопку формируется и отправляется request. @@ -100,51 +100,51 @@ def form_view(request): ### request -Объект `request`, тот который мы принимаем первым параметром функции обработчика и его же передаём первым в -функцию `render`. Зачем он нужен, и из чего он состоит? +Объект `request` мы принимаем первым параметром функции обработчика и его же передаём первым в +функцию `render`. Зачем он нужен и из чего он состоит? -Зачем нужен. Нужен для того что бы обрабатывать любые пользовательские или служебные данные которые были переданы +Зачем нужен? Чтобы обрабатывать любые пользовательские или служебные данные, которые были переданы. -Из чего состоит. Состоит из переданных данных или файлов (если были переданы), и служебной информации (информация о -пользователе, метод запроса, о том на какой url был запрос, из какого браузера, другая системная информация, о ней -отдельная лекция) +Из чего состоит? Состоит из переданных данных или файлов (если были переданы) и служебной информации (информации о +пользователе, методе запроса, о том, на какой url был запрос, из какого браузера, другой системной информации, о ней +отдельная лекция). -### Давайте отправим реквест +### Давайте отправим request ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/filled_get_form.png) Что будет после нажатия кнопки `Send name`? -Будет сформирован `GET` (метод формы) запрос всеми заполненными нами данными и отправлен на сервер. +Будет сформирован `GET` (метод формы) запрос со всеми заполненными нами данными и отправлен на сервер. Обратите внимание на новый url. ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/data_get_form.png) -`my_name` - это предварительно указанный атрибут `name` на нашей форме, а `Vlad` значение которое я передал в этот +`my_name` - это предварительно указанный атрибут `name` на нашей форме, а `Vlad` - значение, которое я передал в этот инпут. -В случае `GET` запроса данные передаются явно, прям в url в виде ключ-значение. Если бы значений было бы больше одного -они были бы соединены при помощи символа `&` (например если бы я добавил еще и поле с указанным атрибутом `name` со -значением `age` и заполнил бы его значением 26, то url после запроса выглядел бы так `/form-url/?my_name=Vlad&age=26`. -Никакой разницы между заполнением формы, или записью этих данных руками прям в строке браузера для `GET` запроса нет. +В случае `GET` запроса данные передаются явно, прям в url в виде ключ-значение. Если бы значений было больше одного, +они были бы соединены при помощи символа `&` (например, если бы я добавил к полю с указанным атрибутом `name` еще и поле +с атрибутом `age` и заполнил бы его значением 26, то url после запроса выглядел бы так `/form-url/?my_name=Vlad&age=26`). +Никакой разницы между заполнением формы или записью этих данных руками прям в строке браузера для `GET` запроса нет. ### Обработка данных во view -Мы можем обработать данные во `view`, при помощи переменной `request`, данные из `GET` запроса, будет находиться в +Мы можем обработать данные во `view` при помощи переменной `request`. Данные из `GET` запроса будут находиться в переменной `request.GET` ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/get_request_view.png) -Данные находятся в виде словаря, где ключами является атрибут `name` в каждом инпуте формы. +Данные находятся в виде словаря, где ключами являются атрибуты `name` в каждом инпуте формы. -Эти данные можно использовать для любых целей, но чаще всего через `GET` передаются данные по фильтрации, или -дополнительные параметры отображения, например, когда вы добавляете фильтры в интернет магазине, пишете текст в поиске, -или когда на ютубе пересылаете ссылку с таймкодом, она тоже передаётся как `GET` параметр +Эти данные можно использовать для любых целей, но чаще всего через `GET` передаются данные по фильтрации или +дополнительные параметры отображения. Например, когда вы добавляете фильтры в интернет магазине, пишете текст в поиске, +или когда на YouTube пересылаете ссылку с таймкодом, она тоже передаётся как `GET` параметр. ## POST запрос -Давайте заменим метод нашей формы с `get` на `post`: +Давайте заменим метод нашей формы с `GET` на `POST`: В `form.html`: @@ -163,33 +163,33 @@ def form_view(request): Что произойдёт при отправке такого запроса? -Произойдёт ошибка, примерно вот такая: +Произойдёт примерно такая ошибка: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/csrf_failure.png) Это ошибка CSRF токена. -Что бы понять что это, нужно понимать разницу того, где используются разные запросы. +Чтобы понять, что это, нужно понимать разницу того, где используются разные запросы. -`GET` запросы, это запросы общедоступные и информационные, открыть страницу, отфильтровать данные итд. +`GET` запросы - это запросы общедоступные и информационные: открыть страницу, отфильтровать данные и т. д. -`POST` запросы, это запросы с чувствительными данными, создание записей в базе, передача пароля, отправка денег со счёта -на счёт, итд. +`POST` запросы - это запросы с чувствительными данными: создание записей в базе, передача пароля, отправка денег со счёта +на счёт и т. д. Так вот, если `GET` запрос отправить 5 раз подряд, то с точки зрения сервера ничего не изменится, вы просто 5 раз подряд запросите одну и туже информацию. Если изменить параметры, то тоже ничего страшного не произойдёт, просто запросятся другие данные. -А вот если повторить несколько раз или подделать данные в `POST` запросе, то можно совершить разные проблемные действия, -создание лишних записей в базе данных, или перевод средств на счёт злоумышленников вместо ожидаемого, итд. +А вот если повторить несколько раз или подделать данные в `POST` запросе, то можно совершить разные проблемные действия: +создание лишних записей в базе данных, перевод средств на счёт злоумышленников вместо ожидаемого и т. д. -Поэтому в Django изначально есть дополнительное требование к `POST` формам, это еще одно скрытое поле, заранее +Поэтому в Django изначально есть дополнительное требование к `POST` формам - это еще одно скрытое поле, заранее сгенерированное сервером. Оно называется `CSRF токен`, где он проверяется и почему мы видим ошибку, мы разберём на следующих занятиях. -Для того что бы добавить нужный токен, используется специальный темплейт тег `{% csrf_token %}`, его нужно добавить в -любом месте внутри тега `
` +Чтобы добавить нужный токен, используется специальный темплейт тег `{% csrf_token %}`. Его нужно добавить в +любом месте внутри тега ``. ```html {% extends 'base.html' %} @@ -207,38 +207,38 @@ def form_view(request): {% endblock %} ``` -Что изменится с точки зрения html: +Что изменится с точки зрения `HTML`: ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/csrf_html.png) -Появилось поле типа `hidden`, это значит, что оно не будет отображаться, но эти данные все равно попадут на сервер. Это -часто используется когда вам нужно передать данные которые у тебя уже есть при отрисовке, но их не видно явно, допустим -есть мы пишем коментарий к коментарию, то что бы грамотно его создать, нам нужен id родителя, его обычно и передают -как `hidden` филд. +Появилось поле типа `hidden`. Это значит, что оно не будет отображаться, но эти данные все равно попадут на сервер. Это +часто используется, когда вам нужно передать данные, которые у вас уже есть при отрисовке, но их не видно явно. Допустим, +если мы пишем комментарий к комментарию, то чтобы грамотно его создать, нам нужен `id` родителя, его обычно и передают +как `hidden` поле. Теперь наш запрос отправится успешно. -Обратите внимание, урл не изменятся! +Обратите внимание, что url не изменится! Потому что данные отправленные через `POST` не должны быть общедоступны. ### Обработка во view -Обработать данные из `POST` запроса, можно точно так же, данные будут находиться в переменной request.POST, если это -просто данные и в request.FILES если были переданы файлы. +Обработать данные из `POST` запроса можно точно также, данные будут находиться в переменной `request.POST`, если это +просто данные, и в `request.FILES`, если были переданы файлы. ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/post_request_view.png) Обратите внимание, что вместе с нашими данными был передан и `csrf` токен. Обычно при обработке данных он не нужен, но -данные были переданы, а значит они прийдут на сервер. +данные были переданы, а значит они придут на сервер. -### Джанго формы +### Django Forms -Джанго предоставляет нам возможность генерировать html формы из кода на python! +Django предоставляет нам возможность генерировать `HTML` формы из кода на Python! -Что для этого нужно. Создадим в нашем приложении файл `forms.py` +Что для этого нужно? Создадим в нашем приложении файл `forms.py` -Внутри этого файла укажем. +Внутри этого файла укажем: `forms.py` @@ -306,7 +306,7 @@ def form_view(request): {% endblock %} ``` -Что именно мы сделали. +Что именно мы сделали? ### Описание формы @@ -314,133 +314,133 @@ def form_view(request): Они будут соответствовать двум инпутам, текстовому и числовому. -Типов существует естественно гораздо больше +Естественно, типов существует гораздо больше. Основные типы: -BooleanField - булеан +`BooleanField` - булево значение -CharField - текст +`CharField` - текст -ChoiceField - поле для выбора +`ChoiceField` - поле для выбора -DateTimeField - дата время +`DateTimeField` - дата/время -EmailField - имейл +`EmailField` - имейл -FileField - файл +`FileField` - файл -IntegerField - целое число +`IntegerField` - целое число -MultipleChoiceField - множественный выбор +`MultipleChoiceField` - множественный выбор И многие другие, почитать про них -нужно [тут](https://docs.djangoproject.com/en/3.1/ref/forms/fields/#built-in-field-classes) +нужно [тут](https://docs.djangoproject.com/en/4.2/ref/forms/fields/#built-in-field-classes) -У полей формы есть такое понятие как виджет, он отвечает за то, как именно будет отображаться конкретный филд, например, -для текста базово это текстовое поле, а для даты и времени, это встроенный пикер (выпадающее окно с календарём и часами) -итд. +У полей формы есть такое понятие как виджет. Он отвечает за то, как именно будет отображаться конкретное поле, например, +для текста - это текстовое поле, а для даты и времени - это встроенный пикер (выпадающее окно с календарём и часами) +и т. д. Виджет можно указать отличающийся от стандартного. -Прочитать про виджеты нужно [тут](https://docs.djangoproject.com/en/3.1/ref/forms/widgets/#built-in-widgets). +Прочитать про виджеты нужно [тут](https://docs.djangoproject.com/en/4.2/ref/forms/widgets/#built-in-widgets). -К каждому полю, мы можем указать дополнительные аттрибуты +Каждому полю мы можем указать дополнительные аттрибуты: -required - является ли поле обязательным +`required - является ли поле обязательным -label - лейбл, текст к инпуту +`label`- лейбл, подпись к инпуту -label_suffix - символ между лейблом и инпутом +`label_suffix` - символ между `label` и инпутом -initial - значение по умолчанию +`initial` - значение по умолчанию -widget - читай выше +`widget` - читай выше -help_text - подсказка к инпуту +`help_text` - подсказка к инпуту -error_messages - переписать стандартные тексты для ошибок типов полей +`error_messages` - переписать стандартные тексты для ошибок типов полей -validators - дополнительные проверки поля +`validators` - дополнительные проверки поля -localize - информация о переводе формы на другие языки +`localize` - информация о переводе формы на другие языки -disabled - сделать поле не активным (без возможности изменения) +`disabled` - сделать поле не активным (без возможности изменения) ### Описание view -В переменной request хранится информация о том, какой именно тип запроса к нам пришел, а это значит, что простым if мы -можем разграничить логику которая будет обрабатывать разные типы запросов. +В переменной `request` хранится информация о том, какой именно тип запроса к нам пришел, а это значит, что простым if мы +можем разграничить логику, которая будет обрабатывать разные типы запросов. Если мы просто открываем страницу в браузере, то на самом деле мы посылаем обыкновенный `GET` запрос. -Взглянем на код. При `GET` запросе, мы не попадаем в первое условие, переменной `form` назначаем объект класса `MyForm` -без каких либо данных, и после этого рендерим страницу передав на страницу пустой объект класса формы. +Взглянем на код. При `GET` запросе мы не попадаем в первое условие, переменной `form` назначаем объект класса `MyForm` +без каких-либо данных, и после этого рендерим страницу, передав на страницу пустой объект класса формы. -При рендере объекта класса формы в шаблоне, этот объект преобразуется в набор инпутов, с уже указанными +При рендере объекта класса формы в шаблоне этот объект преобразуется в набор инпутов с уже указанными атрибутами `name` ![](https://djangoalevel.s3.eu-central-1.amazonaws.com/lesson32/django_form_get.png) -Если мы заполним данные, и нажмём на кнопку `Send form`, то мы отправим по этому же урлу запрос, но уже типа `POST` с -заполнеными данными. +Если мы заполним данные и нажмём на кнопку `Send form`, то мы отправим по этому же url запрос, но уже типа `POST` с +заполненными данными. -Посмотрим в код еще раз, мы попадём в первый if, и переменной `form` назначим объект класса `MyFrom`, но предварительно, +Посмотрим в код еще раз, мы попадём в первый if и переменной `form` назначим объект класса `MyFrom`, но предварительно передав туда данные через `request.POST`. -А значит на этом этапе у нас есть объект, с данными переданными нам от клиента. +А значит на этом этапе у нас есть объект с данными, переданными нам от клиента. -Данные которые мы получили из реквеста всегда нужно валидировать (проверять) +Данные, которые мы получили из реквеста, всегда нужно валидировать (проверять). ## Валидация формы -[Тут](https://docs.djangoproject.com/en/3.1/ref/forms/validation/) вся дока по валидации. +[Тут](https://docs.djangoproject.com/en/4.2/ref/forms/validation/) вся дока по валидации. За валидацию данных в форме отвечает встроенный метод `is_valid()` который применяется к объекту класса формы. -Этот метод возвращает нам булевое значение, True если данные валидны, False если нет. +Этот метод возвращает нам булево значение: `True`, если данные валидны, `False`, если нет. -После вызова этого метода у переменной к которой он был вызван (в нашем случае переменная `form`) появляются -дополнительные атрибуты +После вызова этого метода у переменной, к которой он был вызван (в нашем случае переменная `form`), появляются +дополнительные атрибуты. -Если форма была валидна, то появляется дополнительный аттрибут `cleaned_data`, это словарь, в котором будет хранятся все -данные которые прислал нам пользователь (например логин и пароль) +Если форма валидна, то появляется дополнительный аттрибут `cleaned_data` - это словарь, в котором хранятся все +данные, присланные нам пользователем (например, логин и пароль). -Если форма не была валидна, то появляется дополнительные аттрибут `errors`, который хранит в себе информацию об ошибках -конкретных полей, или общих ошибках. +Если форма не валидна, то появляется дополнительные аттрибут `errors`, который хранит в себе информацию об ошибках +конкретных полей или общих ошибках. -Этот атрибут сразу хранит информацию как отображать эти ошибки в шаблоне, если они существуют +Этот атрибут сразу хранит информацию о том, как отображать эти ошибки в шаблоне, если они существуют. ### Валидность -Что же вообще такое валидность? +Что же такое валидность? -Валидность это соответсвие заданным критериям. Например, если мы ожидаем, что нам пришлют возраст, и мы ожидаем цифру, -но нам присылают букву, это значит, что данные не валидны. +Валидность - это соответствие заданным критериям. Например, если мы ожидаем в поле возраста получить числовой тип, а +пользователь отправляет текст, то данные не валидны. -Некоторые распространённые виды валидаций можно указать как атрибут поля формы, например, максимальную длинну для -строки, или максимальное и минимальное значение для числа. +Некоторые распространённые виды валидаций можно указать как атрибут поля формы, например, максимальную длину для +строки, максимальное и минимальное значение для числа. -#### clean_`field` +#### clean_`field`() -Если мы вызываем метод `is_valid()` мы проверяем все описанные валидации. Но где они описаны, и можем ли мы добавить +Если мы вызываем метод `is_valid()`, мы проверяем все описанные валидации. Но где они описаны, и можем ли мы добавить свои? -Описаны они в классе формы, и да мы можем добавить свои. +Описаны они в классе формы, и да, мы можем добавить свои. Все базовые валидации были описаны при создании полей. -Но допустим мы считаем что для нашей формы валидным является только чётный возраст, как нам это проверить? +Но допустим, что мы считаем, что для нашей формы валидным является только чётный возраст, как нам это проверить? -Для проверки конкретного поля, в форме класса нужно указать, метод который будет начинатся со слова `clean_` и после +Для проверки конкретного поля в форме класса нужно указать метод, который будет начинаться со слова `clean_` и после этого название поля, которое мы валидируем. Все данные будут лежать в аттрибуте `self.cleaned_data`. Если значение валидно, то метод должен возвращать значение этого аттрибута. -Если значение не валидно, то метод должен рейзить ошибку `ValidationError` с описанием ошибки, которая позже будет -отображаться на html. +Если значение не валидно, то метод должен возбуждать ошибку `ValidationError` с описанием ошибки, которая позже будет +отображаться в `html`. В forms.py: @@ -456,18 +456,18 @@ class MyForm(forms.Form): def clean_age(self): age = self.cleaned_data.get('age') if not age % 2: - raise ValidationError('Age should be odd') + raise ValidationError('Age should be even.') return age ``` -#### clean +#### clean() -А что делать если нужно проверить соответствие данных между собой, например, что пользователь не использовал свой -возраст как часть своего никнейма? +А что делать если нужно проверить соответствие данных между собой? Например, что пользователь не использовал свой +возраст, как часть своего никнейма? -Для этого мы можем использовать метод `clean` в котором можем выполнить любые необходимые нам проверки. +Для этого мы можем использовать метод `clean()`, в котором можем выполнить все необходимые нам проверки. -Для выполнения всех базовых проверок обычно используется `super` +Для выполнения всех базовых проверок обычно используется `super()`. В forms.py @@ -483,7 +483,7 @@ class MyForm(forms.Form): def clean_age(self): age = self.cleaned_data.get('age') if not age % 2: - raise ValidationError('Age should be odd') + raise ValidationError('Age should be even.') return age def clean(self): @@ -491,16 +491,16 @@ class MyForm(forms.Form): age = cleaned_data.get('age') nickname = cleaned_data.get('nickname') if str(age) in nickname: - raise ValidationError('Age cannot be in nickname') + raise ValidationError('Age can\'t be in nickname') ``` -Метод `clean` ничего не возвращает, это нормально :) +Метод `clean()` ничего не возвращает, это нормально :) Если при проверке у вас может быть больше одной ошибки, то `raise` вам не подходит. -Для этого может использоваться метод класса формы `add_error`, принимает два параметра, название поля к которому -относится ошибка (может быть None, если ошибка не относится к какому либо полю, например неправильный юзернейм и/или -пароль. +Для этого может использоваться метод класса формы `add_error()`. Он принимает два параметра: название поля, к которому +относится ошибка (может быть None, если ошибка не относится к какому-либо полю), и сообщение, например, неправильные +имя пользователя и/или пароль. В forms.py @@ -516,7 +516,7 @@ class MyForm(forms.Form): def clean_age(self): age = self.cleaned_data.get('age') if not age % 2: - raise ValidationError('Age should be odd') + raise ValidationError('Age should be even') return age def clean(self): @@ -524,32 +524,32 @@ class MyForm(forms.Form): age = cleaned_data.get('age') nickname = cleaned_data.get('nickname') if str(age) in nickname: - self.add_error('age', 'Age cannot be in nickname') - self.add_error(None, 'This form always incorrect') + self.add_error('age', 'Age can\'t be in nickname') + self.add_error(None, 'This form is always incorrect') ``` -### Отображение формы в шаблоне. +### Отображение формы в шаблоне -Итак, если наша форма была валидна, то мы отрендерили вообще другую страницу, но если всё таки была не валидна, то мы +Итак, если наша форма была валидна, то мы отрендерили вообще другую страницу, но если всё-таки была не валидна, то мы отрендерим форму, у которой есть атрибут `errors`, ошибки сразу же будут отрисованы. -Так же у нас есть способы по разному отрисовывать формы: +Также у нас есть способы по разному отрисовывать формы: -У объекта формы есть стандартные поля и методы, которые мы можем указывать в шаблоне, например. +У объекта формы есть стандартные поля и методы, которые мы можем указывать в шаблоне, например: -{{ form.as_table }} - рендер в виде таблицы, через теги +`{{ form.as_table }}` - рендер в виде таблицы, через теги -{{ form.as_p }} рендер каждого поля через

теги +`{{ form.as_p }}` - рендер каждого поля через теги

-{{ form.as_ul }} рендер в виде списка через

  • теги +`{{ form.as_ul }}` - рендер в виде списка через теги
  • -Так-же, можно рендерить форму не целиком, а например, по отдельным филдам, при помощи стандартного обращения через -точку, например `{{ form.name }}` +Также можно рендерить форму не целиком, а, например, по отдельным полям, при помощи стандартного обращения через +точку: `{{ form.name }}`. -У каждого поля есть аттрибут `errors` который хранит информацию об ошибках по этому полю, если они были -обнаружены. `{{ form.my_field.errors }}` +У каждого поля есть аттрибут `errors`, который хранит информацию об ошибках по этому полю, если они были +обнаружены: `{{ form.my_field.errors }}`. -Если запустить форму через for в итерируемом объекте будут поля. +Если запустить форму через `for` в итерируемом объекте будут поля. ```html {% for field in form %} @@ -564,15 +564,15 @@ class MyForm(forms.Form): ``` И многие другие атрибуты и методы, подробно можно -прочитать [тут](https://docs.djangoproject.com/en/3.1/topics/forms/#working-with-form-templates) +прочитать [тут](https://docs.djangoproject.com/en/4.2/topics/forms/#working-with-form-templates) ## Методы и свойства модели User ## Модель User -Django предоставляет нам встроенную модель юзера, у которой уже реализовано много полей и методов +Django предоставляет нам встроенную модель юзера, у которой уже реализовано много полей и методов. -Подробнейшая инфа про юзера [Тут](https://docs.djangoproject.com/en/3.1/topics/auth/default/) +Подробнейшая информация про юзера [тут](https://docs.djangoproject.com/en/4.2/topics/auth/default/) Стандартный юзер содержит в себе такие полезные поля как: @@ -586,9 +586,9 @@ last_name Также содержит много системных полей. -Также содержит базовый метод set_password и информацию о группах доступа. +Также содержит базовый метод `set_password()` и информацию о группах доступа. -Для использования модели пользователя, которую нам нужно расширить (а это нужно почти всегда) используется наследование +Для использования модели пользователя, которую нам нужно расширить (а это нужно почти всегда), используется наследование от базового абстрактного юзера. Выглядит примерно так: @@ -603,30 +603,30 @@ class MyUser(AbstractUser): avatar = models.ImageField(blank=True, null=True) ``` -Для того, что бы Django оценивала эту модель как пользователя, в `settings.py` нужно в любом месте указать: +Чтобы Django оценивала эту модель как пользователя в `settings.py` нужно в любом месте указать: ```python AUTH_USER_MODEL = 'myapp.MyUser' ``` -Где myapp - название приложения, MyUser - название модели. +Где `myapp` - название приложения, `MyUser` - название модели. -Юзер `обязательно` должен быть описан до первой миграции!! Иначе джанго автоматически будет использовать базового -встроенного юзера, и использовать сразу несколько у вас не получится. Так как по дефолту, если этой переменной нет, то -django считает, что там указанна ссылка на базового юзера, и создаёт таблицу юзера основываясь на базовом юзере, +Юзер `обязательно` должен быть описан до первой миграции!! Иначе Django автоматически будет использовать базового +встроенного юзера, и использовать сразу несколько юзеров у вас не получится. Так как по дефолту, если этой переменной +нет, то Django считает, что там указанна ссылка на базового юзера, и создаёт таблицу юзера, основываясь на базовом юзере, поменять такую таблицу нельзя. Все возможные подробности про модель -юзера [Тут](https://docs.djangoproject.com/en/3.1/ref/contrib/auth/#django.contrib.auth.models.User) +юзера [тут](https://docs.djangoproject.com/en/4.2/ref/contrib/auth/#django.contrib.auth.models.User) -Кроме стандартных полей юзер содержит в себе информацию о группах которых состоит пользователь, о пользовательских -правах, содержит два поля статуса `is_staff` и `is_superuser` чаще всего стафф это сотрудника которым можно в админку, -но у них ограниченные права, суперюзеру можно всё, но всегда зависит от ситуации. +Кроме стандартных полей юзер содержит в себе информацию о группах, в которых состоит пользователь, о пользовательских +правах, два поля статуса `is_staff` и `is_superuser` (чаще всего стафф - это сотрудник, которым можно в админку, +но у них ограниченные права, суперюзеру можно всё, но всегда зависит от ситуации). -`is_stuff` - поле для определения сотрудника (допустим сотрудник магазина, который добавляет товары) +`is_stuff` - поле для определения сотрудника (допустим, сотрудник магазина, который добавляет товары) `is_superuser` - поле для определения администратора (например, может изменять список сотрудников, а чаще всего обладает -практически не ограниченными параметрами) +практически неограниченными параметрами) Также хранит инфо о последнем логине пользователя и дате создания пользователя. @@ -637,7 +637,7 @@ get_username() # получить юзернейм get_full_name() # получить имя и фамилию через пробел -set_password(raw_password) # установить хешированый и подсоленный пароль +set_password(raw_password) # установить хешированный пароль check_password(raw_password) # проверить пароль на правильность @@ -653,7 +653,7 @@ u = User.objects.get(username='blabla') u.check_password('some_cool_password') # True ``` -И другие методы, отвечающие за доступы, группы итд. +И другие методы, отвечающие за доступы, группы и т. д. ### Менеджер юзера @@ -663,22 +663,22 @@ u.check_password('some_cool_password') # True `create_superuser(username, email, password, **extra_fields)` -`create_user` отличается от `create` тем, что `create_user` правильно задаст пароль, через `set_password` +`create_user()` отличается от `create()` тем, что `create_user()` правильно задаст пароль через `set_password()` -** Мы не храним пароль в чистом виде, только хешированым ** +** Мы не храним пароль в чистом виде, только хешированным. ** ## Логин -На самом деле логин состоит из нескольких частей, давайте их рассмотрим +На самом деле логин состоит из нескольких частей, давайте их рассмотрим. -### Аутентификация, Идентификация, Авторизация +### Аутентификация, идентификация, авторизация Аутентификация - процесс проверки подлинности доступа. Например, проверить логин и пароль на соответствие. -Если говорить о бытовом примере, то когда вы проходите на любую проходную, например заходя в университет, вы должны -предъявить студенческий. То что он у вас есть и есть процесс аутентификации. +Если говорить о бытовом примере, то когда вы проходите на любую проходную, например, заходя в университет, вы должны +предъявить студенческий. То, что он у вас есть и есть процесс аутентификации. Идентификация - процесс определения конкретного лица. @@ -689,22 +689,21 @@ u.check_password('some_cool_password') # True Авторизация - процесс предоставления доступа. -Охранник нас пропустит. +Охранник вас пропустит. -### Как это работает +### Как это работает? -Для того, что бы пользователь мог авторизоваться на сайте, нам нужны его входные данные и стандартные -методы `authenticate, login` +Чтобы пользователь мог авторизоваться на сайте, нам нужны его входные данные и стандартные методы `authenticate, login` -Метод `authenticate` отвечает сразу за два процесса, аутентификации и идентификации, принимает, имя пользователя и -пароль, и если находит совпадение то, возвращает объект пользователя(модели), если не находит, то возвращает `None`. +Метод `authenticate` отвечает сразу за два процесса: аутентификацию и идентификацию. Он принимает имя пользователя и +пароль, и если находит совпадение, то возвращает объект пользователя(модели), если не находит, то возвращает `None`. -Если нам вернулся объект юзера, значит что аутентификация пройдена, и пользователь идентифицирован. +Если нам вернулся объект юзера, значит, аутентификация пройдена, и пользователь идентифицирован. -Метод `login` принимает, реквест, и объект модели пользователя и отвечает за процесс авторизации, после этого действия, -во всех следующих запросах, в переменной `request` будет хранится наш текущий пользователь. +Метод `login` принимает реквест и объект модели пользователя и отвечает за процесс авторизации, после этого действия +во всех следующих запросах в переменной `request` будет храниться наш текущий пользователь. -По этому стандартным способом, для авторизации является примерно такой код: +Поэтому стандартным способом авторизации является примерно такой код: В forms.py @@ -739,7 +738,7 @@ def my_login(request): if request.method == 'POST': # create a form instance and populate it with data from the request: form = AuthenticationForm(request.POST) - # check whether it's valid: + # check validity: if form.is_valid(): # process the data in form.cleaned_data as required # some actions @@ -755,7 +754,7 @@ def my_login(request): ### Logout -Для вывода пользователя из системы, используется метод `logout` который принимает только реквест. +Для вывода пользователя из системы используется метод `logout`, который принимает только реквест. ```python from django.contrib.auth import logout @@ -768,17 +767,17 @@ def logout_view(request): ### Проверка на то, что пользователь уже зашел в систему -В реквесте всегда есть поле `user`, у которого всегда есть аттрибут `is_authenticated` проверяя его, мы можем определять -является ли пользователь авторизированным +В реквесте всегда есть поле `user`, у которого всегда есть аттрибут `is_authenticated`, проверяя его, мы можем +определять является ли пользователь авторизированным. ```python request.user.is_authenticated ``` -### Закрыть страницу от не залогиненего пользователя +### Закрыть страницу от незалогиненного пользователя -Для того, что бы не предоставлять доступ, для не залогиненых пользователей, существует два способа, для функционально -описанных вью это декоратор `@login_required` +Чтобы не предоставлять доступ незалогиненным пользователям, существует два способа: для функционально +описанных views - это декоратор `@login_required` ```python from django.contrib.auth.decorators import login_required @@ -789,7 +788,7 @@ def my_view(request): ... ``` -Он так-же может принимать ссылку, на страницу логина, и автоматически отправлять на эту страницу, для не залогиненного +Он также может принимать ссылку на страницу логина и автоматически отправлять на эту страницу незалогиненного пользователя. ```python @@ -803,11 +802,11 @@ def my_view(request): # Практика / Домашка: -1. Пишем страницы для логина и для регистрации (на каждой из них должна быть ссылка на другую) -2. Если пользователь не залогинен, то его должно перебрасывать на страницу с логином +1. Пишем страницы для логина и для регистрации (на каждой из них должна быть ссылка на другую). +2. Если пользователь не залогинен, то его должно перебрасывать на страницу с логином. 3. Добавляем в верхнюю часть главной страницы перечисление существующих топиков. При нажатии на которые мы должны видеть - отфильтрованый список блогов, только относящихся к выбранному топику (гет запрос) -4. Добавляем строку для поиска по блогам. После поиска должны отображаться посты в названии которых есть частичное - совпадение без учета регистра с искомыми данными.(гет форма) + отфильтрованный список блогов, относящихся только к выбранному топику (GET запрос). +4. Добавляем строку для поиска по блогам. После поиска должны отображаться посты, в названии которых есть частичное + совпадение без учета регистра с искомыми данными. (GET форма) 5. Добавляем возможность создания поста. -6. На странице с деталями поста добавляем возможность писать комментарии. \ No newline at end of file +6. На странице с деталями поста добавляем возможность писать комментарии. From bfb52f4b0cbe9e1d23bde8785215dea98dadbbff Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Mon, 24 Jul 2023 14:06:26 +0300 Subject: [PATCH 06/23] edit grammar and style; update links --- lesson33.md | 300 ++++++++++++++++++++++++++-------------------------- 1 file changed, 150 insertions(+), 150 deletions(-) diff --git a/lesson33.md b/lesson33.md index f971e4e8a..affff086f 100644 --- a/lesson33.md +++ b/lesson33.md @@ -4,9 +4,9 @@ ## ModelForm -Дока [Тут](https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/) +Дока [Тут](https://docs.djangoproject.com/en/4.2/topics/forms/modelforms/) -ModelForm - это тип формы который генерируется напрямую из модели. Это очень мощный инструмент работы с моделями. +ModelForm - это тип формы, который генерируется напрямую из модели. Это очень мощный инструмент работы с моделями. `models.py` @@ -52,9 +52,9 @@ class BookForm(ModelForm): fields = ['name', 'authors'] ``` -Поле `fields` либо `exclude` являются обязательными +Поле `fields` или поле `exclude` являются обязательными. -Такой вид форм, равнозначен с +Такой вид форм эквивалентен обычной форме с объявлением всех полей: `forms.py` @@ -78,19 +78,19 @@ class BookForm(forms.Form): ### Валидация -Помимо стандартного метода `is_valid()` у моделформы существует так же встроенный метод `full_clean()` +Помимо стандартного метода `is_valid()` у ModelForm существует также встроенный метод `full_clean()` Если первый отвечает за валидацию формы, то второй отвечает за валидацию для объекта модели. ### Метод save() у формы -Метод save в ModelForm объекте выполняет по сути два действия, получает объект модели основываясь на переданных данных, -и вызывает метод `save`, но уже для модели. +Метод `save()` в ModelForm объекте выполняет по сути два действия: получает объект модели, основываясь на переданных +данных, и вызывает метод `save()`, но уже для модели. -Метод `save()` может принимать аргумент `commit`, по умолчанию `True`. Если указать `commit` как False, метод `save` -модели не будет вызван (объект не будет сохранён в базу), будет только создан предварительный объект модели, -используется, когда нужно "дополнить" данные перед сохранением в базу. Очень часто используется! Например добавление -пользователя из request. +Метод `save()` может принимать аргумент `commit`, по умолчанию - `True`. Если указать `commit=False`, метод `save()` +модели не будет вызван (объект не будет сохранён в базу), будет только создан предварительный объект модели. Такой +подход используется, когда нужно "дополнить" данные перед сохранением в базу. Очень часто используется! Например, +добавление пользователя из `request`. ```python form = PartialAuthorForm(request.POST) @@ -120,59 +120,59 @@ f = ArticleForm(request.POST, instance=a) f.save() ``` -Так как мы можем не только создавать новый инстанс, но и обновлять существующий. +По аналогии, мы можем не только создавать новый объект, но и обновлять существующий. -# Class Base View +# Class-Based View -С этого момента мы переходим на использование `view` основанных исключительно на классах. +С этого момента мы переходим на использование `view`, основанных исключительно на классах. Все основные существующие классы описаны [Тут](https://ccbv.co.uk/) ## Class View -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.base/View/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.base/View/) -Основой всех классов используемых во `view` является класс `View`, методы этого класса используются всеми остальными +Основой всех классов, используемых во `view`, является класс `View`. Методы этого класса используются всеми остальными классами. -Основные аттрибуты: +Основные атрибуты: ```http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']``` -Атрибут который нужен, что бы определить какие виды запросов будут доступны для запросов. +Этот атрибут нужен для определения того, какие виды HTTP методов будут доступны для запросов. Основные функции: -`as_view` - метод который всегда вызывается что-бы использовать класс в `urls.py`, внутри вызывает методы `setup` +`as_view()` - метод который всегда вызывается, чтобы использовать класс в `urls.py`, внутри вызывает методы `setup` и `dispatch` -`setup` - метод, который добавляет `request` в `self`, благодаря чему `request` будет доступен в абсолютно любом методе +`setup()` - метод, который добавляет `request` в `self`, благодаря чему `request` будет доступен в абсолютно любом методе всех наших `view` классов -`http_method_not_allowed` - метод, который генерирует ошибку запроса (не могу обработать, например, POST запрос. +`http_method_not_allowed()` - метод, который генерирует ошибку запроса (не могу обработать, например, POST запрос). -`dispatch` - метод отвечающий за вызов обработчика при запросе. +`dispatch()` - метод, отвечающий за вызов обработчика при запросе. ```python def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. - if request.method.lower() in self.http_method_names: # Если запрос находится в списке разрешенных то заходим. + if request.method.lower() in self.http_method_names: # Если запрос находится в списке разрешенных, то заходим. handler = getattr(self, request.method.lower(), - self.http_method_not_allowed) # Пытаемся из self получить аттрибут или метод совпадающий названием с методом запроса (POST - post, GET - get), если не получается, то вернуть метод http_method_not_allowed + self.http_method_not_allowed) # Пытаемся из self получить атрибут или метод, совпадающий названием с методом запроса (POST - post, GET - get), если не получается, то вернуть метод http_method_not_allowed else: handler = self.http_method_not_allowed # Вернуть метод http_method_not_allowed return handler(request, *args, - **kwargs) # Вызвать метод который мы получили ранее, если удалось, то, например, get(), или post(), если нет, то http_method_not_allowed() + **kwargs) # Вызвать метод, который мы получили ранее, если удалось, то, например, get(), или post(), если нет, то http_method_not_allowed() ``` Как это работает? -Если наследоваться от этого класса, то мы можем описать функцию `get` и/или `post` что бы описать что необходимо делать -при запросе методами `GET` или `POST` +Если наследоваться от этого класса, то мы можем описать функцию `get` и/или `post`, чтобы описать, что необходимо делать +при запросе методами `GET` или `POST`. -И можем описать какие вообще запросы мы ожидаем принимать в аттрибуте `http_method_names` +И можем описать, какие вообще запросы мы ожидаем принимать в аттрибуте `http_method_names` Например: @@ -201,25 +201,25 @@ path('some-url/', MyView.as_view(), name='some-name') Чем такая конструкция лучше, чем обычная функция? Тем, что обычная функция обязана принимать любой запрос и дальше только при помощи `if` разделять разные запросы. -Такой класс, будет принимать только запросы описанных методов, отклоняя все остальные, и каждый запрос будет написан в +Такой класс будет принимать только запросы описанных методов, отклоняя все остальные, и каждый запрос будет написан в отдельном методе, что сильно улучшает читабельность кода. ## Class TemplateView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.base/TemplateView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.base/TemplateView/) -Класс необходимый для рендера html файлов +Класс, необходимый для рендера html файлов Основные атрибуты: ```python -template_name = None # Имя html файла который нужно рендерить +template_name = None # Имя html файла, который нужно рендерить extra_content = None # Словарь с контентом ``` Основные методы: -Описан метод `get` +Описан метод `get()` ```python def get(self, request, *args, **kwargs): @@ -227,7 +227,7 @@ def get(self, request, *args, **kwargs): return self.render_to_response(context) ``` -`get_context_data` - Метод возвращающий данные которые будут добавлены в контекст +`get_context_data()` - метод, возвращающий данные, которые будут добавлены в контекст Как этим пользоваться? @@ -246,7 +246,7 @@ class HomePageView(TemplateView): return context ``` -Мы описали класс, который будет рендерить файл `home.html`, в контексте которого будет переменная `latest_articles` в +Мы описали класс, который будет рендерить файл `home.html`, в контексте которого будет переменная `latest_articles`, в которой будет коллекция из объектов модели. То же самое можно было сделать через `extra_context`: @@ -264,28 +264,28 @@ class HomePageView(TemplateView): ## Class RedirectView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.base/RedirectView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.base/RedirectView/) -Класс необходимый, что бы перенаправлять запросы с одного url на другой. +Класс, необходимый для перенаправления запросов с одного URL на другой. Основные атрибуты: ```python -query_string = False # сохранить ли квери параметры (то что в строке браузера после ?) при редиректе -url = None # Урл на который надо перейти -pattern_name = None # Имя урла, на который надо перейти +query_string = False # сохранить ли квери параметры (то, что в строке браузера после ?) при редиректе +url = None # URL, на который надо перейти +pattern_name = None # Имя URL, на который надо перейти ``` Основные методы: -Описаны все HTTP методы, и все они ссылаются на `get`, например `delete`: +Описаны все HTTP методы, и все они ссылаются на `get()`, например, `delete()`: ```python def delete(self, request, *args, **kwargs): return self.get(request, *args, **kwargs) ``` -Метод `get_redirect_url` отвечает за то, что бы получить url на который надо перейти +Метод `get_redirect_url()` отвечает за то, чтобы получить URL, на который надо перейти Как пользоваться @@ -297,11 +297,11 @@ class ArticleRedirectView(RedirectView): ## Class DetailView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.detail/DetailView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.detail/DetailView/) -Класс, который необходим для того что бы сделать страницу для просмотра одного объекта. +Класс, который необходим для того, чтобы сделать страницу для просмотра одного объекта. -Ему необходимо передать `pk` либо `slug` и это позволит отобразить один объект (статью, товар итд.) +Ему необходимо передать `pk` либо `slug`, и это позволит отобразить один объект (статью, товар и т. д.) Как этим пользоваться? @@ -336,16 +336,16 @@ urlpatterns = [ ] ``` -Этого уже достаточно что бы отрисовать страницу деталей объекта. Если `template_name` не указан явно, то Django, будет -пытаться отобразить `templates/app_name/model_detail.html` где `app_name` - название приложения, `model` - название +Этого уже достаточно, чтобы отрисовать страницу деталей объекта. Если `template_name` не указан явно, то Django будет +пытаться отобразить `templates/app_name/model_detail.html`, где `app_name` - название приложения, `model` - название модели, `detail` - константа. -В контекст будет передана переменная `object`. Методы `post`, `put` итд. не определены. +В контекст будет передана переменная `object`. Методы `post`, `put` и т. д. не определены. Важные параметры. ```python -pk_url_kwarg = 'pk' # Как переменная называется в urls.py, например `` +pk_url_kwarg = 'pk' # Как переменная называется в urls.py, например, `` queryset = None # если указан, то возможность ограничить доступ только для части объектов (например, убрать из возможности обновления деактивированные объекты). template_name = None # указать имя шаблона. model = None # класс модели, если не указан queryset, сгенерирует queryset из модели. @@ -353,15 +353,15 @@ model = None # класс модели, если не указан queryset, с Важные методы: -`get_queryset` - переопределить queryset +`get_queryset()` - переопределить queryset -`get_context_data` - тоже что и у TemplateView +`get_context_data()` - то же, что и у TemplateView -`get_object` - определяет логику получения объекта +`get_object()` - определяет логику получения объекта ## Class ListView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.list/ListView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.list/ListView/) Класс, необходимый для отображения списка объектов. @@ -378,18 +378,19 @@ class CommentListView(ListView): ### Пагинация -[Дока](https://docs.djangoproject.com/en/3.1/topics/pagination/) +[Дока](https://docs.djangoproject.com/en/4.2/topics/pagination/) -Очень часто наше приложение хранит большое кол-во данных, и при отображении нам не нужно показывать прям всё (допустим у -нас блог на 1000000000 статей), для выдачи данных порциями, это и называется пагинация, или разбиение на страницы. +Очень часто наше приложение хранит большое количество данных, и при отображении нам не нужно показывать прям всё +(допустим у нас блог на 1 000 000 000 статей). Логично отдавать данные порциями - это и называется пагинация, +или разбиение на страницы. -Когда вы листаете ленту, на самом деле, вы подгружаете всё новые и новые страницы, просто при помощи JSa это сделано +При прокрутке ленты в соцсети вы подгружаете всё новые и новые страницы, просто при помощи JavaScript это сделано так, что вы этого не замечаете. За это отвечает параметр: ```python -paginate_by = None # можно указать сколько должно быть объектов на одной странице +paginate_by = None # можно указать, сколько должно быть объектов на одной странице ``` В шаблон будут переданы как список объектов, так и данные по пагинации @@ -398,47 +399,47 @@ paginate_by = None # можно указать сколько должно бы context = { 'paginator': paginator, # объект класса пагинации, хранит все подробности, которые только могут быть. 'page_obj': page, - # информация о текущей странице, какая это страница, сколько всего страниц, урл на следующую и предыдущую страницу + # информация о текущей странице, какая это страница, сколько всего страниц, URL на следующую и предыдущую страницу 'is_paginated': is_paginated, # были ли данные вообще пагинированы, возможно у вас 10 объектов на страницу, а их всего 5, тогда нет смысла в пагинации - 'object_list': queryset # Сам кверисет с объектами + 'object_list': queryset # Сам queryset с объектами } ``` -Важные параметры, такие же как у DetailView, и еще новые +Важные параметры такие же как у DetailView и еще новые ```python allow_empty = True # разрешить ли отображение пустого списка -ordering = None # явно указать ордеринг +ordering = None # явно указать порядок сортировки ``` -Всё еще описан только метод `get`. Методы `post`, `put` итд не разрешены. +Всё еще описан только метод `get()`. Методы `post()`, `put()` и т. д. не разрешены. Важные методы: -`get_queryset` - переопределить queryset +`get_queryset()` - переопределить queryset -`get_context_data` - тоже что и у TemplateView +`get_context_data()` - то же, что и у TemplateView -`get_paginator` - определяет логику получения класса пагинатора +`get_paginator()` - определяет логику получения класса Paginator -`get_paginate_by` - определяет логику получения значение paginate_by +`get_paginate_by()` - определяет логику получения значение paginate_by -`get_ordering` - определяет логику получения ордеринга +`get_ordering()` - определяет логику получения переменной ordering -`get_allow_empty` - определяет логику получения переменной allow_empty +`get_allow_empty()` - определяет логику получения переменной allow_empty ## Class FormView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/FormView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.edit/FormView/) Не все классы предназначены только для чтения данных. -FormView класс необходимый для обработки формы. +FormView класс необходим для обработки формы. Как пользоваться? -В forms.py +В `forms.py`: ```python from django import forms @@ -453,7 +454,7 @@ class ContactForm(forms.Form): pass ``` -Во views.py +Во `views.py`: ```python from myapp.forms import ContactForm @@ -472,7 +473,7 @@ class ContactView(FormView): return super().form_valid(form) ``` -В contact.html: +В `contact.html`: ```html @@ -484,17 +485,17 @@ class ContactView(FormView): Важные параметры: -Такие же, как у TemplateView и еще свои +Такие же, как у TemplateView, и еще свои ```python -form_class = None # сам класс формы -success_url = None # На какую страницу перейти если форма была валидна -initial = {} # Словарь с базовыми значениями формы +form_class = None # сам класс формы +success_url = None # на какую страницу перейти, если форма была валидна +initial = {} # словарь с базовыми значениями формы ``` Важные методы: -Тут наконец определён метод `post`: +Тут наконец определён метод `post()`: ```python def post(self, request, *args, **kwargs): @@ -511,27 +512,27 @@ def post(self, request, *args, **kwargs): Так же все методы из TemplateView -`get_context_data` - дополнительно добавляет переменную `form` в темплейт +`get_context_data()` - дополнительно добавляет переменную `form` в темплейт -`get_form` - получить объект формы +`get_form()` - получить объект формы -`get_form_class` - получить класс формы +`get_form_class()` - получить класс формы -`form_valid` - Что делать если форма валидна +`form_valid()` - что делать, если форма валидна -`form_invalid` - Что делать если форма не валидна +`form_invalid()` - что делать, если форма не валидна -`get_success_url` - Переопределить генерацию урла на который будет совершен переход если форма валидна +`get_success_url()` - переопределить генерацию URL, на который будет совершен переход, если форма валидна ## Class CreateView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/CreateView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.edit/CreateView/) Класс для создания объектов. Как этим пользоваться? -Во views.py +Во `views.py` ```python from django.views.generic.edit import CreateView @@ -544,7 +545,7 @@ class AuthorCreate(CreateView): fields = ['name'] ``` -В author_create.html +В `author_create.html`: ```html @@ -554,38 +555,38 @@ class AuthorCreate(CreateView): ``` -В класс нужно передать либо ModelForm, либо модель и поля, что бы класс сам сгенерировал такую форму. +В класс нужно передать либо ModelForm, либо модель и поля, чтобы класс сам сгенерировал такую форму. -Метод `get` откроет страницу, на которой будет переменая `form`, как и другие view, к которым добавляется форма. +Метод `get()` откроет страницу, на которой будет переменная `form`, как и другие view, к которым добавляется форма. -Метод `post` выполнит те же действия, что и FormView, но в случае валидности формы, предварительно +Метод `post()` выполнит те же действия, что и FormView, но в случае валидности формы предварительно выполнит `form.save()` Важные параметры: -Такие же, как у FormView и еще свои +Такие же, как у FormView, и еще свои. ```python form_class = None # Должен принимать ModelForm -model = None # Можно указать модель вместо формы, что бы сгенерировать её на ходу -fields = None # Поля модели, если не указана форма +model = None # Можно указать модель вместо формы, чтобы сгенерировать её на ходу +fields = None # Поля модели, если не указана форма ``` Важные методы: -Все методы из FormView, но дополненные под создание объкта: +Все методы из FormView, но дополненные под создание объекта: -`post` - предварительно добавит классу атрибут `self.object = None` +`post()` - предварительно добавит классу атрибут `self.object = None` -`form_valid` - дополнительно выполнит такую строку `self.object = form.save()` +`form_valid()` - дополнительно выполнит такую строку `self.object = form.save()` # Class UpdateView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/UpdateView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.edit/UpdateView/) -Класс для обновления объекта, как пользоваться: +Класс для обновления объекта. Как пользоваться? -Во views.py: +Во `views.py`: ```python from django.views.generic.edit import UpdateView @@ -598,7 +599,7 @@ class AuthorUpdate(UpdateView): template_name_suffix = '_update_form' ``` -в myapp/author_update_form.html: +В `myapp/author_update_form.html`: ```html @@ -608,14 +609,14 @@ class AuthorUpdate(UpdateView): ``` -Методы и атрибуты почти полностью совпадают с CreateView, только UpdateView, перед действиями вызывает -метод `get_object`, для получения нужного объекта, и url должен принимать `pk` для определения этого объекта +Методы и атрибуты почти полностью совпадают с CreateView, только UpdateView перед действиями вызывает +метод `get_object()` для получения нужного объекта, и url должен принимать `pk` для определения этого объекта. ## Class DeleteView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.views.generic.edit/DeleteView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.views.generic.edit/DeleteView/) -Класс для удаления обхектов. +Класс для удаления объектов. Как пользоваться? @@ -642,21 +643,21 @@ class AuthorDelete(DeleteView): ``` -Не принимает форму! Принимает модель или кверисет, и обязательно url должен принимать идентификатор, для определения -объекта +Не принимает форму! Принимает модель или queryset и обязательно url, должен принимать идентификатор для определения +объекта. ## Class LoginView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.contrib.auth.views/LoginView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.contrib.auth.views/LoginView/) -Класс реализующий логику логина. +Класс, реализующий логику логина. -Основан на FormView, если форма не была заменена, то по умолчанию -использует `django.contrib.auth.forms.AuthenticationForm`, эта форма содержит два поля, `username` и `password`, -проверяет, что данные валидны, и в случае если данные валидны и пользователь активен, добавляет пользователя в объект +Основан на FormView, если форма не была заменена, то по умолчанию использует +`django.contrib.auth.forms.AuthenticationForm`. Эта форма содержит два поля, `username` и `password`, и +проверяет, что данные валидны, и в случае, если данные валидны и пользователь активен, добавляет пользователя в объект формы. -Так же в LoginView переписан метод form_valid: +Также в LoginView переписан метод form_valid(): ```python def form_valid(self, form): @@ -669,17 +670,17 @@ def form_valid(self, form): ## Class LogoutView -[Дока](https://ccbv.co.uk/projects/Django/3.1/django.contrib.auth.views/LogoutView/) +[Дока](https://ccbv.co.uk/projects/Django/4.2/django.contrib.auth.views/LogoutView/) Класс для логаута. -У LogoutView, переписан метод `dispatch`, так что каким бы методом вы не обратились к классу, вы всё равно будете +У LogoutView переписан метод `dispatch`, так что, каким бы методом вы не обратились к классу, вы всё равно будете разлогинены. ## Регистрация -По сути регистрация, это CreateView со своими особенностями (пароль хешируется), поэтому для регистрации используют -просто CreateView, и существует заранее описанная форма UserCreationForm +По сути регистрация - это CreateView со своими особенностями (пароль хешируется), поэтому для регистрации используют +просто CreateView, и существует заранее описанная форма UserCreationForm() ```python class UserCreationForm(forms.ModelForm): @@ -694,7 +695,7 @@ class UserCreationForm(forms.ModelForm): widget=forms.PasswordInput) password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput, - help_text=_("Enter the same password as above, for verification.")) + help_text=_("Enter the same password as above for verification.")) class Meta: model = User @@ -718,12 +719,12 @@ class UserCreationForm(forms.ModelForm): return user ``` -Принимает `username` и два раза пароль, проверяет, что бы пароли были одинаковые, и при сохранении записывает пароль в +Принимает `username` и два раза пароль, проверяет, чтобы пароли были одинаковые, и при сохранении записывает пароль в хешированном виде. ## LoginRequiredMixin -Если необходимо закрыть доступ для не залогиненых юзеров от какого либо из классов, то используют `LoginRequiredMixin`, +Если необходимо закрыть доступ для незалогиненных юзеров от какого-либо из классов, то используют `LoginRequiredMixin`, При наследовании его необходимо указать перед основным классом. Например: @@ -741,18 +742,18 @@ class MyFormView(LoginRequiredMixin, FormView): login_url = '/login/' ``` -Добавляет в класс аттрибут `login_url` который определяет куда нужно перейти, если пользователь пытается получить +Добавляет в класс атрибут `login_url`, который определяет, куда нужно перейти, если пользователь пытается получить доступ, но он не авторизирован. ## Живой пример -Допустим нам нужен сайт, на котором можно зарегистрироваться, залогинится, разлогинится и написать заметку, если ты -залогинен. Заметки должны отображаться списком, последняя созданная, отображается первой. Все пользователи видят все -заметки, возле тех которые создал текущий пользователь, должна быть кнопка удалить. +Допустим, нам нужен сайт, на котором можно зарегистрироваться, залогиниться, разлогиниться и написать заметку, если ты +залогинен. Заметки должны отображаться списком, последняя созданная отображается первой. Все пользователи видят все +заметки. Возле тех, которые создал текущий пользователь, должна быть кнопка удалить. Как это сделать? -Разработка всегда начинается с описания моделей, нам нужно две сущности, юзер и заметка. +Разработка всегда начинается с описания моделей, нам нужно две сущности: юзер и заметка. Мы не будем изменять юзера, нам подходит стандартный. @@ -765,7 +766,6 @@ from django.contrib.auth.models import User from django.db import models -# Create your models here. class Note(models.Model): text = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now=True) @@ -777,14 +777,14 @@ class Note(models.Model): Не забываем про миграции, и про добавление приложения в settings.py -Создадим необходимые шаблоны: `base`, `index`, `login`, `register`. Пока пустые, заполним чуть позже. +Создадим необходимые шаблоны: `base.html`, `index.html`, `login.html`, `register.html`. Пока пустые, заполним чуть позже. -Создадим view. Для базовой страницы на которой отображается список заметок, лучше всего подходит ListView, для логина, и -логаута, существующие классы, для регистрации CreateView. +Создадим view. Для базовой страницы, на которой отображается список заметок, лучше всего подходит ListView, для логина, +и логаута - существующие классы, для регистрации - CreateView. Для логина и регистрации воспользуемся готовой формой. -Базовую страницу и логаут закроем от не залогиненых пользователей. +Базовую страницу и логаут закроем от незалогиненных пользователей. Получается как-то так: @@ -824,7 +824,7 @@ class Logout(LoginRequiredMixin, LogoutView): login_url = 'login/' ``` -В urls.py проекта добавим через инклюд urls.py приложения +В urls.py проекта добавим через `include` urls.py приложения В app/urls.py: @@ -889,7 +889,7 @@ login.html {% endblock %} ``` -register +register.html ```html {% extends 'base.html' %} @@ -931,7 +931,7 @@ register Но как добавить создание заметок? -Нам нужна форма для создания заметок, и CreateView +Нам нужна форма для создания заметок и CreateView. В forms.py @@ -947,11 +947,11 @@ class NoteCreateForm(ModelForm): fields = ('text',) ``` -В полях только текст, потому что время создания будет заполняться автоматически, айди тоже, а юзера мы будем брать из +В полях только текст, потому что время создания будет заполняться автоматически, id тоже, а юзера мы будем брать из реквеста. -Будем ли мы отображать отдельную страницу для создания? Нет, значит отдельный хтмл файл нам не нужен, а раз мы не будем -отображать страницу, то и метод `get` нам не нужен. Оставим только post. +Будем ли мы отображать отдельную страницу для создания? Нет, значит отдельный html файл нам не нужен, а раз мы не будем +отображать страницу, то и метод `get()` нам не нужен. Оставим только `post()`. Создадим CreateView: @@ -965,7 +965,7 @@ class NoteCreateView(LoginRequiredMixin, CreateView): success_url = '/' ``` -B выведем урл под этот класс: +Выведем URL под этот класс: В urls.py @@ -982,10 +982,10 @@ urlpatterns = [ ] ``` -Достаточно ли этого, что бы создавать заметки? Нет, потому что мы никуда не вывели форму для создания заметок. Давайте +Достаточно ли этого, чтобы создавать заметки? Нет, потому что мы никуда не вывели форму для создания заметок. Давайте выведем её на нашу основную страницу. -Во views.py изменим класс `NoteListView`, добавив аттрибут `extra_context = {'create_form': NoteCreateForm()}` +Во views.py изменим класс `NoteListView`, добавив атрибут `extra_context = {'create_form': NoteCreateForm()}` ```python class NoteListView(LoginRequiredMixin, ListView): @@ -1020,12 +1020,12 @@ class NoteListView(LoginRequiredMixin, ListView): {% endblock %} ``` -Достаточно ли этого? Нет. Наша заметка должна хранить в себе пользователя, а мы нигде его не добавляем. при попытке +Достаточно ли этого? Нет. Наша заметка должна хранить в себе пользователя, а мы нигде его не добавляем. При попытке вызвать `save()` мы получим ошибку, не могу сохранить без юзера. -Что будем делать? Переписывать логику `form_valid`, мы знаем, что метод `save()` для CreateView вызывается там. +Что будем делать? Переписывать логику `form_valid()`, мы знаем, что метод `save()` для CreateView вызывается там. -Что бы добавить пользователя, будем использовать `commit=False` для ModelForm, а пользователя возьмем из реквеста. +Чтобы добавить пользователя, будем использовать `commit=False` для ModelForm, а пользователя возьмем из реквеста. Перепишем класс NoteCreateView: @@ -1045,11 +1045,11 @@ class NoteCreateView(LoginRequiredMixin, CreateView): return super().form_valid(form=form) ``` -Обратите внимание после успеха, мы попадаем обратно на `/` (success_url) где мы сразу же увидим новую заметку. +Обратите внимание, после успеха мы попадаем обратно на `/` (success_url), где мы сразу же увидим новую заметку. Создание готово. -Как добавим удаление? Создадим новую DeleteView, она даже не требует форму. +Как добавить удаление? Создадим новую DeleteView, она даже не требует форму. Во views.py @@ -1059,7 +1059,7 @@ class NoteDeleteView(LoginRequiredMixin, DeleteView): success_url = '/' ``` -Не забываем добавить url +Не забываем добавить URL В urls.py: @@ -1077,7 +1077,7 @@ urlpatterns = [ ] ``` -И добавляем форму, для удаления в шаблон. +И добавляем форму для удаления в шаблон. В index.html: @@ -1108,7 +1108,7 @@ urlpatterns = [ {% endblock %} ``` -Это уже будет работать. Но нам же нужно, что бы кнопка удалять была только у своих заметок. Ок добавим `if`. +Это уже будет работать. Но нам же нужно, чтобы кнопка для удаления была только у своих заметок. Ок, добавим `if`. ```html {% extends 'base.html' %} @@ -1139,10 +1139,10 @@ urlpatterns = [ {% endblock %} ``` -Осталась маленькая деталь, сейчас мы отображаем все существующие заметки, а что если их будет миллион? это не +Осталась маленькая деталь, сейчас мы отображаем все существующие заметки, а что если их будет миллион? Это не рационально, давайте добавим пагинацию. -ListView уже передаёт все необходимые данные, нам нужно только добавить размер страницы, и добавить отображение по +ListView уже передаёт все необходимые данные, нам нужно только добавить размер страницы и добавить отображение по страницам в шаблоне. Во views.py изменим NoteListView @@ -1206,4 +1206,4 @@ class NoteListView(LoginRequiredMixin, ListView): ``` -Профит! Всё работает. Переходим к заданию на модуль. Все задания должны быть выполнены через Class Based View. \ No newline at end of file +Профит! Всё работает. Переходим к заданию на модуль. Все задания должны быть выполнены через Class-Based View. From 00fd886245c4918c11b0b02832a4a7fedb4ede77 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Tue, 25 Jul 2023 00:29:47 +0300 Subject: [PATCH 07/23] edit grammar and style; update links --- lesson34.md | 168 ++++++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/lesson34.md b/lesson34.md index 6a02c4b4d..9e343f140 100644 --- a/lesson34.md +++ b/lesson34.md @@ -2,9 +2,9 @@ ## Куки и сессии -### Что такое куки(печеньки) и причём тут сессия? +### Что такое Cookie (печеньки), и причём тут сессия? -Понятие "Сессий" основано на том, что состояние пользователя каким-то образом сохраняется, когда он переходит с одной +Понятие "сессий" основано на том, что состояние пользователя каким-то образом сохраняется, когда он переходит с одной страницы на другую. Вспомните, что HTTP не сохраняет состояний, поэтому только браузер или ваше приложение может "запомнить" то, что нужно запомнить. @@ -16,34 +16,34 @@ определенного срока. Они применимы практически для любой задачи, но чаще всего их используют, чтобы сохранить пользователя в том же месте веб-страницы, если он потеряет интернет-соединение, или, чтобы хранить простые настройки отображения сайта. Например, корзины интернет магазинов чаще всего делают именно через куки, ведь вы не теряете данные, -при переходе со страницы на страницу, а хранить данные что вы набираете в корзину в базе данных, это слишком избыточно. -Вы можете также хранить в них данные пользователя или даже пароли, но это не очень хорошая идея, не стоит хранить в -обычных куках браузера информацию, которая должна быть защищенной или сохраняться между сессиями браузера. Пользователь -может легко потерять данные, очистив кэш, или украсть/использовать незащищенные данные из куков. +при переходе со страницы на страницу, а хранить данные о том, что вы набираете в корзину, в базе данных, слишком +избыточно. Вы можете также хранить в них данные пользователя или даже пароли, но это не очень хорошая идея, не стоит +хранить в обычных куках браузера информацию, которая должна быть защищенной или сохраняться между сессиями браузера. +Пользователь может легко потерять данные, очистив кэш, или украсть/использовать незащищенные данные из куков. ![](https://www.web-labs.kz/wp-content/uploads/2020/05/cookie.png) -Куки добавляются в request/response для хранения абсолютно разных данных. Например, стандартная джанго авторизация -добавляет куку с данными о пользователя, что бы можно было определить кто именно делает запрос. Поэтому там и нужны csrf -токены в формах или просто токены в рест запросах, так как перехватить значение куки при запросе очень просто, а мы -должны быть уверенны, что запрос пришел именно от авторизированного пользователя (Куки хранит информацию, кто это, а -токены позволяют проверить, что это был именно этот пользователь.). +Куки добавляются в request/response для хранения совершенно разных данных. Например, стандартная Django авторизация +добавляет куку с данными о пользователя, чтобы можно было определить, кто именно делает запрос. Поэтому там и нужны CSRF +токены в формах или просто токены в REST запросах. Так как перехватить значение куки при запросе очень просто, а мы +должны быть уверены, что запрос пришел именно от авторизированного пользователя. Куки хранит информацию, кто это, а +токены позволяют проверить, что это был именно этот пользователь. ### Сессия ![]() Задумайтесь о том, каким образом браузеры следят, что пользователь залогинен, когда страница перезагружается. HTTP -запросы не имеют состояний, так как же вы определите, что запрос пришел именно от залогиненого пользователя? Вот почему +запросы не имеют состояний, так как же вы определите, что запрос пришел именно от залогиненного пользователя? Вот почему важны куки — они позволяют вам отслеживать пользователя от запроса к запросу, пока не истечет их срок действия. -Особый случай — это когда вы хотите отслеживать данные пользовательской "сессии", которая включает все, что пользователь +Особый случай — это когда нужно отслеживать данные пользовательской "сессии", которая включает все, что пользователь делает, пока вы хотите "запоминать" это, обычно до тех пор, пока пользователь не закроет окно браузера. В этом случае -каждая страница, которую пользователь посетил до закрытия браузера будет частью одной сессии. +каждая страница, которую пользователь посетил до закрытия браузера, будет частью одной сессии. -Если упростить, сессия это набор запросов от одного и того же пользователя (Или от разных в рамках одного процесса). +Если упростить, сессия - это набор запросов от одного и того же пользователя (или от разных в рамках одного процесса). -В случае с джанго, при стандартных настройках, сессия хранит набор куки, которые храняться в формате JSON. (А значит, +В случае с Django, при стандартных настройках сессия хранит набор куки, которые хранятся в формате JSON. (А значит, что данные можно сериализовать) #### Сессии и HTTP @@ -51,21 +51,21 @@ ![](http://techbriefers.com/wp-content/uploads/2019/10/cookie-and-session-management-process-in-codeigniter.jpg) С точки зрения протокола HTTP, сессия является абстракцией. То есть физически её не существует. Если разобрать любой -запрос, там вы сможете увидеть только куки, схему использования можно изучить на картинке +запрос, там вы сможете увидеть только куки, схему использования можно изучить на картинке. -Сессиями можно пользоваться только для залогиненых пользователей, иначе это не имело бы смысл. +Сессиями можно пользоваться только для залогиненных пользователей, иначе это не имело бы смысла. -При отправке успешного запроса на логин, на уровне бекенда, в нашем случае джанго создаёт в базе данных объект сессии, -для которого генерируется уникальный идентификатор (id) и уже он добавляется к каждому последующему реквесту и респонсу +При отправке успешного запроса на логин, на уровне бекенда, в нашем случае Django создаёт в базе данных объект сессии, +для которого генерируется уникальный идентификатор (id), и уже он добавляется к каждому последующему реквесту и респонсу в качестве куки. -Как именно это значение попадает в каждый реквест мы рассмотрим на следующем занятии, а пока давайте разберёмся как мы +Как именно это значение попадает в каждый реквест, мы рассмотрим на следующем занятии, а пока давайте разберёмся, как мы можем это использовать? ### Как этим пользоваться? -В джанго сессия всегда храниться в реквесте, а значит вы можете использовать сессию как временное хранилище в любом -месте где у вас есть доступ к реквесту. `request.session` в виде словаря. +В Django сессия всегда хранится в реквесте, а значит, вы можете использовать сессию как временное хранилище в любом +месте, где у вас есть доступ к реквесту. `request.session` в виде словаря. Рассмотрим несколько примеров. @@ -78,10 +78,11 @@ 'bar' ``` -Данные хранятся в формате JSON, а значит что ключи будут преобразованы в строки. +Данные хранятся в формате JSON, а значит ключи будут преобразованы в строки. -Допустим вам нужно "запомнить" комментировал ли этот пользователь только что статью, что бы не позволить написать -большое кол-во комментариев подряд. Конечно можно сохранить эти данные в базе, но зачем? Проще воспользоваться сессией: +Допустим, вам нужно "запомнить", комментировал ли этот пользователь только что статью, чтобы не позволить написать +большое количество комментариев подряд. Конечно, можно сохранить эти данные в базе, но зачем? +Проще воспользоваться сессией: ```python def post_comment(request, new_comment): @@ -95,15 +96,15 @@ def post_comment(request, new_comment): Сохраним это состояние в сессии и будем перепроверять именно его. -Допустим вам нужно хранить сколько времени назад пользователь последний раз совершал действие после логина. +Допустим, вам нужно хранить, сколько времени назад пользователь последний раз совершал действие после логина. ```python request.session['last_action'] = timezone.now() ``` -Теперь мы можем проверить когда было выполнено последнее действие и добавить любую нужную нам логику. +Теперь мы можем проверить, когда было выполнено последнее действие, и добавить любую нужную нам логику. -Если нам нужно воспользоваться сессией вне мест, где есть доступ к реквесту (Никогда не пользовался, но мало ли): +Если нам нужно воспользоваться сессией вне мест, где есть доступ к реквесту (никогда не пользовался, но мало ли): ```python from django.contrib.sessions.backends.db import SessionStore @@ -119,10 +120,10 @@ s['last_login'] 1376587691 ``` -Мы можем получить сессию по ключу (Любая созданная джанго сессия автоматически хранит переменную `session_key`) по -которой получить нужные нам данные. +Мы можем получить сессию по ключу (любая созданная Django сессия автоматически хранит переменную `session_key`), по +которой можно получить нужные нам данные. -Данные в любой сессии хранятся в кодированном виде, что бы получить все данные сессии, не зная конкретного ключа их +Данные в любой сессии хранятся в кодированном виде, чтобы получить все данные сессии, не зная конкретного ключа, их можно получить через метод `.get_decoded()` ```python @@ -132,7 +133,7 @@ s.get_decoded() {'user_id': 42} ``` -Сохранение данных в сессии происходит только тогда когда меняется значение request.session: +Сохранение данных в сессии происходит только тогда, когда меняется значение request.session: ```python # Session is modified. @@ -149,26 +150,26 @@ request.session['foo'] = {} request.session['foo']['bar'] = 'baz' ``` -В последнем случае данные не будут сохранены, т.к. модифицируется не request.session, а `request.session['foo']` +В последнем случае данные не будут сохранены, т. к. модифицируется не request.session, а `request.session['foo']` -Это поведение можно изменить, если добавить настройку в `settings.py` `SESSION_SAVE_EVERY_REQUEST = True` тогда запись -в сессию будет происходить каждый запрос, а не только в момент изменения. +Это поведение можно изменить, если добавить настройку в `settings.py` `SESSION_SAVE_EVERY_REQUEST = True`, тогда запись +в сессию будет происходить при каждом запросе, а не только в момент изменения. . Так как сессии хранятся в базе данных, то теоретически может произойти так, что через какое-то длительное время сессии -будут занимать большой объем в базе, и если нам они не нужны, то необходимо их переодически очищать, если мы хотим -удалить все данные, то мы можем воспользоваться менедж командой `python manage.py clearsessions` +будут занимать большой объем в базе. Если нам они не нужны, то необходимо их периодически очищать. Если мы хотим +удалить все данные, то мы можем воспользоваться manage-командой `python manage.py clearsessions`. -Если же нам необходимо удалить только часть, то мы можем воспользоваться тем, что сессия, это такая же модель как и все -остальные, а значит мы можем импортировать её, отфильтровать и удалить необходимые нам данные. +Если же нужно удалить только часть сессии, то можно воспользоваться тем, что сессия - это такая же модель, +как и все остальные. Значит, мы можем импортировать её, отфильтровать и удалить необходимые нам данные. Некоторые настройки можно поменять и перенастроить, как и полностью кастомизировать любые действия с сессиями. Подробнее -об этом [Тут](https://docs.djangoproject.com/en/3.1/topics/http/sessions/) +об этом [Тут](https://docs.djangoproject.com/en/4.2/topics/http/sessions/) ## Кеш ![](https://miro.medium.com/max/577/1*hjXc3KHBcFGfRw5rr5lv4A.jpeg) -Официальная документация [Тут](https://docs.djangoproject.com/en/3.1/topics/cache/) +Официальная документация [Тут](https://docs.djangoproject.com/en/4.2/topics/cache/) Что такое кеш? @@ -182,29 +183,29 @@ request.session['foo']['bar'] = 'baz' #### Пример -Предположим мы разрабатываем новостной сайт, и знаем, что новости у нас обновляются раз в час. +Предположим, мы разрабатываем новостной сайт, и знаем, что новости у нас обновляются раз в час. -В течение часа, пока новости не обновятся, абсолютно каждый пользователь заходящий на сайт, будет видеть один и тот же -набор статей, а значит нам необязательно каждый раз доставать этот набор из базы данных, мы можем закешировать его! +В течение часа, пока новости не обновятся, абсолютно каждый заходящий на сайт пользователь будет видеть один и тот же +набор статей, а значит, нам необязательно каждый раз доставать этот набор из базы данных, мы можем закешировать его! -Для использования кеша, мы можем воспользоваться огромным кол-вом заранее заготовленных решений для кеша. +Для использования кеша мы можем воспользоваться огромным количеством заранее заготовленных решений для кеша. ### Виды кеша -Два основных используемых в реальности вида кеша это `Memcached` и `redis` +Два основных используемых в реальности вида кеша - это `Memcached` и `redis`. -Как можно догадаться всё настраивается через `settings.py` +Как можно догадаться, всё настраивается через `settings.py`. #### Memcached (memory cached - кэшированная память) Способ хранения данных в виде хеш-таблицы (словаря) в оперативной памяти. -**требует предварительной установки!** +**Требует предварительной установки!** -Как и с большинством необходимых сторонних приложений, очень легко ставится на линукс и с определёнными сложностями на -виндовс, изучите это самостоятельно. +Как и с большинством необходимых сторонних приложений очень легко ставится на Linux и с определёнными сложностями на +Windows, изучите это самостоятельно. -Memcache запускается как отдельный сервис или служба, и стандартным портом для доступа к нему является 11211. +Memcached запускается как отдельный сервис или служба, и стандартным портом для доступа к нему является 11211. Хранение данных с использованием Memcached на практике: @@ -217,7 +218,7 @@ CACHES = { } ``` -Хранение в файле сокета (временный файл хранилище в юникс системах): +Хранение в файле сокета (временный файл хранилища в UNIX системах): ```python CACHES = { @@ -228,7 +229,7 @@ CACHES = { } ``` -Хранение на нескольких серверах, для уменьшения нагрузки: +Хранение на нескольких серверах для уменьшения нагрузки: ```python CACHES = { @@ -244,12 +245,12 @@ CACHES = { #### Redis -Для использования кеша через редис, необходимо знать, что такое редис. +Для использования кеша через Redis необходимо знать, что такое Redis. -Мы будем изучать целый вид баз данных среди которых будет и редис, но на данном этапе нам нужно знать, что редис это -специальная база данных, которая может хранить данные в виде хеш-таблицы (ключ-значение) +Мы будем изучать целый вид баз данных среди, которых будет и Redis. На данном этапе нам нужно знать, что Redis - это +специальная база данных, которая может хранить данные в виде хеш-таблицы (ключ-значение). -Для использования такого кеша, нам необходимо, **установить редис**, стандартный порт 6379 +Для использования такого кеша нам необходимо, **установить Redis**, стандартный порт 6379. И установить сторонние библиотеки для работы с ним: @@ -257,7 +258,7 @@ CACHES = { pip install django-redis ``` -После чего так же прописать настройки в `settings.py` и держать сервис редиса запущеным +После чего прописать настройки в `settings.py` и держать сервис Redis запущенным: ```python CACHES = { @@ -274,7 +275,7 @@ CACHES = { #### Другие способы хранения кеша -Можно хранить кеш прям в базе данных, для этого нужно указать таблицу в которую складывать кеш: +Можно хранить кеш прям в базе данных, для этого нужно указать таблицу, в которую складывать кеш: ```python CACHES = { @@ -285,13 +286,13 @@ CACHES = { } ``` -Для использования кеша через базу, таблицу нужно предварительно создать, сделать это можно при помощи менедж команды: +Для использования кеша через базу таблицу нужно предварительно создать, сделать это можно при помощи manage-команды: ```python manage.py createcachetable``` Можно хранить кеш в обычном файле: -Линукс\мак: +Linux\MacOS: ```python CACHES = { @@ -302,7 +303,7 @@ CACHES = { } ``` -Виндовс: +Windows: ```python CACHES = { @@ -323,7 +324,7 @@ CACHES = { } ``` -Как и всё остальное, кеш можно кастомизировать написав собственные классы для управления кешем: +Как и всё остальное, кеш можно кастомизировать, написав собственные классы для управления кешем: ```python CACHES = { @@ -333,15 +334,15 @@ CACHES = { } ``` -Любой тип кеширования поддерживает большое кол-во доп настроек, подробно о которых в документации. +Любой тип кеширования поддерживает большое количество дополнительных настроек, подробно о которых в документации. ### Как же этим пользоваться? Существует два основных способа использовать кеш. -Кешировать весь сайт, или кешировать конкретную вью. +Кешировать весь сайт или кешировать конкретную вью. -Для того что бы кешировать весь сайт, нужно добавить две мидлвары (Как это работает, на следующем занятии), до и +Чтобы кешировать весь сайт, нужно добавить две middleware (как это работает, на следующем занятии) до и после `CommonMiddleware` (это важно, иначе работать не будет): В `settings.py` @@ -356,9 +357,9 @@ MIDDLEWARE = [ ] ``` -Время кеширование или ограничения на кеш выставляются через переменные `settings.py`, подробно в документации. +Время кеширования или ограничения на кеш выставляются через переменные `settings.py`, подробно в документации. -Для того, что бы кешировать, отдельный метод или класс используется декоратор `cache_page` +Для того чтобы кешировать отдельный метод или класс, используется декоратор `cache_page` ```python from django.views.decorators.cache import cache_page @@ -369,10 +370,10 @@ def my_view(request): ... ``` -В скобках указывается время которое кеш должен хранится, обычно записывается в виде умножения на секунды\минуты, для -простоты чтения (15*60 это 15 минут, никакой разницы от того что бы записать 900, но так проще воспринимать на вид). +В скобках указывается время, которое кеш должен храниться, обычно записывается в виде умножения на секунды\минуты для +простоты чтения (15*60 - это 15 минут, никакой разницы от того, чтобы записать 900, но так проще воспринимать на вид). -Чаще всего декоратор используется в урлах: +Чаще всего декоратор используется в URL: ```python from django.views.decorators.cache import cache_page @@ -382,7 +383,7 @@ urlpatterns = [ ] ``` -Для кеширования class base view кешируется весь класс: +Для кеширования class-based view кешируется весь класс: ```python from django.views.decorators.cache import cache_page @@ -390,7 +391,7 @@ from django.views.decorators.cache import cache_page url(r'^my_url/?$', cache_page(60 * 60)(MyView.as_view())), ``` -Так же можно закешировать часть темплейта при помощи темплейт тега `cache`: +Также можно закешировать часть темплейта при помощи темплейт тега `cache`: ```html { % load cache %} @@ -413,7 +414,7 @@ None cache.set('add_key', 'Initial value') cache.add('add_key', 'New value') -# .add() сработает только если в указанном ключе ничего не было +# .add() сработает, только если в указанном ключе ничего не было cache.get('add_key') 'Initial value' @@ -440,19 +441,18 @@ cache.delete_many(['a', 'b', 'c']) cache.clear() -cache.touch('a', 10) # Обновить время хранения +cache.touch('a', 10) # обновить время хранения ``` -И многие другие тонкости и особенности, например декоратор `from django.views.decorators.cache import never_cache` -который можно использовать, что бы не кешировать данные, если вы уже кешируете весь сайт. И многое другое, подробности в +И многие другие тонкости и особенности, например, декоратор `from django.views.decorators.cache import never_cache`, +который можно использовать, чтобы не кешировать данные, если вы уже кешируете весь сайт. И многое другое, подробности в документации. Практика: -1. Пользователь открывает одну и туже страницу. Каждый четвертый раз когда он открывает страницу добавте вверху - надпись, "Это был 4-ый раз", если обновить страницу еще раз, то счёт 4-ех открытий начинаем сначала. +1. Пользователь открывает одну и ту же страницу. Каждый четвертый раз, когда он открывает страницу, добавьте вверху + надпись "Это был 4-ый раз". Если обновить страницу еще раз, то счёт 4-х открытий начинаем с начала. -2. Множество пользователей открывает одну и туже страницу, каждый 10-ый кто открывает страницу должен видеть надпись, " - Вы наш 10-ый покупатель" (Если один пользователь открыл 10 раз, это тоже подходит, один пользователь 6 раз и еще один - 4 раза, тоже ок) - +2. Множество пользователей открывает одну и ту же страницу, каждый 10-ый, кто открывает страницу, должен видеть надпись + "Вы наш 10-ый покупатель" (Если один пользователь открыл 10 раз, это тоже подходит, один пользователь 6 раз и еще один + 4 раза, тоже ок). From ba6c8ad123988858a42ce5617c4be8cb6b612690 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Tue, 25 Jul 2023 15:34:42 +0300 Subject: [PATCH 08/23] edit grammar and style; update links --- lesson35.md | 182 +++++++++++++++++++++++++--------------------------- 1 file changed, 89 insertions(+), 93 deletions(-) diff --git a/lesson35.md b/lesson35.md index 51f981bee..438e1c58d 100644 --- a/lesson35.md +++ b/lesson35.md @@ -1,19 +1,17 @@ -# Урок 35. Middlewares. Signals. Messages +# Урок 35. Middleware. Signals. Messages -## Middlewares +## Middleware ![](https://memegenerator.net/img/instances/81631865.jpg) -Дока [Тут](https://docs.djangoproject.com/en/3.1/topics/http/middleware/) +Дока [Тут](https://docs.djangoproject.com/en/4.2/topics/http/middleware/) -Мы с вами рассмотрели основные этапы того какие этапы должен пройти реквест на всём пути нашей реквест-респонс системы, -но на самом деле каждый реквест проходит кучу дополнительных обработок таких как мидлвары, причём каждый реквест делает -это дважды, при "входе" и при "выходе". +Мы с вами рассмотрели основные этапы того, какие этапы должен пройти request на всём пути нашей request-response системы, +но на самом деле каждый request проходит кучу дополнительных обработок, таких как middleware, причём каждый request +делает это дважды, при "входе" и при "выходе". -Если открыть файл `settings.py` то там можно обнаружить переменную `MIDDLEWARES`, или `MIDDLEWARE_CLASSES` (Для старых -версий Django) - -Которая выглядит примерно вот так: +Если открыть файл `settings.py`, то там можно обнаружить переменную `MIDDLEWARE`, или `MIDDLEWARE_CLASSES` (для старых +версий Django), которая выглядит примерно так: ```python MIDDLEWARE = [ @@ -27,21 +25,21 @@ MIDDLEWARE = [ ] ``` -Каждая из этих строк, это отдельная мидлварина, и абсолютно **каждый** реквест проходит через код описанный в этих -файлах, например `django.contrib.auth.middleware.AuthenticationMiddleware` отвечает за то, что бы в нашем реквесте -всегда был пользователь если он залогинен, а `django.middleware.csrf.CsrfViewMiddleware` отвечает за проверку наличия и +Каждая из этих строк - это отдельная мидлварина, и абсолютно **каждый** request проходит через код, описанный в этих +файлах, например, `django.contrib.auth.middleware.AuthenticationMiddleware` отвечает за то, чтобы в нашем request +всегда был пользователь, если он залогинен, а `django.middleware.csrf.CsrfViewMiddleware` отвечает за проверку наличия и правильности CSRF токена, которые мы рассматривали ранее. -Причём при "входе" реквест будет проходить сверху вниз (Сначала секьюрити, потом сессии итд), а при "выходе" снизу -вверх (начиная с XFrame, заканчивая Security) +Причём при "входе" request будет проходить сверху вниз (сначала секьюрити, потом сессии и т. д.), а при "выходе" снизу +вверх (начиная XFrame и заканчивая Security) -**Мидлвар это по своей сути это декоратор над реквестом** +**Middleware - это декоратор над request** ### Как этим пользоваться? -Если мы хотим использовать самописные мидлвары, мы должны понимать как они работают. +Если мы хотим использовать самописные мидлвары, мы должны понимать, как они работают. -Можно описать мидлвар двумя способами функциональным и основанным на классах, рассмотрим оба: +Можно описать мидлвар двумя способами: функциональным и основанным на классах. Рассмотрим оба: Функционально: @@ -63,13 +61,13 @@ def simple_middleware(get_response): return middleware ``` -Как вы можете заметить синтаксис очень близок к декораторам. +Как вы можете заметить, синтаксис очень близок к декораторам. -`get_response` - функция, которая отвечает за всё что происходит мне мидлвары и отвечает за обработку запроса, по сути -это будет наша `view`, а мы можем дописать любой нужный нам код, до или после, соответственно на "входе" реквеста или +`get_response()` - функция, которая отвечает за всё, что происходит вне мидлвары и отвечает за обработку запроса, по сути +это будет наша `view`, а мы можем дописать любой нужный нам код до или после, соответственно на "входе" реквеста или на "выходе" респонса. -Так почти никто не пишет :) рассмотрим как этот же функционал работает для классов: +Так почти никто не пишет :) Рассмотрим, как этот же функционал работает для классов: ```python class SimpleMiddleware: @@ -89,16 +87,16 @@ class SimpleMiddleware: return response ``` -При таком подходе функционал работает при помощи меджик методов, функционально выполняет то же самое, но по моему +При таком подходе функционал работает при помощи магических методов, функционально выполняет то же самое, но по моему личному мнению гораздо элегантнее. -При инициализации, мы получаем обработчик, а при выполнении вызываем его же, но с возможностью добавить нужный код до +При инициализации мы получаем обработчик, а при выполнении вызываем его же, но с возможностью добавить нужный код до или после. -Что бы активировать мидлвар нам необходимо дописать путь к нему, в переменную `MIDDLEWARES` в `settings.py`. +Чтобы активировать мидлвар, необходимо дописать путь к нему в переменную `MIDDLEWARE` в `settings.py`. -Допустим, если мы создали файл `middlewares.py` в приложении под названием `main` и в этом файле создали -класс `CheckUserStatus` который нужен, что бы мы могли обработать какой-либо статус пользователя, нужно дописать в +Допустим, если мы создали файл `middleware.py` в приложении под названием `main`, и в этом файле создали +класс `CheckUserStatus`, который нужен, чтобы мы могли обработать какой-либо статус пользователя, нужно дописать в переменную этот класс: ```python @@ -108,39 +106,39 @@ MIDDLEWARE = [ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'main.middlewares.CheckUserStatus', # Новый мидлвар + 'main.middleware.CheckUserStatus', # Новый мидлвар 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ``` -Обратите внимание я добавил мидлвар после `django.contrib.auth.middleware.AuthenticationMiddleware` так как до этого +Обратите внимание, я добавил мидлвар после `django.contrib.auth.middleware.AuthenticationMiddleware`, так как до этого мидлвара в нашем реквесте нет переменной юзер. ## Миксин для мидлвар -На самом деле для написания мидлвар существует миксин, что бы упростить наш код. +На самом деле для написания мидлвар существует миксин, чтобы упростить наш код. ```python django.utils.deprecation.MiddlewareMixin ``` -В нём уже расписаны методы `__init__` и `__call__` +В нём уже расписаны методы `__init__` и `__call__`. -Инит принимает метод для обработки реквеста, а в вызове расписаны методы для обработки ревеста или респонса. +`__init_()` принимает метод для обработки request, а в `__call__` расписаны методы для обработки request или response. -Метод колл вызывает 4 действия: +Метод `__call__` вызывает 4 действия: -1. Вызывает `self.process_request(request)` (Если описан) для обработки ревекста. -2. Вызывает `self.get_response(request)` что бы получить респонс для дальнейшего использования. -3. Вызывает `self.process_response(request, response)` (Если описан) для обработки респонса. -4. Возвращает респонс +1. Вызывает `self.process_request(request)` (если описан) для обработки request. +2. Вызывает `self.get_response(request)`, чтобы получить response для дальнейшего использования. +3. Вызывает `self.process_response(request, response)` (если описан) для обработки response. +4. Возвращает response. Зачем это нужно? -Что бы описывать только тот функционал который мы будем использовать, и случайно не зацепить, что-то рядом. +Чтобы описывать только тот функционал, который мы будем использовать, и случайно не зацепить что-то рядом. -Например так выглядит миддлвар для добавления юзера в реквест. +Например, так выглядит мидлвар для добавления юзера в реквест. ```python from django.contrib import auth @@ -164,32 +162,30 @@ class AuthenticationMiddleware(MiddlewareMixin): ) request.user = SimpleLazyObject(lambda: get_user(request)) ``` - -Всё что тут описано, это что делать при реквесте, добавить реквесту юзера, информация о котором как мы помним, хранится -в сессии. +Описание: при получении реквеста добавить в него пользователя, информация о котором хранится в сессии. ## Signals -Сигналы. Часто мы оказываемся к ситуации когда нам нужно выполнять какие-либо действия до\после какого-то определённого -события, мы конечно можем прописать код там где нам нужно, но вместо этого ммы можем использоваться сигналы. +Сигналы. Часто мы оказываемся в ситуации, когда нам нужно выполнять какие-либо действия до или после определённого +события. Конечно, мы можем прописать код там, где нам нужно, но вместо этого мы можем использовать сигналы. -Сигналы отлавливают что определённое действие выполнено или будет следующим, и выполняет необходимый нам код. +Сигналы отлавливают, что определённое действие выполнено или будет следующим, и выполняют необходимый нам код. -Список экшенов [тут](https://docs.djangoproject.com/en/3.1/ref/signals/). -Описание [тут](https://docs.djangoproject.com/en/3.1/topics/signals/). +Список экшенов [тут](https://docs.djangoproject.com/en/4.2/ref/signals/). +Описание [тут](https://docs.djangoproject.com/en/4.2/topics/signals/). Примеры сигналов: ``` -django.db.models.signals.pre_save & django.db.models.signals.post_save # Выполняется перед сохраннием или сразу после сохранения объекта +django.db.models.signals.pre_save & django.db.models.signals.post_save # Выполняется перед сохранением или сразу после сохранения объекта django.db.models.signals.pre_delete & django.db.models.signals.post_delete # Выполняется перед удалением или сразу после удаления объекта -django.db.models.signals.m2m_changed # Выполняется при изменении любых мэни ту мени связей (добавили студента в группу или убрали, например) -django.core.signals.request_started & django.core.signals.request_finished # Выполняется при начале запроса, или при тего завершении. +django.db.models.signals.m2m_changed # Выполняется при изменении любых ManyToMany связей (добавили студента в группу или убрали, например) +django.core.signals.request_started & django.core.signals.request_finished # Выполняется при начале запроса или при его завершении. ``` -Это далеко не полный список действий на которые могут реагировать сигналы. +Это далеко не полный список действий, на которые могут реагировать сигналы. -Каждый сигнал имеет функции `connect` и `disconnect` для того что бы привязать\отвязать к действию сигнал +Каждый сигнал имеет функции `connect()` и `disconnect()` для того, чтобы привязать/отвязать сигнал к действию. ```python from django.core.signals import request_finished @@ -197,7 +193,7 @@ from django.core.signals import request_finished request_finished.connect(my_callback) ``` -где `my_callback` это функция, которую нужно выполнять по получению сигнала. +где `my_callback` - это функция, которую нужно выполнять по получению сигнала. Но гораздо чаще применяется синтаксис с использованием декоратора `receiver` @@ -211,8 +207,8 @@ def my_callback(sender, **kwargs): print("Request finished!") ``` -У сигнала есть параметр `receiver` и может быть параметр `sender`, сендер, это объект который отправляет сигнал, -например модель, для которой описывается сигнал. +У сигнала есть параметр `receiver` и может быть параметр `sender`. Сендер - это объект, который отправляет сигнал +(например, модель, для которой описывается сигнал). ```python from django.db.models.signals import pre_save @@ -225,7 +221,7 @@ def my_handler(sender, **kwargs): ... ``` -Сигнал можно создать под любое действие если это необходимо. Допустим нужно отправить сигнал, что пицца готова. +Сигнал можно создать под любое действие, если это необходимо. Допустим, нужно отправить сигнал, что пицца готова. Сначала создадим сигнал. @@ -235,7 +231,7 @@ import django.dispatch pizza_done = django.dispatch.Signal() ``` -И в нужном месте можно отправить +И в нужном месте можно отправить: ```python class PizzaStore: @@ -248,7 +244,7 @@ class PizzaStore: ## Messages -Дока [тут](https://docs.djangoproject.com/en/3.1/ref/contrib/messages/) +Дока [тут](https://docs.djangoproject.com/en/4.2/ref/contrib/messages/) Довольно часто в веб-приложениях вам необходимо отображать одноразовое уведомление для пользователя после обработки формы или некоторых других типов пользовательского ввода ("Вы успешно зарегистрировались", "Скидка активирована", @@ -265,22 +261,22 @@ class PizzaStore: ### Подключение -По дефолту, если проект был создан через `django-admin` то `messages` изначально подключены. +По дефолту, если проект был создан через `django-admin`, то `messages` изначально подключены. `django.contrib.messages` должны быть в `INSTALLED_APPS`. В переменной `MIDDLEWARE` должны быть `django.contrib.sessions.middleware.SessionMiddleware` and `django.contrib.messages.middleware.MessageMiddleware`. -По дефолту данные сообщений хранятся в сессии, это является причиной почему мидлвар для сессий должен быть подключен. +По дефолту данные сообщений хранятся в сессии, это является причиной, почему мидлвар для сессий должен быть подключен. В переменной `context_processors` в переменной `TEMPLATES` должны -содержаться `django.contrib.messages.context_processors.messages` +содержаться `django.contrib.messages.context_processors.messages`. #### context_processors -Ключ в переменной `OPTIONS` в переменной `TEMPLATES`, отвечает за то, что по дефолту будет присутствовать как переменная -во всех наших темплейтах, изначально выглядит вот так: +Ключ в переменной `OPTIONS` в переменной `TEMPLATES` отвечает за то, что по дефолту будет присутствовать как переменная +во всех наших темплейтах. Изначально выглядит вот так: ```python 'context_processors': [ @@ -291,14 +287,14 @@ and `django.contrib.messages.middleware.MessageMiddleware`. ] ``` -`django.template.context_processors.debug` - Если в `settings.py` переменная `DEBUG`==`True` добавляет в темплейт -информацию о подробностях, если произошла ошибка +`django.template.context_processors.debug`, если в `settings.py` переменная `DEBUG`==`True`, добавляет в темплейт +информацию о подробностях, если произошла ошибка. -`django.template.context_processors.request` - Добавляет в контекст данные из реквеста, переменная `request`. +`django.template.context_processors.request` добавляет в контекст данные из реквеста, переменная `request`. -`django.contrib.auth.context_processors.auth` - Добавляет переменную `user` с информацией о пользователе. +`django.contrib.auth.context_processors.auth` добавляет переменную `user` с информацией о пользователе. -`django.contrib.messages.context_processors.messages` - Добавляет сообщения на страницу. +`django.contrib.messages.context_processors.messages` добавляет сообщения на страницу. ### Storage backends @@ -306,29 +302,29 @@ and `django.contrib.messages.middleware.MessageMiddleware`. По дефолту существует три варианта хранения: -`class storage.session.SessionStorage` - Хранение в сессии +`class storage.session.SessionStorage` - хранение в сессии -`class storage.cookie.CookieStorage` - Хранение в куке +`class storage.cookie.CookieStorage` - хранение в куке -`class storage.fallback.FallbackStorage` - Пытаемся хранить в куке, если не помещается используем сессию. Будет -использовано, по умолчанию. +`class storage.fallback.FallbackStorage` - пытаемся хранить в куке, если не помещается используем сессию. Будет +использовано по умолчанию. -Если нужно изменить, добавте в `settings.py` переменную: +Если нужно изменить, добавьте в `settings.py` переменную: ```python MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' ``` -Если нужно написать свой класс для хранения сообщений то нужно наследоваться от `class storage.base.BaseStorage` и -описать 2 метода `_get` и `_store` +Если нужно написать свой класс для хранения сообщений, то нужно наследоваться от `class storage.base.BaseStorage` и +описать 2 метода `_get()` и `_store()`. -### Как этим пользоваться +### Как этим пользоваться? -Во view, необходимо добавить сообщение. +Во view необходимо добавить сообщение. Это можно сделать несколькими способами: -#### add_message +#### add_message() ```python from django.contrib import messages @@ -336,10 +332,10 @@ from django.contrib import messages messages.add_message(request, messages.INFO, 'Hello world.') ``` -Метод `add_message` позволяет добавить сообщение к реквесту, принимает сам реквест, тип сообщения (успех, провал -информация итд.), и сам текст сообщения. На самом деле второй параметр это просто цифра, а текст добавлен для чтения. +Метод `add_message()` позволяет добавить сообщение к реквесту, принимает сам реквест, тип сообщения (успех, провал +информация и т. д.) и сам текст сообщения. На самом деле, второй параметр - это просто цифра, а текст добавлен для чтения. -Чаще всего используется в методах **form_valid**, **form_invalid** +Чаще всего используется в методах **form_valid()**, **form_invalid()** #### Сокращенные методы @@ -351,13 +347,13 @@ messages.warning(request, 'Your account expires in three days.') messages.error(request, 'Document deleted.') ``` -Эти 5 типов сообщений являются стандартными, но если необходимо, всегда можно добавить свои типы, как это сделать +Эти 5 типов сообщений являются стандартными, но, если необходимо, всегда можно добавить свои типы. Как это сделать описано в доке. -### Как отобразить +### Как отобразить? -Контекст процессор, который находится в настройках уже добавляет нам в темплейт переменную `messages`, а дальше мы можем -использовать классические темплейт теги +`context_processors`, который находится в настройках, уже добавляет нам в темплейт переменную `messages`, а дальше +мы можем использовать классические темплейт теги. Примеры: @@ -387,12 +383,12 @@ messages.error(request, 'Document deleted.') {% endif %} ``` -Чаще всего такой код располагают в блоке в базовом шаблоне, что-бы не указывать его на каждой странице отдельно. +Чаще всего такой код располагают в блоке в базовом шаблоне, чтобы не указывать его на каждой странице отдельно. #### Использование во view -Если нам вдруг необходимо получить список текущих сообщение во view, мы можем это сделать, при помощи -метода `get_messages` +Если нам вдруг необходимо получить список текущих сообщение во view, мы можем это сделать при помощи +метода `get_messages()`. ```python from django.contrib.messages import get_messages @@ -402,9 +398,9 @@ for message in storage: do_something_with_the_message(message) ``` -### Messages и class-base view +### Messages и Class-Based Views -Можно добавлять сообщения через миксины, примеры: +Можно добавлять сообщения при помощи миксинов, примеры: ```python from django.contrib.messages.views import SuccessMessageMixin @@ -438,8 +434,8 @@ class ComplicatedCreate(SuccessMessageMixin, CreateView): Практика: -1. Задания с прошлого занятия изменить так, что бы логика работала не на одной конкретной странице, а вообще на любой. +1. Задания с прошлого занятия изменить так, чтобы логика работала не на одной конкретной странице, а вообще на любой. -2. Для прошлого задания сделать вывод текста не на странице, а при помощи `messages` +2. Для прошлого задания сделать вывод текста не на странице, а при помощи `messages`. -3. Прошлое задание сделать не для всех страниц, а только для любых двух (на ваш выбор). \ No newline at end of file +3. Прошлое задание сделать не для всех страниц, а только для любых двух (на ваш выбор). From f15ca73d3b2b4f7f79a3e6e5db409fb3535438d4 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Tue, 25 Jul 2023 15:54:06 +0300 Subject: [PATCH 09/23] edit grammar and style; update links --- lesson36.md | 158 ++++++++++++++++++++++++++-------------------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/lesson36.md b/lesson36.md index 9e6c28b60..7bed9622f 100644 --- a/lesson36.md +++ b/lesson36.md @@ -1,10 +1,10 @@ -# Урок 36. Менедж команды. Менедж команды приложений. Кастомные менедж команды. +# Урок 36. Manage-команды. Manage-команды приложений. Кастомные manage-команды. -## Менедж команды и настройки. +## Manage-команды и настройки. -Менедж команды в рамках Django это возможность запустить скрипт из консоли, для выполнения абсолютно различных действий. +Manage-команды в рамках Django - это возможность запустить скрипт из консоли для выполнения абсолютно различных действий. -Существует три способа запуска менедж команды +Существует три способа запуска manage-команды: ``` django-admin [options] @@ -12,11 +12,11 @@ python manage.py [options] python -m django [options] ``` -В случае запуска через `django-admin` вы можете указать какой файл настроек использовать при помощи опции `--settings`. +В случае запуска через `django-admin` вы можете указать, какой файл настроек использовать при помощи опции `--settings`. -Если вы запускаете команду через `manage.py` (самый распространенный способ) файл настроек будет выбран в соответствии с -самим файлом `manage.py` (Напоминаю структуру, информация о файле настроек для Django проекта находится именно в -файле `manage.py`) +Если вы запускаете команду через `manage.py` (самый распространенный способ), файл настроек будет выбран в соответствии +с самим файлом `manage.py` (Напоминаю структуру, информация о файле настроек для Django проекта находится именно в +файле `manage.py`). Мы использовали некоторые команды, но давайте посмотрим подробнее. @@ -30,15 +30,15 @@ python -m django [options] python manage.py check [app_label [app_label ...]] ``` -Например +Например: ``` django-admin check auth admin myapp ``` -Команда для запуска проверки кода на качество (например, что неправильно указаны аргументы модели, или некорректно -указано свойство для класса админки, итд. список огромный) -Посмотреть базовый список проверок можно [Тут](https://docs.djangoproject.com/en/3.1/ref/checks/) +Команда для запуска проверки кода на качество (например, что неправильно указаны аргументы модели или некорректно +указано свойство для класса админки и т. д., список огромный) +Посмотреть базовый список проверок можно [Тут](https://docs.djangoproject.com/en/4.2/ref/checks/) ### makemessages @@ -46,23 +46,23 @@ django-admin check auth admin myapp python manage.py makemessages ``` -Командна для работы с переводами(локализацией) сайтов. +Командна для работы с переводами (локализацией) сайтов. -Команда проходит через весь код и ищет места которое заготовлены для перевода (Для python кода, это везде где вы -используете метод `gettext`, для шаблонов везде где используется темплейт тег `translate`, -подробнее [Тут](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/)) +Команда проходит через весь код и ищет места, которые заготовлены для перевода (для Python кода - это везде, где вы +используете метод `gettext`, для шаблонов - везде, где используется темплейт тег `translate`, +подробнее [Тут](https://docs.djangoproject.com/en/4.2/topics/i18n/translation/)). -Создаёт\Обновляет файлы в которых хранятся\будут храниться переводы текста на друге языки. Принимает параметры `--all` -, `--extension`, `--locale`, `--exclude`, `--domain`, `--ignore` итд. +Создаёт\обновляет файлы, в которых хранятся\будут храниться переводы текста на друге языки. Принимает параметры `--all` +, `--extension`, `--locale`, `--exclude`, `--domain`, `--ignore` и т. д. Подробности использования -параметров [тут](https://docs.djangoproject.com/en/3.1/ref/django-admin/#django-admin-makemessages) +параметров [тут](https://docs.djangoproject.com/en/4.2/ref/django-admin/#django-admin-makemessages) Обсудим основные. -`--locale LOCALE, -l LOCALE` нужно, что бы указать на какой язык планируется перевод (на самом деле повлияет только на то, -как будет называться файл с переводами, и как этот перевод будет называться в системе), например для французского можно -назвать файл `fr`, для итальянского `it` итд. +`--locale LOCALE, -l LOCALE` нужно для указания языка, на который планируется перевод (на самом деле повлияет только +на то, как будет называться файл с переводами, и как этот перевод будет называться в системе), например, для +французского можно назвать файл `fr`, для итальянского `it` и т. д. ``` django-admin makemessages --locale=pt_BR @@ -71,10 +71,10 @@ django-admin makemessages -l pt_BR django-admin makemessages -l pt_BR -l fr ``` -`--ignore PATTERN` - Игнорировать (не искать) переводы в определённых местах, например `--ignore *.py' - игнорировать -все python файлы. +`--ignore PATTERN` - игнорировать (не искать) переводы в определённых местах, например, `--ignore *.py' - игнорировать +все Python файлы. -Создаст файлы с расширением `.po` и списком всех мест где нужно будет указать перевод +Создаст файлы с расширением `.po` и списком всех мест, где нужно будет указать перевод. ``` #. Translators: This message appears on the home page only @@ -83,14 +83,14 @@ msgid "Welcome to my site." msgstr "" ``` -Комментом указано, откуда конкретно взят текст для перевода, ниже сам текст который нужно перевести, и место где мы +Комментарием указано, откуда конкретно взят текст для перевода, ниже сам текст, который нужно перевести, и место, где мы можем указать перевод. ### compilemessages Компилирует файлы для переводов. -Делает из `.po` файлов `.mo` файлы. Django принимает именно `.mo` как файлы откуда брать перевод. +Делает из `.po` файлов `.mo` файлы. Django принимает именно `.mo` в качестве файлов, откуда брать перевод. Поддерживает указание локали и игнор, подробнее в доке. @@ -100,53 +100,53 @@ msgstr "" ### shell -Уже известная вам команда `shell` открывает интерактивную `python` консоль, с уже импортированными библиотеками вашего -проекта, например Django. +Уже известная вам команда `shell` открывает интерактивную `python` консоль с уже импортированными библиотеками вашего +проекта, например, Django. ### dbshell По аналогии со знакомой нам командой `shell` открывает консоль со всеми необходимыми импортированными данными, но для базы данных. -Например, для postgres, откроется `psql` итд. +Например, для PostgreSQL, откроется `psql` и т. д. ### diffsettings -Команда, которая покажет чем, отличается ваш файл `settings.py` от оригинала. +Команда, которая покажет, чем отличается ваш файл `settings.py` от оригинала. ### dumpdata Команда для работы с фикстурами. -Фикстуры, это файлы отображения базы данных в формат JSON. +Фикстуры - это файлы отображения базы данных в формат JSON. -Команда `dumpdata` вытащит все данные из базы данных, и преобразует всё в формат JSON. +Команда `dumpdata` вытащит все данные из базы данных и преобразует всё в формат JSON. -Может принимать имя только нескольких приложений, или даже только некоторых моделей, или наоборот исключить какие-то -приложения или модели +Может принимать имя только нескольких приложений или даже только некоторых моделей, или наоборот - исключить какие-то +приложения или модели. ### loaddata -Команда, обратная команде `dumpdata`. Для загрузки JSON файла в базу данных. +Команда, обратная команде `dumpdata`, для загрузки JSON файла в базу данных. -Подробно будем рассматривать эти командны на практике занятия по тестированию django. +Подробно будем рассматривать эти командны на практике во время занятия по тестированию Django. ### flush -Команда необходимая для очистки базы данных, но не отмены миграций (Сохраняем структуру, теряем все данные) +Команда, необходимая для очистки базы данных, но не отмены миграций (сохраняем структуру, теряем все данные). ### sqlflush -Отпечатает какой SQL код будет выполнен при применении команды `flush` +Отпечатает, какой SQL код будет выполнен при применении команды `flush`. ### inspectdb -Команда необходимая для проверки соответствия ваших моделей и вашей базы данных. Незаменимо при переносе проекта из вне +Команда, необходимая для проверки соответствия ваших моделей и вашей базы данных. Незаменимо при переносе проекта извне на Django. ### makemigrations -Уже известная вам команда, которая создаёт файлы миграций, и может принимать имя приложения, что бы создать только для +Уже известная вам команда, которая создаёт файлы миграций, и может принимать имя приложения, чтобы создать только для конкретного приложения. Может принимать важный параметр `--empty`, при этом флаге создастся пустая миграция, никак не привязанная к моделям. @@ -167,15 +167,15 @@ class Migration(migrations.Migration): ] ``` -Тут указано приложение для которого миграция будет применена, и прошлая миграция с которой текущая миграция будет +Тут указано приложение, для которого миграция будет применена, и прошлая миграция, с которой текущая миграция будет связана. Зачем это вообще надо? -Мы можем в операции добавить любые интересующие нас действия, например выполнения кода на python +Мы можем в операции добавить любые интересующие нас действия, например, выполнения кода на Python. Для этого нужно добавить класс `RunPython` из пакета `migrations`, который будет принимать два метода, первый будет -выполнен в случае выполнения миграции, второй в случае отката миграции. +выполнен в случае выполнения миграции, второй - в случае отката миграции. ```python # Generated by Django 3.0.7 on 2020-10-29 11:59 @@ -204,9 +204,9 @@ class Migration(migrations.Migration): ] ``` -Такие миграции называются **Data Migrations** +Такие миграции называются **Data Migrations**. -Чаще всего для того что бы занести какие-либо данные в базу данных на этапе миграции, например создать заведомо +Чаще всего для того, чтобы занести какие-либо данные в базу данных на этапе миграции, например, создать заведомо известные объекты, как в моём примере, или для установки вычисляемого значения по умолчанию. Для обратной миграции чаще всего действия не требуются (хоть и далеко не всегда), поэтому чаще всего обратная миграция @@ -214,7 +214,7 @@ class Migration(migrations.Migration): ![](https://lh3.googleusercontent.com/proxy/a3WuV3A8umBdVGJA7UVrM50cqloRtS9MhMcq9GYmYwwExEAnYkqNVajL5BBHi_3gwnu4-3s8xDHzTQStrjgrMDZg5lQ) -Типовая Data Migrations: +Типовая Data Migration: ```python # Generated by Django 3.0.7 on 2020-10-29 11:59 @@ -247,37 +247,37 @@ class Migration(migrations.Migration): django-admin migrate [app_label] [migration_name] ``` -Может быть указано приложение к которому применяется, и имя миграции (на самом деле достаточно первых четырех цифр), -указывание имени нужно для отката миграций, допустим у вас уже применена миграция номер 8, а вы поняли, что проблема -была в миграции номер 6, это значит что можно откатить базу до миграции номер 5. Естественно с потерей данных, и провести -новые миграции, для этого нужно сделать: +Может быть указано приложение, к которому применяется, и имя миграции (на самом деле достаточно первых четырех цифр). +Указывание имени нужно для отката миграций. Допустим, у вас уже применена миграция номер 8, а вы поняли, что проблема +была в миграции номер 6, это значит, что можно откатить базу до миграции номер 5. Естественно с потерей данных, и +провести новые миграции, для этого нужно сделать: ``` manage.py migrate my_app 0005 ``` -Важным флагом является ```--fake```, при применении этого флага, изменения в базу внесены не будут, но Django будет -видеть, что миграция была применена, нужно, что бы использовать базы с уже заполненными данными, созданными вне django +Важным флагом является ```--fake```, при применении этого флага изменения в базу внесены не будут, но Django будет +видеть, что миграция была применена. Нужно, чтобы использовать базы с уже заполненными данными, созданными вне Django проекта. -Вместо цифр можно указать значение *zero*, что позволяет откатить всю миграции для этого приложения. +Вместо цифр можно указать значение *zero*, что позволяет откатить все миграции для этого приложения. ### sqlmigrate -Отпечатает какой SQL код будет выполнен при применении команды `migrate` +Отпечатает, какой SQL код будет выполнен при применении команды `migrate` ### showmigrations -Также уже известная вам команда, которая отобразит список миграций, и их состояние (Применена или нет) +Также уже известная вам команда, которая отобразит список миграций и их состояние (применена или нет). ### runserver Команда для запуска тестового сервера, можно указывать порт и многие другие настройки **Не применяется на продакшене, -только для разработки**. Как это делается на продакшене рассмотрим в следующих лекциях. +только для разработки**. Как это делается на продакшене, рассмотрим в следующих лекциях. ### sendtestemail -Отправка тестового имейла (работает только если отправка писем была настроена) принимает два параметра, от кого и кому. +Отправка тестового имейла (работает, только если отправка писем была настроена) принимает два параметра - от кого и кому. Например: @@ -287,19 +287,19 @@ manage.py migrate my_app 0005 Команда для сброса последовательностей базы данных, может принимать название приложения. -Если вы удалите все объекты из базы, и начнёте создавать новые, id будут продолжаться в не зависимости от того сколько -объектов было раньше, потому что `id` вычисляется из специальных объектов базы которые называются `sequence`. +Если вы удалите все объекты из базы и начнёте создавать новые, id будут продолжаться вне зависимости от того, сколько +объектов было раньше, потому что `id` вычисляется из специальных объектов базы, которые называются `sequence`. -Если из сбросить, то id будет назначаться снова с `1`. +Если их сбросить, то `id` будет назначаться снова с `1`. **Не применять на базах с данными!!** ### squashmigrations -Команда, которая применяется для того, что бы `сжать` несколько миграций в одну. +Команда, которая применяется для того, чтобы `сжать` несколько миграций в одну. -Например, в приложении `myapp` миграции от 4-ой до 7-ой это добавления новых полей в одну и туже модель, что бы сжать эти -миграции в одну нужно выполнить +Например, в приложении `myapp` миграции от 4-ой до 7-ой - это добавления новых полей в одну и ту же модель. Чтобы сжать +эти миграции в одну, нужно выполнить: ```python manage.py squashmigrations myapp 0004 0007``` @@ -321,13 +321,13 @@ manage.py migrate my_app 0005 ### changepassword -Команда для смены пароля конкретному пользователю +Команда для смены пароля конкретному пользователю. ```manage.py changepassword ringo``` ### createsuperuser -Команда для создания пользователя со всеми правами +Команда для создания пользователя со всеми правами. ### django.contrib.sessions @@ -338,20 +338,20 @@ manage.py migrate my_app 0005 ### django.contrib.staticfiles -Команды для статики, вообще работу статики и медиа, рассмотрим на следующих занятиях +Команды для статики, вообще работу статики и медиа рассмотрим на следующих занятиях. ## Написание своих скриптов -![](https://lh3.googleusercontent.com/proxy/Tj0Ro_XqtHiWbvJP08D-uhTnI6sGCjJ6qtnzcN5iHgUFdgC21si6dj7mPyso3B0IsiDwkaafjR4IkFVSGahEnbwbxyw) +[//]: # (![](https://lh3.googleusercontent.com/proxy/Tj0Ro_XqtHiWbvJP08D-uhTnI6sGCjJ6qtnzcN5iHgUFdgC21si6dj7mPyso3B0IsiDwkaafjR4IkFVSGahEnbwbxyw)) -По факту все выше описанные команды написаны на python, а это значит, что мы можем написать свои команды. +По факту, все вышеописанные команды написаны на Python, а это значит, что мы можем написать свои команды. -Допустим у нас есть проект пиццерии в рамках которого есть приложение `orders` отвечающее за заказы, и мы хотим, что -бы все заказы которые не были закрыты вручную, ровно в 18:00 были переведены в статус, для ручной проверки, а владелец +Допустим, у нас есть проект пиццерии, в рамках которого есть приложение `orders`, отвечающее за заказы, и мы хотим, +чтобы все заказы, которые не были закрыты вручную, ровно в 18:00 были переведены в статус для ручной проверки, а владелец заведения получил письмо о том, что такие заказы есть. -Самый простой путь создать manage команду. В приложении создадим папку `management`, а в ней папку `commands`, названия -созданных в этой папке файлов будет соответствовать кастомной manage команде. +Самый простой путь - это создать `manage-команду`. В приложении создадим папку `management`, а в ней папку `commands`, +названия созданных в этой папке файлов будет соответствовать кастомной manage-команде. ``` orders/ @@ -364,7 +364,7 @@ orders/ views.py ``` -В файле нужно создать класс наследованный от `BaseCommand`: +В файле нужно создать класс, наследованный от `BaseCommand`: ```python from django.core.management.base import BaseCommand @@ -387,15 +387,15 @@ class Command(BaseCommand): ```python manage.py close_orders``` -Теперь можно при помощи любой утилиты для работы с консолью поставить задачу в расписание, например для UNIX систем, -можно использовать CRON +Теперь можно при помощи любой утилиты для работы с консолью поставить задачу в расписание, например, для UNIX систем +можно использовать CRON. -``` +```python 0 18 * * 1-5 /some/path/pizza/manage.py close_orders ``` ## Задания 1. Снять фикстуру с вашей базы, изучить полученный файл. -2. Для вашего модуля создать дата миграцию, которая будет создавать два новых товаров. -3. Написать manage команду, что бы отклонить все заявки на возврат. \ No newline at end of file +2. Для вашего модуля создать дата миграцию, которая будет создавать два новых товара. +3. Написать manage команду, чтобы отклонить все заявки на возврат. From 8c60d694feb3f510a1b7fc0da50207f0eada0d07 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Fri, 28 Jul 2023 16:10:23 +0300 Subject: [PATCH 10/23] edit grammar and style; update links --- lesson37.md | 243 ++++++++++++++++++++++++++-------------------------- 1 file changed, 123 insertions(+), 120 deletions(-) diff --git a/lesson37.md b/lesson37.md index ab8d89e96..da5232613 100644 --- a/lesson37.md +++ b/lesson37.md @@ -1,21 +1,21 @@ # Урок 37. Что такое API. REST и RESTful. Django REST Framework. -![](https://www.meme-arsenal.com/memes/6200d14d795eab11d26a3afabed68439.jpg) +[/не отображается/]: #(![](https://www.meme-arsenal.com/memes/6200d14d795eab11d26a3afabed68439.jpg)) ## Что же такое API? Итак, начнём с определения. API (Application Programming Interface) — это интерфейс программирования, интерфейс создания приложений. -В нашем конкретном случае, под API практически всегда будет подразумеваться REST API о котором дальше, но для нас это -эндпоинт (урл, на который можно отправить запрос) который выполняет какие-либо действия, или возвращает нам какую либо -информацию. +В нашем конкретном случае под API практически всегда будет подразумеваться REST API, о котором мы поговорим дальше. +Сейчас для нас - это endpoint (url, на который можно отправить запрос), который выполняет какие-либо действия или +возвращает нам информацию. ## Что такое REST? -![](https://i1.wp.com/www.ybouglouan.pl/wp-content/uploads/2017/04/rest_api_sortof.jpg) +[/не отображается/]: # (![](https://i1.wp.com/www.ybouglouan.pl/wp-content/uploads/2017/04/rest_api_sortof.jpg)) -REST (Representational State Transfer — «передача состояния представления») - по сути, это архитектурный стиль +REST (Representational State Transfer — «передача состояния представления») - это архитектурный стиль (рекомендации к разработке), но это в теории, практику рассмотрим дальше. ![](https://automated-testing.info/uploads/default/original/2X/e/eaf77634076d45e18c501abf936a9a8ad1913bb4.png) @@ -24,15 +24,15 @@ REST (Representational State Transfer — «передача состояния Свойства архитектуры, которые зависят от ограничений, наложенных на REST-системы: -1. **Client-Server**. Система должна быть разделена на клиентов, и на сервер(ы). Разделение интерфейсов означает, что, - например, клиенты не связаны с хранением данных, которое остается внутри каждого сервера, так что мобильность кода +1. **Client-Server**. Система должна быть разделена на клиентов и на сервер(ы). Разделение интерфейсов означает, что + клиенты не связаны с хранением данных, которое остается внутри каждого сервера, так что мобильность кода клиента улучшается. Серверы не связаны с интерфейсом пользователя или состоянием, так что серверы могут быть проще и масштабируемы. Серверы и клиенты могут быть заменяемы и разрабатываться независимо, пока интерфейс не изменяется. 2. **Stateless**. Сервер не должен хранить какой-либо информации о клиентах. В запросе должна храниться вся необходимая - информация для обработки запроса и если необходимо, идентификации клиента. + информация для обработки запроса и, если необходимо, идентификации клиента. -3. **Cache**․ Каждый ответ должен быть отмечен является ли он кэшируемым или нет, для предотвращения повторного +3. **Cache**․ Каждый ответ должен быть отмечен, является ли он кэшируемым или нет, для предотвращения повторного использования клиентами устаревших или некорректных данных в ответ на дальнейшие запросы. 4. **Uniform Interface**. Единый интерфейс определяет интерфейс между клиентами и серверами. Это упрощает и отделяет @@ -41,7 +41,7 @@ REST (Representational State Transfer — «передача состояния Четыре принципа единого интерфейса: 4.1) **Identification of resources (основан на ресурсах)**. В REST ресурсом является все то, чему можно дать имя. - Например, пользователь, изображение, предмет (майка, голодная собака, текущая погода) и т.д. Каждый ресурс в REST + Например, пользователь, изображение, предмет (майка, голодная собака, текущая погода) и т. д. Каждый ресурс в REST должен быть идентифицирован посредством стабильного идентификатора, который не меняется при изменении состояния ресурса. Идентификатором в REST является URI. @@ -50,33 +50,33 @@ REST (Representational State Transfer — «передача состояния текущее или желаемое состояние ресурса. Например, если ресурсом является пользователь, то представлением может являться XML или HTML описание этого пользователя. - 4.3) **Self-descriptive messages (само-документируемые сообщения)**. Под само-описательностью имеется ввиду, что + 4.3) **Self-descriptive messages (само-документируемые сообщения)**. Под само-описательностью имеется в виду, что запрос и ответ должны хранить в себе всю необходимую информацию для их обработки. Не должны быть дополнительные - сообщения или кэши для обработки одного запроса. Другими словами отсутствие состояния, сохраняемого между запросами к + сообщения или кэши для обработки одного запроса. Другими словами, отсутствие состояния, сохраняемого между запросами к ресурсам. Это очень важно для масштабирования системы. 4.4) **HATEOAS (hypermedia as the engine of application state)**. Статус ресурса передается через содержимое body, параметры строки запроса, заголовки запросов и запрашиваемый URI (имя ресурса). Это называется гипермедиа (или - гиперссылки с гипертекстом). HATEOAS также означает, что, в случае необходимости ссылки могут содержаться в теле + гиперссылки с гипертекстом). HATEOAS также означает, что в случае необходимости ссылки могут содержаться в теле ответа (или заголовках) для поддержки URI, извлечения самого объекта или запрошенных объектов. 5. Layered System. В REST допускается разделить систему на иерархию слоев, но с условием, что каждый компонент может - видеть компоненты только непосредственно следующего слоя. Например, если вы вызываете службу PayPal, а он в свою + видеть компоненты только непосредственно следующего слоя. Например, если вы вызываете службу PayPal, а она в свою очередь вызывает службу Visa, вы о вызове службы Visa ничего не должны знать. 6. Code-On-Demand (опционально). В REST позволяется загрузка и выполнение кода или программы на стороне клиента. Если выполнены первые 4 пункта и не нарушены 5 и 6, такое приложение называется **RESTful** -**Важно!** Сама архитектура REST не привязана к конкретным технологиям и протоколам, но в реалиях современного Веб, +**Важно!** Сама архитектура REST не привязана к конкретным технологиям и протоколам, но в реалиях современного WEB, построение RESTful API почти всегда подразумевает использование HTTP и каких-либо распространенных форматов -представления ресурсов, например JSON, или, менее популярного сегодня, XML. +представления ресурсов, например, JSON, или менее популярного сегодня XML. ### Идемпотентность ![](http://risovach.ru/upload/2015/12/mem/kot-bezyshodnost_100253424_orig_.jpg) -С точки зрения RESTful-сервиса, операция (или вызов сервиса) идемпотентна тогда, когда клиенты могут делать один и тот +С точки зрения RESTful-сервиса операция (или вызов сервиса) идемпотентна тогда, когда клиенты могут делать один и тот же вызов неоднократно при одном и том же результате на сервере. Другими словами, создание большого количества идентичных запросов имеет такой же эффект, как и один запрос. Заметьте, что в то время, как идемпотентные операции производят один и тот же результат на сервере, ответ сам по себе может не быть тем же самым (например, состояние ресурса может @@ -84,11 +84,11 @@ REST (Representational State Transfer — «передача состояния Методы PUT и DELETE по определению идемпотентны. Тем не менее есть один нюанс с методом DELETE. Проблема в том, что успешный DELETE-запрос возвращает статус 200 (OK) или 204 (No Content), но для последующих запросов будет все время -возвращать 404 (Not Found), Состояние на сервере после каждого вызова DELETE то же самое, но ответы разные. +возвращать 404 (Not Found). Состояние на сервере после каждого вызова DELETE то же самое, но ответы разные. Методы GET, HEAD, OPTIONS и TRACE определены как безопасные. Это означает, что они предназначены только для получения информации и не должны изменять состояние сервера. Они не должны иметь побочных эффектов, за исключением безобидных -эффектов, таких как: логирование, кеширование, показ баннерной рекламы или увеличение веб-счетчика. +эффектов таких как: логирование, кеширование, показ баннерной рекламы или увеличение веб-счетчика. По определению, безопасные операции идемпотентны, так как они приводят к одному и тому же результату на сервере. Безопасные методы реализованы как операции только для чтения. Однако безопасность не означает, что сервер должен @@ -98,70 +98,72 @@ REST (Representational State Transfer — «передача состояния ![](http://img1.reactor.cc/pics/post/http-status-code-it-http-%D0%BA%D0%BE%D1%82%D1%8D-4397611.jpeg) -1xx: Information +Полный список кодов состояний [тут](https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml). -100: Continue +**1xx: Information** -2xx: Success + 100: Continue -200: OK +**2xx: Success** -201: Created + 200: OK -202: Accepted + 201: Created -204: No Content + 202: Accepted -3xx: Redirect + 204: No Content -301: Moved Permanently +**3xx: Redirect** -307: Temporary Redirect + 301: Moved Permanently -4xx: Client Error + 307: Temporary Redirect -400: Bad Request +**4xx: Client Error** -401: Unauthorized + 400: Bad Request -403: Forbidden + 401: Unauthorized -404: Not Found + 403: Forbidden -5xx: Server Error + 404: Not Found -500: Internal Server Error +**5xx: Server Error** -501: Not Implemented + 500: Internal Server Error -502: Bad Gateway + 501: Not Implemented -503: Service Unavailable + 502: Bad Gateway -504: Gateway Timeout + 503: Service Unavailable + + 504: Gateway Timeout ### Postman -На практике обычно бекенд разработчики вообще не имеют отношения к тому что происходит на фронте (Если ты не фулстек:). -А только подготавливают для фронта апи для различных действий, чаще всего CRUD. +На практике обычно backend-разработчики вообще не имеют отношения к тому, что происходит на фронте (если ты не +fullstack :) ). А только подготавливают для фронта API для различных действий, чаще всего CRUD. Для проверки работоспособности API чаще всего используется **postman** скачать можно [ТУТ](https://www.getpostman.com/) -Это программа, которая позволяет создавать запросы любой сложности к серверу. Рекомендую разобраться как этим -пользоваться подробно. +Это программа, которая позволяет создавать запросы любой сложности к серверу. Рекомендую тщательно разобраться, как этим +пользоваться. ### Общая информация. -Хоть REST не является протоколом, но в современном вебе это почти всегда HTTP и JSON. +Хоть REST и не является протоколом, но в современном вебе это почти всегда HTTP и JSON. ### JSON -JSON (JavaScript Object Notation) - Текстовый формат обмена данными, легко читается, очень похож на словарь в python. +JSON (JavaScript Object Notation) - текстовый формат обмена данными, легко читается, очень похож на словарь в Python. -## Как это работает на практике и при чём тут Django +## Как это работает на практике и при чём тут Django? Для Django существует несколько различных пакетов для применения REST архитектуры, но основным является **Django REST -Framework** дока [тут](https://www.django-rest-framework.org/) +Framework** дока [тут](https://www.django-rest-framework.org/). ### Установка @@ -171,9 +173,9 @@ Framework** дока [тут](https://www.django-rest-framework.org/) ## Сериалайзеры -Сериалайзер в DRF - это класс для преобразования данных в требуемый формат (Обычно JSON). +Сериалайзер в DRF - это класс для преобразования данных в требуемый формат (обычно JSON). -Допустим у нас есть такая модель: +Допустим, у нас есть такая модель: ```python from django.db import models @@ -197,7 +199,7 @@ class Snippet(models.Model): ordering = ['created'] ``` -То мы можем описать сериалайзер так: +Мы можем описать сериалайзер так: ```python from rest_framework import serializers @@ -231,7 +233,7 @@ class SnippetSerializer(serializers.Serializer): return instance ``` -Как мы можем этим пользоваться, в shell: +Как мы можем этим пользоваться? В shell: ```python from snippets.models import Snippet @@ -248,7 +250,7 @@ serializer.data # {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'} ``` -Преобразование +Преобразование: ```python import json @@ -265,39 +267,39 @@ json.loads(string) # # Преобразовать cтроку в JSON Любое из полей может иметь такие аргументы: -`read_only` - Поле только для чтения, используется для полей которые не планируются к заполнению (например время -создания комментария), но планируются к чтению (например отобразить когда был написан комментарий). Такие поля не -принимаются при создании или изменении. По дефолту False +`read_only` - поле только для чтения. Используется для полей, которые не планируются к заполнению (например, время +создания комментария), но планируются к чтению (например, отобразить, когда был написан комментарий). Такие поля не +принимаются при создании или изменении. По дефолту False. -`write_only` - Ровно наоборот, поля не планируемые для отображения, но необходимые для записи (Пароль, номер карточки, -итд.). По дефолту False +`write_only` - ровно наоборот. Поля, не планируемые для отображения, но необходимые для записи (пароль, номер карточки, +и т. д.). По дефолту False. -`required` - Обязательность поля, поле которое можно не указывать при создании\изменении, но его же может и не быть при -чтении, допустим отчества. По дефолту True +`required` - обязательность поля. Поле, которое можно не указывать при создании/изменении, но его же может не быть при +чтении, допустим, отчества. По дефолту True. -`default` - Значение по умолчанию, если не указано ничего другого. Не поддерживается при частичном обновлении, +`default` - значение по умолчанию, если не указано ничего другого. Не поддерживается при частичном обновлении. -`allow_null` - Позволить значению поля быть None. По дефолту False. +`allow_null` - позволить значению поля быть None. По дефолту False. -`source` - Поле значение которого необходимо получить в модели, допустим при помощи какого-то метода (вычисление полного -адреса из его частей при помощи метода модели и декоратора property, `CharField(source='get_full_address')`), или из -какого-то вложенного объекта (Foreign Key на юзера, но необходим только его имейл, а не целый -объект, `EmailField(source='user.email')`). Имеет спец значение `*` обозначает что источник данных будет передан позже, -тогда его нужно будет указать в необходимых методах. По дефолту, это имя поля. +`source` - поле, значение которого необходимо получить в модели, допустим, при помощи какого-то метода (вычисление +полного адреса из его частей при помощи метода модели и декоратора @property, `CharField(source='get_full_address')`), +или из какого-то вложенного объекта (Foreign Key на юзера, но необходим только его имейл, а не целый +объект, `EmailField(source='user.email')`). Имеет спец значение: `*` обозначает, что источник данных будет передан позже, +тогда его нужно будет указать в необходимых методах. По дефолту - это имя поля. -`validators` - Список валидаторов, о нём поговорим ниже +`validators` - список валидаторов, о нём поговорим ниже. -`error_messages` - Словарь с кодом ошибок. +`error_messages` - словарь с кодом ошибок. -Есть и другие, но это наиболее используемые. +Есть и другие, но эти наиболее используемые. -У разных полей могут быть свои атрибуты, такие как максимальная длинна, или кол-знаков после запятой. +У разных полей могут быть свои атрибуты, такие как максимальная длина, или количество знаков после запятой. -Виды полей, по аналогии с моделями и формами, могут быть практически какими угодно, за деталями в доку. +Виды полей по аналогии с моделями и формами могут быть практически какими угодно, за деталями в доку. ### Специфичные поля -ListField - поле для передачи списка. +`ListField` - поле для передачи списка. Сигнатура `ListField(child=, allow_empty=True, min_length=None, max_length=None)` ```python @@ -306,21 +308,21 @@ scores = serializers.ListField( ) ``` -DictField - Поле для передачи словаря. Сигнатура `DictField(child=, allow_empty=True)` +DictField - поле для передачи словаря. Сигнатура `DictField(child=, allow_empty=True)` ```python document = DictField(child=CharField()) ``` -HiddenField - Скрытое поле, может быть нужно для валидаций. +HiddenField - скрытое поле, может быть нужно для валидаций. ```python modified = serializers.HiddenField(default=timezone.now) ``` -SerializerMethodField - Поле основанное на методе +`SerializerMethodField` - поле, основанное на методе. -Сигнатура `SerializerMethodField(method_name=None)`, `method_name` - название метода, по дефолту `get_` +Сигнатура: `SerializerMethodField(method_name=None)`, `method_name` - название метода, по дефолту `get_` ```python from django.contrib.auth.models import User @@ -349,7 +351,7 @@ serializer.save() # True ``` -По аналогии с формами, мы можем добавить валидацию каждого отдельного поля, при помощи метода `validate_` +По аналогии с формами мы можем добавить валидацию каждого отдельного поля при помощи метода `validate_` ```python from rest_framework import serializers @@ -368,9 +370,9 @@ class BlogPostSerializer(serializers.Serializer): return value ``` -Возвращает значение, или рейзит ошибку валидации. +Возвращает значение или возбуждает ошибку валидации. -Так же валидация может быть осуществлена на уровне объекта. Метод `validate`. +Также валидация может быть осуществлена на уровне объекта. Метод `validate()`. ```python from rest_framework import serializers @@ -390,7 +392,7 @@ class EventSerializer(serializers.Serializer): return data ``` -Так же можно прописать валидаторы как отдельные функции: +Также можно прописать валидаторы как отдельные функции: ```python def multiple_of_ten(value): @@ -421,7 +423,7 @@ class EventSerializer(serializers.Serializer): ] ``` -Так же мы можем передать в сериалайзер список или кверисет из объектов указав при этом атрибут `many=True` +Также мы можем передать в сериалайзер список или queryset из объектов, указав при этом атрибут `many=True` ```python serializer = SnippetSerializer(Snippet.objects.all(), many=True) @@ -429,7 +431,7 @@ serializer.data # [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello, world")'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] ``` -По аналогии с Формами и МоделФормами, у сериалайзеров существуют МоделСериалайзеры +По аналогии с Формами и ModelForm, у сериалайзеров существуют ModelSerializer. ```python class SnippetSerializer(serializers.ModelSerializer): @@ -454,11 +456,11 @@ print(repr(serializer)) # style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')... ``` -Чаще всего вы будете пользоваться именно сериалайзерами моделей. +Чаще всего вы будете пользоваться именно ModelSerializer. ### Вложенные сериалайзеры: -Сериалайзер, может полем другого сериалайзера, такие сериалайзеры, называются вложенными. +Сериалайзер может быть полем другого сериалайзера. Такие сериалайзеры называются вложенными. ```python class UserSerializer(serializers.Serializer): @@ -472,8 +474,8 @@ class CommentSerializer(serializers.Serializer): created = serializers.DateTimeField() ``` -Совмещаем с предыдущими знаниями, и получаем вложенное поле с атрибутом many=True, а значит что оно принимает список или -кверисет таких объектов: +Совмещаем с предыдущими знаниями и получаем вложенное поле с атрибутом `many=True`, а значит оно принимает список или +queryset таких объектов: ```python class CommentSerializer(serializers.Serializer): @@ -485,8 +487,8 @@ class CommentSerializer(serializers.Serializer): ### Передача данных в разные стороны: -Обратите внимание, что когда мы обрабатываем данные полученные от пользователя(например запрос), то мы передаём данные в -сериалайзер через атрибут, `data=`, и после этого обязаны провалидировать данные, так как там могут быть ошибки: +Обратите внимание, что когда мы обрабатываем данные, полученные от пользователя (например запрос), то мы передаём +данные в сериалайзер через атрибут, `data=` и после этого обязаны провалидировать данные, так как там могут быть ошибки: ```python serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'}) @@ -496,10 +498,10 @@ serializer.errors # {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']} ``` -Если ошибок нет, то данные будут находиться в атрибуте `validated_data` +Если ошибок нет, то данные будут находиться в атрибуте `validated_data`. -Но если мы сериализуем данные которые мы взяли из базы то у нас нет необходимости их валидировать. Мы передаём их без -каких либо аттрибутов, данные будут находиться в аттрибуте `data`: +Но если мы сериализуем данные, которые мы взяли из базы, то у нас нет необходимости их валидировать. Мы передаём их без +каких-либо атрибутов, данные будут находиться в атрибуте `data`: ```python comment = Comment.objects.first() @@ -507,10 +509,10 @@ serializer = CommentSerializer(comment) serializer.data ``` -### Метод save +### Метод save() -У моделсериалайзеров, по аналогии с моделформами есть метод `save()`, но в отличие от моделформ, дополнительные данные -можно передать прямо в атрибуты метода `save`. +У ModelSerializer по аналогии с ModelForm есть метод `save()`, но в отличие от ModelForm дополнительные данные +можно передать прямо в атрибуты метода `save()`. ```python e = EventSerializer(data={'start': "05/05/2021", 'finish': "06/05/2021"}) @@ -519,10 +521,10 @@ e.save(description='bla-bla') ## Связи в сериалайзерах -Все мы знаем, что бывают связи в базе данных. Данные нужно каким то образом получать, но в случае сериализации нам, -часто нет необходимости получать весь объект, а нужны, допустим, только id, или название. DRF это предусмотрел. +Все мы знаем, что бывают связи в базе данных. Данные нужно каким-то образом получать, но в случае сериализации нам +часто нет необходимости получать весь объект, а нужны, допустим, только `id` или название. DRF это предусмотрел. -Предположим у нас есть вот такие модели: +Предположим, у нас есть вот такие модели: ```python class Album(models.Model): @@ -544,7 +546,7 @@ class Track(models.Model): return '%d: %s' % (self.order, self.title) ``` -Что бы получить в сериалайзере альбома все его треки, мы можем сделать, например, так: +Чтобы получить в сериалайзере альбома все его треки, мы можем сделать, например, так: ```python class TrackSerializer(serializers.ModelSerializer): @@ -561,9 +563,9 @@ class AlbumSerializer(serializers.ModelSerializer): fields = ['album_name', 'artist', 'tracks'] ``` -Но, есть и другие варианты получение данных. +Но есть и другие варианты получения данных. -### StringRelatedField +### StringRelatedField() ```python class AlbumSerializer(serializers.ModelSerializer): @@ -574,7 +576,7 @@ class AlbumSerializer(serializers.ModelSerializer): fields = ['album_name', 'artist', 'tracks'] ``` -Вернёт значение меджик метода __str__ для каждого объекта: +Вернёт значение dunder-метода `__str__` для каждого объекта: ```json { @@ -588,7 +590,7 @@ class AlbumSerializer(serializers.ModelSerializer): } ``` -### PrimaryKeyRelatedField +### PrimaryKeyRelatedField() ```python class AlbumSerializer(serializers.ModelSerializer): @@ -599,7 +601,7 @@ class AlbumSerializer(serializers.ModelSerializer): fields = ['album_name', 'artist', 'tracks'] ``` -Вернёт id: +Вернёт `id`: ```json { @@ -613,7 +615,7 @@ class AlbumSerializer(serializers.ModelSerializer): } ``` -### HyperlinkedRelatedField +### HyperlinkedRelatedField() ```python class AlbumSerializer(serializers.ModelSerializer): @@ -628,7 +630,7 @@ class AlbumSerializer(serializers.ModelSerializer): fields = ['album_name', 'artist', 'tracks'] ``` -Вернёт ссылку на обработку объекта, как работает эта магия поговорим на следующем занятии. +Вернёт ссылку на обработку объекта. О том, как работает эта магия, поговорим на следующем занятии. ```json { @@ -642,7 +644,7 @@ class AlbumSerializer(serializers.ModelSerializer): } ``` -### SlugRelatedField +### SlugRelatedField() ```python class AlbumSerializer(serializers.ModelSerializer): @@ -657,7 +659,7 @@ class AlbumSerializer(serializers.ModelSerializer): fields = ['album_name', 'artist', 'tracks'] ``` -Вернёт то, что указано в атрибуте `slug_field` +Вернёт то, что указано в атрибуте `slug_field`. ```json { @@ -671,7 +673,7 @@ class AlbumSerializer(serializers.ModelSerializer): } ``` -И другие. О том как сделать записываемые вложенные сериалайзеры и многое другое, читайте в документации. +И другие. О том, как сделать записываемые вложенные сериалайзеры и многое другое, читайте в документации. ## Немного забегая вперёд @@ -710,22 +712,23 @@ urlpatterns = [ ] ``` -# Практика / Домашнее задание: +# Практика / домашнее задание: -1. Создать сериалайзер для обработки данных из 1 задания из лекции про формы (Напишите форму, в которой можно указать - имя, пол, возраст и уровень владения английским (выпадающим списком), если введенные данные это парень старше 20-и ( - включительно) и уровнем английского B2 выше, или девушка старше 22-ух и уровнем выше чем B1 то перейти на страницу - где будет написано, что вы нам подходите, или что не подходит соответственно.) +1. Создать сериалайзер для обработки данных из 1 задания из лекции про формы (напишите форму, в которой можно указать + имя, пол, возраст и уровень владения английским (выпадающим списком), если введенные данные - это парень старше 20-и + (включительно) и уровнем английского выше B2 или девушка старше 22-х и уровнем выше B1, то перейти на страницу, + где будет написано, что вы нам подходите или что не подходите, соответственно.) - 1.1 Зайти в shell. Заполнить сериалайзер через `data=` данными. Убедиться что валидация работает в соответствии с требованиями. Прислать мне скрины. + 1.1 Зайти в shell. Заполнить сериалайзер через `data=` данными. + Убедиться, что валидация работает в соответствии с требованиями. Прислать мне скрины. 2. Создать сериалайзеры для Юзера, Покупки, Товара. - 2.1 Создать объект Юзера, Товара, Покупки, связанных между собой (данные передать через `data=`), прислать мне скрины + 2.1 Создать объект Юзера, Товара, Покупки, связанных между собой (данные передать через `data=`), прислать мне скрины. - 2.2 Получить объекты из базы, передать в сериалайзер без `data=`, посмотреть что у них хранится в атрибуте `.data` + 2.2 Получить объекты из базы, передать в сериалайзер без `data=`, посмотреть, что у них хранится в атрибуте `.data`. -3. Написать сериалайзер для Покупки (новый), который будет хранить вложенный сералайзер Юзера. +3. Написать сериалайзер для Покупки (новый), который будет хранить вложенный сериалайзер Юзера. 3.1 Получить данные любого товара вместе с данными о юзере. Прислать скрины. @@ -733,4 +736,4 @@ urlpatterns = [ 4.1 Получить данные любого юзера, прислать скрины. -5*. Дописать сериалайзеры из пунктов 4 и 5 так, что бы можно было создавать объекты. (Это сложно) \ No newline at end of file +5*. Дописать сериалайзеры из пунктов 4 и 5 так, чтобы можно было создавать объекты. (Это сложно.) From 39ad82232eba12df30a0ecc78e9f294ae9c32d97 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Mon, 31 Jul 2023 00:42:58 +0300 Subject: [PATCH 11/23] edit grammar and style --- lesson15.md | 264 ++++++++++++++++++++++++++-------------------------- 1 file changed, 133 insertions(+), 131 deletions(-) diff --git a/lesson15.md b/lesson15.md index 3f799efd6..449fec9ea 100644 --- a/lesson15.md +++ b/lesson15.md @@ -4,63 +4,64 @@ ## Что такое ООП, и что же такое класс и объект. -Объектно-ориентированное программирование (в дальнейшем ООП) — парадигма программирования, в которой основными +Объектно-ориентированное программирование (в дальнейшем - ООП) — парадигма программирования, в которой основными концепциями являются понятия объектов и классов. Взаимодействие между операциями при помощи объектов. -Есть говорить человеческим языком, то в обычной жизни, мы не оперируем понятиями строка или кортеж, мы оперируем +Есть говорить человеческим языком, то в обычной жизни мы не оперируем понятиями строка или кортеж, мы оперируем объектами. -Мы говорим: "Подай чашку", "Пришли фотку", "Прикольный стол" итд. Так вот чашка, фотка и стол в этих примерах будут -являться объектами. Объекты могут обладать некоторыми атрибутами (цвет, название, размер итд.) и методами (они же -действия, например, на кнопку можно нажать, автомобиль может ехать, карандаш может писать итд.) +Мы говорим: "Подай чашку", "Пришли фотку", "Прикольный стол" и т. д. Так вот чашка, фотка и стол в этих примерах будут +являться объектами. Объекты могут обладать некоторыми атрибутами (цвет, название, размер и т. д.) и методами (они же +действия, например, на кнопку можно нажать, автомобиль может ехать, карандаш может писать и т. д.) В центре ООП находится понятие `объекта`. Объекты создаются на основе `классов`. -Класс это шаблон для создания объекта, допустим у нас есть класс автомобиль, автомобиль это класс, а конкретный лексус -2015 года выпуска, это уже объект класса автомобиль. +Класс - это шаблон для создания объекта. Допустим, у нас есть класс автомобиль, автомобиль - это класс, а конкретный +Lexus 2015 года выпуска - это уже объект класса автомобиль. -Объект — это сущность, экземпляр класса, содержащий свои аттрибуты и свои методы, созданный при помощи шаблона (т.е. -Класса). +Объект — это сущность, экземпляр класса, содержащий свои атрибуты и свои методы, созданный при помощи шаблона (т. е. +класса). -Аттрибут класса это данные принадлежащие классу, например цвет автомобиля (каждый автомобиль какого-то цвета, но пока у -нас нет "экземпляра" мы не можем сказать какого цвета просто понятие автомобиль) +Атрибут класса - это данные, принадлежащие классу, например, цвет автомобиля (каждый автомобиль какого-то цвета, но +пока у нас нет "экземпляра," мы не можем сказать, какого цвета просто понятие автомобиль). -Метод класса, это функция описанная внутри объекта и описывающая определенное действие, например кофемашина делает кофе. +Метод класса - это функция, описанная внутри объекта, которая описывает определенное действие, например, кофемашина +делает кофе. ### Ключевое слово `self` Что такое `self`? `self` - это специальный аргумент в методах класса, который является ссылкой на экземпляр. -В большинстве случаев (когда нет обсудим отдельно на следующих занятиях), первым аргументом любого метода будет `self`. +В большинстве случаев (когда нет, обсудим отдельно на следующих занятиях), первым аргументом любого метода будет `self`. Он обязательный. Чисто технически можно написать любое слово первым аргументом, и это тоже будет работать. Но не надо так делать. Не сбивайте ни себя, ни других разработчиков. Первый аргумент большинства методов это `self`. `self` - это конкретный объект внутри метода класса. Если у нас есть класс студент, то через `self` мы можем получить доступ ко всем атрибутам и методам конкретного -студента, например списку оценок, или методу "прогулять занятие". +студента, например, списку оценок или методу "прогулять занятие". -Доступ к атрибутам и методам получается через точку. +Доступ к атрибутам и методам предоставляется через точку. Еще про `self`: -- у котов внутри есть мурчалка -- она реализована для всех котов в классе Кот -- в объекте кот надо как то вызвать метод мурчало у класса Кот +- у котов внутри есть мурчалка; +- она реализована для всех котов в классе Кот; +- в объекте кот надо как-то вызвать метод мурчало у класса Кот; - как ты это сделаешь? - Кот.мурчало() -- ежели ты вызовешь Кот.мурчало(), муркнут сразу все коты на свете -- а ежели ты вызовешь self.мурчало(), муркнет только тот кот, на которого указывает self +- ежели ты вызовешь Кот.мурчало(), муркнут сразу все коты на свете; +- а ежели ты вызовешь self.мурчало(), муркнет только тот кот, на которого указывает self. -Наконец к коду: +Наконец, к коду: ```python # Используем ключевое слово `class` class Car: - # Опишем класс Машина, у которого будет два атрибута цвет и максимальная скорость. - # Я указал для них типы данных, но для питона это не обязательно, скорее удобный инструмент. + # Опишем класс Машина, у которого будет два атрибута: цвет и максимальная скорость. + # Я указал для них типы данных, но для Python это не обязательно, скорее, удобный инструмент. color: str = 'red' top_speed: int = 250 @@ -70,7 +71,7 @@ class Car: def find_color_and_top_speed(self): return f'this car top speed is {self.top_speed} and color is {self.color}' - # Вернуть булеан который отвечает на вопрос, может ли машина ехать с указанной скоростью + # Вернуть булево значение, которое отвечает на вопрос, может ли машина ехать с указанной скоростью def is_car_can_go_with_needed_speed(self, speed: int): return speed < self.top_speed @@ -78,39 +79,39 @@ class Car: def set_top_speed(self, speed: int): self.top_speed = speed - # Назначить кол-во колес. Обратите внимания, + # Назначить количество колес. Обратите внимания, # такого атрибута изначально вообще не было # (лучше так не делать, но технически нет никаких ограничений) def set_count_of_wheels(self, wheels: int): self.wheels = wheels -# Как создать объект? для этого нужно просто "вызвать" класс +# Как создать объект? Для этого нужно просто "вызвать" класс -lamborgini = Car() -print(lamborgini.find_color_and_top_speed()) # this car top speed is 250 and color is red -print(lamborgini.is_car_can_go_with_needed_speed(200)) # True +lamborghini = Car() +print(lamborghini.find_color_and_top_speed()) # This car's top speed is 250 and color is red +print(lamborghini.is_car_can_go_with_needed_speed(200)) # True # Доступ к атрибутам можно получить напрямую -print(lamborgini.top_speed) # 250 -lamborgini.set_top_speed(150) # Назначаем новую максимальную скорость для этого объекта -print(lamborgini.is_car_can_go_with_needed_speed(200)) # False +print(lamborghini.top_speed) # 250 +lamborghini.set_top_speed(150) # Назначаем новую максимальную скорость для этого объекта +print(lamborghini.is_car_can_go_with_needed_speed(200)) # False cherry = Car() -print(cherry.find_color_and_top_speed()) # this car top speed is 250 and color is red +print(cherry.find_color_and_top_speed()) # This car's top speed is 250 and color is red cherry.top_speed = 140 cherry.color = 'yellow' print(cherry.wheels) # ВЫЗОВЕТ ОШИБКУ, у нас нет такого атрибута cherry.set_count_of_wheels(4) print(cherry.wheels) # Все ок, напечатает 4 -cherry.size = 'small' # Можно добавлять атрибуты к любому объекту если это необходимо, у ламборджини такого атрибута не будет +cherry.size = 'small' # Можно добавлять атрибуты к любому объекту если это необходимо, у ламборгини такого атрибута не будет ``` **В python вообще все является объектом** -И строка, и число, и список, итд. +И строка, и число, и список и т. д. ## Парадигмы ООП -![](https://waksoft.susu.ru/wp-content/uploads/2020/08/oop.jpg) +[/не отображается/]: # (![](https://waksoft.susu.ru/wp-content/uploads/2020/08/oop.jpg)) ООП держится на трёх основных и одной второстепенной парадигме. @@ -122,17 +123,17 @@ cherry.size = 'small' # Можно добавлять атрибуты к лю полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским. Новый класс — потомком, наследником или производным классом. -Например, у нас есть базовый класс автомобиль и три наследника, легковой, самосвал и фура, все три класса могут иметь -общие атрибуты, например двигатель или материал лобового стекла, или методы, например газ и тормоз, но при этом иметь -свои особенные атрибуты или методы, например, только у фуры будет больше чем 4 колеса, или у самосвала, будет метод -поднять кузов, или у фуры отцепить груз. +Например, у нас есть базовый класс автомобиль и три наследника, легковой, самосвал и фура. Все три класса могут иметь +общие атрибуты, например, двигатель или материал лобового стекла, или методы, например, газ и тормоз, но при этом иметь +свои особенные атрибуты или методы, например, только у фуры будет больше 4 колес, у самосвала будет метод +поднять кузов, или у фуры - отцепить груз. -При описании ООП, мне очень нравятся бытовые примеры на автомобилях. Представте что при разработке новой модели BMW, -конструкторы решили бы начисто забыть, о том, что у них уже были предыдущие модели. Тогда им пришлось бы абсолютно +При описании ООП мне очень нравятся бытовые примеры на автомобилях. Представьте, что при разработке новой модели BMW, +конструкторы решили бы начисто забыть о том, что у них уже были предыдущие модели. Тогда им пришлось бы абсолютно каждую новую модель разрабатывать с нуля. Возможно ли это? Конечно. Есть ли в этом необходимость? Очень сомнительно. -Если можно взять прошлую модель немного изменить дизайн, поменять систему тормозов и вставить новые фары, и вуаля, новая -модель готова, при минимальных затратах и максимальном результате. С наследованием точно так же, можно описывать все -классы с нуля, но часто это очень не удобно и трудозатратно. +Если можно взять прошлую модель, немного изменить дизайн, поменять систему тормозов и вставить новые фары, и вуаля, +новая модель готова при минимальных затратах и максимальном результате. С наследованием точно также, можно описывать все +классы с нуля, но часто это очень неудобно и затратно. #### Пример кода @@ -150,13 +151,13 @@ class Car: self.current_speed = 0 -# тут класс унаследовал атрибуты wheels и current_speed, а так же все методы +# тут класс унаследовал атрибуты wheels и current_speed, а также все методы class Track(Car): doors = 2 max_speed = 120 -# а тут класс унаследовал все атрибуты кроме max_speed, а так же все методы +# а тут класс унаследовал все атрибуты кроме max_speed, а также все методы class SportCar(Car): max_speed = 350 @@ -172,36 +173,36 @@ print(sport.curent_speed) # 175 ![](https://studme.org/imag/inform/tuz_oop/image002.jpg) -### Метод `super` +### Метод `super()` -Практически всегда когда нам нужно в дочернем классе выполнить такое же действие как и в родительском нам необходимо -основываться на данных из родительского (в конце блока вы узнаете, что это даже целое правило) +Практически всегда, когда нам нужно в дочернем классе выполнить такое же действие как и в родительском, нам необходимо +основываться на данных из родительского (в конце блока вы узнаете, что это даже целое правило). -Но как вызвать код из другого класса? Нам поможет метод `super` +Но как вызвать код из другого класса? Нам поможет метод `super()`. -Допустим у нас есть класс, который занимается тем, что просто возвращает нам цену продукта. -И еще два класса которые считают скидку для цены в 20%. И второй еще 20% уже от уменьшенной цены. +Допустим, у нас есть класс, который занимается тем, что просто возвращает нам цену продукта. +И еще два класса, которые вычисляют скидку 20%. И второй класс отнимает еще 20% уже от уменьшенной цены. ```python class PriceCounter: price = 100 def calculate_price(self): - print('In original price') + print('In PriceCounter calculate price') return self.price class DiscountCounter(PriceCounter): def calculate_price(self): - print('In discount calculate') + print('In DiscountCounter calculate price: ') return super().calculate_price() * 0.8 class SuperDiscountCounter(DiscountCounter): def calculate_price(self): - print('In super discount calculate') + print('In SuperDiscountCounter calculate price: ') return super().calculate_price() * 0.8 @@ -211,20 +212,20 @@ super_discount_counter = SuperDiscountCounter() print(price_counter.calculate_price()) """ -In original price +In PriceDiscounter calculate price: 100 """ print(discount_counter.calculate_price()) """ -In discount calculate -In original price +In DiscountCounter calculate price: +In PriceDiscounter calculate price: 80.0 """ print(super_discount_counter.calculate_price()) """ -In super discount calculate -In discount calculate -In original price +In SuperDiscountCounter calculate price: +In DiscountCounter calculate price: +In PriceDiscounter calculate price: 64.0 """ ``` @@ -233,10 +234,10 @@ In original price #### Устаревшие синтаксисы -Для питона существуют несколько различных версий, включая 2.х и 3.х +Для Python существуют несколько различных версий, включая 2.х и 3.х. -Версии 2.х считаются устаревшими, но все таки иногда можно встретить код на втором питоне или "отнаследовавшийся от -него" +Версии 2.х считаются устаревшими, но все-таки иногда можно встретить код на Python 2.x или "отнаследовавшийся от +него". ```python class A(): # Вариант из python2, работать будет, но и удивлять коллег тоже @@ -245,9 +246,9 @@ class A(): # Вариант из python2, работать будет, но и class B(object): """ - Вариант который тоже будет работать - и на самом деле показывает нам суть любого класса и объекта в питоне, - вообще все унаследовано от объекта + Вариант, который тоже будет работать + и на самом деле показывает нам суть любого класса и объекта в Python, + вообще все унаследовано от объекта. """ @@ -255,16 +256,16 @@ class C: # Традиционный способ для объявления к pass ``` -Заодно познакомились с ключевым словом `pass`. Которое нужно в качестве `заглушки`, так как класс или функция не могут -быть пустыми, но могут быть с такой заглушкой. Вместо заглушки лучше все таки ставить комментарий, если есть +Заодно познакомились с ключевым словом `pass`, которое нужно в качестве `заглушки`, так как класс или функция не могут +быть пустыми, но могут быть с такой заглушкой. Вместо заглушки лучше все-таки ставить комментарий, если есть необходимость создать пустой класс (а она вполне бывает). -#### Методы `type`, `isinstance` и `issubclass` +#### Методы `type()`, `isinstance()` и `issubclass()` -У любого объекта всегда есть тип данных. И по факту этим типом данных всегда является класс (Да, `str`, `int`, `list` -итд. и даже функции это тоже классы) +У любого объекта всегда есть тип данных. И по факту этим типом данных всегда является класс (да, `str`, `int`, `list` +и т. д. и даже функции - это тоже классы) -Что бы узнать тип данных любого объекта необходимо вызвать метод `type` +Чтобы узнать тип данных любого объекта, необходимо вызвать метод `type()`. ```python class A: @@ -288,10 +289,10 @@ print(type(collection)) # print(type(some_func)) # ``` -Для того что бы сравнить узнать является ли объект подклассом существует специальная функция `isinstance`, принимает на -вход объект и класс, либо кортеж из классов, а возвращает булеан. +Для того чтобы сравнить и узнать, является ли объект подклассом, существует специальная функция `isinstance()`. Она +принимает на вход объект и класс, либо кортеж из классов, а возвращает булево значение. -И есть такая же функция, которая принимает не объект, а сам класс. `issubclass` +И есть такая же функция, которая принимает не объект, а сам класс: `issubclass()`. ```python class A: @@ -329,14 +330,14 @@ print(issubclass(type(a), (B, C))) # False Абстракция в объектно-ориентированном программировании — это использование только тех характеристик объекта, которые с достаточной точностью представляют его в данной системе. -Часто говорят что абстракция это не обязательная парадигма ООП. +Часто говорят, что абстракция - это не обязательная парадигма ООП. Вернемся к примерам с автомобилями. Когда мы управляем автомобилем, мы часто используем руль и поворотники, но часто ли -мы меняем настройку зеркал или подогрева сидений? Не особо. Так вот абстракция о том, что бы выделять главное, и не +мы меняем настройку зеркал или подогрева сидений? Не особо. Так вот абстракция о том, чтобы выделять главное и не тратить лишние ресурсы на второстепенное. Если говорить простыми словами, то это возможность описать реализацию метода только в том классе, где это необходимо. А -в родительском только описать название и возможно какие-то комментарии к будущей реализации. +в родительском только описать название и, возможно, какие-то комментарии к будущей реализации. ```python class Animal: @@ -358,22 +359,22 @@ class Lion(Animal): ``` -Если в этом примере не описать метод `sound`. То технически все будет работать, но любая IDE будет подсвечивать, что вы -не описали абстрактный метод. +Если в этом примере не описать метод `sound()`, то технически все будет работать, но любая IDE будет подсвечивать, что +вы не описали абстрактный метод. ### Полиморфизм ![](https://cdn.javarush.com/images/article/5d67355f-5702-4ba8-b1c2-5eb3f27fa5b5/800.jpeg) Полиморфизм — это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней -структуре объекта, т.е. способность одной функции (метода), -действовать по-разному в зависимости от обстоятельств (которые мы сами указываем). +структуре объекта, т. е. способность одной функции (метода), действовать по-разному в зависимости от обстоятельств +(которые мы сами указываем). -По сути это возможность использовать одни и те же методы или интерфейсы к различным структурам, например обычный +По сути, это возможность использовать одни и те же методы или интерфейсы к различным структурам, например, обычный знак `+`, ведь мы можем сложить числа, а можем и строки, и получим разный результат, но применим один и тот же метод. В примере с автомобилем, если мы умеем водить мерседес, то скорее всего у нас не вызовет проблема управлять тойотой или -фордом. Ведь для управления мы будем использовать точно те же самые интерфейсы (руль, педали, поворотники, итд.) +фордом. Ведь для управления мы будем использовать точно те же самые интерфейсы (руль, педали, поворотники и т. д.) ```python class English: @@ -393,44 +394,43 @@ def intro(language): flora = English() -aalase = French() +michelle = French() intro(flora) -intro(aalase) +intro(michelle) ``` Прошлый пример из абстракции тоже подходит, ведь разные звери издают разные звуки. А для нас это не имеет значения, мы -все-го лишь вызовем метод `sound`. +всего лишь вызовем метод `sound()`. ### Инкапсуляция ![](https://cdn.javarush.com/images/article/a3a32228-a349-4de8-b402-16c19fc4db88/1024.jpeg) -`Инкапсуляция` - Принцип "убирания с глаз" функционала или данных с предоставлением только входных и выходных +`Инкапсуляция` - принцип "убирания с глаз" функционала или данных с предоставлением только входных и выходных параметров. Например, если в автомобиле повернуть руль налево, то колёса тоже повернутся налево, но как именно это -происходит от нас скрыто, мы не -знаем какие именно рычаги и шестерёнки в этот момент двигаются внутри автомобиля. +происходит от нас скрыто, мы не знаем, какие именно рычаги и шестерёнки в этот момент двигаются внутри автомобиля. -Так же, например, с кофемашиной, мы знаем, что если нажать кнопку с капучино, то мы получим чашку кофе. Какие в этот +Так же, например, с кофе машиной, мы знаем, что если нажать кнопку с капучино, то мы получим чашку кофе. Какие в этот момент процессы происходят внутри, нас практически не интересует. Что-то нагревается, где-то по трубкам бежит жидкость. -Но нас интересует только результат. Могу ли я разобрать кофемашину, и посмотреть из чего она состоит? Могу, но у меня +Но нас интересует только результат. Могу ли я разобрать кофемашину и посмотреть, из чего она состоит? Могу, но у меня нет в этом необходимости. -Так же и в программировании, мы можем "скрыть" внутренние процессы +Так же и в программировании, мы можем "скрыть" внутренние процессы. ![](https://senior.ua/storage/article/651ea79a-83e1-4f4a-9ca4-8968b7ae86f9.jpeg) -В пайтоне инкапсуляция очень условная (всегда можно получить доступ куда угодно, было бы желание). Как сказал создатель -языка, Гвидо Ван Россум, ну мы все же взрослые люди, зачем мы будем кого-то ограничивать. +В Python инкапсуляция очень условная (всегда можно получить доступ куда угодно, было бы желание). Как сказал создатель +языка, Гвидо Ван Россум: "Мы все же взрослые люди, зачем мы будем кого-то ограничивать?" В первую очередь код пишется для людей, поэтому и разделение существует на уровне понимания людей. Существует три вида состояния атрибутов и свойств, и для их разделения используется специальный синтаксис. -1. Атрибуты и методы чьи названия начинаются с букв, называются `public`. И они доступны везде. В объекте, в классе, в +1. Атрибуты и методы, чьи названия начинаются с букв, называются `public`. И они доступны везде. В объекте, в классе, в наследовании. -2. Атрибуты и методы чьи названия начинаются с символа `_`. Они называются `protected`, и подразумевается, что мы будем +2. Атрибуты и методы, чьи названия начинаются с символа `_`. Они называются `protected`, и подразумевается, что мы будем их использовать исключительно в классе и в наследовании, но не будем использовать в объектах. 3. Атрибуты и методы которые начинаются с `__`. Они называются `private` и подразумевается, что мы будем их использовать @@ -445,22 +445,22 @@ class Car: __max_carrying = 1000 def find_color_and_top_speed(self): - return 'this car top speed is {} and color is {}'.format(self._top_speed, self.color) + return f'This car top speed is {self._top_speed} and color is {self.color}.' - def is_can_go_with_needed_speed(self, speed): + def can_go_with_needed_speed(self, speed): return speed < self._top_speed - def is_can_get_weight(self, weight): + def can_get_weight(self, weight): return self.__max_carrying > weight def change_max_carrying(self, new_carrying): self.__max_carrying = new_carrying def __private_method(self): - print('this is private method') + print('This is private method') def _this_is_protected_method(self): - print('this is protected method') + print('This is protected method') def run_hidden_and_protected_methods(self): self.__private_method() @@ -469,9 +469,9 @@ class Car: car = Car() car.color # всё нормально -car._top_speed # сработает, но мы же сами описали это свойство так что бы сообщить, что не надо так его использовать +car._top_speed # сработает, но мы же сами описали это свойство так, чтобы сообщить, что не надо так его использовать car.__max_carrying # не сработает (будет ошибка, что этот атрибут не найден) -car._Car__max_carrying # Сработает и это как раз описание того, что добраться можно куда угодно. Но сам синтаксис нам говорит, что мы что-то не то делаем +car._Car__max_carrying # Сработает, и это как раз описание того, что добраться можно куда угодно. Но сам синтаксис нам говорит, что мы что-то не то делаем car.__max_carrying = 800 # не сработает car.change_max_carrying(800) # сработает сar.__hidden_method() # не сработает (будет ошибка, что этого метода не существует) @@ -486,44 +486,46 @@ car.run_hidden_and_protected_methods() # сработает и вызовет 1. Описываем телефон: Класс телефон. У него должны быть: - - Поле, для описания номера - - Метод, что-бы задать номер телефона - - Защищенное поле для счетчика входящих звонков - - Метод, который вернет нам количество принятых звонков - - Метод принять звонок, который добавляет к счетчику единицу + - Поле для описания номера. + - Метод, чтобы задать номер телефона. + - Защищенное поле для счетчика входящих звонков. + - Метод, который вернет нам количество принятых звонков. + - Метод принять звонок, который добавляет к счетчику единицу. Создайте три разных объекта телефона. Поменяйте всем изначальный номер. - Примите по несколько звонков на каждом (разное кол-во) + Примите по несколько звонков на каждом (разное количество) - Напишите функцию, которая принимает список из объектов телефонов, а возвращает общее кол-во принятых звонков со всех - телефонов. + Напишите функцию, которая принимает список из объектов телефонов, а возвращает общее количество принятых звонков + со всех телефонов. 2. Опишите класс для шахматной фигуры. Фигура должна содержать такие атрибуты: - - Цвет (белый или черный) + - Цвет (белый или черный). - Место на доске (тут есть варианты, или два отдельных поля, для описания координат или одно, но, например, кортеж - из двух чисел) + из двух чисел). И такие методы как: - - Изменить цвет (ничего не принимает, только меняет цвет на противоположный) + - Изменить цвет (ничего не принимает, только меняет цвет на противоположный). - Изменить место на доске (принимает или две переменные, или один кортеж из двух элементов), не забудьте проверить, - что мы не пытаемся поставить фигуру за пределы доски (оба значения от 0 до 7) - - Абстрактный метод проверки потенциального хода (детали ниже) - На данном этапе фигуры могут стоять на одной и тоже же клетке, пока нам это не важно - - Опишите классы, для пешки, коня, офицера, ладьи, ферзя и короля. - Все что в них нужно добавить это один метод для проверки, - возможно ли за один ход поменять место фигуры на доске (все ходят по-разному, у пешек будет еще и разница от цвета). Метод принимает опять же или две цифры, или один кортеж. И - опять же проверяем не выходит ли значение за пределы доски (Так как нам необходим этом функционал дважды, я бы делал - его как отдельный защищенный метод в родительском классе) - - И функцию, которая принимает список фигур и потенциальную новую клетку, а возвращает список из фигур. Но только тех + что мы не пытаемся поставить фигуру за пределы доски (оба значения от 0 до 7). + - Абстрактный метод проверки потенциального хода (детали ниже). + На данном этапе фигуры могут стоять на одной и той же клетке, пока нам это не важно. + + Опишите классы для пешки, коня, офицера, ладьи, ферзя и короля. + Все, что в них нужно добавить - это один метод для проверки, + возможно, ли за один ход поменять место фигуры на доске (все ходят по-разному, у пешек будет еще и разница от цвета). + Метод принимает опять же или две цифры, или один кортеж. И опять же проверяем, не выходит ли значение за пределы + доски (Так как нам необходим этом функционал дважды, я бы делал его как отдельный защищенный метод в родительском + классе) + + И функцию, которая принимает список фигур и потенциальную новую клетку, а возвращает список из фигур. Но только тех, которые могут за один ход добраться до этой клетки. -3. Везде описать типизации (в функциях, атрибутах и методах) -4. ЗАДАЧА СО ЗВЁЗДОЧКОЙ. Сохранять информацию о принятых звонках в базу данных при помощи кода. (Пока только сохранять, читать будем позже) +3. Везде описать типизации (в функциях, атрибутах и методах). +4. ЗАДАЧА СО ЗВЁЗДОЧКОЙ. Сохранять информацию о принятых звонках в базу данных при помощи кода. + (Пока только сохранять, читать будем позже) - Для этого понадобится установить модуль `psycopg2` через `pip install` и разобраться как им пользоваться. \ No newline at end of file + Для этого понадобится установить модуль `psycopg2` через `pip install` и разобраться, как им пользоваться. From 95d90c9df7915f8128e06a670d10bb97a5902902 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Mon, 31 Jul 2023 01:14:20 +0300 Subject: [PATCH 12/23] edit grammar and style --- lesson16.md | 202 ++++++++++++++++++++++++++-------------------------- 1 file changed, 100 insertions(+), 102 deletions(-) diff --git a/lesson16.md b/lesson16.md index 616ba104c..3b7dbb187 100644 --- a/lesson16.md +++ b/lesson16.md @@ -6,22 +6,21 @@ ![](https://python-course.eu/images/oop/clock_calendar_500w.webp) -Множественное наследование это возможность у класса потомка наследовать функционал не от одного, а от нескольких -родителей. -Благодаря этому мы можем создавать сложные структуры, сохраняя простой и легко-поддерживаемый код. +Множественное наследование - это возможность у класса потомка наследовать функционал не от одного, а от нескольких +родителей. Благодаря этому мы можем создавать сложные структуры, сохраняя простой и легко-поддерживаемый код. -Во многих языках программирования нет множественного наследования, так что давайте разбираться как это вообще работает. +Во многих языках программирования нет множественного наследования, так что давайте разбираться, как это вообще работает. Например, у нас есть класс автомобиля: ```python class Auto: def ride(self): - print("Riding on a ground") + print("Riding on ground") ``` -Так же у нас есть класс для лодки: +Также у нас есть класс для лодки: ```python class Boat: @@ -31,7 +30,7 @@ class Boat: ``` Теперь, если нам нужно запрограммировать автомобиль-амфибию, который будет плавать в воде и ездить по земле, мы вместо -написания нового класса, можем просто унаследовать от уже существующих, просто написав их через запятую: +написания нового класса можем просто унаследовать от уже существующих, просто написав их через запятую: ```python class Auto: @@ -55,9 +54,9 @@ a.swim() ![](https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSYboaqixXBn-OWESELK2fLejC9fvKLL4QvBA&usqp=CAU) -Теперь наш класс имеет атрибуты и методы обоих родителей (их может быть сколько угодно) +Теперь наш класс имеет атрибуты и методы обоих родителей (их может быть сколько угодно). -Обратите внимание, что объект класса Amphibian, будет одновременно объектом класса Auto и Boat, то есть: +Обратите внимание, что объект класса Amphibian будет одновременно объектом класса Auto и Boat, то есть: ```python a = Amphibian() @@ -69,9 +68,9 @@ isinstance(a, Amphibian) # True ``` -### Миксины +### Миксины (Mixins) -Миксин, он же примесь, это тип классов которые нужны, что бы добавлять к обычным классам какие-то методы или атрибуты, +Миксин, он же примесь, — это тип классов, которые нужны, чтобы добавлять к обычным классам какие-то методы или атрибуты, но эти классы не используются для создания объектов, только как примесь. (Нас ничего не останавливает создать объект этого класса, но задача в другом) @@ -85,7 +84,7 @@ class Car: print("Riding a car") def play_music(self, song): - print("Now playing: {} ".format(song)) + print(f"Now playing: {song}.") c = Car() @@ -95,13 +94,13 @@ c.play_music("Queen - Bohemian Rhapsody") # Now playing: Queen - Bohemian Rhapsody ``` -Но что если, у нас есть еще и телефон, радио или любой другой девайс, с которого мы будем слушать музыку. -В таком случае, лучше вынести функционал проигрывания музыки в отдельный класс-миксин: +Но что, если у нас есть еще и телефон, радио или любой другой девайс, с которого мы будем слушать музыку. +В таком случае лучше вынести функционал проигрывания музыки в отдельный класс-миксин: ```python class MusicPlayerMixin: def play_music(self, song): - print("Now playing: {}".format(song)) + print(f"Now playing: {song}.") ``` Мы можем "домешивать" этот класс в любой, где нужна функция проигрывания музыки: @@ -154,37 +153,34 @@ d = D() d.hi() ``` -Эта ситуация, так называемое ромбовидное наследование (diamond problem) решается в Python путем установления порядка +Эта ситуация, так называемое ромбовидное наследование (diamond problem), решается в Python путем установления порядка разрешения методов. В Python3 для определения порядка используется алгоритм поиска в ширину, то есть сначала интерпретатор будет искать -метод hi в классе B, если его там нету - в классе С, потом A. +метод `hi()` в классе B, если его там нет - в классе С, потом A. В Python второй версии используется алгоритм поиска в глубину, то есть в данном случае - сначала B, потом - А, потом С. -В Pytho3 можно посмотреть в каком порядке будут проинспектированы родительские классы при помощи метода класса mro(): +В Pytho3 можно посмотреть в каком порядке будут проинспектированы родительские классы при помощи метода класса `mro()`: ### MRO - Method resolution order -Что бы посмотреть в каком порядке питон будет искать атрибуты или методы у родителей, у любого класса можно вызывать -метод `mro`: +Чтобы посмотреть, в каком порядке Python будет искать атрибуты или методы у родителей, у любого класса можно вызывать +метод `mro()`: ```python ->> > D.mro() -[ < - - -class '__main__.D'>, < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object' >] +>> D.mro() +[ , < class '__main__.B' >, < class '__main__.C' >, < class '__main__.A' >, < class 'object'>] ``` -Обратите внимание в конце всегда будет `object` если вы используете любой питон версии 3. +Обратите внимание, в конце всегда будет `object`, если вы используете любой Python версии 3. Потому что вообще все отнаследовано от него, как я и говорил на прошлом занятии. -Если по какой-то причине вас не устраивает существующий порядок есть возможность вызвать метод ровно из того класса +Если по какой-то причине вас не устраивает существующий порядок, есть возможность вызвать метод ровно из того класса, откуда вам надо, но это считается плохой практикой и лучше так не делать, а полностью поменять структуру. -Если вам необходимо использовать метод конкретного родителя, например hi() класса С, нужно напрямую вызвать его по имени -класса, передав self в качестве аргумента: +Если вам необходимо использовать метод конкретного родителя, например, `hi()` класса С, нужно напрямую вызвать его по +имени класса, передав `self` в качестве аргумента: ```python class D(B, C): @@ -198,23 +194,22 @@ d.call_hi() [Большая статья про МРО и вообще множественное наследование тут](https://habr.com/ru/post/62203/?_ga=2.205768979.1207595081.1598867257-330984554.1578271027) -## Magic methods (Они же ногда называются дандер методы) +## Magic methods (Они же иногда называются dunder-методы) -Что такое магические методы? Они всё в объектно-ориентированном Питоне. -Это специальные методы, с помощью которых вы -можете добавить в ваши классы «магию». -Они всегда обрамлены двумя нижними подчеркиваниями, два вначале, два в конце (например, __init__ или __lt__) +Что такое магические методы? Они всё в объектно-ориентированном Python. +Это специальные методы, с помощью которых вы можете добавить в ваши классы «магию». +Они всегда обрамлены двумя нижними подчеркиваниями, два вначале, два в конце (например, `__init__` или `__lt__`). -Это методы, которые отвечают за любые действия под "капотом". Те которые выполняются неявно, допустим, -вам нужно сложить два объекта через `+`, нам нужен магический метод `__add__` или мы хотим поменять поведение при удалении `__del__`, поведение при переборе объекта -в цикле `__iter__` или `__next__` итд. +Это методы, которые отвечают за любые действия под "капотом". Те, которые выполняются неявно. Допустим, +вам нужно сложить два объекта через оператор `+`, нам нужен магический метод `__add__`, или мы хотим поменять поведение +при удалении `__del__`, поведение при переборе объекта в цикле `__iter__` или `__next__` и т. д. -Почти любое действие которое вам кажется выполняется само собой, скорее всего описано в меджик методе. +Почти любое действие, которое вам кажется выполняется само собой, скорее всего описано в magic методе. -### init, new, del +### init(), new(), del() При создании (инициализации) объекта вызывается метод `__init__` и помогает нам задать стартовые параметры для нашего -класса +класса. ```python class A: @@ -229,42 +224,46 @@ a.some_arg_one a.some_arg_two # 22 ``` -Такой способ куда предпочтительнее, чем задавать какое-то бесполезное значение вначале и сразу же его поменять (номер телефона или место на доске из прошлой домашки) +Такой способ предпочтительнее, чем вначале задавать какое-то бесполезное значение и сразу же его поменять (номер +телефона или место на доске из прошлой домашки). -Когда я пишу x = SomeClass(), `__init__` не самое первое, что вызывается. На самом деле, экземпляр объекта создаёт метод `__new__`, а затем аргументы передаются в инициализатор. На другом конце жизненного цикла объекта находится метод `__del__`. +Когда я пишу `x = SomeClass()`, `__init__` не самое первое, что вызывается. На самом деле, экземпляр объекта создаёт +метод `__new__`, а затем аргументы передаются в инициализатор. На другом конце жизненного цикла объекта находится +метод `__del__`. Давайте подробнее рассмотрим эти три магических метода: `__new__(cls, [...)` Это первый метод, который будет вызван при инициализации объекта. Он принимает в качестве параметров класс и потом любые -другие аргументы, которые будут переданы в `__init__`. `__new__` используется весьма редко, но иногда бывает полезен, в -частности, когда класс наследуется от неизменяемого (immutable) типа, такого как кортеж (tuple) или строка. Я не намерен -очень детально останавливаться на `__new__`, так как он не то чтобы очень часто нужен, но этот метод очень хорошо и +другие аргументы, которые будут переданы в `__init__`. Метод `__new__` используется весьма редко, но иногда бывает +полезен, в частности, когда класс наследуется от неизменяемого (immutable) типа, такого как кортеж (tuple) или строка. +Я не намерен очень детально останавливаться на `__new__`, так как он не очень часто нужен, и этот метод очень хорошо и детально описан в документации. `__init__(self, [...)` Инициализатор класса. Ему передаётся всё, с чем был вызван первоначальный конструктор (так, например, если мы вызываем -x = SomeClass(10, 'foo'), `__init__` получит 10 и 'foo' в качестве аргументов. `__init__` почти повсеместно используется при -определении классов. +`x = SomeClass(10, 'foo')`, `__init__` получит 10 и 'foo' в качестве аргументов. `__init__` почти повсеместно +используется при определении классов. `__del__(self)` -Если `__new__` и `__init__` образуют конструктор объекта, `__del__` это его деструктор. Он не определяет поведение для -выражения `del x` (поэтому этот код не эквивалентен `x.__del__()`). Скорее, он определяет поведение объекта в то время, -когда объект попадает в сборщик мусора. Это может быть довольно удобно для объектов, которые могут требовать +Если `__new__` и `__init__` образуют конструктор объекта, то `__del__` - это его деструктор. Он не определяет поведение +для выражения `del x` (поэтому этот код не эквивалентен `x.__del__()`). Скорее, он определяет поведение объекта в то +время, когда объект попадает в сборщика мусора. Это может быть довольно удобно для объектов, которые могут требовать дополнительных чисток во время удаления, таких как сокеты или файловые объекты. Однако, нужно быть осторожным, так как нет гарантии, что `__del__` будет вызван, если объект продолжает жить, когда интерпретатор завершает работу. Поэтому `__del__` не может служить заменой для хороших программистских практик (всегда завершать соединение, если -закончил с ним работать и тому подобное). Фактически, из-за отсутствия гарантии вызова, `__del__` не должен использоваться -почти никогда; используйте его с осторожностью +закончил с ним работать и тому подобное). Фактически, из-за отсутствия гарантии вызова `__del__` не должен +использоваться почти никогда; используйте его с осторожностью. #### Магические методы сравнения -В Питоне огромное количество магических методов, созданных для определения интуитивного сравнения между объектами -используя операторы, а не плохо выглядящие методы (что лучше? `a==b` или `a.is_equal(b)` . Кроме того, они предоставляют способ переопределить поведение Питона -по-умолчанию для сравнения объектов (по ссылке). Вот список этих методов и что они делают: +В Python огромное количество магических методов, созданных для определения интуитивного сравнения между объектами при +помощи операторов, а не плохо выглядящих методов (Что лучше? `a==b` или `a.is_equal(b)` . Кроме того, они предоставляют +способ переопределить поведение Python по умолчанию для сравнения объектов (по ссылке). Вот список этих методов и что +они делают: `__eq__(self, other)` @@ -296,14 +295,14 @@ x = SomeClass(10, 'foo'), `__init__` получит 10 и 'foo' в качест ```python class Word(str): - '''Класс для слов, определяющий сравнение по длине слов.''' + """Класс для слов, определяющий сравнение по длине слов.""" def __new__(cls, word): # Мы должны использовать __new__, так как тип str неизменяемый # и мы должны инициализировать его раньше (при создании) if ' ' in word: print("Value contains spaces. Truncating to first space.") - word = word[:word.index(' ')] # Теперь Word это все символы до первого пробела + word = word[:word.index(' ')] # Теперь Word - это все символы до первого пробела return str.__new__(cls, word) def __gt__(self, other): @@ -356,7 +355,7 @@ class Word(str): `__divmod__(self, other)` -Определяет поведение для встроенной функции divmod(). +Определяет поведение для встроенной функции `divmod()`. `__pow__` @@ -384,8 +383,8 @@ class Word(str): ### Магические методы преобразования типов -Кроме того, в Питоне множество магических методов, предназначенных для определения поведения для встроенных функций -преобразования типов, таких как float(). Вот они все: +Кроме того, в Python множество магических методов, предназначенных для определения поведения для встроенных функций +преобразования типов, таких как `float()`. Вот они все: `__int__(self)` @@ -414,7 +413,7 @@ class Word(str): `__index__(self)` Преобразование типа к int, когда объект используется в срезах (выражения вида [start:stop:step]). Если вы определяете -свой числовой тип, который может использоваться как индекс списка, вы должны определить __index__. +свой числовой тип, который может использоваться как индекс списка, вы должны определить `__index__`. `__trunc__(self)` @@ -422,13 +421,13 @@ class Word(str): `__coerce__(self, other)` -Метод для реализации арифметики с операндами разных типов. __coerce__ должен вернуть None если преобразование типов -невозможно. Если преобразование возможно, он должен вернуть пару (кортеж из 2-х элементов) из self и other, +Метод для реализации арифметики с операндами разных типов. `__coerce__` должен вернуть `None`, если преобразование типов +невозможно. Если преобразование возможно, он должен вернуть пару (кортеж из 2-х элементов) из `self` и `other`, преобразованные к одному типу. ### Представление своих классов -Часто бывает полезно представление класса в виде строки. В Питоне существует несколько методов, которые вы можете +Часто бывает полезно представление класса в виде строки. В Python существует несколько методов, которые вы можете определить для настройки поведения встроенных функций при представлении вашего класса. `__str__(self)` @@ -437,28 +436,28 @@ class Word(str): `__repr__(self)` -Определяет поведение функции repr(), вызыванной для экземпляра вашего класса. Главное отличие от str() в целевой -аудитории. repr() больше предназначен для машинно-ориентированного вывода (более того, это часто должен быть валидный -код на Питоне), а str() предназначен для чтения людьми. +Определяет поведение функции repr(), вызванной для экземпляра вашего класса. Главное отличие от `str()` в целевой +аудитории. `repr()` больше предназначен для машинно-ориентированного вывода (более того, это часто должен быть валидный +код на Python), а `str()` предназначен для чтения людьми. `__unicode__(self)` -Определяет поведение функции unicode(), вызыванной для экземпляра вашего класса. unicode() похож на str(), но возвращает -строку в юникоде. Будте осторожны: если клиент вызывает str() на экземпляре вашего класса, а вы определили только _ -_unicode__(), то это не будет работать. Постарайтесь всегда определять __str__() для случая, когда кто-то не имеет такой -роскоши как юникод. +Определяет поведение функции unicode(), вызванной для экземпляра вашего класса. `unicode()` похож на `str()`, но возвращает +строку в Unicode. Будьте осторожны: если клиент вызывает str() на экземпляре вашего класса, а вы определили только +`__unicode__()`, то это не будет работать. Постарайтесь всегда определять `__str__()` для случая, когда кто-то не имеет +такой роскоши как Unicode. `__format__(self, formatstr)` Определяет поведение, когда экземпляр вашего класса используется в форматировании строк нового стиля. Например, "Hello, -{0:abc}!".format(a) приведёт к вызову a.__format__("abc"). Это может быть полезно для определения ваших собственных +{0:abc}!".format(a) приведёт к вызову `a.__format__("abc")`. Это может быть полезно для определения ваших собственных числовых или строковых типов, которым вы можете захотеть предоставить какие-нибудь специальные опции форматирования. `__hash__(self)` Определяет поведение функции hash(), вызванной для экземпляра вашего класса. Метод должен возвращать целочисленное значение, которое будет использоваться для быстрого сравнения ключей в словарях. Заметьте, что в таком случае обычно -нужно определять и __eq__ тоже. Руководствуйтесь следующим правилом: a == b подразумевает hash(a) == hash(b). +нужно определять и `__eq__` тоже. Руководствуйтесь следующим правилом: `a == b` подразумевает `hash(a) == hash(b)`. `__bool__(self)` (`__nonzero__(self)` в python < 3.0) @@ -468,15 +467,14 @@ _unicode__(), то это не будет работать. Постарайте `__dir__(self)` Определяет поведение функции dir(), вызванной на экземпляре вашего класса. Этот метод должен возвращать пользователю -список атрибутов. Обычно, определение __dir__ не требуется, но может быть жизненно важно для интерактивного -использования вашего класса, если вы переопределили __getattr__ или __getattribute__ (с которыми вы встретитесь в +список атрибутов. Обычно определение __dir__ не требуется, но может быть жизненно важно для интерактивного +использования вашего класса, если вы переопределили `__getattr__` или `__getattribute__` (с которыми вы встретитесь в следующей части), или каким-либо другим образом динамически создаёте атрибуты. `__sizeof__(self)` -Определяет поведение функции sys.getsizeof(), вызыванной на экземпляре вашего класса. Метод должен вернуть размер вашего -объекта в байтах. Он главным образом полезен для классов, определённых в расширениях на C, но всё-равно полезно о нём -знать. +Определяет поведение функции sys.getsizeof(), вызванной на экземпляре вашего класса. Метод должен вернуть размер вашего +объекта в байтах. Он полезен для классов, определённых в расширениях на C, но всё-равно полезно о нём знать. ### Протоколы @@ -488,7 +486,7 @@ _unicode__(), то это не будет работать. Постарайте #### Магия контейнеров -Без дальнейшего промедления, вот магические методы, используемые контейнерами: +Без дальнейшего промедления вот магические методы, используемые контейнерами: `__len__(self)` @@ -496,14 +494,14 @@ _unicode__(), то это не будет работать. Постарайте `__getitem__(self, key)` -Определяет поведение при доступе к элементу, используя синтаксис `self[key]`. Тоже относится и к протоколу изменяемых и -к протоколу неизменяемых контейнеров. Должен выбрасывать соответствующие исключения: TypeError если неправильный тип -ключа и KeyError если ключу не соответствует никакого значения. +Определяет поведение при доступе к элементу, используя синтаксис `self[key]`. То же относится и к протоколу изменяемых и +к протоколу неизменяемых контейнеров. Должен выбрасывать соответствующие исключения: TypeError, если неправильный тип +ключа, и KeyError, если ключу не соответствует никакого значения. `__setitem__(self, key, value)` Определяет поведение при присваивании значения элементу, используя синтаксис `self[nkey] = value`. Часть протокола -изменяемого контейнера. Опять же, вы должны выбрасывать KeyError и TypeError в соответсвующих случаях. +изменяемого контейнера. Опять же, вы должны выбрасывать KeyError и TypeError в соответствующих случаях. `__delitem__(self, key)` @@ -512,57 +510,57 @@ _unicode__(), то это не будет работать. Постарайте `__iter__(self)` -Должен вернуть итератор для контейнера. Итераторы возвращаются в множестве ситуаций, главным образом для встроенной +Должен вернуть итератор для контейнера. Итераторы возвращаются в множестве ситуаций, главным образом, для встроенной функции `iter()` и в случае перебора элементов контейнера выражением `for x in container:`. Итераторы сами по себе объекты и они тоже должны определять метод `__iter__`, который возвращает `self`. `__reversed__(self)` -Вызывается чтобы определить поведения для встроенной функции `reversed()`. Должен вернуть обратную версию -последовательности. Реализуйте метод только если класс упорядоченный, как список или кортеж. +Вызывается, чтобы определить поведения для встроенной функции `reversed()`. Должен вернуть обратную версию +последовательности. Реализуйте метод, только если класс упорядоченный, как список или кортеж. `__contains__(self, item)` `__contains__` предназначен для проверки принадлежности элемента с помощью `in` и `not in`. Вы спросите, почему же это -не часть протокола последовательности? Потому что когда `__contains__` не определён, Питон просто перебирает всю -последовательность элемент за элементом и возвращает True если находит нужный. +не часть протокола последовательности? Потому что когда `__contains__` не определён, Python просто перебирает всю +последовательность элемент за элементом и возвращает True, если находит нужный. `__next__(self)` -Возвращает следующий объкт в последовательности или StopIteration если больше обхектов нет. +Возвращает следующий объект в последовательности или StopIteration, если больше объектов нет. ### Вызываемые объекты -Как вы наверное уже знаете, в Питоне функции являются объектами первого класса. Это означает, что они могут быть +Как вы, наверное, уже знаете, в Python функции являются объектами первого класса. Это означает, что они могут быть переданы в функции или методы так же, как любые другие объекты. Это невероятно мощная особенность. -Специальный магический метод позволяет экземплярам вашего класса вести себя так, как будто они функции, тоесть вы +Специальный магический метод позволяет экземплярам вашего класса вести себя так, как будто они функции, то есть вы сможете «вызывать» их, передавать их в функции, которые принимают функции в качестве аргументов и так далее. Это другая -удобная особенность, которая делает программирование на Питоне таким приятным. +удобная особенность, которая делает программирование на Python таким приятным. `__call__(self, [args...])` -Позволяет любому экземпляру вашего класса быть вызванным как-будто он функция. Главным образом это означает, что `x()` -означает то же, что и `x.__call__()`. Заметьте, `__call__` принимает произвольное число аргументов; то есть, вы можете -определить `__call__` так же как любую другую функцию, принимающую столько аргументов, сколько вам нужно. +Позволяет любому экземпляру вашего класса быть вызванным как будто он функция. Главным образом, это означает, что `x()` +означает то же, что и `x.__call__()`. Заметьте, `__call__()` принимает произвольное число аргументов; то есть вы можете +определить `__call__()` так же как любую другую функцию, принимающую столько аргументов, сколько вам нужно. -`__call__`, в частности, может быть полезен в классах, чьи экземпляры часто изменяют своё состояние. «Вызвать» экземпляр -может быть интуитивно понятным и элегантным способом изменить состояние объекта. Примером может быть класс, +`__call__()`, в частности, может быть полезен в классах, чьи экземпляры часто изменяют своё состояние. «Вызвать» +экземпляр может быть интуитивно понятным и элегантным способом изменить состояние объекта. Примером может быть класс, представляющий положение некоторого объекта на плоскости: ```python class Entity: - '''Класс, описывающий объект на плоскости. "Вызываемый", чтобы обновить позицию объекта.''' + """Класс, описывающий объект на плоскости. "Вызываемый", чтобы обновить позицию объекта.""" def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, x, y): - '''Изменить положение объекта.''' + """Изменить положение объекта.""" self.x, self.y = x, y ``` -На самом деле меджик методов гораздо больше, и вы можете изучить их из документации если вам необходимо, но практически -любое действие можно переопределить при помощи мейджик методов. +На самом деле, magic методов гораздо больше, и вы можете изучить их из документации, если вам необходимо, но практически +любое действие можно переопределить при помощи magic методов. -[хорошая статья по теме](https://habr.com/ru/post/186608/) +[Хорошая статья по теме](https://habr.com/ru/post/186608/) From cd8f63b35df3b0b24d5f784b05c6e0343ed185b4 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Mon, 31 Jul 2023 15:56:16 +0300 Subject: [PATCH 13/23] edit grammar and style --- lesson18.md | 129 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 89 insertions(+), 40 deletions(-) diff --git a/lesson18.md b/lesson18.md index 3be86aa23..54dd1ba7f 100644 --- a/lesson18.md +++ b/lesson18.md @@ -2,31 +2,38 @@ ![](https://cdn.buttercms.com/LUta2G5oQ0iKH4DLd6jm) -Что вы предпринимаете, когда с работой вашей программы что-то идет не так? Допустим, вы пытаетесь открыть файл, но вы ввели неверный путь, или вы хотите узнать информацию у пользователей и они пишут какую-то бессмыслицу. Вы не хотите, чтобы ваша программа крэшилась, по-этому вы выполняете обработку исключений. В Пайтоне, конструкция всегда обернута в то, что называется try/except. +Что вы предпринимаете, когда с работой вашей программы что-то идет не так? Допустим, вы пытаетесь открыть файл, но вы +ввели неверный путь, или вы хотите узнать информацию у пользователей, и они пишут какую-то бессмыслицу. Вы не хотите, +чтобы ваша программа завершалась с ошибкой, поэтому вы выполняете обработку исключений. В Python, конструкция всегда +обернута в то, что называется try/except. Иерархия исключений выглядит вот так: ![](https://w3.cs.jmu.edu/lam2mo/cs240_2014_08/images/exception_hierarchy.png) -Начнем со знакомства с самыми обычными исключениями, которые вы увидите в Пайтоне. Обратите внимание на то, что ошибка и исключение – два разных слова, описывающие одно и то же, в контексте обработки исключений. +Начнем со знакомства с самыми обычными исключениями, которые вы увидите в Python. Обратите внимание на то, что ошибка и +исключение – два разных слова, описывающих одно и то же в контексте обработки исключений. ## Основные исключения -Ниже изложен список основных встроенных исключений (определение в документации к Пайтону): +Ниже изложен список основных встроенных исключений (определение в документации к Python): `Exception` – то, на чем фактически строятся все остальные ошибки; `AttributeError` – возникает, когда ссылка атрибута или присвоение не могут быть выполнены; -`IOError` – возникает в том случае, когда операция I/O (такая как оператор вывода, встроенная функция open() или метод объекта-файла) не может быть выполнена, по связанной с I/O причине: «файл не найден», или «диск заполнен», иными словами. +`IOError` – возникает в том случае, когда операция I/O (например, оператор вывода, встроенная функция `open()` или метод +объекта-файла) не может быть выполнена, по связанной с I/O причине: «файл не найден», или «диск заполнен», +иными словами. -`ImportError` – возникает, когда оператор import не может найти определение модуля, или когда оператор не может найти имя файла, который должен быть импортирован; +`ImportError` – возникает, когда оператор `import` не может найти определение модуля, или когда оператор не может найти +имя файла, который должен быть импортирован; `IndexError` – возникает, когда индекс последовательности находится вне допустимого диапазона; `KeyError` – возникает, когда ключ сопоставления (dictionary key) не найден в наборе существующих ключей; -`KeyboardInterrupt` – возникает, когда пользователь нажимает клавишу прерывания(обычно Delete или Ctrl+C); +`KeyboardInterrupt` – возникает, когда пользователь нажимает клавишу прерывания (обычно `Delete` или `Ctrl+C`); `NameError` – возникает, когда локальное или глобальное имя не найдено; @@ -34,9 +41,11 @@ `SyntaxError` — возникает, когда синтаксическая ошибка встречается синтаксическим анализатором; -`TypeError` – возникает, когда операция или функция применяется к объекту несоответствующего типа. Связанное значение представляет собой строку, в которой приводятся подробные сведения о несоответствии типов; +`TypeError` – возникает, когда операция или функция применяется к объекту несоответствующего типа. Связанное значение +представляет собой строку, в которой приводятся подробные сведения о несоответствии типов; -`ValueError` – возникает, когда встроенная операция или функция получают аргумент, тип которого правильный, но неправильно значение, и ситуация не может описано более точно, как при возникновении IndexError; +`ValueError` – возникает, когда встроенная операция или функция получают аргумент, тип которого правильный, но +неправильно значение, и ситуация не может быть описана более точно, как при возникновении `IndexError`; `ZeroDivisionError` – возникает, когда второй аргумент операции division или modulo равен нулю; @@ -44,7 +53,8 @@ ### Как обрабатывать исключения? -Обработка исключений в Пайтон – это очень просто. Потратим немного времени и напишем несколько примеров, которые их вызовут. Мы начнем с одной из самых элементарных проблем: деление на ноль. +Обработка исключений в Python – это очень просто. Потратим немного времени и напишем несколько примеров, которые их +вызовут. Мы начнем с одной из самых элементарных проблем: деление на ноль. ```python 1 / 0 @@ -66,7 +76,8 @@ except ZeroDivisionError: >>> You cannot divide by zero! ``` -Если мы обратимся к урокам элементарной математики, то вспомним, что на ноль делить нельзя. В Пайтоне данная операция вызовет ошибку, как мы можем видеть в примере выше. Чтобы поймать ошибку, мы завернем операцию в оператор try/except. +Если мы обратимся к урокам элементарной математики, то вспомним, что на ноль делить нельзя. В Python данная операция +вызовет ошибку, как мы можем видеть в примере выше. Чтобы поймать ошибку, мы завернем операцию в оператор try/except. ### «Голое» исключение @@ -80,15 +91,19 @@ except: # ЭТО СРАБОТАЕТ, НО ТАК ДЕЛАТЬ НЕЛЬЗЯ ``` -На жаргоне Пайтона, это известно как голое исключение, что означает, что будут найдены вообще все исключения. Причина, по которой так делать не рекомендуется, заключается в том, что вы не узнаете, что именно за исключение вы выловите. Когда у вас возникло что-то в духе ZeroDivisionError, вы хотите выявить фрагмент, в котором происходит деление на ноль. В коде, написанном выше, вы не можете указать, что именно вам нужно выявить. Давайте взглянем еще на несколько примеров: +На жаргоне Python это известно как голое исключение, что означает, что будут найдены вообще все исключения. Причина, +по которой так делать не рекомендуется, заключается в том, что вы не узнаете, что именно за исключение вы выловите. +Когда у вас возникло что-то в духе `ZeroDivisionError`, вы хотите выявить фрагмент, в котором происходит деление на +ноль. В написанном выше коде вы не можете указать, что именно вам нужно выявить. Давайте взглянем еще на несколько +примеров: ```python -my_dict = {"a":1, "b":2, "c":3} +my_dict = {"a": 1, "b": 2, "c": 3} try: value = my_dict["d"] except KeyError: - print("That key does not exist!") + print("Thе key does not exist!") ``` ```python @@ -100,12 +115,17 @@ except IndexError: print("That index is not in the list!") ``` -В первом примере, мы создали словарь из трех элементов. После этого, мы попытались открыть доступ ключу, которого в словаре нет. Так как ключ не в словаре, возникает KeyError, которую мы выявили. Второй пример показывает список, длина которого состоит из пяти объектов. Мы попытались взять седьмой объект из индекса. +В первом примере мы создали словарь из трех элементов. После этого мы попытались открыть доступ к ключу, которого в +словаре нет. Так как ключа нет в словаре, возникает ошибка `KeyError`, которую мы выявили. Второй пример показывает +список, длина которого составляет пять элементов. Мы попытались взять седьмой элемент из индекса. -Помните, что списки в Пайтоне начинаются с нуля, так что когда вы говорите 6, вы запрашиваете 7. В любом случае, в нашем списке только пять объектов, по этой причине возникает IndexError, которую мы выявили. Вы также можете выявить несколько ошибок за раз при помощи одного оператора. Для этого существует несколько различных способов. Давайте посмотрим: +Помните, что списки в Python начинаются с нуля, так что когда вы говорите 6, вы запрашиваете 7. В любом случае в нашем +списке только пять объектов, по этой причине возникает `IndexError`, которую мы выявили. Вы также можете выявить +несколько ошибок за раз при помощи одного оператора. Для этого существует несколько различных способов. +Давайте посмотрим: ```python -my_dict = {"a":1, "b":2, "c":3} +my_dict = {"a": 1, "b": 2, "c": 3} try: value = my_dict["d"] @@ -117,7 +137,14 @@ except: print("Some other error occurred!") ``` -Это самый стандартный способ выявить несколько исключений. Сначала мы попробовали открыть доступ к несуществующему ключу, которого нет в нашем словаре. При помощи try/except мы проверили код на наличие ошибки KeyError, которая находится во втором операторе except. Обратите внимание на то, что в конце кода у нас появилась «голое» исключение. Обычно, это не рекомендуется, но вы, возможно, будете сталкиваться с этим время от времени, так что лучше быть проинформированным об этом. Кстати, также обратите внимание на то, что вам не нужно использовать целый блок кода для обработки нескольких исключений. Обычно, целый блок используется для выявления одного единственного исключения. Изучим второй способ выявления нескольких исключений: +Это самый стандартный способ выявить несколько исключений. Сначала мы попробовали открыть доступ к несуществующему +ключу, которого нет в нашем словаре. При помощи `try/except` мы проверили код на наличие ошибки `KeyError`, которая +находится во втором операторе `except`. Обратите внимание на то, что в конце кода у нас появилась «голое» исключение. +Обычно это не рекомендуется, но вы, возможно, будете сталкиваться с этим время от времени, так что лучше быть +проинформированным об этом. Кстати, также обратите внимание на то, что вам не нужно использовать целый блок кода для +обработки нескольких исключений. Обычно, целый блок используется для выявления одного единственного исключения. + +Изучим второй способ выявления нескольких исключений: ```python try: @@ -126,16 +153,22 @@ except IndexError, KeyError: print("An IndexError or KeyError occurred!") ``` -Обратите внимание на то, что в данном примере мы помещаем ошибки, которые мы хотим выявить, внутри круглых скобок. Проблема данного метода в том, что трудно сказать какая именно ошибка произошла, так что предыдущий пример, мы рекомендуем больше чем этот. Зачастую, когда происходит ошибка, вам нужно уведомить пользователя, при помощи сообщения. +Обратите внимание на то, что в данном примере мы помещаем ошибки, которые мы хотим выявить, внутри круглых скобок. +Проблема данного метода в том, что трудно сказать, какая именно ошибка произошла, так что предыдущий пример, мы +рекомендуем больше, чем этот. Зачастую, когда происходит ошибка, вам нужно уведомить пользователя, при помощи сообщения. -В зависимости от сложности данной ошибки, вам может понадобиться выйти из программы. Иногда вам может понадобиться выполнить очистку, перед выходом из программы. Например, если вы открыли соединение с базой данных, вам нужно будет закрыть его, перед выходом из программы, или вы можете закончить с открытым соединением. Другой пример – закрытие дескриптора файла, к которому вы обращаетесь. Теперь нам нужно научиться убирать за собой. Это очень просто, если использовать оператор `finally`. +В зависимости от сложности данной ошибки вам может понадобиться выйти из программы. Иногда вам может понадобиться +выполнить очистку перед выходом из программы. Например, если вы открыли соединение с базой данных, вам нужно будет +закрыть его перед выходом из программы, или вы можете закончить с открытым соединением. Другой пример – закрытие +дескриптора файла, к которому вы обращаетесь. Теперь нам нужно научиться убирать за собой. Это очень просто, если +использовать оператор `finally`. ## Оператор finally -Оператор finally очень прост в использовании. Давайте взглянем на нижеизложенный пример: +Оператор `finally` очень прост в использовании. Давайте взглянем на нижеизложенный пример: ```python -my_dict = {"a":1, "b":2, "c":3} +my_dict = {"a": 1, "b": 2, "c": 3} try: value = my_dict["d"] @@ -145,14 +178,17 @@ finally: print("The finally statement has executed!") ``` -Если вы запустите это код, оно отобразиться и в операторе `except` и в `finally`. Весьма просто, не так ли? Теперь вы можете использовать оператор `finally`, чтобы убрать за собой. Вы можете также вписать код `exit` в конце оператора `finally`. +Если вы запустите этот код, оно отобразится и в операторе `except`, и в `finally`. Весьма просто, не так ли? Теперь вы +можете использовать оператор `finally`, чтобы убрать за собой. Вы можете также вписать код `exit` в конце оператора +`finally`. -Попробуйте except или else +Попробуйте `except` или `else`. -Оператор try/except также имеет пункт else. Он работает только в том случае, если в вашем коде нет ни единой ошибки. Давайте потратим немного времени и взглянем на парочку примеров: +Оператор `try/except` также имеет блок `else`. Он работает только в том случае, если в вашем коде нет ни единой ошибки. +Давайте потратим немного времени и взглянем на парочку примеров: ```python -my_dict = {"a":1, "b":2, "c":3} +my_dict = {"a": 1, "b": 2, "c": 3} try: value = my_dict["a"] @@ -162,10 +198,12 @@ else: print("No error occurred!") ``` -Мы видим словарь, состоящий из трех элементов, и в операторе try/except мы открываем доступ к существующему ключу. Это работает, так что ошибка KeyError не возникает. Так как ошибки нет, else работает, и надпись “No error occurred!” появляется на экране. Теперь добавим оператор finally: +Мы видим словарь, состоящий из трех элементов, и в операторе `try/except` мы открываем доступ к существующему ключу. +Это работает, так что ошибка `KeyError` не возникает. Так как ошибки нет, `else` работает, и надпись +“No error occurred!” появляется на экране. Теперь добавим оператор `finally`: ```python -my_dict = {"a":1, "b":2, "c":3} +my_dict = {"a": 1, "b": 2, "c": 3} try: value = my_dict["a"] @@ -177,11 +215,15 @@ finally: print("The finally statement ran!") ``` -В данном коде работают и оператор else и finally. Большую часть времени вы не будете сталкиваться с оператором else, используемый в том или ином коде, который следует за оператором try/except, если ни одна ошибка не была найдена. Единственное полезное применение оператора else, которое я видел, это когда вы хотите запустить вторую часть кода, в которой может быть ошибка. Конечно, если ошибка возникает в else, то она не будет поймана. +В данном коде работают и оператор `else`, и `finally`. Большую часть времени вы не будете сталкиваться с оператором +`else`, используемым в том или ином коде, который следует за оператором `try/except`, если ни одна ошибка не была +найдена. Единственное полезное применение оператора `else`, которое я видел, — это когда вы хотите запустить вторую +часть кода, в которой может быть ошибка. Конечно, если ошибка возникает в `else`, то она не будет поймана. ### Оператор raise -Если в вашем коде какие-либо данные не соответсвуют вашим ожиданиям, вы всегда можете вызвать исключение если вам это необходимо, для этого используется ключевое слово `raise`. +Если в вашем коде какие-либо данные не соответствуют вашим ожиданиям, вы всегда можете вызвать исключение, если вам это +необходимо, для этого используется ключевое слово `raise`. ```python def even_the_odds(odds): @@ -192,7 +234,8 @@ def even_the_odds(odds): Любое исключение завершает наш код, а значит, что до возврата в случае исключения функция не дойдет. -Мы можем использовать `raise` внутри любой конструкции, допустим нам нужно отправлять ошибку на сторонний сервис, но не обрабатывать её +Мы можем использовать `raise` внутри любой конструкции. Допустим, нам нужно отправлять ошибку на сторонний сервис, но +не обрабатывать её. ```python try: @@ -204,9 +247,9 @@ finally: free_expensive_resource(resource) ``` -`raise` в этом случае просто повторится +`raise` в этом случае просто повторится. -еще один такой пример +Еще один такой пример: ```python try: @@ -216,7 +259,9 @@ except ZeroDivisionError: raise ``` -Имейте в виду, однако, что кто-то еще выше в стеке вызовов может все же перехватить исключение и как-то обработать его. Готовый вывод может быть неприятным в этом случае, потому что это произойдет в любом случае (пойман или не пойман). Поэтому может быть лучше создать другое исключение, содержащее ваш комментарий о ситуации, а также исходное исключение: +Имейте в виду, однако, что кто-то еще выше в стеке вызовов может все же перехватить исключение и как-то обработать его. +Готовый вывод может быть неприятным в этом случае, потому что это произойдет в любом случае (пойман или не пойман). +Поэтому может быть лучше создать другое исключение, содержащее ваш комментарий о ситуации, а также исходное исключение: ```python try: @@ -225,11 +270,14 @@ except ZeroDivisionError as e: raise ZeroDivisionError("Got an error", e) ``` -в переменной `e` будет хранится вся информация о исключении, и таким рейзом мы вызовем нужный нам тип исключения, с нашим коментарием, и всей системной информацией. +в переменной `e` будет храниться вся информация об исключении, и таким рейзом мы вызовем нужный нам тип исключения, с +нашим комментарием и всей системной информацией. ### Исключения тоже объекты -Исключением являются только обычные объекты Python , которые наследуют от встроенного BaseException . Сценарий Python может использовать raise заявление , чтобы прервать выполнение, в результате чего Python для печати трассировки стеки из стека вызовов в этой точке и представление экземпляра исключения. +Исключением являются только обычные объекты Python, которые наследуют от встроенного `BaseException`. Сценарий Python +может использовать `raise` заявление, чтобы прервать выполнение, в результате чего Python для печати трассировки стека +из стека вызовов в этой точке и представление экземпляра исключения. Например: @@ -243,7 +291,8 @@ Traceback (most recent call last): ValueError: Example error! ``` -который говорит , что `ValueError` с сообщением 'Example error!' был поднят нашей `failing_function()` , который был выполнен в интерпретаторе. +Который говорит, что `ValueError` с сообщением 'Example error!' был поднят нашей `failing_function()`, который был +выполнен в интерпретаторе. Код вызова может выбрать обработку любых исключений, которые может вызвать вызов: @@ -255,7 +304,7 @@ ValueError: Example error! Handled the error ``` -Или вывесли оригинальный текст: +Или вывести оригинальный текст: ```python >>> try: @@ -267,7 +316,7 @@ Caught exception ValueError('Example error!',) ## Создание пользовательских типов исключений -Создайте класс , унаследованный от Exception : +Создайте класс, унаследованный от `Exception`: ```python class FooException(Exception): pass @@ -278,7 +327,7 @@ except FooException: print("A FooException was raised.") ``` -или другой тип исключения: +Или другой тип исключения: ```python class NegativeError(ValueError): @@ -296,4 +345,4 @@ except NegativeError: print("You entered a negative number!") else: print("The result was " + str(result)) -``` \ No newline at end of file +``` From ad8bba4e26aaf0d0db021c7dde67e1c6fe3facf9 Mon Sep 17 00:00:00 2001 From: Julia Chuprova Date: Wed, 2 Aug 2023 18:21:28 +0300 Subject: [PATCH 14/23] edit grammar and style --- lesson17.md | 647 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 429 insertions(+), 218 deletions(-) diff --git a/lesson17.md b/lesson17.md index 4c8a0bece..436d92aee 100644 --- a/lesson17.md +++ b/lesson17.md @@ -3,43 +3,62 @@ ![](https://ymatuhin.ru/assets/img/styleguide/styleguide.jpg) -## PEP8 +## [PEP 8](https://peps.python.org/pep-0008/) PEP - Python Enhanced Proposal, рекомендация по код стайлингу (следовать обязательно:) -Гвидо ван Россум - создатель языка python +Гвидо ван Россум - создатель языка Python. -Ключевая идея Гвидо такова: код читается намного больше раз, чем пишется. Собственно, рекоммендации о стиле написания кода направлены на то, чтобы улучшить читабельность кода и сделать его согласованным между большим числом проектов. В идеале, весь код будет написан в едином стиле, и любой сможет легко его прочесть. Как говорится в PEP 20 «Читабельность имеет значение». +Ключевая идея Гвидо такова: код читается намного больше раз, чем пишется. Собственно, рекомендации о стиле написания +кода направлены на то, чтобы улучшить читабельность кода и сделать его согласованным между большим числом проектов. +В идеале, весь код будет написан в едином стиле, и любой сможет легко его прочесть. Как говорится в PEP 20 +«Читабельность имеет значение». -Это руководство о согласованности и единстве. Согласованность с этим руководством очень важна. Согласованность внутри одного проекта еще важнее. А согласованность внутри модуля или функции — самое важное. Но важно помнить, что иногда это руководство неприменимо, и понимать, когда можно отойти от рекоммендаций. Когда вы сомневаетесь, просто посмотрите на другие примеры и решите, какой выглядит лучше. +Это руководство о согласованности и единстве. Согласованность с этим руководством очень важна. Согласованность внутри +одного проекта еще важнее. А согласованность внутри модуля или функции — самое важное. Но важно помнить, что иногда это +руководство неприменимо, и понимать, когда можно отойти от рекомендаций. Когда вы сомневаетесь, просто посмотрите на +другие примеры и решите, какой выглядит лучше. ### Две причины, чтобы нарушить правила: 1. Когда применение правила сделает код менее читабельным даже для того, кто привык читать код, который следует правилам. -2. Чтобы писать в едином стиле с кодом, который уже есть в проекте и который нарушает правила (может быть, в силу исторических причин) — впрочем, это возможность подчистить чужой код. +2. Чтобы писать в едином стиле с кодом, который уже есть в проекте и нарушает правила (может быть, в силу + исторических причин) — впрочем, это возможность подчистить чужой код. ### Внешний вид кода #### Отступы -Используйте 4 пробела на один уровень отступа. В старом коде, который вы не хотите трогать, можно продолжить пользоваться 8 пробелами для отступа. +Используйте 4 пробела на один уровень отступа. В старом коде, который вы не хотите трогать, можно продолжить +пользоваться 8 пробелами для отступа. #### Табуляция или пробелы? Никогда не смешивайте символы табуляции и пробелы. -Самый распространенный способ отступов — пробелы. На втором месте — отступы только с использованием табуляции. Код, в котором используются и те, и другие типы отступов, должен быть исправлен так, чтобы отступы в нем были расставлены только с помощью пробелов. Когда вы вызываете интерпретатор в командной строке с параметром -t, он выдает предупреждения (warnings) при использовании смешанного стиля в отступах, а запустив интерпретатор с параметром -tt, вы получите в этих местах ошибки (errors). Используйте этии опции! +Самый распространенный способ отступов — пробелы. На втором месте — отступы только с использованием табуляции. Код, в +котором используются и те, и другие типы отступов, должен быть исправлен так, чтобы отступы в нем были расставлены +только с помощью пробелов. Когда вы вызываете интерпретатор в командной строке с параметром `-t`, он выдает +предупреждения (warnings) при использовании смешанного стиля в отступах, а запустив интерпретатор с параметром `-tt`, +вы получите в этих местах ошибки (errors). Используйте эти опции! -В новых проектах для отступов мы настоятельно рекомендуем использовать пробелы. К тому же, многие редакторы позволяют легко делать. +В новых проектах для отступов мы настоятельно рекомендуем использовать пробелы. К тому же, многие редакторы позволяют +легко это делать. #### Максимальная длина строки Ограничьте максимальную длину строки 79 символами. -Пока еще существует немало устройств, где длина строки равна 80 символам; к тому же, ограничив ширину окна 80 символами, мы сможем расположить несколько окон рядом друг с другом. Автоматический перенос строк на таких устройствах нарушит форматирование, и код будет труднее понять. Так что, пожалуйста, ограничьте длину строки 79 символами, и 72 символами в случае длинных блоков текста (строки документации или комментарии). +Пока еще существует немало устройств, где длина строки равна 80 символам; к тому же, ограничив ширину окна 80 символами, +мы сможем расположить несколько окон рядом друг с другом. Автоматический перенос строк на таких устройствах нарушит +форматирование, и код будет труднее понять. Так что, пожалуйста, ограничьте длину строки 79 символами, и 72 символами +в случае длинных блоков текста (строки документации или комментарии). -Предпочтительный способ переноса длинных строк — использование подразумевающегося продолжения строки между обычными, квадратными и фигурными скобками. В случае необходимости можно добавить еще одну пару скобок вокруг выражения, но часто лучше выглядит обратный слэш. Постарайтесь сделать правильные отступы для перенесённой строки. Предпочтительнее вставить перенос строки после бинарного оператора, но не перед ним. +Предпочтительный способ переноса длинных строк — использование подразумевающегося продолжения строки между обычными, +квадратными и фигурными скобками. В случае необходимости можно добавить еще одну пару скобок вокруг выражения, но часто +лучше выглядит обратный слэш. Постарайтесь сделать правильные отступы для перенесённой строки. Предпочтительнее вставить +перенос строки после бинарного оператора, но не перед ним. Вот несколько примеров: @@ -50,11 +69,10 @@ class Rectangle(Blob): if width == 0 and height == 0 and \ color == 'red' and emphasis == 'strong' or \ highlight > 100: - raise ValueError("sorry, you lose") + raise ValueError("Sorry, you lose") if width == 0 and height == 0 and (color == 'red' or emphasis is None): - raise ValueError("I don't think so -- values are %s, %s" % - (width, height)) + raise ValueError(f"I don't think so -- values are {width}, {height}") Blob.__init__(self, width, height, color, emphasis, highlight) @@ -62,23 +80,34 @@ class Rectangle(Blob): ### Пустые строки -Отделяйте функции (верхнего уровня, не функции внутри функций) и определения классов двумя пустыми строчками. +Отделяйте функции (верхнего уровня, не функции внутри функций) и определения классов двумя пустыми строками. Определения методов внутри класса отделяйте одной пустой строкой. -Дополнительные отступы строками могут быть изредка использованы для выделения группы логически связанных функций. Пустые строки могут быть пропущены, между несколькими выражениями, записанными в одну строку, например, «заглушки» функций. +Дополнительные отступы строками могут быть изредка использованы для выделения группы логически связанных функций. Пустые +строки могут быть пропущены между несколькими выражениями, записанными в одну строку, например, «заглушки» функций. Используйте (без энтузиазма) пустые строки в коде функций, чтобы отделить друг от друга логические части. -Python расценивает символ control+L как незначащий (whitespace), и вы можете использовать его, потому что многие редакторы обрабатывают его как разрыв страницы — таким образом логические части в файле будут на разных страницах. +Python расценивает символ `Ctrl+L` как незначащий (whitespace), и вы можете использовать его, потому что многие +редакторы обрабатывают его как разрыв страницы — таким образом, логические части в файле будут на разных страницах. -### Кодировки (PEP 263) +### Кодировки ([PEP 263](https://peps.python.org/pep-0263/)) -Код ядра python всегда должен использовать ASCII или Latin-1 кодировку (также известную как ISO-8859-1). Начиная с версии python 3.0, предпочтительной является кодировка UTF-8 (смотрите PEP 3120). +Код ядра Python всегда должен использовать ASCII или Latin-1 кодировку (также известную как ISO-8859-1). Начиная с +версии Python 3.0, предпочтительной является кодировка UTF-8 (см. [PEP 3120](https://peps.python.org/pep-3120/)). -Files using ASCII (or UTF-8, for Python 3.0) should not have a coding cookie. Используйте Latin-1 (или UTF-8), только если это необходимо, чтобы указать в комментарии или строке документации имя автора, содержащее в себе символ из Latin-1. В противном случае предпочтительнее использовать escape-символы \x, \u или \U для не-ASCII символов в строках. +Files using ASCII (or UTF-8, for Python 3.0) should not have a coding cookie. Используйте Latin-1 (или UTF-8), только +если это необходимо, чтобы указать в комментарии или строке документации имя автора, содержащее в себе символ из +Latin-1. В противном случае предпочтительнее использовать escape-символы `\x`, `\u` или `\U` для не-ASCII символов в +строках. -Начиная с версии python 3.0 в стандартной библиотеке действует следующая политика (смотрите PEP 3131): все идентификаторы обязаны содержать только ASCII символы, и означать английские слова везде, где это возможно (во многих случаях используются сокращения или неанглийские технические термины). Кроме того, строки и комментарии тоже должны содержать лишь ASCII символы. Исключения составляют: (а) test case, тестирующий не-ASCII особенности программы, и (б) имена авторов. Авторы, буквы в именах которых не из латинского алфавита, должны транслитерировать свои имена в латиницу. +Начиная с версии Python 3.0 в стандартной библиотеке действует следующая политика +(см. [PEP 3131](https://peps.python.org/pep-3131/)): все идентификаторы обязаны содержать только ASCII символы, и +означать английские слова везде, где это возможно (во многих случаях используются сокращения или неанглийские +технические термины). Кроме того, строки и комментарии тоже должны содержать лишь ASCII символы. Исключения составляют: +(а) test case, тестирующий не-ASCII особенности программы, и (б) имена авторов. Авторы, буквы в именах которых не из +латинского алфавита, должны транслитерировать свои имена в латиницу. Проектам с открытым кодом для широкой аудитории также рекомендуется использовать это соглашение. @@ -98,11 +127,12 @@ import sys import os, sys ``` -В то же время, можно писать вот так: +В то же время можно писать вот так: ```python from subprocess import Popen, PIPE ``` -Импортирование всегда нужно делать сразу после комментариев к модулю и строк документации, перед объявлением глобальных переменных и констант. +Импортирование всегда нужно делать сразу после комментариев к модулю и строк документации, перед объявлением глобальных +переменных и констант. Группируйте импорты в следующем порядке: @@ -112,16 +142,20 @@ from subprocess import Popen, PIPE Вставляйте пустую строку между каждой группой импортов. -Относительные импорты крайне не рекомендуются — всегда указывайте абсолютный путь к модулю для всех импортирований. Даже сейчас, когда PEP 328 реализован в версии python 2.5, использовать явные относительные импорты нежелательно, потому что абсолютные импорты лучше переносимы и читабельны. +Относительные импорты крайне не рекомендуются — всегда указывайте абсолютный путь к модулю для всех импортирований. +Даже сейчас, когда PEP 328 реализован в версии Python 2.5, использовать явные относительные импорты нежелательно, +потому что абсолютные импорты лучше переносимы и читабельны. Когда вы импортируете класс из модуля, вполне можно писать вот так: ```python -from myclass import MyClass from foo.bar.yourclass import YourClass +from myclass import MyClass +from foo.bar.yourclass import YourClass ``` Если такое написание вызывает конфликт имен, тогда пишите: ```python -import myclass import foo.bar.yourclass +import myclass +import foo.bar.yourclass ``` И используйте `«myclass.MyClass»` и `«foo.bar.yourclass.Yourclass»`. @@ -132,288 +166,394 @@ import myclass import foo.bar.yourclass - Сразу после или перед скобками (обычными, фигурными и квадратными) ```python -можно: +# можно: spam(ham[1], {eggs: 2}) -нельзя: +# нельзя: spam( ham[ 1 ], { eggs: 2 } ) ``` - Сразу перед запятой, точкой с запятой, двоеточием: ```python -if x == 4: print x, y; x, y = y, x - -if x == 4 : print x , y ; x , y = y , x +# можно: + if x == 4: + print(x, y) + x, y = y, x +# нельзя: + if x == 4 : + print(x , y) + x , y = y , x ``` - Сразу перед открывающей скобкой, после которой начинается список аргументов при вызове функции: ```python -spam(1) - -spam (1) +# можно: + spam(1) +# нельзя: + spam (1) ``` - Сразу перед открывающей скобкой, после которой следует индекс или срез: ```python -dict['key'] = list[index] - -dict ['key'] = list [index] +# можно: + dict['key'] = list[index] +# нельзя: + dict ['key'] = list [index] ``` -- Использование более одного пробела вокруг оператора присваивания (или любого другого) для того, чтобы выровнять его с другим таким же оператором на соседней строке: +- Использование более одного пробела вокруг оператора присваивания (или любого другого) для того, чтобы выровнять его + с другим таким же оператором на соседней строке: ```python -x = 1 -y = 2 -long_variable = 3 +# можно: + x = 1 + y = 2 + long_variable = 3 -x = 1 -y = 2 -long_variable = 3 +# нельзя: + x = 1 + y = 2 + long_variable = 3 ``` -Прочие рекоммендации: -- Вседа окружайте эти бинарные операторы одним пробелом с каждой стороны: присваивание (`=`, `+=`, `-=` и прочие), сравнения (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`, `in`, `not in`, `is`, `is not`), логические операторы (`and`, `or`, `not`). +Прочие рекомендации: +- Всегда окружайте эти бинарные операторы одним пробелом с каждой стороны: присваивание (`=`, `+=`, `-=` и прочие), + сравнения (`==`, `<`, `>`, `!=`, `<>`, `<=`, `>=`, `in`, `not in`, `is`, `is not`), логические операторы + (`and`, `or`, `not`). - Ставьте пробелы вокруг арифметических операций. ```python -i = i + 1 -submitted += 1 -x = x * 2 - 1 -hypot2 = x * x + y * y -c = (a + b) * (a - b) +# можно: + i = i + 1 + submitted += 1 + x = x * 2 - 1 + hypot2 = x * x + y * y + c = (a + b) * (a - b) -i=i+1 -submitted +=1 -x = x*2 - 1 -hypot2 = x*x + y*y -c = (a+b) * (a-b) +# нельзя: + i=i+1 + submitted +=1 + x = x*2 - 1 + hypot2 = x*x + y*y + c = (a+b) * (a-b) ``` -- Не используйте пробелы для отделения знака `=`, когда он употребляется для обозначения аргумента-ключа (keyword argument) или значения параметра по умолчанию. +- Не используйте пробелы для отделения знака `=`, когда он употребляется для обозначения аргумента-ключа + (keyword argument) или значения параметра по умолчанию. ```python -def complex(real, imag=0.0): - return magic(r=real, i=imag) +# можно: + def complex(real, imag=0.0): + return magic(r=real, i=imag) -def complex(real, imag = 0.0): - return magic(r = real, i = imag) +# нельзя: + def complex(real, imag = 0.0): + return magic(r = real, i = imag) ``` - Не используйте составные инструкции (несколько команд в одной строке). ```python -if foo == 'blah': - do_blah_thing() -do_one() -do_two() -do_three() +# можно: + if foo == 'blah': + do_blah_thing() + do_one() + do_two() + do_three() -if foo == 'blah': do_blah_thing() -do_one(); do_two(); do_three() +# нельзя: + if foo == 'blah': do_blah_thing() + do_one(); do_two(); do_three() ``` -- Иногда можно писать тело циклов while, for или ветку if в той же строке, если команда короткая, но если команд несколько, никогда так не пишите. +- Иногда можно писать тело циклов `while`, `for` или ветку `if` в той же строке, если команда короткая, но если команд + несколько, никогда так не пишите. ```python -if foo == 'blah': do_blah_thing() -for x in lst: total += x -while t < 10: t = delay() +# можно: + if foo == 'blah': do_blah_thing() + for x in lst: total += x + while t < 10: t = delay() -if foo == 'blah': do_blah_thing() -else: do_non_blah_thing() -try: something() -finally: cleanup() -do_one(); do_two(); do_three(long, argument, - list, like, this) -if foo == 'blah': one(); two(); three() +# нельзя: + if foo == 'blah': do_blah_thing() + else: do_non_blah_thing() + try: something() + finally: cleanup() + do_one(); do_two(); do_three(long, argument, list, like, this) + if foo == 'blah': one(); two(); three() ``` ### Комментарии -Комментарии, которые противоречат коду, хуже, чем отсутствие комментариев. Всегда исправляйте комментарии, если меняете код! +Комментарии, которые противоречат коду, хуже, чем отсутствие комментариев. Всегда исправляйте комментарии, если +меняете код! -Комментарии должны являться законченными предложениями. Если комментарий — фраза или предложение, первое слово должно быть написано с большой буквы, если только это не имя переменной, которая начинается с маленькой буквы (кстати, никогда не отступайте от этого правила для имен переменных). +Комментарии должны являться законченными предложениями. Если комментарий — фраза или предложение, первое слово должно +быть написано с большой буквы, если только это не имя переменной, которая начинается с маленькой буквы (кстати, +никогда не отступайте от этого правила для имен переменных). -Если комментарий короткий, можно опустить точку в конце предложения. Блок комментариев обычно состоит из одного или более абзацев, составленных из полноценных предложений, поэтому каждое предложение должно оканчиваться точкой. +Если комментарий короткий, можно опустить точку в конце предложения. Блок комментариев обычно состоит из одного или +более абзацев, составленных из полноценных предложений, поэтому каждое предложение должно оканчиваться точкой. Ставьте два пробела после точки в конце предложения. -Если вы пишете по-английски, не забывайте о Странке и Уайте (имеется в виду книга Strunk & White, “Elements of style”, которая является практически эталонным руководством по правильному написанию текстов на английском языке, — прим. перев.) +Если вы пишете по-английски, не забывайте о Странке и Уайте (имеется в виду книга Strunk & White, “Elements of style”, +которая является практически эталонным руководством по правильному написанию текстов на английском языке.) -Программисты, которые не говорят на английском языке, пожалуйста, пишите комментарии на английском, если только вы не уверены на 120 процентов, что ваш код никогда не будут читать люди, не знающие вашего родного языка. +Программисты, которые не говорят на английском языке, пожалуйста, пишите комментарии на английском, если только вы не +уверены на 120 процентов, что ваш код никогда не будут читать люди, не знающие вашего родного языка. #### Блок комментариев -Блок комментариев обычно объясняет код (весь, или только некоторую часть), идущий после блока, и должен иметь тот же отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа # и одного пробела после него (если только сам текст комментария не имеет отступа). +Блок комментариев обычно объясняет код (весь или только некоторую часть), идущий после блока, и должен иметь тот же +отступ, что и сам код. Каждая строчка такого блока должна начинаться с символа `#` и одного пробела после него (если +только сам текст комментария не имеет отступа). -Абзацы внутри блока комментариев лучше отделять строкой, состоящей из одного символа #. +Абзацы внутри блока комментариев лучше отделять строкой, состоящей из одного символа `#`. -Комментарии в строке с кодом +**Комментарии в строке с кодом** Старайтесь реже использовать подобные комментарии. -Такой комментарий находится в той же строке, что и инструкция. «Встрочные» комментарии должны отделяться хотя бы двумя пробелами от инструкции. Они должны начинаться с символа # и одного пробела. +Такой комментарий находится в той же строке, что и инструкция. «Встрочные» комментарии должны отделяться хотя бы двумя +пробелами от инструкции. Они должны начинаться с символа `#` и одного пробела. Комментарии в строке с кодом не нужны и только отвлекают от чтения, если они объясняют очевидное. Не пишите вот так: ```python x = x + 1 # Увеличиваем X на один ``` -Впрочем, иногда такие комметарии полезны: +Впрочем, иногда такие кометарии полезны: ```python x = x + 1 # Место для рамки окна ``` ### Строки документации -Соглашения о написании хорошей документации (docstrings) увековечены (да, забавно, но автор использует именно такое слово, — прим. перев.) в PEP 257. +Соглашения о написании хорошей документации (docstrings) увековечены (да, забавно, но автор использует именно такое +слово, — прим. перев.) в [PEP 257](https://peps.python.org/pep-0257/). -Пишите документацию для всех модулей, функций, классов, методов, которые объявлены как public. Строки документации необязательны для не-public методов, но лучше написать, что делает метод. Комментарий нужно писать после строки с def. +Пишите документацию для всех модулей, функций, классов, методов, которые объявлены как `public`. Строки документации +необязательны для не-public методов, но лучше написать, что делает метод. Комментарий нужно писать после строки с `def`. -PEP 257 объясняет, как правильно и хорошо документировать. Заметьте, очень важно, чтобы закрывающие """ стояли на отдельной строчке. А еще лучше, если перед ними будет ещё и пустая строка, например: +PEP 257 объясняет как правильно и хорошо документировать. Заметьте, очень важно, чтобы закрывающие кавычки `"""` стояли +на отдельной строке. А еще лучше, если перед ними будет ещё и пустая строка, например: ```python """Return a foobang Optional plotz says to frobnicate the bizbaz first. """ ``` -Для однострочной документации можно оставить """ на той же строке. +Для однострочной документации можно оставить `"""` на той же строке. ### Имена -Соглашения об именах переменных в python немного туманны, поэтому их список никогда не будет полным — тем не менее, ниже мы приводим список рекоммендаций, действующих на данный момент. Новые модули и пакеты должны быть написаны согласно этим стандартам, но если в какой-либо уже существующей библиотеке эти правила нарушаются, предпочтительнее писать в едином с ней стиле. +Соглашения об именах переменных в Python немного туманны, поэтому их список никогда не будет полным — тем не менее, +ниже мы приводим список рекомендаций, действующих на данный момент. Новые модули и пакеты должны быть написаны согласно +этим стандартам, но если в какой-либо уже существующей библиотеке эти правила нарушаются, предпочтительнее писать в +одном стиле с ней. -### Описание: Стили имен +### Описание: стили имен -Существует много разных стилей. Поможем вам распознать, какой стиль именования используется, независимо от того, для чего он используется. +Существует много разных стилей. Поможем вам распознать, какой стиль именования используется, независимо от того, для +чего он используется. Обычно различают следующие стили: -b (одиночная маленькая буква) +`b` (одиночная маленькая буква) -B (одиночная заглавная буква) +`B` (одиночная заглавная буква) -lowercase (слово в нижнем регистре) +`lowercase` (слово в нижнем регистре) -lower_case_with_underscores (слова из маленьких букв с подчеркиваниями) +`lower_case_with_underscores` (слова из маленьких букв с подчеркиваниями) -UPPERCASE (заглавные буквы) +`UPPERCASE` (заглавные буквы) -UPPERCASE_WITH_UNDERSCORES (слова из заглавных букв с подчеркиваниями) +`UPPERCASE_WITH_UNDERSCORES` (слова из заглавных букв с подчеркиваниями) -CapitalizedWords (слова с заглавными буквами, или CapWords, или CamelCase 5. Иногда называется StudlyCaps). Замечание: когда вы используете аббревиатуры в таком стиле, пишите все буквы аббревиатуры заглавными — HTTPServerError лучше, чем HttpServerError. +`CapitalizedWords` (слова с заглавными буквами, или CapWords, или CamelCase. Иногда называется StudlyCaps). +Замечание: когда вы используете аббревиатуры в таком стиле, пишите все буквы аббревиатуры заглавными — HTTPServerError +лучше, чем HttpServerError. -mixedCase (отличается от CapitalizedWords тем, что первое слово начинается с маленькой буквы) +`mixedCase` (отличается от `CapitalizedWords` тем, что первое слово начинается с маленькой буквы) -Capitalized_Words_With_Underscores (слова с заглавными буквами и подчеркиваниями — уродливо!) -Ещё существует стиль, в котором имена, принадлежащие одной логической группе, имеют один короткий префикс. Этот стиль редко используется в python, но мы упоминаем его для полноты. Например, функция os.stat() возвращает кортеж, имена в котором традиционно имеют вид st_mode, st_size, st_mtime и так далее. (Так сделано, чтобы подчеркнуть соответствие этих полей структуре системных вызовов POSIX, что помогает знакомым с ней программистам). +`Capitalized_Words_With_Underscores` (слова с заглавными буквами и подчеркиваниями — уродливо!) +Ещё существует стиль, в котором имена, принадлежащие одной логической группе, имеют один короткий префикс. Этот стиль +редко используется в Python, но мы упоминаем его для полноты. Например, функция `os.stat()` возвращает кортеж, имена +в котором традиционно имеют вид `st_mode`, `st_size`, `st_mtime` и так далее. (Так сделано, чтобы подчеркнуть +соответствие этих полей структуре системных вызовов POSIX, что помогает знакомым с ней программистам). -В библиотеке X11 используется префикс Х для всех public-функций. В python этот стиль считается излишним, потому что перед полями и именами методов стоит имя объекта, а перед именами функций стоит имя модуля. +В библиотеке X11 используется префикс Х для всех public-функций. В Python этот стиль считается излишним, потому что +перед полями и именами методов стоит имя объекта, а перед именами функций стоит имя модуля. -В дополнение к этому, используются следующие специальные формы записи имен с добавлением символа подчеркивания в начало или конец имени: +В дополнение к этому используются следующие специальные формы записи имен с добавлением символа подчеркивания в начало +или конец имени: -_single_leading_underscore: слабый индикатор того, что имя используется для «внутренних нужд». Например, from M import * не будет импортировать объекты, чьи имена начинаются с символа подчеркивания. +`_single_leading_underscore`: слабый индикатор того, что имя используется для «внутренних нужд». Например, +`from M import *` не будет импортировать объекты, чьи имена начинаются с символа подчеркивания. -single_trailing_underscore_: используется по соглашению для избежания конфликтов с ключевыми словами языка python, например: +`single_trailing_underscore_`: используется по соглашению для избежания конфликтов с ключевыми словами языка Python, +например: -Tkinter.Toplevel(master, class_='ClassName') +`Tkinter.Toplevel(master, class_='ClassName') ` -__double_leading_underscore: изменяет имя атрибута класса, т.е. в class FooBar поле __boo становится _FooBar__boo. +`__double_leading_underscore`: изменяет имя атрибута класса, т.е. в `class FooBar` поле `__boo` становится +`_FooBar__boo`. -__double_leading_and_trailing_underscore__ (двойное подчеркивание в начале и в конце имени): «волшебные» объекты или атрибуты, которые «живут» в пространствах имен, управляемых пользователем (user-controlled namespaces). Например, __init__, __import__ или __file__. Не изобретайте такие имена, используйте их только так, как написано в документации. +`__double_leading_and_trailing_underscore__` (двойное подчеркивание в начале и в конце имени): «волшебные» объекты или +атрибуты, которые «живут» в пространствах имен, управляемых пользователем (user-controlled namespaces). Например, +`__init__`, `__import__` или `__file__`. Не изобретайте такие имена, используйте их только так, как написано в +документации. ### Стили имен -Имена, которых следует избегать +**Имена, которых следует избегать** -Никогда не используйте символы l (малелькая латинская буква «эль»), O (заглавная латинская буква «о») или I (заглавная латинская буква «ай») как однобуквенные идентификаторы. +Никогда не используйте символы `l` (маленькая латинская буква «эль»), `O` (заглавная латинская буква «о») или `I` +(заглавная латинская буква «ай») как односимвольные идентификаторы. -В некоторых шрифтах эти символы неотличимы от цифры один и нуля (и символа вертикальной палочки, — прим. перев.) Если очень нужно использовать l имена, пишите вместо неё заглавную L. +В некоторых шрифтах эти символы неотличимы от цифры один и нуля (и символа вертикальной палочки, — прим. перев.) +Если очень нужно использовать `l`-имена, пишите вместо неё заглавную `L`. -Имена модулей и пакетов +### Имена модулей и пакетов -Модули должны иметь короткие имена, состоящие из маленьких букв. Можно использовать и символы подчеркивания, если это улучшает читабельность. То же, за исключением символов подчеркивания, относится и к именам пакетов. +Модули должны иметь короткие имена, состоящие из маленьких букв. Можно использовать и символы подчеркивания, если это +улучшает читабельность. То же, за исключением символов подчеркивания, относится и к именам пакетов. -Так как имена модулей отображаются в имена файлов, а некоторые файловые системы являются нечувствительными к регистру символов и обрезают длинные имена, очень важно использовать достаточно короткие имена модулей — это не проблема в Unix, но, возможно, код окажется непереносимым в старые версии Windows или Mac, или DOS. Когда модуль расширения, написанный на С или C++, имеет сопутствующий python-модуль (содержащий интерфейс высокого уровня), С/С++ модуль начинается с символа подчеркивания, например, _socket. +Так как имена модулей отображаются в имена файлов, а некоторые файловые системы являются нечувствительными к регистру +символов и обрезают длинные имена, очень важно использовать достаточно короткие имена модулей — это не проблема в Unix, +но, возможно, код окажется непереносимым в старые версии Windows или Mac, или DOS. Когда модуль расширения, написанный +на С или C++, имеет сопутствующий Python-модуль (содержащий интерфейс высокого уровня), С/С++ модуль начинается с +символа подчеркивания, например, _socket. ### Имена классов -Все имена классов должны следовать соглашению CapWords почти без исключений. Классы внутреннего использования могут начинаться с символа подчеркивания. +Все имена классов должны следовать соглашению CapWords почти без исключений. Классы внутреннего использования могут +начинаться с символа подчеркивания. #### Имена исключений (exceptions) -Так как исключения являются классами, к исключениями применяется стиль именования классов. Однако вы можете добавить Error в конце имени (если конечно исключение действительно является ошибкой). +Так как исключения являются классами, к исключениям применяется стиль именования классов. Однако вы можете добавить +`Error` в конце имени (если конечно исключение действительно является ошибкой). #### Имена глобальных переменных -Будем надеяться, что такие имена используются только внутри одного модуля. Руководствуйтесь теми же соглашениями, что и для имен функций. +Будем надеяться, что такие имена используются только внутри одного модуля. Руководствуйтесь теми же соглашениями, что и +для имен функций. -Добавляйте в модули, которые написаны так, чтобы их использовали с помощью from M import *, механизм __all__ чтобы предотвратить экспортирование глобальных переменных. Или же, используйте старое соглашение, добавляя перед именами таких глобальных переменных один символ подчеркивания (которым вы можете обозначить те глобальные переменные, которые используются только внутри модуля). +Добавляйте в модули, которые написаны так, чтобы их использовали с помощью `from M import *`, механизм `__all__`, чтобы +предотвратить экспортирование глобальных переменных. Или же используйте старое соглашение, добавляя перед именами таких +глобальных переменных один символ подчеркивания (которым вы можете обозначить те глобальные переменные, которые +используются только внутри модуля). ### Имена функций -Имена функций должны состоять из маленьких букв, а слова разделяться символами подчеркивания — это необходимо, чтобы увеличить читабельность. +Имена функций должны состоять из маленьких букв, а слова разделяться символами подчеркивания — это необходимо, чтобы +увеличить читабельность. -Стиль mixedCase допускается в тех местах, где уже преобладает такой стиль, например во threading.py, для сохранения обратной совместимости. +Стиль mixedCase допускается в тех местах, где уже преобладает такой стиль, например, в threading.py, для сохранения +обратной совместимости. ### Аргументы функций и методов -Всегда используйте self в качестве первого аргумента метода экземпляра объекта (instance method). +Всегда используйте `self` в качестве первого аргумента метода экземпляра объекта (instance method). -Всегда используйте cls в качестве первого аргумента метода класса (class method). +Всегда используйте `cls` в качестве первого аргумента метода класса (class method). -Если имя аргумента конфликтует с зарезервированным ключевым словом python, обычно лучше добавить в конец имени символ подчеркивания, чем исказить написание слова или использовать аббревиатуру. Таким образом, print_ лучше, чем prnt. (Возможно, хорошим вариантом будет подобрать синоним). +Если имя аргумента конфликтует с зарезервированным ключевым словом Python, обычно лучше добавить в конец имени символ +подчеркивания, чем исказить написание слова или использовать аббревиатуру. Таким образом, print_ лучше, чем prnt. +(Возможно, хорошим вариантом будет подобрать синоним). #### Имена методов и переменные экземпляров классов -Используйте тот же стиль, что и для имен функций: имена должны состоять из маленьких букв, а слова разделяться символами подчеркивания. +Используйте тот же стиль, что и для имен функций: имена должны состоять из маленьких букв, а слова разделяться символами +подчеркивания. -Чтобы избежать конфликта имен с подклассами, добавьте два символа подчеркивания, чтобы включить механизм изменения имен. Если класс Foo имя атрибут с именем __foo, к нему нельзя обратиться, написав Foo.__a. (Настойчивый пользователь всё равно может получить доступ, написав Foo._Foo__a). Вообще, двойное подчеркивание в именах должно использоваться, чтобы избежать конфликта имен с атрибутами классов, спроектированных так, чтобы от них наследовали подклассы. +Чтобы избежать конфликта имен с подклассами, добавьте два символа подчеркивания, чтобы включить механизм изменения имен. +Если класс `Foo` содержит имя атрибута с именем `__a`, к нему нельзя обратиться, написав `Foo.__a`. (Настойчивый +пользователь всё равно может получить доступ, написав `Foo._Foo__a`). Вообще, двойное подчеркивание в именах должно +использоваться, чтобы избежать конфликта имен с атрибутами классов, спроектированных так, чтобы от них наследовали +подклассы. ### Константы -Константы обычно объявляются на уровне модуля и записываются только заглавными буквами, а слова разделяются символами подчеркивания. Например: MAX_OVERFLOW, TOTAL. +Константы обычно объявляются на уровне модуля и записываются только заглавными буквами, а слова разделяются символами +подчеркивания. Например: `MAX_OVERFLOW`, `TOTAL`. ### Проектирование наследования -Обязательно решите, каким должен быть метод класса или переменная экземпляра класса (в общем, атрибут) — public или не-public. Если вы сомневаетесь, выберите закрытый, не-public атрибут. Потом будет проще сделать их public, чем наоборот. +Обязательно решите, каким должен быть метод класса или переменная экземпляра класса (в общем, атрибут) — public или +не-public. Если вы сомневаетесь, выберите закрытый, не-public атрибут. Потом будет проще сделать их public, чем +наоборот. -Открытые атрибуты — это те, которые будут использовать потребители ваших классов, и вы должны быть уверены в отсутствии обратной несовместимости. Не-public атрибуты, в свою очередь, не предназначены для использования третьими лицами, поэтому вы можете не гарантировать, что не измените или не удалите эти атрибуты. +Открытые атрибуты — это те, которые будут использовать потребители ваших классов, и вы должны быть уверены в отсутствии +обратной несовместимости. Не-public атрибуты, в свою очередь, не предназначены для использования третьими лицами, +поэтому вы можете не гарантировать, что не измените или не удалите эти атрибуты. -Мы не используем термин «закрытый член» (private), потому что на самом деле в python таких членов не бывает. +Мы не используем термин «закрытый член» (private), потому что на самом деле в Python таких членов не бывает. -Другой тип аттрибутов классов принадлежит так называемому API подклассов (в других языках они часто называются protected). Некоторые классы проектируются так, чтобы от них наследовали другие классы, которые расширяют или модифицируют поведение базового класса. Когда вы проектируете такой класс, решите и явно укажите, какие атрибуты являются открытыми (public), какие принадлежат API подклассов (subclass API), а какие используются только базовым классом. +Другой тип атрибутов классов принадлежит к так называемому API подклассов (в других языках они часто называются +protected). Некоторые классы проектируются так, чтобы от них наследовали другие классы, которые расширяют или +модифицируют поведение базового класса. Когда вы проектируете такой класс, решите и явно укажите, какие атрибуты +являются открытыми (public), какие принадлежат API подклассов (subclass API), а какие используются только базовым +классом. -Теперь сформулируем рекомендации: +**Теперь сформулируем рекомендации:** -Открытые атрибуты не должны иметь в начале имени символа подчеркивания +Открытые атрибуты не должны иметь в начале имени символа подчеркивания. -Если имя открытого атрибута конфликтует с ключевым словом языка, добавьте в конец имени один символ подчеркивания. Это более предпочтительно, чем аббревиатура или искажение написания (однако, у этого правила есть исключение — аргумента который означает класс, и особенно первый аргумент метода класса (class method) должен иметь имя cls). +Если имя открытого атрибута конфликтует с ключевым словом языка, добавьте в конец имени один символ подчеркивания. Это +более предпочтительно, чем аббревиатура или искажение написания (однако, у этого правила есть исключение — аргумент, +который означает класс, и особенно первый аргумент метода класса (`class method`) должен иметь имя `cls`). -Назовите простые открытые атрибуты понятными именами и не пишите сложные методы доступа и изменения (accessor/mutator, get/set, — прим. перев.) Помните, что в python очень легко добавить их потом, если потребуется. В этом случае используйте свойства (properties), чтобы скрыть функциональную реализацию за синтаксисом доступа к атрибутам. +Назовите простые открытые атрибуты понятными именами и не пишите сложные методы доступа и изменения (`accessor/mutator`, +`get/set`, — прим. перев.) Помните, что в Python очень легко добавить их потом, если потребуется. В этом случае +используйте свойства (properties), чтобы скрыть функциональную реализацию за синтаксисом доступа к атрибутам. -Свойства (properties) работают только в классах нового стиля (new-style classes) +Свойства (properties) работают только в классах нового стиля (new-style classes). -Постарайтесь избавиться от побочных эффектов, связанным с функциональным поведением; впрочем, такие вещи, как кэширование, вполне допустимы. +Постарайтесь избавиться от побочных эффектов, связанным с функциональным поведением; впрочем, такие вещи как +кэширование, вполне допустимы. -Избегайте использования вычислительно затратных операций, потому что из-за записи с помощью атрибутов создается впечатление, что доступ происходит (относительно) быстро. +Избегайте использования вычислительно затратных операций, потому что из-за записи с помощью атрибутов создается +впечатление, что доступ происходит (относительно) быстро. -Если вы планируете класс таким образом, чтобы от него наследовались другие классы, но не хотите, чтобы подклассы унаследовали некоторые атрибуты, добавьте в имена два символа подчеркивания в начало, и ни одного — в конец. Механизм изменения имен в python (name mangling, — прим. перев.) сработает так, что имя класса добавится к имени такого атрибута, что позволит избежать конфликта имен с атрибутами подклассов. +Если вы планируете класс таким образом, чтобы от него наследовались другие классы, но не хотите, чтобы подклассы +унаследовали некоторые атрибуты, добавьте в имена два символа подчеркивания в начало, и ни одного — в конец. Механизм +изменения имен в Python (`name mangling`, — прим. перев.) сработает так, что имя класса добавится к имени такого +атрибута, что позволит избежать конфликта имен с атрибутами подклассов. Будьте внимательны: если подкласс будет иметь то же имя класса и имя атрибута, то вновь возникнет конфликт имен. -Механизм изменения имен может затруднить отладку или работу с __getattr__(), однако он хорошо документирован и легко реализуется вручную. +Механизм изменения имен может затруднить отладку или работу с `__getattr__()`, однако он хорошо документирован и легко +реализуется вручную. -Не всем нравится этот механизм, поэтому старайтесь достичь компромисса между необходимостью избежать конфликта имен и возможностью доступа к этим атрибутам. +Не всем нравится этот механизм, поэтому старайтесь достичь компромисса между необходимостью избежать конфликта имен и +возможностью доступа к этим атрибутам. ### Общие рекомендации -Код должен быть написан так, чтобы не зависеть от разных реализация языка (PyPy, Jython, IronPython, Pyrex, Psyco и пр.). Например, не полагайтесь на эффективную реализацию в CPython конкатенации строк в выражениях типа a+=b или a=a+b. Такие инструкции выполняются значительно медленнее в Jython. В критичных к времени выполнения частях программы используйте ''.join() — таким образом склеивание строк будет выполнено за линейное время независимо от реализации python. +Код должен быть написан так, чтобы не зависеть от разных реализация языка (PyPy, Jython, IronPython, Pyrex, Psyco и +пр.). Например, не полагайтесь на эффективную реализацию в CPython конкатенации строк в выражениях типа `a+=b` или +`a=a+b`. Такие инструкции выполняются значительно медленнее в Jython. В критичных к времени выполнения частях программы +используйте `''.join()` — таким образом склеивание строк будет выполнено за линейное время независимо от реализации +Python. -Сравнения с None должны обязательно выполняться с использованием операторов is или is not, а не с помощью операторов равенства или неравенства. Кроме того, не пишите if x, если имеете в виду if x is not None — если, к примеру, при тестировании такая переменная или аргумент примет значение иного типа, то при приведении к булевскому типу получится false. +Сравнения с `None` должны обязательно выполняться с использованием операторов `is` или `is not`, а не с помощью +операторов равенства или неравенства. Кроме того, не пишите `if x`, если имеете в виду `if x is not None` — если, к +примеру, при тестировании такая переменная или аргумент примет значение иного типа, то при приведении к булевому типу +получится `false`. -Создавайте исключения на основе классов. Впрочем, начиная с версии python 2.6, мы уже не можем использовать строки в качестве исключений. В модулях или пакетах создавайте свои базовые классы исключений, наследуя их от встроенного класса Exception и обязательно их документируйте: +Создавайте исключения на основе классов. Впрочем, начиная с версии Python 2.6, мы уже не можем использовать строки в +качестве исключений. В модулях или пакетах создавайте свои базовые классы исключений, наследуя их от встроенного класса +Exception и обязательно их документируйте: ```python class MessageError(Exception): """Base class for errors in the email package.""" ``` -Здесь применимы те же правила, что и для именования классов. Если исключение по своему смыслу является ошибкой, вы можете добавить в конце имени Error. +Здесь применимы те же правила, что и для именования классов. Если исключение по своему смыслу является ошибкой, вы +можете добавить в конце имени `Error`. -Когда вы генерируете исключение, пишите raise ValueError('message') вместо старого синтаксиса raise ValueError, message. Такое использование предпочтительнее, потому что из-за скобок не нужно использовать символы для продолжения перенесенных строк, если эти строки длинные или если используется форматирование. Старая форма записи запрещена в python 3000. +Когда вы генерируете исключение, пишите `raise ValueError('message')` вместо старого синтаксиса +`raise ValueError, message`. Такое использование предпочтительнее, потому что из-за скобок не нужно использовать +символы для продолжения перенесенных строк, если эти строки длинные или если используется форматирование. Старая форма +записи запрещена в Python 3.0. -Когда код перехватывает исключения, «ловите» конкретные ошибки вместо простого выражнения except:. К примеру, пишите вот так: +Когда код перехватывает исключения, «ловите» конкретные ошибки вместо простого выражения `except:`. К примеру, пишите +вот так: ```python try: @@ -422,11 +562,14 @@ except ImportError: platform_specific_module = None ``` -Простое написание 'except:' также перехватит и SystemExit, и KeyboardInterrupt, что породит проблемы, например, сложнее будет завершить программу нажатием control+C. Если вы действительно собираетесь перехватить все исключения, пишите 'except Exception:'. +Простое написание `except:` также перехватит и `SystemExit`, и `KeyboardInterrupt`, что породит проблемы, например, +сложнее будет завершить программу нажатием `Ctrl+C`. Если вы действительно собираетесь перехватить все исключения, +пишите `except Exception:`. -Ограничьтесь использованием чистого 'except:' в двух случаях: -1. Если обработчик исключения выводит пользователю всё о случившейся ошибке (например, traceback) -2. Если нужно выполнить некоторый код после перехвата исключения, а потом вновь «бросить» его для обработки где-то в другом месте. Обычно же лучше пользоваться конструкцией `try...finally`. +Ограничьтесь использованием чистого `except:` в двух случаях: +1. Если обработчик исключения выводит пользователю всё о случившейся ошибке (например, `traceback`). +2. Если нужно выполнить некоторый код после перехвата исключения, а потом вновь «бросить» его для обработки где-то в + другом месте. Обычно же лучше пользоваться конструкцией `try...finally`. Постарайтесь заключать в каждую конструкцию `try...except` минимум кода, чтобы легче отлавливать ошибки. ```python try: @@ -444,41 +587,53 @@ except KeyError: return key_not_found(key) ``` -Используйте строковые методы вместо модуля string — они всегда быстрее и имеют тот же API для unicode-строк. Можно отказаться от этого правила, если необходима совместимость с версиями python младше 2.0. +Используйте строковые методы вместо модуля `string` — они всегда быстрее и имеют тот же API для unicode-строк. Можно +отказаться от этого правила, если необходима совместимость с версиями Python младше 2.0. -Пользуйтесь `''.startswith()` и `''.endswith()` вместо обработки частей строк (string slicing) для проверки суффиксов или префиксов. `startswith()` и `endswith()` выглядят чище и порождают меньше ошибок. Например: +Пользуйтесь `''.startswith()` и `''.endswith()` вместо обработки частей строк (string slicing) для проверки суффиксов +или префиксов. `startswith()` и `endswith()` выглядят чище и порождают меньше ошибок. Например: ```python -да: if foo.startswith('bar'): +# да: + if foo.startswith('bar'): -нет: if foo[:3] == 'bar': +# нет: + if foo[:3] == 'bar': ``` -Сравнение типов объектов нужно делать с помощью isinstance(), а не прямым сравнением типов: +Сравнение типов объектов нужно делать с помощью `isinstance()`, а не прямым сравнением типов: ```python -да: if isinstance(obj, int): +# да: + if isinstance(obj, int): -нет: if type(obj) is type(1): +# нет: + if type(obj) is type(1): ``` -Для последовательностей (строк, списков, кортежей) можно использовать тот факт, что пустая последовательность есть false: +Для последовательностей (строк, списков, кортежей) можно использовать тот факт, что пустая последовательность есть +`false`: ```python -да: if not seq: +# да: + if not seq: if seq: -нет: if len(seq): - if not len(seq): +# нет: + if len(seq): + if not len(seq): ``` -Не пользуйтесь строковыми константами, которые имеют важные пробелы в конце — они невидимы, а многие редакторы (а теперь и reindent.py) обрезают их. +Не пользуйтесь строковыми константами, которые имеют важные пробелы в конце — они невидимы, а многие редакторы (а +теперь и reindent.py) обрезают их. -Не сравнивайте логические типы с True и False с помощью ==: +Не сравнивайте логические типы с `True` и `False` с помощью оператора `==`: ```python -да: if greeting: -нет: if greeting == True: -А вот так писать совсем плохо: -if greeting is True: +# да: + if greeting: +# нет: + if greeting == True: +# А вот так писать совсем плохо: + if greeting is True: ``` @@ -488,11 +643,12 @@ if greeting is True: Модуль: любой файл *.py. Имя модуля — имя этого файла. -Встроенный модуль: «модуль», который был написан на Си, скомпилирован и встроен в интерпретатор Python, и потому не имеет файла *.py. +Встроенный модуль: «модуль», который был написан на языке С, скомпилирован и встроен в интерпретатор Python, и потому +не имеет файла *.py. -Пакет: любая папка, которая содержит файл __init__.py. Имя пакета — имя папки. +Пакет: любая папка, которая содержит файл `__init__.py`. Имя пакета — имя папки. -С версии Python 3.3 любая папка (даже без __init__.py) считается пакетом. +С версии Python 3.3 любая папка (даже без `__init__.py`) считается пакетом. Объект: в Python почти всё является объектом — функции, классы, переменные и т. д. ### Пример структуры директорий @@ -518,31 +674,46 @@ test/ # Корневая папка ## Что делает import -При импорте модуля Python выполняет весь код в нём. При импорте пакета Python выполняет код в файле пакета __init__.py, если такой имеется. Все объекты, определённые в модуле или __init__.py, становятся доступны импортирующему. +При импорте модуля Python выполняет весь код в нём. При импорте пакета Python выполняет код в файле пакета +`__init__.py`, если такой имеется. Все объекты, определённые в модуле или `__init__.py`, становятся доступны +импортирующему. -Встроенные функции Python: какие нужно знать и на какие не стоит тратить время +Встроенные функции Python: какие нужно знать и на какие не стоит тратить время. ### Основы import и sys.path -Вот как оператор import производит поиск нужного модуля или пакета согласно документации Python: +Вот как оператор `import` производит поиск нужного модуля или пакета согласно документации Python: -При импорте модуля spam интерпретатор сначала ищёт встроенный модуль с таким именем. Если такого модуля нет, то идёт поиск файла spam.py в списке директорий, определённых в переменной sys.path. sys.path инициализируется из следующих мест: +При импорте модуля **spam** интерпретатор сначала ищет встроенный модуль с таким именем. Если такого модуля нет, то +идёт поиск файла spam.py в списке директорий, определённых в переменной `sys.path`. `sys.path` инициализируется из +следующих мест: - директории, содержащей исходный скрипт (или текущей директории, если файл не указан); - директории по умолчанию, которая зависит от дистрибутива Python; -- PYTHONPATH (список имён директорий; имеет синтаксис, аналогичный переменной окружения PATH). +- `PYTHONPATH` (список имён директорий; имеет синтаксис, аналогичный переменной окружения PATH). -Программы могут изменять переменную sys.path после её инициализации. Директория, содержащая запускаемый скрипт, помещается в начало поиска перед путём к стандартной библиотеке. Это значит, что скрипты в этой директории будут импортированы вместо модулей с такими же именами в стандартной библиотеке. +Программы могут изменять переменную `sys.path` после её инициализации. Директория, содержащая запускаемый скрипт, +помещается в начало поиска перед путём к стандартной библиотеке. Это значит, что скрипты в этой директории будут +импортированы вместо модулей с такими же именами в стандартной библиотеке. -Технически документация не совсем полна. Интерпретатор будет искать не только файл (модуль) `spam.py`, но и папку (пакет) spam. +Технически документация не совсем полна. Интерпретатор будет искать не только файл (модуль) `spam.py`, но и папку +(пакет) `spam`. -Обратите внимание, что Python сначала производит поиск среди встроенных модулей — тех, которые встроены непосредственно в интерпретатор. Список встроенных модулей зависит от дистрибутива Python, а найти этот список можно в `sys.builtin_module_names` (Python 2 и Python 3). Обычно в дистрибутивах есть модули `sys` (всегда включён в дистрибутив), `math`, `itertools`, `time` и прочие. +Обратите внимание, что Python сначала производит поиск среди встроенных модулей — тех, которые встроены непосредственно +в интерпретатор. Список встроенных модулей зависит от дистрибутива Python, а найти этот список можно в +`sys.builtin_module_names` (Python 2 и Python 3). Обычно в дистрибутивах есть модули `sys` (всегда включён в +дистрибутив), `math`, `itertools`, `time` и прочие. -В отличие от встроенных модулей, которые при поиске проверяются первыми, остальные (не встроенные) модули стандартной библиотеки проверяются после директории запущенного скрипта. Это приводит к сбивающему с толку поведению: возможно «заменить» некоторые, но не все модули стандартной библиотеки. Допустим, модуль `math` является встроенным модулем, а `random` — нет. Таким образом, `import math в start.py` импортирует модуль из стандартной библиотеки, а не наш файл `math.py` из той же директории. В то же время, `import random` в `start.py` импортирует наш файл `random.py`. +В отличие от встроенных модулей, которые при поиске проверяются первыми, остальные (не встроенные) модули стандартной +библиотеки проверяются после директории запущенного скрипта. Это приводит к сбивающему с толку поведению: возможно +«заменить» некоторые, но не все модули стандартной библиотеки. Допустим, модуль `math` является встроенным модулем, +а `random` — нет. Таким образом, `import math в start.py` импортирует модуль из стандартной библиотеки, а не наш +файл `math.py` из той же директории. В то же время, `import random` в `start.py` импортирует наш файл `random.py`. Кроме того, импорты в Python регистрозависимы: `import Spam` и `import spam` — разные вещи. -Функцию `pkgutil.iter_modules()` (Python 2 и Python 3) можно использовать, чтобы получить список всех модулей, которые можно импортировать из заданного пути: +Функцию `pkgutil.iter_modules()` (Python 2 и Python 3) можно использовать, чтобы получить список всех модулей, которые +можно импортировать из заданного пути: ```python import pkgutil search_path = ['.'] # Используйте None, чтобы увидеть все модули, импортируемые из sys.path @@ -558,36 +729,49 @@ print(all_modules) import sys print(sys.path) ``` -Документация Python описывает sys.path так: +Документация Python описывает `sys.path` так: -Список строк, указывающих пути для поиска модулей. Инициализируется из переменной окружения `PYTHONPATH` и директории по умолчанию, которая зависит от дистрибутива Python. +Список строк, указывающих пути для поиска модулей. Инициализируется из переменной окружения `PYTHONPATH` и директории +по умолчанию, которая зависит от дистрибутива Python. -При запуске программы после инициализации первым элементом этого списка, `path[0]`, будет директория, содержащая скрипт, который был использован для вызова интерпретатора Python. Если директория скрипта недоступна (например, если интерпретатор был вызван в интерактивном режиме или скрипт считывается из стандартного ввода), то `path[0]` является пустой строкой. Из-за этого Python сначала ищет модули в текущей директории. Обратите внимание, что директория скрипта вставляется перед путями, взятыми из `PYTHONPATH`. +При запуске программы после инициализации первым элементом этого списка, `path[0]`, будет директория, содержащая скрипт, +который был использован для вызова интерпретатора Python. Если директория скрипта недоступна (например, если +интерпретатор был вызван в интерактивном режиме или скрипт считывается из стандартного ввода), то `path[0]` является +пустой строкой. Из-за этого Python сначала ищет модули в текущей директории. Обратите внимание, что директория скрипта +вставляется перед путями, взятыми из `PYTHONPATH`. -Документация к интерфейсу командной строки Python добавляет информацию о запуске скриптов из командной строки. В частности, при запуске python