Files
SecondBrain/.trash/Подготовка к экзамену по веб-разработке.html
T
2026-06-04 22:53:32 +03:00

2600 lines
215 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Подготовка к экзамену по веб-разработке</title>
<style>
:root {
color-scheme: light;
--ink: #1f2933;
--muted: #5f6c7b;
--line: #d7dee8;
--paper: #ffffff;
--bg: #f5f7fa;
--accent: #16697a;
--accent-soft: #e5f4f6;
--warn-soft: #fff4df;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.48;
color: var(--ink);
background: var(--bg);
}
main {
width: min(1120px, calc(100% - 32px));
margin: 0 auto;
padding: 28px 0 56px;
}
header {
padding: 28px 0 20px;
border-bottom: 2px solid var(--line);
margin-bottom: 24px;
}
h1, h2, h3, h4 {
line-height: 1.2;
margin: 0 0 12px;
letter-spacing: 0;
}
h1 { font-size: 34px; }
h2 { font-size: 25px; margin-top: 34px; }
h3 { font-size: 18px; }
h4 { font-size: 16px; margin-top: 18px; }
p { margin: 0 0 10px; }
a {
color: var(--accent);
text-decoration: none;
border-bottom: 1px solid rgba(22, 105, 122, 0.35);
}
a:hover { border-bottom-color: var(--accent); }
code {
padding: 1px 5px;
border-radius: 4px;
background: #edf1f5;
font-family: "JetBrains Mono", "Consolas", monospace;
font-size: 0.94em;
}
table {
width: 100%;
border-collapse: collapse;
margin: 12px 0 18px;
background: var(--paper);
}
th, td {
border: 1px solid var(--line);
padding: 8px 10px;
text-align: left;
vertical-align: top;
}
th { background: #edf3f7; }
ul { margin: 8px 0 12px 20px; padding: 0; }
li { margin: 4px 0; }
img {
display: block;
max-width: 100%;
height: auto;
margin: 10px auto 14px;
page-break-inside: avoid;
}
.meta {
color: var(--muted);
max-width: 760px;
}
.toc {
background: var(--paper);
border: 1px solid var(--line);
border-radius: 8px;
padding: 18px;
margin-bottom: 28px;
}
.toc-block {
break-inside: avoid;
margin: 0 0 18px;
}
.toc-block:last-child { margin-bottom: 0; }
.toc-links {
display: flex;
flex-wrap: wrap;
gap: 8px 10px;
font-size: 14px;
}
.toc-links a {
border: 1px solid var(--line);
border-radius: 6px;
padding: 4px 8px;
background: #fbfcfd;
}
.block {
margin: 30px 0;
page-break-inside: avoid;
}
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 12px;
}
.card {
background: var(--paper);
border: 1px solid var(--line);
border-radius: 8px;
padding: 14px;
min-height: 160px;
page-break-inside: avoid;
}
.card p { color: #2d3748; }
.detail-link {
display: inline-block;
margin-top: 6px;
font-weight: 700;
}
.details {
margin-top: 44px;
padding-top: 18px;
border-top: 3px solid var(--line);
}
.note {
background: var(--paper);
border: 1px solid var(--line);
border-radius: 8px;
padding: 18px;
margin: 18px 0;
page-break-inside: avoid;
}
.note-source {
padding: 8px 10px;
border-radius: 6px;
background: var(--accent-soft);
color: #24464d;
margin-bottom: 12px;
}
.exam {
background: var(--warn-soft);
border-left: 4px solid #d78b00;
padding: 10px 12px;
margin: 12px 0;
}
.back {
margin-top: 14px;
font-size: 14px;
}
@media print {
body { background: #fff; }
main { width: 100%; padding: 0; }
.toc, .card, .note { box-shadow: none; }
a { color: #124f5f; }
}
</style>
</head>
<body>
<main>
<header id="top">
<h1>Подготовка к экзамену по веб-разработке</h1>
<p class="meta">Краткий конспект по темам из файла подготовки. Оглавление ведёт к короткой карточке темы; ссылка в карточке ведёт к подробному блоку внизу; подробный блок возвращает обратно к краткому разделу.</p>
</header>
<nav class="toc" aria-label="Оглавление">
<h2>Оглавление</h2>
<div class="toc-block">
<h3>Блок 1. Общие вопросы</h3>
<div class="toc-links">
<a href="#s-ip-addressing">Адресация, IPv4, IPv6</a>
<a href="#s-tcp">TCP</a>
<a href="#s-subnet">Маска подсети</a>
<a href="#s-nat-localhost">NAT, localhost, подсети</a>
<a href="#s-dns">DNS</a>
<a href="#s-http">HTTP</a>
<a href="#s-http-message">HTTP-запрос/ответ</a>
<a href="#s-https">HTTPS</a>
<a href="#s-http3">HTTP/3</a>
<a href="#s-ports">Порты</a>
<a href="#s-auth">Идентификация/auth</a>
<a href="#s-oauth">OAuth 2.0</a>
<a href="#s-uri">URI, URL, URN</a>
</div>
</div>
<div class="toc-block">
<h3>Блок 2. HTML</h3>
<div class="toc-links">
<a href="#s-html-structure">Структура документа</a>
<a href="#s-html-comments">Комментарии</a>
<a href="#s-head">head</a>
<a href="#s-meta">Мета-теги</a>
<a href="#s-flow">Нормальный поток</a>
<a href="#s-block-inline">Блочные и строчные</a>
<a href="#s-semantic">Семантика</a>
<a href="#s-attrs">Атрибуты</a>
<a href="#s-class">class</a>
<a href="#s-id">id</a>
<a href="#s-text-table">Текст, списки, таблицы</a>
<a href="#s-img-link">Картинки и ссылки</a>
<a href="#s-whitespace">Пробелы и переносы</a>
</div>
</div>
<div class="toc-block">
<h3>Блок 3. HTML Forms</h3>
<div class="toc-links">
<a href="#s-form">Форма</a>
<a href="#s-form-id-name">id/name</a>
<a href="#s-inputs">radio/checkbox/submit</a>
<a href="#s-label">label</a>
<a href="#s-fieldset">Группировка</a>
<a href="#s-required">required</a>
<a href="#s-placeholder">placeholder</a>
<a href="#s-button-type">button type</a>
<a href="#s-textarea">textarea</a>
</div>
</div>
<div class="toc-block">
<h3>Блок 4. CSS</h3>
<div class="toc-links">
<a href="#s-css-universal">Универсальный селектор</a>
<a href="#s-css-basic-selectors">Элемент/class/id</a>
<a href="#s-css-combinators">Комбинаторы</a>
<a href="#s-css-attr">Атрибуты</a>
<a href="#s-css-pseudo">Псевдоклассы</a>
<a href="#s-css-group">Группировка</a>
<a href="#s-css-cascade">Каскад</a>
<a href="#s-css-specificity">Специфичность</a>
<a href="#s-box-model">Блочная модель</a>
<a href="#s-background">Фон</a>
<a href="#s-margin-align">Margin</a>
<a href="#s-fonts">Шрифты</a>
<a href="#s-text-align">Текст</a>
<a href="#s-line-letter">Интервалы</a>
<a href="#s-text-shadow">Тень текста</a>
</div>
</div>
<div class="toc-block">
<h3>Блок 5. Flexbox и Grid</h3>
<div class="toc-links">
<a href="#s-flex-container">Flex-контейнер</a>
<a href="#s-inline-flex">inline-flex</a>
<a href="#s-flex-align">Выравнивание</a>
<a href="#s-flex-grow">grow/shrink/basis</a>
<a href="#s-grid-container">Grid-контейнер</a>
<a href="#s-fr">fr</a>
<a href="#s-repeat-minmax">repeat/minmax</a>
</div>
</div>
<div class="toc-block">
<h3>Блоки 6-12. Django</h3>
<div class="toc-links">
<a href="#s-django-models">Модели</a>
<a href="#s-primary-key">Первичный ключ</a>
<a href="#s-migrations">Миграции</a>
<a href="#s-django-meta">Class Meta</a>
<a href="#s-null-blank">null/blank</a>
<a href="#s-charfield">CharField</a>
<a href="#s-foreignkey">ForeignKey</a>
<a href="#s-manytomany">Many-to-Many</a>
<a href="#s-fbv">FBV</a>
<a href="#s-render">render</a>
<a href="#s-context">Контекст</a>
<a href="#s-get-object">get_object_or_404</a>
<a href="#s-routing-fbv">Маршрутизация FBV</a>
<a href="#s-cbv-types">Template/List/DetailView</a>
<a href="#s-cbv-models">CBV и модели</a>
<a href="#s-cbv-extra">Доп. контекст</a>
<a href="#s-cbv-default-vars">Переменные по умолчанию</a>
<a href="#s-cbv-queryset">get_queryset/get_object</a>
<a href="#s-pagination">Паджинация</a>
<a href="#s-cbv-templates">Шаблоны CBV</a>
<a href="#s-cbv-super">super()</a>
<a href="#s-cbv-routing">Маршрутизация CBV</a>
<a href="#s-django-urls">Django URLs</a>
<a href="#s-django-templates">DTL</a>
<a href="#s-django-queries">Django queries</a>
<a href="#s-django-forms">Django forms</a>
</div>
</div>
</nav>
<section class="block" id="block-1">
<h2>Блок 1. Общие вопросы</h2>
<div class="cards">
<article class="card" id="s-ip-addressing">
<h3>Адресация в Интернете. IPv4. IPv6.</h3>
<p>В TCP/IP есть физический адрес, IP-адрес и символьное DNS-имя. IPv4 — 32 бита, 4 октета по 0-255; IPv6 — 128 бит, шестнадцатеричная запись и намного больше адресов.</p>
<a class="detail-link" href="#d-ip">Подробно: IP-адресация</a>
</article>
<article class="card" id="s-tcp">
<h3>Протокол TCP</h3>
<p>TCP — транспортный протокол с установлением соединения, порядком пакетов, подтверждениями и повторной передачей. Надёжнее UDP, но тяжелее по задержкам.</p>
<a class="detail-link" href="#d-tcp">Подробно: TCP</a>
</article>
<article class="card" id="s-subnet">
<h3>Маска подсети</h3>
<p>Маска отделяет сетевую часть IPv4-адреса от хостовой. `/24` значит 24 бита сети и 8 бит хоста; `255.255.255.0` — классический пример такой маски.</p>
<a class="detail-link" href="#d-subnet">Подробно: подсети</a>
</article>
<article class="card" id="s-nat-localhost">
<h3>Распределение IP, NAT, localhost, подсети</h3>
<p>Публичные адреса распределяются через ICANN/IANA и региональные регистраторы. NAT переводит приватные адреса во внешний адрес. `127.0.0.1`/`localhost` указывает на сам хост.</p>
<a class="detail-link" href="#d-nat">Подробно: NAT и localhost</a>
</article>
<article class="card" id="s-dns">
<h3>DNS. DNS-серверы</h3>
<p>DNS сопоставляет доменные имена с IP-адресами. Цепочка обычно идёт через кэширующий resolver, корневые серверы, TLD-серверы и авторитативные серверы домена.</p>
<a class="detail-link" href="#d-dns">Подробно: DNS</a>
</article>
<article class="card" id="s-http">
<h3>Протокол HTTP</h3>
<p>HTTP — прикладной протокол WWW. Клиент отправляет запрос, сервер возвращает ответ. HTTP stateless: связь между запросами не хранится без cookies, сессий или токенов.</p>
<a class="detail-link" href="#d-http">Подробно: HTTP</a>
</article>
<article class="card" id="s-http-message">
<h3>HTTP-запрос, ответ, методы, заголовки, коды</h3>
<p>Сообщение состоит из стартовой строки, заголовков, пустой строки и тела. Методы: `GET`, `POST`, `HEAD`, `PUT`, `PATCH`, `DELETE`. Коды: `2xx` успех, `3xx` редирект, `4xx` ошибка клиента, `5xx` ошибка сервера.</p>
<a class="detail-link" href="#d-http">Подробно: HTTP-сообщения</a>
</article>
<article class="card" id="s-https">
<h3>Протокол HTTPS</h3>
<p>HTTPS — HTTP поверх TLS. Даёт шифрование, проверку подлинности сервера через сертификат и защиту от подмены данных. Порт по умолчанию — `443`.</p>
<a class="detail-link" href="#d-http">Подробно: HTTPS</a>
</article>
<article class="card" id="s-http3">
<h3>Протокол HTTP/3</h3>
<p>HTTP/3 работает поверх QUIC/UDP. Семантика HTTP сохраняется, но транспорт меняется: независимые потоки, встроенный TLS 1.3, меньше блокировок при потере пакетов.</p>
<a class="detail-link" href="#d-http">Подробно: HTTP/3</a>
</article>
<article class="card" id="s-ports">
<h3>Сетевые порты. HTTP и HTTPS</h3>
<p>Порт указывает процесс внутри IP-адреса. Всего 0-65535. HTTP обычно `80/tcp`, HTTPS `443/tcp`, DNS `53`, SSH `22`, альтернативный HTTP часто `8080`.</p>
<a class="detail-link" href="#d-ports">Подробно: порты</a>
</article>
<article class="card" id="s-auth">
<h3>Идентификация, аутентификация, авторизация</h3>
<p>Идентификация: "кто ты?". Аутентификация: "докажи". Авторизация: "что тебе разрешено?". В HTTP часто встречаются Basic, Digest и Bearer.</p>
<a class="detail-link" href="#d-auth">Подробно: auth</a>
</article>
<article class="card" id="s-oauth">
<h3>OAuth 2.0</h3>
<p>OAuth 2.0 — протокол авторизации: приложение получает ограниченный доступ к ресурсам пользователя через токен, не получая пароль пользователя.</p>
<a class="detail-link" href="#d-oauth">Подробно: OAuth 2.0</a>
</article>
<article class="card" id="s-uri">
<h3>URI, URL, URN</h3>
<p>URI — общий идентификатор ресурса. URL указывает местоположение и способ получения. URN задаёт устойчивое имя без привязки к расположению. Все URL — URI, но не все URI — URL.</p>
<a class="detail-link" href="#d-uri">Подробно: URI/URL/URN</a>
</article>
</div>
</section>
<section class="block" id="block-2">
<h2>Блок 2. HTML</h2>
<div class="cards">
<article class="card" id="s-html-structure"><h3>Структура документа</h3><p>HTML5-документ начинается с <code>&lt;!doctype html&gt;</code>, затем <code>&lt;html&gt;</code>, внутри <code>&lt;head&gt;</code> с метаданными и <code>&lt;body&gt;</code> с видимым содержимым.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-html-comments"><h3>Комментарии в HTML</h3><p>Комментарий пишется как <code>&lt;!-- текст --&gt;</code>. Браузер его не отображает, но он виден в исходном коде.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-head"><h3>Теги внутри head</h3><p>В <code>&lt;head&gt;</code> обычно находятся <code>&lt;title&gt;</code>, <code>&lt;meta&gt;</code>, <code>&lt;link&gt;</code>, <code>&lt;style&gt;</code>, <code>&lt;script&gt;</code> и технические данные страницы.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-meta"><h3>Мета-теги и viewport</h3><p><code>&lt;meta charset="utf-8"&gt;</code> задаёт кодировку. <code>viewport</code> нужен адаптивной вёрстке: <code>width=device-width, initial-scale=1.0</code>.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-flow"><h3>Нормальный поток</h3><p>Нормальный поток — базовое расположение элементов без flex/grid/position: блочные идут сверху вниз, строчные текут внутри строки.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-block-inline"><h3>Блочные и строчные элементы</h3><p>Блочные занимают доступную ширину и начинаются с новой строки: `div`, `p`, `section`. Строчные занимают место по содержимому: `span`, `a`, `strong`.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-semantic"><h3>Семантические теги</h3><p>`header`, `nav`, `main`, `section`, `article`, `aside`, `footer` описывают смысл блоков. Это важно для доступности, SEO и читаемости кода.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-attrs"><h3>Атрибуты тегов</h3><p>Атрибуты уточняют элемент: `href`, `src`, `alt`, `class`, `id`, `name`, `type`. Обычно пишутся в открывающем теге.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-class"><h3>Атрибут class</h3><p>`class` задаёт один или несколько CSS-классов. Один класс можно использовать на многих элементах.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-id"><h3>Идентификатор элемента</h3><p>`id` должен быть уникален на странице. Используется для якорей, JS и CSS-селектора `#id`.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-text-table"><h3>Заголовки, абзацы, списки, таблицы</h3><p>`h1-h6` задают структуру заголовков, `p` — абзац, `ul/ol/li` — списки, `table/tr/th/td` — табличные данные.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-img-link"><h3>Картинки и ссылки</h3><p>Ссылка: <code>&lt;a href="..."&gt;</code>. Картинка: <code>&lt;img src="..." alt="..."&gt;</code>; <code>alt</code> важен для доступности и случаев, когда изображение не загрузилось.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
<article class="card" id="s-whitespace"><h3>Переносы строк и пробелы</h3><p>В HTML последовательности пробелов схлопываются. Перенос строки в коде обычно не равен переносу на странице; для явного переноса есть <code>&lt;br&gt;</code>.</p><a class="detail-link" href="#d-html">Подробно: HTML</a></article>
</div>
</section>
<section class="block" id="block-3">
<h2>Блок 3. HTML Forms</h2>
<div class="cards">
<article class="card" id="s-form"><h3>Форма и атрибуты</h3><p>`form` собирает пользовательский ввод. Главные атрибуты: `action` — куда отправлять, `method` — `get` или `post`.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-form-id-name"><h3>id и name</h3><p>`id` связывает поле с `label` и должен быть уникальным. `name` — имя параметра, под которым значение уйдёт на сервер.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-inputs"><h3>radio, checkbox, submit</h3><p>`radio` выбирает один вариант в группе с одинаковым `name`. `checkbox` — независимый флажок. `submit` отправляет форму.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-label"><h3>Label</h3><p>`label` подписывает поле и улучшает доступность. Связь делается через `for="field-id"` или вложением input внутрь label.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-fieldset"><h3>Группировка элементов</h3><p>`fieldset` объединяет связанные поля, `legend` задаёт подпись группы. Часто используется для radio-групп.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-required"><h3>required</h3><p>`required` запрещает отправку формы браузером, если поле пустое. Серверную валидацию всё равно нужно делать отдельно.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-placeholder"><h3>Placeholder</h3><p>`placeholder` показывает подсказку внутри пустого поля. Это не замена `label`, потому что исчезает при вводе.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-button-type"><h3>type для button</h3><p>`button` без `type` внутри формы часто ведёт себя как `submit`. Для обычной кнопки явно пишут `type="button"`.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
<article class="card" id="s-textarea"><h3>Textarea</h3><p>`textarea` — многострочное текстовое поле. Текст по умолчанию пишется между открывающим и закрывающим тегом.</p><a class="detail-link" href="#d-html">Подробно: HTML forms</a></article>
</div>
</section>
<section class="block" id="block-4">
<h2>Блок 4. CSS selectors и базовые свойства</h2>
<div class="cards">
<article class="card" id="s-css-universal"><h3>Универсальный селектор</h3><p>`*` выбирает все элементы. Часто применяют для сброса `box-sizing`, но специфичность у него нулевая.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-basic-selectors"><h3>Элемент, класс, идентификатор</h3><p>`p` выбирает теги, `.card` — класс, `#main` — уникальный id. ID специфичнее класса и тега.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-combinators"><h3>Дочерний, потомок, сестринский</h3><p>`A B` — потомок, `A > B` — прямой ребёнок, `A + B` — следующий сосед, `A ~ B` — последующие соседи.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-attr"><h3>Селектор атрибута</h3><p>`[attr]`, `[type="email"]`, `a[href^="https"]` выбирают элементы по наличию или значению атрибута.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-pseudo"><h3>Псевдоклассы</h3><p>`:hover`, `:focus`, `:checked`, `:nth-child()` выбирают состояние или позицию элемента. Структурные псевдоклассы зависят от места в DOM.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-group"><h3>Группировка селекторов</h3><p>Селекторы через запятую получают одно правило: `h1, h2, h3 { margin: 0; }`.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-cascade"><h3>Наследование и каскадирование</h3><p>Каскад решает конфликт правил по важности, специфичности и порядку. Наследуются в основном текстовые свойства, например `color` и `font-family`.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-css-specificity"><h3>Специфичность</h3><p>Условный вес: inline &gt; ID &gt; классы/атрибуты/псевдоклассы &gt; теги/псевдоэлементы. При равенстве побеждает правило ниже в коде.</p><a class="detail-link" href="#d-css">Подробно: CSS selectors</a></article>
<article class="card" id="s-box-model"><h3>Блочная модель</h3><p>Блок состоит из content, padding, border и margin. `box-sizing: border-box` включает padding и border в заданную ширину.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
<article class="card" id="s-background"><h3>Фон элемента</h3><p>Основные свойства: `background-color`, `background-image`, `background-repeat`, `background-position`, `background-size`.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
<article class="card" id="s-margin-align"><h3>Margin и выравнивание блоков</h3><p>`margin` задаёт внешний отступ. Для центрирования блочного элемента с заданной шириной часто используют `margin-left/right: auto`.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
<article class="card" id="s-fonts"><h3>Шрифты</h3><p>`font-family`, `font-size`, `font-weight`, `font-style` задают семейство, размер, толщину и наклон текста.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
<article class="card" id="s-text-align"><h3>Выравнивание и подчёркивание текста</h3><p>`text-align` управляет горизонтальным выравниванием текста, `text-decoration` — подчёркиванием и другими линиями.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
<article class="card" id="s-line-letter"><h3>line-height и letter-spacing</h3><p>`line-height` задаёт высоту строки, `letter-spacing` — расстояние между буквами. На читаемость влияют сильнее, чем кажется.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
<article class="card" id="s-text-shadow"><h3>Тень текста</h3><p>`text-shadow: x y blur color;` добавляет тень к тексту. В тестах важно помнить порядок смещения, размытия и цвета.</p><a class="detail-link" href="#d-css">Подробно: CSS свойства</a></article>
</div>
</section>
<section class="block" id="block-5">
<h2>Блок 5. Flexbox Layout. Grid Layout</h2>
<div class="cards">
<article class="card" id="s-flex-container"><h3>Flex-контейнер и оси</h3><p>`display: flex` создаёт flex-контейнер. Главная ось задаётся `flex-direction`, поперечная идёт перпендикулярно.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
<article class="card" id="s-inline-flex"><h3>Строчный flex-контейнер</h3><p>`display: inline-flex` делает контейнер flex-контейнером, но сам контейнер ведёт себя как строчный элемент во внешнем потоке.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
<article class="card" id="s-flex-align"><h3>Выравнивание по осям</h3><p>`justify-content` выравнивает по главной оси, `align-items` — по поперечной. `align-content` работает при нескольких строках.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
<article class="card" id="s-flex-grow"><h3>flex-grow, shrink, basis</h3><p>`flex-basis` — базовый размер, `flex-grow` — как элемент растёт, `flex-shrink` — как сжимается при нехватке места.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
<article class="card" id="s-grid-container"><h3>Grid-контейнер</h3><p>`display: grid` создаёт двумерную сетку. Колонки задаются `grid-template-columns`, строки — `grid-template-rows`.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
<article class="card" id="s-fr"><h3>Величина fr</h3><p>`fr` — доля свободного пространства grid-контейнера. `1fr 2fr` делит свободное место в пропорции 1 к 2.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
<article class="card" id="s-repeat-minmax"><h3>repeat и minmax</h3><p>`repeat(3, 1fr)` повторяет трек. `minmax(200px, 1fr)` задаёт минимум и максимум размера трека.</p><a class="detail-link" href="#d-layout">Подробно: Flex/Grid</a></article>
</div>
</section>
<section class="block" id="block-django">
<h2>Блоки 6-12. Django</h2>
<div class="cards">
<article class="card" id="s-django-models"><h3>Создание моделей</h3><p>Модель — класс-наследник `models.Model`; поля класса описывают столбцы таблицы. Модели обычно лежат в `models.py` приложения.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-primary-key"><h3>Первичный ключ</h3><p>Первичный ключ уникально идентифицирует запись. Если не задать явно, Django создаёт поле `id` автоматически.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-migrations"><h3>Миграции</h3><p>`makemigrations` создаёт файлы миграций из изменений моделей, `migrate` применяет их к базе данных.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-django-meta"><h3>Class Meta</h3><p>`class Meta` задаёт метаданные модели: `ordering`, `verbose_name`, `db_table`, ограничения и другие настройки.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-null-blank"><h3>null=True и blank=True</h3><p>`null=True` разрешает `NULL` в БД. `blank=True` разрешает пустое значение на уровне форм/валидации Django.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-charfield"><h3>CharField</h3><p>`CharField` хранит короткую строку и требует `max_length`. Для длинного текста обычно берут `TextField`.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-foreignkey"><h3>ForeignKey</h3><p>`ForeignKey` задаёт связь многие-к-одному. `on_delete` определяет поведение при удалении связанного объекта; `related_name` задаёт имя обратной связи.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-manytomany"><h3>Many-to-Many</h3><p>`ManyToManyField` задаёт связь многие-ко-многим. Django создаёт промежуточную таблицу автоматически, либо можно указать свою через `through`.</p><a class="detail-link" href="#d-django-models">Подробно: Django models</a></article>
<article class="card" id="s-fbv"><h3>FBV</h3><p>Function-Based View — функция, которая принимает `request` и возвращает `HttpResponse`, `render`, `redirect` или ошибку.</p><a class="detail-link" href="#d-django-views">Подробно: Django FBV</a></article>
<article class="card" id="s-render"><h3>render</h3><p>`render(request, template, context)` рендерит шаблон с контекстом и возвращает `HttpResponse`.</p><a class="detail-link" href="#d-django-views">Подробно: Django FBV</a></article>
<article class="card" id="s-context"><h3>Передача данных в шаблон</h3><p>Данные передаются словарём context: ключи становятся именами переменных в шаблоне.</p><a class="detail-link" href="#d-django-views">Подробно: Django FBV</a></article>
<article class="card" id="s-get-object"><h3>Объект или 404</h3><p>`get_object_or_404(Model, pk=pk)` возвращает объект или генерирует HTTP 404, если записи нет.</p><a class="detail-link" href="#d-django-views">Подробно: Django FBV</a></article>
<article class="card" id="s-routing-fbv"><h3>Маршрутизация FBV</h3><p>В `urlpatterns` функция передаётся напрямую: `path("posts/&lt;int:pk&gt;/", views.detail, name="detail")`.</p><a class="detail-link" href="#d-django-views">Подробно: Django FBV</a></article>
<article class="card" id="s-cbv-types"><h3>TemplateView, DetailView, ListView</h3><p>`TemplateView` показывает шаблон, `DetailView` — один объект, `ListView` — список объектов.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-models"><h3>CBV и модели</h3><p>Для `ListView`/`DetailView` можно указать `model = Post`; Django сам построит queryset и стандартные имена шаблонов.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-extra"><h3>Дополнительные данные</h3><p>Дополнительный контекст обычно добавляют через `get_context_data`, вызывая `super()` и дополняя словарь.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-default-vars"><h3>Переменные по умолчанию</h3><p>`ListView` отдаёт `object_list` и `modelname_list`; `DetailView` отдаёт `object` и `modelname`.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-queryset"><h3>Переопределение выгрузки</h3><p>`get_queryset()` меняет список объектов, `get_object()` — получение одного объекта. Часто используют фильтрацию по пользователю или статусу.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-pagination"><h3>Паджинация</h3><p>В `ListView` достаточно задать `paginate_by = N`; в шаблоне появится `page_obj`.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-templates"><h3>Шаблоны по умолчанию</h3><p>Для `DetailView`: `app/model_detail.html`; для `ListView`: `app/model_list.html`, если не задан `template_name`.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-super"><h3>Вызов базовой логики</h3><p>При переопределении методов вроде `get_context_data` обычно сначала вызывают `super()`, чтобы не потерять стандартный контекст.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-cbv-routing"><h3>Маршрутизация CBV</h3><p>Класс подключается через `.as_view()`: `path("", PostListView.as_view(), name="post_list")`.</p><a class="detail-link" href="#d-django-views">Подробно: Django CBV</a></article>
<article class="card" id="s-django-urls"><h3>Django URLs</h3><p>`path()` задаёт маршрут, view, kwargs и имя. `re_path()` использует regex. `reverse()` строит URL по имени маршрута.</p><a class="detail-link" href="#d-django-views">Подробно: Django URLs</a></article>
<article class="card" id="s-django-templates"><h3>Язык шаблонов Django</h3><p>Переменные пишутся как `{{ value }}`, теги как `{% for %}`, фильтры как `{{ name|lower }}`. Наследование делается через `extends` и `block`.</p><a class="detail-link" href="#d-django-views">Подробно: Django templates</a></article>
<article class="card" id="s-django-queries"><h3>Django queries</h3><p>QuerySet ленивый и цепочечный. Важно знать `get`, `filter`, `exclude`, `order_by`, `annotate`, `aggregate`, `Q`, `F`, `select_related`, `prefetch_related`.</p><a class="detail-link" href="#d-django-models">Подробно: Django queries</a></article>
<article class="card" id="s-django-forms"><h3>Django forms</h3><p>`forms.Form` — ручная форма, `ModelForm` строится по модели. Валидация через `is_valid`, `cleaned_data`, `clean_field`, `clean`.</p><a class="detail-link" href="#d-django-forms">Подробно: Django forms</a></article>
</div>
</section>
<section class="details" id="details">
<h2>Подробные заметки</h2>
<article class="note full-note" id="d-ip">
<h3>IP-адрес и IPv6</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="IP-адрес">
<h1>IP-адрес</h1>
<p><strong>IP - Internet Protocol -</strong> Сетевой адрес</p>
<p>IP-адрес - это просто набор из 32 нулей и единиц, который
преобразовали в 10-ричную систему счисления и разбили на 4 части для
удобства.</p>
<p>В стандарте IPv4 мы можем использовать только <strong>4 294 967
296</strong> адресов, что может не хватить. На замену ему потихоньку
вводится IPv6 - число из 128 единиц и нулей, переведенный в 16-чную С.С.
и разбитый на 8 частей. Тут доступно уже сильно больше вариантов…</p>
<h2>Статическая И Динамическая
Адресация</h2>
<p>Статика - задается вручную и не меняется в ходе работы. Динамика -
задается автоматически посредством DHCP-сервера из определенного ему
пула IP.</p>
<p>Т.к. переход на IPv6 дорог, да и иметь всем конкретный IP не очень
безопасно, придумали NAT (Network Address Translation) и его надстройку
PAT (Port Address Translation). Суть в том, что много устройств могут
выходить в сеть под “одним” белым (внешним) IP. Но есть нюанс, при
маршрутизации трафика к белому IP добавляется порт, который уточняет, к
какому именно устройству сети и с каким “внутренним” IP направить эти
пакеты.</p>
</div>
<div class="embedded-note" data-note-title="IPv6">
<h1>IPv6</h1>
<p>При использовании IPv4 с 4 миллиардами адресов, в какой-то момент
очевидно столкнулись с проблемой их нехватки. Даже создание PAT и
использование локальной адресации не сильно спасло ситуацию.</p>
<p>Решением проблемы стало изобретение IP версии 6, где используется не
32 бита для кодирования адреса, а 128, при этом, увеличение количества
бит на единицу, увеличивает кол-во доступных адресов ровно в два раза,
следовательно, для 128 бит вакантных адресов примерно…</p>
<p><span class="math display">$$\large 340\ 282\ 366\ 920\ 938\ 463\
463\ 374\ 607\ 431\ 768\ 211\ 456$$</span></p>
<p>Это много, даже очень.</p>
<p>IPv5 А что случилось с IPv5? Хороший вопрос … IP-версия 5 была
использована для экспериментального проекта под названием “Протокол
интернет-потока”. Он определен в RFC, если вас интересуют исторические
причины: <a
href="http://www.faqs.org/rfcs/rfc1819.html">http://www.faqs.org/rfcs/rfc1819.html</a></p>
<h2>Отличия IPv4 И IPv6</h2>
<p>В чем же основная проблема перехода на IPv6? А в том, что нет
совместимости протоколов для IPv4 и новых поколений.</p>
<ul>
<li><strong>OSPF</strong> был обновлен с 2 (IPv4) до 3 (IPv6)
версии.</li>
<li><strong>ICMP</strong> был обновлен до ICMP 6.</li>
<li><strong>ARP</strong> был заменен на <em>NDP</em> (Neighborhood
Discovery Protocol).</li>
</ul>
<p><img src="../../../99 System/Cache/IPv6_image.png" /></p>
<p>Вместо того чтобы уже добавлять все поля в заголовок, заголовок IPv6
использует “следующий заголовок”, который ссылается на необязательные
заголовки. Поскольку заголовок намного проще, маршрутизаторам придется
выполнять меньше работы.</p>
<p>Маршрутизация тоже сильно поменялась при переходе на новую
версию:</p>
<ol type="1">
<li>RIP -&gt; RIPng (RIP Next Generation)</li>
<li>BGP (Border Gateway Protocol) - протокол, объединяющий интернет
вместе. MP-BGP - Multi-Protocol BGP, он может маршрутизировать IPv6.
EIGRP также поддерживает IPv6.</li>
</ol>
<p>Надо учитывать, что даже при условии поддержки OSPF и EIGRP адресации
обоих версий, это два <strong>разных протокола</strong>. Из этого
следует, что сначала надо поднять протокол для одной версии, а потом для
другой. Использование двух версий IP называется <strong>двойным
стеком</strong>.</p>
<blockquote>
<p>Поскольку эти два протокола несовместимы, в будущем будет происходить
переход с IPv4 на IPv6. Это означает, что вы будете запускать оба
протокола в своей сети и, возможно, однажды вы сможете отключить IPv4,
так как весь интернет будет настроен на IPv6.</p>
</blockquote>
<h2>Формат IPv6</h2>
<pre class="text"><code>2041:0000:140F:0000:0000:0000:875B:131B</code></pre>
<p>Он сильно длиннее, чем IPv4, состоит из 8 частей, каждая часть
состоит из 4 шестнадцатиричных чисел.</p>
<p><img src="../../../99 System/Cache/5_IPv6_image.png" /></p>
<p>Таблица перевода шестнадцатиричных чисел в двоичные</p>
<p>Существуют некоторые правила, позволяющие упростить работу с
адресами:</p>
<ol type="1">
<li>Если есть несколько частей с нулями, то их можно убрать, заменив на
<code>::</code> <code>2041:0000:140F:0000:0000:0000:875B:131B</code>
-&gt; <code>2041: 0000:140F:: 875B:131B</code></li>
<li>Если есть одиночный блок с нулями, то можно его заменить на один
ноль <code>2041: 0000:140F:: 875B:131B</code> -&gt;
<code>2041:0:140F:: 875B:131B</code></li>
<li>При наличии в блоке значащих чисел и нулями перед ними, нули можно
опустить <code>2001:0001:0002:0003:0004:0005:0006:0007</code> -&gt;
<code>2001:1:2:3:4:5:6:7</code></li>
</ol>
<p>При использовании IPv4 и нехватке адресов, люди начали
адаптироваться, придумав частные сети и VLSM (сегментацию сети),
использовали NAT и так далее. При переходе на IPv6 такой проблемы уже не
будет, т.к. пула доступных адресов будет достаточно для снабжения
адресами нескольких планет, таких, как Земля. Протокол IPv6 предлагает
два варианта для одноадресной рассылки:</p>
<ol type="1">
<li>Global Unicast</li>
<li>Unique Local</li>
</ol>
<p>Ранее, как отголосок IPv4 существовал третий диапазон адресов “site
local”, который начинался с <code>FEC0::/10</code>. Он предназначался
для использования во внутренних сетях (аналог
<code>192.168.0.0/24</code>), но был удален из стандарта IPv6.</p>
<p><strong>Global Unicast</strong> передачи похожи на публичные IPv4
адреса. Каждая компания, желающая подключится к интернету, получает пул
адресов, которые они, на своё усмотрение, делят на более мелкие
префиксы, чтобы все устройства сети поулчили IPv6 адрес, притом
уникальный во всем мире. Зарезервированный блорк называется префиксом
глобальной маршрутизации.</p>
<h2>Получение Префиксов</h2>
<ul>
<li><strong>IANA</strong> отвечает за распределение всех префиксов IPv6.
Они будут назначать реестрам разные блоки, которыми будут управлять
региональные компании.</li>
<li><strong>ISP</strong>, попадлающие под реестр региональной компании,
заправшивает блок IPv6. Они получают свой пул адресов, которые могут
распределять между провайдерами. !</li>
</ul>
<p><img src="../../../99 System/Cache/4_IPv6_image.png" /></p>
<blockquote>
<p>Некоторые префиксы <strong>FD</strong>: Unique Local
<strong>FF</strong>: Multicast <strong>FE80</strong>: Link-Local</p>
</blockquote>
<h2>Local И Link-Local</h2>
<p>После получения клиентом от провайдера префикса, например,
<code>2001: 828:105:: / 48</code>, необходимо его разбить на подсети для
различных VLAN и point-to-point соединений, которые могут быть.
Поскольку адресное пространство настолько велико, все используют префикс
<code>/64</code> для подсетей. Не имеет смысла брать меньшие подсети.
Когда мы используем IPv4, мы вынуждены, для подсетей, дробить хостовую
часть!</p>
<p><img src="../../../99 System/Cache/12_IPv6_image.png" /></p>
<p>Тогда, у нас, в целом, будет меньше хостов на подсеть.</p>
<p><img src="../../../99 System/Cache/3_IPv6_image.png" /></p>
<p> В IPv6 такой проблемы нет:</p>
<p><img src="../../../99 System/Cache/11_IPv6_image.png" /></p>
<p>Префикс Global Routing был назначен провайдером. Последние 64 бита
называются <strong>идентификатором интерфейса</strong>, это эквивалетно
части хоста в IPv4.</p>
<p>Путем несложно математики, получается, что у нас 16 бит на подсети, а
это около 65536 подсетей (как правило, этого <em>более чем</em>
достаточно). В этой же схеме, на хосты в каждой подсети отводится около
<strong>18 квинтиллионов</strong> хостов.</p>
<p>Если клиент получил префикс <code>2001: 828: 105:: / 48</code>, то он
может создать такие подсети: </p>
<p><img src="../../../99 System/Cache/2_IPv6_image.png" /></p>
<pre class="text"><code>- 2001:828:105:0000::/64;
- 2001:828:105:0001::/64;
- 2001:828:105:0002::/64;
- 2001:828:105:0003::/64;
- 2001:828:105:0004::/64;
- 2001:828:105:0005::/64;
- 2001:828:105:0006::/64;
- 2001:828:105:0007::/64;
- 2001:828:105:0008::/64;
- 2001:828:105:0009::/64;
- 2001:828:105:000A::/64;
- 2001:828:105:000B::/64;
- 2001:828:105:000C::/64;
- 2001:828:105:000D::/64;
- 2001:828:105:000E::/64;
- 2001:828:105:000F::/64;
- 2001:828:105:0010::/64;
- 2001:828:105:0011::/64;
- 2001:828:105:0012::/64;
- 2001:828:105:0013::/64;
- 2001:828:105:0014::/64;
...</code></pre>
</div>
<p class="back">Назад: <a href="#s-ip-addressing">Адресация, IPv4, IPv6</a></p>
</article>
<article class="note full-note" id="d-tcp">
<h3>TCP vs UDP</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="TCP vs UDP">
<h1>TCP Vs UDP</h1>
<p>Основные протоколы транспортного уровня модели OSI (L4).</p>
<h2><strong>TCP</strong></h2>
<ul>
<li><p>Transmission Control Protocol Перед передачей данных
устанавливает соединение трехсторонним рукопожатием.</p></li>
<li><p>SYN - отправка запроса на возможность передачи данных от
отправителя.</p></li>
<li><p>SYNACK - подтверждение получающей стороной.</p></li>
<li><p>ACK - начало передачи данных.</p></li>
</ul>
<p>После отправки части данных, получающая сторона отправляет
подтверждение передачи.</p>
<p>Этот протокол надежный, но очень медленный.</p>
<p>TCP, как протокол, определен в <strong>RFC (Request For Comments)
793</strong>, выполняет функции, перечисленные в таблице ниже, через
механизмы, предоставляемые конечными устройствами. Он полагается на IP
для сквозной доставки данных, включая вопросы маршрутизации. Независимо
от того, в каких условиях находятся ПК для передачи, TCP работает
одинаково на своем уровне. </p>
<p><img src="../../../99 System/Cache/1_TCP%20vs%20UDP_image.png" /></p>
<p>Функционал TCP</p>
<figure>
<img src="../../../99 System/Cache/TCP%20vs%20UDP_2.png"
alt="Отправка данных с трех приложений Анной Грише" />
<figcaption aria-hidden="true">Отправка данных с трех приложений Анной
Грише</figcaption>
</figure>
<p>Поля заголовка TCP</p>
<h2><strong>UDP</strong></h2>
<ul>
<li>User Datagram Protocol Просто бросается файлами в “сторону”
получающей стороны. Может теряться часть данных, или их порядок. В
случае с видео, звонками, мы можем позволить себе потерю данных.</li>
</ul>
<p><img src="../../../99 System/Cache/2_TCP%20vs%20UDP_image.png" /></p>
</div>
<p class="back">Назад: <a href="#s-tcp">TCP</a></p>
</article>
<article class="note full-note" id="d-subnet">
<h3>Подсети и маска подсети</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Подсети и маска подсети">
<h1>Подсети и маска подсети</h1>
<p>Маска подсети показывает, какая часть IPv4-адреса относится к сети, а
какая — к хосту внутри этой сети.</p>
<h2>Главное</h2>
<p>IPv4-адрес состоит из 32 бит. Без маски нельзя однозначно понять, где
заканчивается номер сети и начинается номер хоста.</p>
<p>Пример:</p>
<pre class="text"><code>IP: 192.168.123.132
Маска: 255.255.255.0
CIDR: /24</code></pre>
<p><code>/24</code> означает, что первые 24 бита — сеть, оставшиеся 8
бит — хосты.</p>
<p>Для примера выше:</p>
<ul>
<li>адрес сети: <code>192.168.123.0</code>;</li>
<li>часть хоста: <code>0.0.0.132</code>;</li>
<li>маска: <code>255.255.255.0</code>.</li>
</ul>
<h2>CIDR</h2>
<p>CIDR-запись <code>/N</code> показывает количество единиц в маске.</p>
<table>
<thead>
<tr>
<th>CIDR</th>
<th>Маска</th>
<th>Типичный смысл</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/32</code></td>
<td><code>255.255.255.255</code></td>
<td>один конкретный IPv4-адрес</td>
</tr>
<tr>
<td><code>/24</code></td>
<td><code>255.255.255.0</code></td>
<td>256 адресов в подсети</td>
</tr>
<tr>
<td><code>/16</code></td>
<td><code>255.255.0.0</code></td>
<td>крупная сеть</td>
</tr>
<tr>
<td><code>/8</code></td>
<td><code>255.0.0.0</code></td>
<td>очень крупная сеть</td>
</tr>
<tr>
<td><code>/0</code></td>
<td><code>0.0.0.0</code></td>
<td>весь IPv4-интернет, маршрут по умолчанию</td>
</tr>
</tbody>
</table>
<h2>Приватные IPv4-диапазоны</h2>
<p>Эти адреса не маршрутизируются напрямую в публичном интернете и часто
используются вместе с NAT:</p>
<ul>
<li><code>10.0.0.0/8</code>;</li>
<li><code>172.16.0.0/12</code>;</li>
<li><code>192.168.0.0/16</code>.</li>
</ul>
<p>Loopback-диапазон:</p>
<ul>
<li><code>127.0.0.0/8</code>, чаще всего <code>127.0.0.1</code> как
<code>localhost</code>.</li>
</ul>
<h2>Связи</h2>
<ul>
<li>IP-адрес</li>
<li>IPv6</li>
<li>NAT</li>
<li>Сетевые порты</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-subnet">Маска подсети</a></p>
</article>
<article class="note full-note" id="d-nat">
<h3>NAT, localhost, подсети</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="NAT">
<h1>NAT</h1>
<p>При использовании IPv4 в какой-то момент возникла глобальная проблема
нехватки IP-адресов, ведь количество устройств росло семимильными
темпами, поэтому долгосрочным решением стал ввод IPv6, но это оказалось
очень долго и дорого, поэтому спасением стала технология NAT - Network
Assress Translation.</p>
<p>Сети, обычно, проектируются с учетом приватной адресации,
например:</p>
<ul>
<li><code>10.0.0.0/8</code></li>
<li><code>172.16.0.0/12</code></li>
<li><code>192.168.0.0/16</code></li>
</ul>
<p>Эти адреса позволяют устройствам внутри сети общаться друг с другом,
при этом не выводя каждое устройство в сеть на всеобщее обозрение. Чтобы
локальному устройству “выйти” в сеть и связаться с внешними
устройствами, нужно его внутренний приватный адрес “перевести” во
внешний “белый” IP.</p>
<p>Как раз для этого и нужна технология <em>NAT</em>, она позволяет
переводить частный адреса в публичные. Это не только позволило, в связке
с частными IPv4, экономить адреса, но и добавляет конфиденциальность,
т.к. за одним публичным IP могут скрываться десятки, сотни, а то и
тысячи устройств.</p>
<p>Маршрутизаторы с поддержкой NAT могут быть настроены на один или
несколько “белых” IP-адресов. Этот список называется <strong>пулом
NAT</strong>. Когда устройство из внутренней сети отправляет трафик во
внешнюю сеть, маршрутизатор переводит внутренний IP во внешний из пула.
Для внешних устройств весь входящий и исходящий трафик выглядит, как
имеющий общедоступный IP-адрес.</p>
<p>Маршрутизатор с NAT, обычно, работает на границе со
<strong>Stub-сетью</strong>.</p>
<blockquote>
<p><em>Stub-сеть</em> — это тупиковая сеть, в которой весь трафик к
другим сетям проходит через один-единственный маршрут, обычно через один
маршрутизатор или шлюз. Такая сеть не используется для транзита чужих
данных между сетями, а только для подключения собственных устройств к
внешнему миру, часто по маршруту по умолчанию.</p>
</blockquote>
<p><img src="../../../99 System/Cache/4_NAT_image.png" /></p>
<p>В терминах NAT внутренней сетью является набор сетей, подлежащий
переводу. Внешняя сеть относится ко всем остальным сетям.</p>
<p>NAT включает 4 типа адресов:</p>
<ol type="1">
<li>Внутренний локальный адрес (Inside Local Address)</li>
<li>Внутренний глобальный адрес (Inside Global Address)</li>
<li>Внешний местный адрес (Outside local address)</li>
<li>Внешний глобальный адрес (Outside global address)</li>
</ol>
<p>При определении того, какой тип адреса используется, важно помнить,
что терминология NAT всегда <em>применяется с точки зрения устройства с
транслированным адресом</em>:</p>
<ul>
<li><strong>Внутренний адрес (Inside address)</strong> - адрес
устройства, которое транслируется NAT</li>
<li><strong>Внешний адрес (Outside address)</strong> - адрес устройства
назначения</li>
<li><strong>Локальный адрес (Local address)</strong> - это любой адрес,
который отображается во внутренней части сети</li>
<li><strong>Глобальный адрес (Global address)</strong> - это любой
адрес, который отображается во внешней части сети</li>
</ul>
<p><img src="../../../99 System/Cache/2_NAT_image.png" /></p>
<h2>Типы NAT</h2>
<ol type="1">
<li>Статическая адресная трансляция (Static NAT) - сопоставление один к
одному между локальными и глобальными адресами.</li>
<li>Динамическая адресная трансляция (Dynamic NAT) - сопоставление
многих ко многим между локальными и глобальными адресами.</li>
<li>Post Address Translation (PAT) - многоадресное сопоставление
локальных и глобальных адресов с использованием портов. Также известен,
как <strong>NAT Overload</strong>.</li>
</ol>
<h3>Static NAT</h3>
<p>Статический NAT использует сопоставление локальных и глобальных
адресов один к одному. Настраиваются администратором сети и остаются
постоянными. При отправке пакетов на внешний адрес, он транслируется на
внутренний, при этом для внешних устройств, они имеют внешний ip-адрес.
Используются, например, для веб-серверов и иных устройств, которые
должны иметь согласованный адрес, доступный в интернете. Статический NAT
требует достаточное количество общедоступных адресов для обеспечения
всех устройств.</p>
<p><img src="../../../99 System/Cache/NAT_image.png" /></p>
<p>Таблица статического NAT</p>
<h3>Dynamic NAT</h3>
<p>Динамический, обратно статическому, автоматически назначает и
освобождает внешние адреса. Занимаются они по принципу “кто первый
пришел, то и занял”. Аналогично статике, требует достаточного количества
адресов для удовлетворения общего количества одновременного
использования всеми устройствами.</p>
<p><img src="../../../99 System/Cache/1_NAT_image.png" /></p>
<p>Таблица динамического NAT</p>
<h3>Port Address Translation
(PAT)</h3>
<p>PAT - это совершенно иная по сути работы система. Тут происходит
параллельная трансляция многих внутренних адресов на один или несколько
внешних адресов. Такая схема чаще всего используется в домашних
маршрутизаторах. Хитрость заключается в том, что адресация идет не
только по адресам, но и для каждого внутреннего адреса, сопоставляется
номер порта. Когда устройство инициирует сеанс <strong>TCP/IP</strong>,
оно генерирует значение порта
источника <strong>TCP</strong> или <strong>UDP</strong> для уникальной
идентификации сеанса. Когда NAT-маршрутизатор получает пакет от клиента,
он использует номер своего исходного порта, чтобы однозначно
идентифицировать конкретный перевод NAT. PAT гарантирует, что устройства
используют разный номер порта TCP для каждого сеанса. Когда ответ
возвращается с сервера, номер порта источника, который становится
номером порта назначения в обратном пути, определяет, какое устройство
маршрутизатор перенаправляет пакеты.</p>
<p><img src="../../../99 System/Cache/3_NAT_image.png" /></p>
<p>Таблица PAT</p>
<p><strong>А что относительно пакетов IPv4, содержащих данные, отличные
от TCP или UDP?</strong><br />
Эти пакеты не содержат номер порта уровня 4. PAT переводит наиболее
распространенные протоколы, переносимые IPv4, которые не используют TCP
или UDP в качестве протокола транспортного уровня. Наиболее
распространенными из них являются <strong>ICMPv4</strong>. Каждый из
этих типов протоколов по-разному обрабатывается PAT. Например, сообщения
запроса ICMPv4, эхо-запросы и ответы включают идентификатор запроса
Query ID. ICMPv4 использует Query ID. для идентификации эхо-запроса с
соответствующим ответом. Идентификатор запроса увеличивается с каждым
отправленным эхо-запросом. PAT использует идентификатор запроса вместо
номера порта уровня 4.</p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr>
<th>Преимущества NAT</th>
<th>Недостатки NAT</th>
</tr>
</thead>
<tbody>
<tr>
<td>Поддержка приватных адресов, экономия публичных IPv4</td>
<td>Увеличение задержек, особенно для VoIP</td>
</tr>
<tr>
<td>Возможность нескольких пулов (резерв, балансировка нагрузки)</td>
<td>Потеря сквозной адресации, проблемы с некоторыми приложениями</td>
</tr>
<tr>
<td>Независимость внутренней адресации от провайдера</td>
<td>Потеря сквозной трассировки, сложнее диагностика</td>
</tr>
<tr>
<td>Повышение безопасности за счёт скрытия внутренней топологии</td>
<td>Сложности с туннелированием (IPsec и др.)</td>
</tr>
<tr>
<td>Лёгкая смена публичной схемы адресации без перенастройки хостов</td>
<td>Нарушение работы сервисов, требующих входящих соединений или
UDP</td>
</tr>
</tbody>
</table>
<h2>Связанные заметки</h2>
<ul>
<li>Terraform — NAT-шлюзы и пулы IP-адресов — типовые сетевые ресурсы,
которые Terraform декларативно управляет в облачной инфраструктуре (VPC,
subnets)</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-nat-localhost">NAT, localhost, подсети</a></p>
</article>
<article class="note full-note" id="d-dns">
<h3>DNS</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="DNS">
<h1>DNS</h1>
<p>DNS - Domain Name System Аналог: контактная книга в телефоне
(сопоставление ФИО номеру телефона).</p>
<p>При вводе названия сайта или его доменного имени в поисковую строку
браузер обращается к кэшу, если вдруг уже заходили на этот сайт с этого
браузера.</p>
<p>Далее идет обращение к локальному DNS серверу (для ПК это файл
<code>hosts</code>).</p>
<p>Если ничего нет, то идет обращение к <strong>resolver</strong>
(распознающий). Обычно, это сервер у хостинг-провайдера, но можно задать
любой другой.</p>
<p>Тот ищет записи в своем кэше (если его пользователи часто ищут твой
сайт) А если не находит, то идет к корневому <strong>root</strong>
серверу, который отвечает за зону <code>.</code>.</p>
<p>Стоит отметить, что все доменные имена, на самом деле, имеют точку в
конце адреса: youtube.com. vk.com. mephi.ru. За эту корневую точку и
отвечают корневые сервера.</p>
<p>Тот не говорит IP-адрес, но говорит, кто может “подсказать”.</p>
<p>Он нас пересылает на TLD (Top Level Domain) сервер, который отвечает
за свою верхнеуровневую доменную зону. Те делятся на:</p>
<ol type="1">
<li>gTLD (Generic Top Level Domain) - доменные зоны, не привязанные к
стране.
<ul>
<li><code>.edu</code> - образование</li>
<li><code>.com</code> - коммерческие</li>
<li><code>.org</code> - организации и некомерческие</li>
</ul></li>
<li>ccTLD (Country Code Top Level Domain) - привязанные к стране:
<ul>
<li><code>.ru</code> - Россия</li>
<li><code>.ua</code> - Украина</li>
<li><code>.by</code> - Беларусь</li>
<li><code>.us</code> - США</li>
</ul></li>
</ol>
<p>Т.е. после корневого сервера, resolver пойдет к серверу верхнего
уровня, но и тот не скажет ему, куда переходить, тот подскажет, к какому
<em>авторитарному</em> серверу обратиться.</p>
<p>Авторитарный сервер или сервер авторитативных имен (<em>Authoritative
nameserver</em>) - это сервер, отвечающий за конкретный пул адресов, как
правило, такими выступают хостинг-провайдеры, которые регистрируют
домены. Вот они как раз говорят, какое нужен ip-адрес.</p>
<p>В терминологии DNS существует 3 вида запросов:</p>
<ol type="1">
<li>Recursive (рекурсивный) - запрос IP-адреса по домену.</li>
<li>Iterative (итеративный) - запрос IP-адреса, либо авторитативного DNS
сервера.</li>
<li>Inverse (обратный) - запрос доменного имени по IP-адресу.</li>
</ol>
</div>
<p class="back">Назад: <a href="#s-dns">DNS</a></p>
</article>
<article class="note full-note" id="d-http">
<h3>HTTP, HTTPS и HTTP/3</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="HTTP, HTTPS и HTTP3">
<h1>HTTP, HTTPS и HTTP/3</h1>
<p>HTTP (HyperText Transfer Protocol) — прикладной протокол для доступа
к ресурсам WWW. Клиент и сервер обмениваются отдельными сообщениями:
клиент отправляет HTTP-запрос, сервер возвращает HTTP-ответ.</p>
<h2>Главное</h2>
<ul>
<li>HTTP сам по себе не хранит состояние между запросами. Сессии поверх
HTTP обычно поддерживаются через cookies или токены.</li>
<li>HTTP/1.0 часто открывал новое TCP-соединение на каждый обмен
запросом и ответом.</li>
<li>HTTP/1.1 добавил постоянные соединения и конвейерную обработку.</li>
<li>HTTP/2 использует бинарные фреймы, сжатие заголовков и
мультиплексирование нескольких потоков в одном TCP-соединении.</li>
<li>HTTPS — это HTTP поверх TLS. Он защищает конфиденциальность,
целостность и аутентичность соединения.</li>
<li>HTTP/3 переносит семантику HTTP на QUIC поверх UDP и уменьшает
проблему head-of-line blocking.</li>
</ul>
<h2>HTTP-запрос</h2>
<p>Стартовая строка запроса состоит из метода, цели запроса и версии
протокола:</p>
<pre class="http"><code>GET /index.html HTTP/1.1</code></pre>
<p>Основные методы:</p>
<table>
<thead>
<tr>
<th>Метод</th>
<th>Что делает</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>GET</code></td>
<td>Получить ресурс; параметры часто передаются в query string.</td>
</tr>
<tr>
<td><code>POST</code></td>
<td>Отправить данные на сервер: форма, JSON, файл.</td>
</tr>
<tr>
<td><code>HEAD</code></td>
<td>Получить только заголовки, как у <code>GET</code>, без тела
ответа.</td>
</tr>
<tr>
<td><code>PUT</code></td>
<td>Создать или полностью заменить ресурс.</td>
</tr>
<tr>
<td><code>PATCH</code></td>
<td>Частично изменить ресурс.</td>
</tr>
<tr>
<td><code>DELETE</code></td>
<td>Удалить ресурс.</td>
</tr>
<tr>
<td><code>OPTIONS</code></td>
<td>Узнать доступные методы/возможности сервера.</td>
</tr>
</tbody>
</table>
<h2>HTTP-сообщение</h2>
<p>И запрос, и ответ имеют общую структуру:</p>
<ol type="1">
<li>Стартовая строка.</li>
<li>Заголовки.</li>
<li>Пустая строка.</li>
<li>Необязательное тело.</li>
</ol>
<p>Заголовок записывается как <code>Имя-Заголовка: значение</code>. Имя
не чувствительно к регистру.</p>
<p>Частые заголовки запроса:</p>
<ul>
<li><code>Host</code> — конкретный домен или IP, к которому обращается
клиент.</li>
<li><code>User-Agent</code> — сведения о клиенте: браузер, ОС,
версия.</li>
<li><code>Accept</code> — какие типы данных клиент готов принять.</li>
<li><code>Cookie</code> — cookies клиента.</li>
<li><code>Authorization</code> — данные аутентификации, например
Bearer-токен.</li>
</ul>
<h2>HTTP-ответ</h2>
<p>Строка статуса ответа содержит версию протокола, код состояния и
текстовое пояснение:</p>
<pre class="http"><code>HTTP/1.1 200 OK</code></pre>
<p>Классы кодов:</p>
<ul>
<li><code>1xx</code> — информационные.</li>
<li><code>2xx</code> — успешная обработка.</li>
<li><code>3xx</code> — перенаправление.</li>
<li><code>4xx</code> — ошибка клиента.</li>
<li><code>5xx</code> — ошибка сервера.</li>
</ul>
<p>Частые коды:</p>
<ul>
<li><code>200 OK</code> — запрос успешно обработан.</li>
<li><code>302 Found</code> — временное перенаправление.</li>
<li><code>400 Bad Request</code> — некорректный запрос.</li>
<li><code>403 Forbidden</code> — доступ запрещён.</li>
<li><code>404 Not Found</code> — ресурс не найден.</li>
<li><code>500 Internal Server Error</code> — внутренняя ошибка
сервера.</li>
</ul>
<h2>HTTPS</h2>
<p>HTTPS использует TLS поверх TCP-соединения. Порт по умолчанию —
<code>443</code>, у обычного HTTP — <code>80</code>.</p>
<p>Базовый смысл TLS:</p>
<ul>
<li>сервер предъявляет сертификат;</li>
<li>клиент проверяет сертификат через центр сертификации;</li>
<li>стороны согласуют ключи;</li>
<li>дальнейший трафик шифруется и защищается от подмены.</li>
</ul>
<p>SSL — устаревшее название семейства протоколов, в современных
системах фактически используется TLS, но термин “SSL-сертификат” всё ещё
распространён.</p>
<h2>HTTP/3</h2>
<p>HTTP/3 работает поверх QUIC, а QUIC работает поверх UDP. QUIC
объединяет надёжную доставку, шифрование TLS 1.3 и управление
потоками.</p>
<p>Ключевые отличия:</p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr>
<th>HTTP/2</th>
<th>HTTP/3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Поверх TCP</td>
<td>Поверх UDP/QUIC</td>
</tr>
<tr>
<td>TLS-хендшейк отдельно</td>
<td>TLS 1.3 встроен в QUIC</td>
</tr>
<tr>
<td>Потеря TCP-пакета может блокировать все потоки</td>
<td>Потеря пакета влияет только на конкретный поток</td>
</tr>
<tr>
<td>Соединение привязано к IP/порту</td>
<td>QUIC поддерживает connection migration через connection ID</td>
</tr>
</tbody>
</table>
<p>HTTP/3 не меняет смысл методов, заголовков и кодов ответа. Меняется
транспортный слой.</p>
<h2>Связи</h2>
<ul>
<li>TCP vs UDP</li>
<li>IP-адрес</li>
<li>DNS</li>
<li>URI, URL, URN</li>
<li>Сетевые порты</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-http">HTTP</a> · <a href="#s-http-message">HTTP-запрос/ответ</a> · <a href="#s-https">HTTPS</a> · <a href="#s-http3">HTTP/3</a></p>
</article>
<article class="note full-note" id="d-ports">
<h3>Сетевые порты</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Сетевые порты">
<h1>Сетевые порты</h1>
<p>Сетевой порт — числовой идентификатор процесса или сервиса внутри
одного IP-адреса. IP-адрес указывает хост, порт указывает приложение на
этом хосте.</p>
<h2>Главное</h2>
<ul>
<li>Порт — число от <code>0</code> до <code>65535</code>.</li>
<li>Порты используются транспортными протоколами, прежде всего TCP и
UDP.</li>
<li>TCP-порт и UDP-порт с одинаковым номером не конфликтуют: это разные
пространства портов.</li>
<li>Для веба важно помнить: HTTP обычно использует TCP <code>80</code>,
HTTPS — TCP <code>443</code>, альтернативный HTTP часто
<code>8080</code>.</li>
</ul>
<h2>Диапазоны</h2>
<table>
<colgroup>
<col style="width: 45%" />
<col style="width: 54%" />
</colgroup>
<thead>
<tr>
<th>Диапазон</th>
<th>Назначение</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0-1023</code></td>
<td>Well-known/system ports. В Unix/Linux для привязки к ним обычно
нужны повышенные права.</td>
</tr>
<tr>
<td><code>1024-49151</code></td>
<td>Registered ports. Могут быть зарегистрированы за службами.</td>
</tr>
<tr>
<td><code>49152-65535</code></td>
<td>Dynamic/private ports. Обычно используются клиентскими приложениями
временно.</td>
</tr>
</tbody>
</table>
<h2>Частые порты</h2>
<table>
<thead>
<tr>
<th>Порт</th>
<th>Сервис</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>21</code></td>
<td>FTP</td>
</tr>
<tr>
<td><code>22</code></td>
<td>SSH</td>
</tr>
<tr>
<td><code>25</code></td>
<td>SMTP</td>
</tr>
<tr>
<td><code>53</code></td>
<td>DNS</td>
</tr>
<tr>
<td><code>80</code></td>
<td>HTTP</td>
</tr>
<tr>
<td><code>110</code></td>
<td>POP3</td>
</tr>
<tr>
<td><code>123</code></td>
<td>NTP</td>
</tr>
<tr>
<td><code>143</code></td>
<td>IMAP</td>
</tr>
<tr>
<td><code>443</code></td>
<td>HTTPS</td>
</tr>
<tr>
<td><code>993</code></td>
<td>IMAPS</td>
</tr>
<tr>
<td><code>3306</code></td>
<td>MySQL</td>
</tr>
<tr>
<td><code>3389</code></td>
<td>RDP</td>
</tr>
<tr>
<td><code>8080</code></td>
<td>альтернативный HTTP</td>
</tr>
</tbody>
</table>
<h2>Связи</h2>
<ul>
<li>HTTP, HTTPS и HTTP3</li>
<li>TCP vs UDP</li>
<li>IP-адрес</li>
<li>NAT</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-ports">Порты</a></p>
</article>
<article class="note full-note" id="d-auth">
<h3>Веб-аутентификация и авторизация</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Веб-аутентификация и авторизация">
<h1>Веб-аутентификация и
авторизация</h1>
<p>Идентификация, аутентификация и авторизация — разные этапы контроля
доступа.</p>
<h2>Термины</h2>
<ul>
<li>Идентификация — пользователь сообщает, кто он: логин, email, номер
телефона, ID.</li>
<li>Аутентификация — система проверяет, что пользователь действительно
тот, за кого себя выдаёт: пароль, одноразовый код, ключ,
сертификат.</li>
<li>Авторизация — система решает, что уже аутентифицированному
пользователю разрешено делать.</li>
</ul>
<p>Коротко: идентификация отвечает на вопрос “кто ты?”, аутентификация —
“докажи”, авторизация — “что тебе можно?”.</p>
<h2>HTTP-аутентификация</h2>
<p>Распространённые схемы:</p>
<ul>
<li><code>Basic</code> — логин и пароль передаются как base64-строка.
Base64 не является шифрованием, поэтому Basic нельзя использовать без
HTTPS для чувствительных данных.</li>
<li><code>Digest</code> — схема с хешированием, исторически безопаснее
Basic, но сейчас используется реже.</li>
<li><code>Bearer</code> — клиент передаёт токен, обычно в заголовке
<code>Authorization</code>.</li>
</ul>
<p>Пример Bearer:</p>
<pre class="http"><code>Authorization: Bearer eyJhbGciOi...</code></pre>
<p>Bearer-токен даёт доступ тому, кто им владеет. Поэтому токены нужно
хранить аккуратно, ограничивать срок их жизни и уметь отзывать.</p>
<h2>Связи</h2>
<ul>
<li>OAuth 2.0</li>
<li>HTTP, HTTPS и HTTP3</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-auth">Идентификация/auth</a></p>
</article>
<article class="note full-note" id="d-oauth">
<h3>OAuth 2.0</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="OAuth 2.0">
<h1>OAuth 2.0</h1>
<p>OAuth 2.0 — протокол авторизации, который позволяет приложению
получить ограниченный доступ к ресурсам пользователя на другом сервисе
без передачи пароля пользователя этому приложению.</p>
<h2>Где встречается</h2>
<ul>
<li>вход на сайт через Google, GitHub, VK или другую внешнюю учётную
запись;</li>
<li>мобильное приложение получает доступ к данным в облаке;</li>
<li>бот или интеграция получает доступ к API сервиса.</li>
</ul>
<h2>Главная идея</h2>
<p>Пользователь подтверждает доступ на стороне сервиса-владельца данных.
Клиентское приложение получает токен и использует его для запросов к
API.</p>
<p>Пароль пользователя при этом остаётся у сервиса-источника, а
стороннее приложение получает только ограниченное разрешение.</p>
<h2>Участники</h2>
<ul>
<li>Resource Owner — владелец ресурса, обычно пользователь.</li>
<li>Client — приложение, которому нужен доступ.</li>
<li>Authorization Server — сервер, который выдаёт токены.</li>
<li>Resource Server — API, который принимает токен и отдаёт защищённые
данные.</li>
</ul>
<h2>Токены</h2>
<ul>
<li>Access token — используется для доступа к API.</li>
<li>Refresh token — используется для получения нового access token без
повторного входа пользователя.</li>
<li>Scope — набор разрешений, ограничивающий действия клиента.</li>
</ul>
<p>OAuth 2.0 — про авторизацию. Сам по себе он не обязан доказывать
личность пользователя как протокол аутентификации; для входа поверх
OAuth обычно используют OpenID Connect.</p>
<h2>Связи</h2>
<ul>
<li>Веб-аутентификация и авторизация</li>
<li>HTTP, HTTPS и HTTP3</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-oauth">OAuth 2.0</a></p>
</article>
<article class="note full-note" id="d-uri">
<h3>URI, URL, URN</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="URI, URL, URN">
<h1>URI, URL, URN</h1>
<p>URI, URL и URN — способы идентифицировать ресурс. Для экзамена важно
различать: URL указывает, где и как получить ресурс; URN даёт устойчивое
имя; URI — общее понятие.</p>
<h2>URL</h2>
<p>URL (Uniform Resource Locator) — адрес ресурса в сети и способ его
получения.</p>
<p>Синтаксис:</p>
<pre class="text"><code>схема://хост:порт/путь?параметры#фрагмент</code></pre>
<p>Пример:</p>
<pre class="text"><code>https://home.mephi.ru/study_groups?level=1&amp;organization_id=1&amp;term_id=20</code></pre>
<p>Компоненты:</p>
<ul>
<li>схема — чаще всего <code>http</code> или <code>https</code>;</li>
<li>хост — домен или IP-адрес;</li>
<li>порт — необязателен, если используется порт по умолчанию;</li>
<li>путь — расположение ресурса на сервере;</li>
<li>query string — параметры после <code>?</code>;</li>
<li>фрагмент — часть документа после <code>#</code>, часто соответствует
<code>id</code> HTML-элемента.</li>
</ul>
<h2>URN</h2>
<p>URN (Uniform Resource Name) — имя ресурса без указания его сетевого
расположения.</p>
<p>Синтаксис:</p>
<pre class="text"><code>urn:NID:NSS</code></pre>
<p>Пример:</p>
<pre class="text"><code>urn:uuid:d5aa821e-52bb-4f7e-a782-7662ed5eaf5c</code></pre>
<p>URN полезен, когда ресурс должен сохранять идентичность независимо от
места хранения. Пример: ISBN книги.</p>
<h2>URI</h2>
<p>URI (Uniform Resource Identifier) — общий идентификатор ресурса. URI
может быть URL, URN или совмещать оба подхода.</p>
<p>Синтаксис:</p>
<pre class="text"><code>схема:[//[пользователь:пароль@]хост[:порт]][/]путь[?запрос][#фрагмент]</code></pre>
<p>Главное правило: все URL являются URI, но не все URI являются
URL.</p>
<h2>Связи</h2>
<ul>
<li>HTTP, HTTPS и HTTP3</li>
<li>DNS</li>
<li>IP-адрес</li>
</ul>
</div>
<p class="back">Назад: <a href="#s-uri">URI, URL, URN</a></p>
</article>
<article class="note full-note" id="d-html">
<h3>Основы HTML и HTML Forms</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Основы HTML (структура, таблицы, формы)">
<h1>Основы HTML</h1>
<p><strong>HTML (Hypertext Markup Language)</strong> — язык разметки для
структурирования и отображения веб-страниц.</p>
<h2>Структура HTML-документа</h2>
<p>Базовый шаблон (HTML5):</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;!DOCTYPE</span> html<span class="dt">&gt;</span></span>
<span><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="ot"> lang</span><span class="op">=</span><span class="st">&quot;ru&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">meta</span><span class="ot"> charset</span><span class="op">=</span><span class="st">&quot;UTF-8&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">meta</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;viewport&quot;</span><span class="ot"> content</span><span class="op">=</span><span class="st">&quot;width=device-width, initial-scale=1.0&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">title</span><span class="dt">&gt;</span>Заголовок страницы<span class="dt">&lt;/</span><span class="kw">title</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="co">&lt;!-- Контент страницы --&gt;</span></span>
<span><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
<ul>
<li><code>&lt;!DOCTYPE html&gt;</code> — объявление типа документа.</li>
<li><code>&lt;head&gt;</code> — метаданные (не отображаются в
браузере).</li>
<li><code>&lt;body&gt;</code> — основное содержимое.</li>
</ul>
<h3>Метатег Viewport</h3>
<p>Критичен для мобильных устройств. Позволяет контролировать
масштаб.</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">meta</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;viewport&quot;</span><span class="ot"> content</span><span class="op">=</span><span class="st">&quot;width=device-width, initial-scale=1.0&quot;</span><span class="dt">&gt;</span></span></code></pre></div>
<h2>Основные теги и семантика</h2>
<p><strong>Тег</strong> — элемент разметки, который говорит браузеру,
что находится на странице: заголовок, абзац, ссылка, форма, таблица.</p>
<p>Обычно тег состоит из открывающей и закрывающей части:</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span>Текст абзаца<span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<p>Некоторые теги не имеют закрывающей пары:</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">img</span><span class="ot"> src</span><span class="op">=</span><span class="st">&quot;cat.jpg&quot;</span><span class="ot"> alt</span><span class="op">=</span><span class="st">&quot;Кот&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">br</span><span class="dt">&gt;</span></span>
<span><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="dt">&gt;</span></span></code></pre></div>
<p><strong>Атрибуты</strong> уточняют поведение тега: -
<code>href</code> — адрес ссылки. - <code>src</code> — путь к
изображению или файлу. - <code>alt</code> — текстовое описание
изображения. - <code>class</code> — CSS-класс для стилизации. -
<code>id</code> — уникальный идентификатор элемента. - <code>name</code>
— имя поля формы, с которым данные отправляются на сервер.</p>
<h3>Блочные и строчные элементы</h3>
<ul>
<li><strong>Блочные элементы</strong> занимают всю доступную ширину и
обычно начинаются с новой строки: <code>&lt;div&gt;</code>,
<code>&lt;p&gt;</code>, <code>&lt;h1&gt;</code>,
<code>&lt;section&gt;</code>, <code>&lt;form&gt;</code>.</li>
<li><strong>Строчные элементы</strong> находятся внутри строки текста и
не ломают поток: <code>&lt;span&gt;</code>, <code>&lt;a&gt;</code>,
<code>&lt;strong&gt;</code>, <code>&lt;em&gt;</code>,
<code>&lt;code&gt;</code>.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> Это абзац с <span class="dt">&lt;</span><span class="kw">strong</span><span class="dt">&gt;</span>важным текстом<span class="dt">&lt;/</span><span class="kw">strong</span><span class="dt">&gt;</span> и</span>
<span><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;/about/&quot;</span><span class="dt">&gt;</span>ссылкой<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span>.</span>
<span><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span></code></pre></div>
<h3>Семантические теги</h3>
<p>Семантика — это использование тегов по смыслу, а не только ради
внешнего вида.</p>
<p>Например, <code>&lt;main&gt;</code> означает главный контент
страницы, <code>&lt;nav&gt;</code> — навигацию,
<code>&lt;article&gt;</code> — самостоятельный материал. Это помогает
браузеру, поисковым системам, скринридерам и другим инструментам лучше
понимать структуру страницы.</p>
<ul>
<li><code>&lt;header&gt;</code> — верхняя часть страницы или секции:
логотип, заголовок, навигация.</li>
<li><code>&lt;nav&gt;</code> — блок навигационных ссылок.</li>
<li><code>&lt;main&gt;</code> — основной контент страницы; обычно один
на страницу.</li>
<li><code>&lt;section&gt;</code> — смысловой раздел страницы.</li>
<li><code>&lt;article&gt;</code> — самостоятельный материал: статья,
пост, карточка новости.</li>
<li><code>&lt;aside&gt;</code> — дополнительный контент: боковая панель,
подсказки, связанные ссылки.</li>
<li><code>&lt;footer&gt;</code> — нижняя часть страницы или секции.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">header</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">h1</span><span class="dt">&gt;</span>Блог<span class="dt">&lt;/</span><span class="kw">h1</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">nav</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;/&quot;</span><span class="dt">&gt;</span>Главная<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;/about/&quot;</span><span class="dt">&gt;</span>О проекте<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">nav</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">header</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">main</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">article</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">h2</span><span class="dt">&gt;</span>Что такое HTML<span class="dt">&lt;/</span><span class="kw">h2</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span>HTML описывает структуру веб-страницы.<span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">article</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">aside</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">h2</span><span class="dt">&gt;</span>Похожие материалы<span class="dt">&lt;/</span><span class="kw">h2</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;/css/&quot;</span><span class="dt">&gt;</span>Основы CSS<span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">aside</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">main</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">footer</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span>2026<span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">footer</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span></code></pre></div>
<p>Неправильно заменять всё на <code>&lt;div&gt;</code>, если есть более
точный тег. <code>&lt;div&gt;</code> нужен как нейтральный контейнер,
когда у блока нет отдельного смыслового значения.</p>
<h3>Текст и форматирование</h3>
<ul>
<li><strong>Заголовки:</strong>
<code>&lt;h1&gt;</code><code>&lt;h6&gt;</code>.</li>
<li><strong>Абзацы и переносы:</strong> <code>&lt;p&gt;</code>,
<code>&lt;br&gt;</code>.</li>
<li><strong>Логическое выделение:</strong> <code>&lt;strong&gt;</code>
(важное, жирный), <code>&lt;em&gt;</code> (акцент, курсив),
<code>&lt;code&gt;</code> (код).</li>
<li><strong>Контейнеры:</strong> <code>&lt;div&gt;</code> (блочный),
<code>&lt;span&gt;</code> (строчный).</li>
</ul>
<h3>Списки</h3>
<ul>
<li><strong>Маркированный:</strong> <code>&lt;ul&gt;</code> +
<code>&lt;li&gt;</code>.</li>
<li><strong>Нумерованный:</strong> <code>&lt;ol&gt;</code> +
<code>&lt;li&gt;</code>.</li>
</ul>
<h3>Ссылки и изображения</h3>
<ul>
<li><strong>Ссылка:</strong>
<code>&lt;a href="URL" target="_blank"&gt;Текст&lt;/a&gt;</code>.</li>
<li><strong>Изображение:</strong>
<code>&lt;img src="./path" alt="описание" width="500"&gt;</code>.</li>
</ul>
<hr />
<h2>Таблицы</h2>
<p>Используются для отображения структурированных данных. Не
рекомендуются для верстки макета.</p>
<p><strong>Структура таблицы:</strong></p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">table</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">caption</span><span class="dt">&gt;</span>Заголовок таблицы<span class="dt">&lt;/</span><span class="kw">caption</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Заголовок столбца 1<span class="dt">&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">th</span><span class="dt">&gt;</span>Заголовок столбца 2<span class="dt">&lt;/</span><span class="kw">th</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">thead</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>Данные 1<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>Данные 2<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tbody</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tfoot</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>Итог 1<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">td</span><span class="dt">&gt;</span>Итог 2<span class="dt">&lt;/</span><span class="kw">td</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tr</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">tfoot</span><span class="dt">&gt;</span></span>
<span><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">table</span><span class="dt">&gt;</span></span></code></pre></div>
<ul>
<li><strong>Объединение ячеек:</strong> <code>colspan</code> (по
горизонтали), <code>rowspan</code> (по вертикали).</li>
</ul>
<hr />
<h2>Формы</h2>
<p>Инструмент для сбора данных пользователя.</p>
<p><strong>Базовая форма:</strong></p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">&quot;/handler-url/&quot;</span><span class="ot"> method</span><span class="op">=</span><span class="st">&quot;post&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;username&quot;</span><span class="dt">&gt;</span>Имя:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;text&quot;</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;username&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;name&quot;</span><span class="ot"> placeholder</span><span class="op">=</span><span class="st">&quot;Введите имя&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> </span>
<span><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;submit&quot;</span><span class="dt">&gt;</span>Отправить<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<h3>Элементы управления:</h3>
<ul>
<li><code>&lt;input&gt;</code> — универсальное поле ввода. Поведение
зависит от <code>type</code>.</li>
<li><code>&lt;select&gt;</code> + <code>&lt;option&gt;</code>
выпадающий список.</li>
<li><code>&lt;textarea&gt;</code> — многострочное поле для текста.</li>
<li><code>&lt;button&gt;</code> — кнопка отправки, сброса или обычного
действия.</li>
<li><code>&lt;fieldset&gt;</code> + <code>&lt;legend&gt;</code>
группировка связанных полей с заголовком группы.</li>
</ul>
<p>Основные <code>type</code> для <code>&lt;input&gt;</code>: -
<code>text</code> — обычная строка. - <code>password</code> — поле
пароля, символы скрываются. - <code>email</code> — email; браузер
проверяет базовый формат адреса. - <code>number</code> — числовой ввод.
- <code>checkbox</code> — независимый флажок: можно выбрать несколько
вариантов. - <code>radio</code> — выбор одного варианта из группы с
одинаковым <code>name</code>. - <code>file</code> — загрузка файла. -
<code>date</code> — выбор даты. - <code>hidden</code> — скрытое поле, не
видно пользователю, но отправляется с формой.</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="dt">&gt;</span>Email:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> required</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;age&quot;</span><span class="dt">&gt;</span>Возраст:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;number&quot;</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;age&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;age&quot;</span><span class="ot"> min</span><span class="op">=</span><span class="st">&quot;18&quot;</span><span class="ot"> max</span><span class="op">=</span><span class="st">&quot;99&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;checkbox&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;agreement&quot;</span><span class="ot"> required</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> Согласен с условиями</span>
<span><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">fieldset</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">legend</span><span class="dt">&gt;</span>Способ связи<span class="dt">&lt;/</span><span class="kw">legend</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;radio&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;contact_method&quot;</span><span class="ot"> value</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a> Email</span>
<span><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;radio&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;contact_method&quot;</span><span class="ot"> value</span><span class="op">=</span><span class="st">&quot;phone&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-22" aria-hidden="true" tabindex="-1"></a> Телефон</span>
<span><a href="#cb9-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-24" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">fieldset</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-25" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-26" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;city&quot;</span><span class="dt">&gt;</span>Город:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-27" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">select</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;city&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;city&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-28" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">option</span><span class="ot"> value</span><span class="op">=</span><span class="st">&quot;moscow&quot;</span><span class="dt">&gt;</span>Москва<span class="dt">&lt;/</span><span class="kw">option</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-29" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">option</span><span class="ot"> value</span><span class="op">=</span><span class="st">&quot;spb&quot;</span><span class="dt">&gt;</span>Санкт-Петербург<span class="dt">&lt;/</span><span class="kw">option</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-30" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">select</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-31" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-32" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;comment&quot;</span><span class="dt">&gt;</span>Комментарий:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-33" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">textarea</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;comment&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;comment&quot;</span><span class="ot"> rows</span><span class="op">=</span><span class="st">&quot;5&quot;</span><span class="dt">&gt;&lt;/</span><span class="kw">textarea</span><span class="dt">&gt;</span></span></code></pre></div>
<h3>Атрибуты валидации:</h3>
<ul>
<li><code>required</code> — поле обязательно для заполнения.</li>
<li><code>minlength</code> — минимальная длина текста.</li>
<li><code>maxlength</code> — максимальная длина текста.</li>
<li><code>min</code> — минимальное значение для числа или даты.</li>
<li><code>max</code> — максимальное значение для числа или даты.</li>
<li><code>pattern</code> — регулярное выражение, которому должно
соответствовать значение.</li>
<li><code>type="email"</code> — проверяет, что введён текст, похожий на
email.</li>
<li><code>type="url"</code> — проверяет, что введён URL.</li>
</ul>
<p>HTML-валидация работает в браузере до отправки формы. Она удобна для
пользователя, но не заменяет проверку на сервере: данные можно отправить
в обход браузера.</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> action</span><span class="op">=</span><span class="st">&quot;/register/&quot;</span><span class="ot"> method</span><span class="op">=</span><span class="st">&quot;post&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;username&quot;</span><span class="dt">&gt;</span>Логин:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span></span>
<span><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;text&quot;</span></span>
<span><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;username&quot;</span></span>
<span><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;username&quot;</span></span>
<span><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="ot"> required</span></span>
<span><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a><span class="ot"> minlength</span><span class="op">=</span><span class="st">&quot;3&quot;</span></span>
<span><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="ot"> maxlength</span><span class="op">=</span><span class="st">&quot;20&quot;</span></span>
<span><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="ot"> pattern</span><span class="op">=</span><span class="st">&quot;[a-zA-Z0-9_]+&quot;</span></span>
<span><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a><span class="ot"> </span><span class="dt">&gt;</span></span>
<span><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="dt">&gt;</span>Email:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;email&quot;</span><span class="ot"> required</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">label</span><span class="ot"> for</span><span class="op">=</span><span class="st">&quot;age&quot;</span><span class="dt">&gt;</span>Возраст:<span class="dt">&lt;/</span><span class="kw">label</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;number&quot;</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;age&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;age&quot;</span><span class="ot"> min</span><span class="op">=</span><span class="st">&quot;18&quot;</span><span class="ot"> max</span><span class="op">=</span><span class="st">&quot;99&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;submit&quot;</span><span class="dt">&gt;</span>Зарегистрироваться<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
</div>
<p class="back">Назад: <a href="#block-2">HTML</a> · <a href="#block-3">HTML Forms</a></p>
</article>
<article class="note full-note" id="d-css">
<h3>CSS: основы, селекторы и блочная модель</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Основы, селекторы и блочная модель">
<h1>CSS: Основы и селекторы</h1>
<p><strong>CSS (Cascading Style Sheets)</strong> — язык таблиц стилей
для оформления HTML-документов.</p>
<h2>Подключение CSS</h2>
<ol type="1">
<li><strong>Внешняя таблица:</strong>
<code>&lt;link rel="stylesheet" href="style.css"&gt;</code>.</li>
<li><strong>Внутренние стили:</strong> Тег <code>&lt;style&gt;</code> в
<code>&lt;head&gt;</code>.</li>
<li><strong>Встроенные (inline) стили:</strong> Атрибут
<code>style</code> у тега.</li>
</ol>
<h2>Селекторы</h2>
<ul>
<li><strong>Универсальный:</strong> <code>*</code> — все элементы.</li>
<li><strong>По тегу:</strong> <code>h1</code>, <code>p</code>.</li>
<li><strong>По классу:</strong> <code>.className</code>.</li>
<li><strong>По ID:</strong> <code>#idName</code>.</li>
<li><strong>Потомки:</strong> <code>div p</code> (все <code>p</code>
внутри <code>div</code>).</li>
<li><strong>Дочерние:</strong> <code>div &gt; p</code> (только прямые
наследники).</li>
<li><strong>Соседние:</strong> <code>h1 + p</code> (первый
<code>p</code> после <code>h1</code>).</li>
<li><strong>По атрибуту:</strong> <code>a[href^="https://"]</code>.</li>
</ul>
<h3>Псевдоклассы и
псевдоэлементы</h3>
<ul>
<li><code>:hover</code>, <code>:focus</code>, <code>:active</code>.</li>
<li><code>:nth-child(odd/even/n)</code>.</li>
<li><code>::before</code>, <code>::after</code> (используются с
<code>content</code>).</li>
</ul>
<h2>Каскадность и Специфичность</h2>
<p>Если правила конфликтуют, побеждает то, у которого выше
специфичность: 1. Inline стили (1, 0, 0, 0) 2. ID (0, 1, 0, 0) 3.
Классы, атрибуты, псевдоклассы (0, 0, 1, 0) 4. Теги и псевдоэлементы (0,
0, 0, 1)</p>
<hr />
<h1>Блочная модель (Box Model)</h1>
<p>Каждый элемент в CSS — это прямоугольный блок.</p>
<ul>
<li><strong>Content:</strong> Содержимое (текст, изображение).</li>
<li><strong>Padding:</strong> Внутренние поля (между контентом и
рамкой).</li>
<li><strong>Border:</strong> Рамка.</li>
<li><strong>Margin:</strong> Внешние отступы (между рамкой и
соседями).</li>
</ul>
<h2>Box-sizing</h2>
<ul>
<li><code>content-box</code> (по умолчанию): <code>width</code> задает
только ширину контента.</li>
<li><code>border-box</code>: <code>width</code> включает в себя
<code>padding</code> и <code>border</code>.</li>
</ul>
<h1>Свойство display</h1>
<ul>
<li><code>block</code>: Занимает всю ширину, начинается с новой
строки.</li>
<li><code>inline</code>: Ширина по контенту, не позволяет задавать
<code>width/height</code>.</li>
<li><code>inline-block</code>: Строчный элемент, но с возможностью
задавать размеры и отступы.</li>
<li><code>none</code>: Убирает элемент из документа.</li>
</ul>
<hr />
<h1>Базовые свойства оформления</h1>
<h2>Текст и шрифты</h2>
<ul>
<li><code>font-family</code>, <code>font-size</code>,
<code>font-weight</code>.</li>
<li><code>text-align</code>, <code>text-transform</code>
(<code>uppercase</code>, <code>capitalize</code>).</li>
<li><code>text-decoration</code> (<code>underline</code>,
<code>none</code>).</li>
<li><code>line-height</code>, <code>letter-spacing</code>.</li>
</ul>
<h2>Фон (Background)</h2>
<ul>
<li><code>background-color</code>.</li>
<li><code>background-image: url(...)</code>.</li>
<li><code>background-size: cover / contain</code>.</li>
<li><code>background-repeat: no-repeat</code>.</li>
<li><code>background-position: center</code>.</li>
</ul>
<h2>Границы и тени</h2>
<ul>
<li><code>border: 1px solid black;</code>.</li>
<li><code>border-radius: 5px;</code> (скругление углов).</li>
<li><code>box-shadow: x y blur spread color;</code>.</li>
<li><code>transition: 0.3s;</code> (плавные переходы).</li>
</ul>
</div>
<p class="back">Назад: <a href="#block-4">CSS</a></p>
</article>
<article class="note full-note" id="d-layout">
<h3>Адаптивность, Flexbox и Grid</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Адаптивность, Flexbox и Grid">
<h1>Адаптивная верстка</h1>
<p>Адаптивность (AWD) и отзывчивость (RWD) позволяют сайту корректно
отображаться на любых устройствах.</p>
<h2>Медиазапросы (<span class="citation"
data-cites="media">@media</span>)</h2>
<p>Синтаксис:</p>
<div class="sourceCode"><pre
class="sourceCode css"><code class="sourceCode css"><span><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">@media</span> <span class="an">screen</span> <span class="kw">and</span> <span class="fu">(</span><span class="kw">max-width</span><span class="ch">:</span> <span class="dv">768</span><span class="dt">px</span><span class="fu">)</span> {</span>
<span><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="co">/* Стили для планшетов и телефонов */</span></span>
<span><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<ul>
<li><strong>Mobile First:</strong> Сначала стили для мобильных, затем
<code>min-width</code>.</li>
<li><strong>Desktop First:</strong> Сначала для десктопов, затем
<code>max-width</code>.</li>
</ul>
<hr />
<h1>Flexbox (Flexible Box Layout)</h1>
<p>Одномерная модель раскладки (по одной оси).</p>
<ul>
<li><strong>Контейнер:</strong> <code>display: flex;</code> /
<code>inline-flex;</code>.</li>
<li><strong>Направление:</strong>
<code>flex-direction: row | column | row-reverse</code>.</li>
<li><strong>Перенос:</strong>
<code>flex-wrap: wrap | nowrap</code>.</li>
<li><strong>Выравнивание по главной оси:</strong>
<code>justify-content: flex-start | center | space-between | space-around</code>.</li>
<li><strong>Выравнивание по поперечной оси:</strong>
<code>align-items: center | stretch</code>.</li>
<li><strong>Свойства элементов:</strong>
<ul>
<li><code>flex-grow</code> (коэффициент роста),</li>
<li><code>flex-shrink</code> (сжатие),</li>
<li><code>flex-basis</code> (базовый размер),</li>
<li><code>order</code> (порядок отображения).</li>
</ul></li>
</ul>
<hr />
<h1>Grid Layout</h1>
<p>Двумерная модель раскладки (строки + столбцы).</p>
<ul>
<li><strong>Контейнер:</strong> <code>display: grid;</code>.</li>
<li><strong>Определение сетки:</strong>
<ul>
<li><code>grid-template-columns: repeat(3, 1fr);</code> (3 колонки
равной ширины).</li>
<li><code>grid-template-rows: auto 1fr;</code>.</li>
</ul></li>
<li><strong>Единица fr:</strong> Доля свободного пространства.</li>
<li><strong>Отступы:</strong> <code>gap: 10px;</code>.</li>
<li><strong>Размещение элементов:</strong>
<ul>
<li><code>grid-column: 1 / 3;</code> (от 1-й до 3-й линии).</li>
<li><code>grid-row: 2 / span 2;</code> (со 2-й линии на 2 ячейки).</li>
</ul></li>
<li><strong>Именованные области:</strong></li>
</ul>
<div class="sourceCode"><pre
class="sourceCode css"><code class="sourceCode css"><span><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">.container</span> {</span>
<span><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="kw">grid-template-areas</span><span class="ch">:</span> </span>
<span><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;header header&quot;</span></span>
<span><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;sidebar content&quot;</span></span>
<span><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;footer footer&quot;</span><span class="op">;</span></span>
<span><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>}</span>
<span><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>header { <span class="kw">grid-area</span><span class="ch">:</span> <span class="dv">header</span><span class="op">;</span> }</span></code></pre></div>
<hr />
<h1>Сравнение Flex и Grid</h1>
<ul>
<li><strong>Flexbox:</strong> Лучше для одномерных структур (меню,
тулбары) и распределения пространства между элементами.</li>
<li><strong>Grid:</strong> Лучше для сложных макетов всей страницы,
перекрытия элементов и точного контроля над строками и столбцами
одновременно.</li>
</ul>
</div>
<p class="back">Назад: <a href="#block-5">Flexbox и Grid</a></p>
</article>
<article class="note full-note" id="d-django-models">
<h3>Django Модели и ORM</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Модели и ORM">
<h1>Django Модели и ORM</h1>
<p><strong>Django ORM (Object-Relational Mapping)</strong> — технология
связи объектно-ориентированного кода и реляционной БД. Реализует паттерн
Active Record.</p>
<h2>Структура проекта</h2>
<ul>
<li><code>manage.py</code>: Утилита командной строки.</li>
<li><code>settings.py</code>: Конфигурация проекта (БД,
<code>INSTALLED_APPS</code>).</li>
<li><code>urls.py</code>: Описание маршрутов.</li>
<li><code>models.py</code>: Определение таблиц БД.</li>
</ul>
<pre class="text"><code>project/
├── manage.py
├── project/
│ ├── settings.py
│ └── urls.py
└── app/
└── models.py</code></pre>
<h2>Определение моделей</h2>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.db <span class="im">import</span> models</span>
<span><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Person(models.Model):</span>
<span><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> first_name <span class="op">=</span> models.CharField(<span class="st">&quot;Имя&quot;</span>, max_length<span class="op">=</span><span class="dv">30</span>)</span>
<span><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> age <span class="op">=</span> models.IntegerField(default<span class="op">=</span><span class="dv">18</span>)</span>
<span><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> bio <span class="op">=</span> models.TextField(blank<span class="op">=</span><span class="va">True</span>)</span>
<span><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> </span>
<span><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">class</span> Meta:</span>
<span><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> verbose_name <span class="op">=</span> <span class="st">&quot;Человек&quot;</span></span>
<span><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> ordering <span class="op">=</span> [<span class="st">&quot;-first_name&quot;</span>]</span></code></pre></div>
<h3>Типы полей</h3>
<ul>
<li><code>AutoField</code> — автоинкрементный числовой первичный
ключ.</li>
<li><code>CharField(max_length=N)</code> — короткая строка фиксированной
максимальной длины.</li>
<li><code>TextField</code> — длинный текст без обязательного ограничения
длины.</li>
<li><code>IntegerField</code> — целое число.</li>
<li><code>DecimalField(max_digits, decimal_places)</code> — точное
десятичное число, удобно для денег и измерений.</li>
<li><code>BooleanField</code> — логическое значение
<code>True</code>/<code>False</code>.</li>
<li><code>DateField</code> — дата без времени.</li>
<li><code>DateTimeField</code> — дата и время.</li>
<li><code>EmailField</code> — строка с валидацией email-адреса.</li>
<li><code>FileField</code> — путь к загруженному файлу.</li>
<li><code>ImageField</code> — файл изображения с дополнительной
проверкой формата.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Article(models.Model):</span>
<span><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="bu">id</span> <span class="op">=</span> models.AutoField(primary_key<span class="op">=</span><span class="va">True</span>)</span>
<span><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> title <span class="op">=</span> models.CharField(max_length<span class="op">=</span><span class="dv">200</span>)</span>
<span><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> body <span class="op">=</span> models.TextField()</span>
<span><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> views <span class="op">=</span> models.IntegerField(default<span class="op">=</span><span class="dv">0</span>)</span>
<span><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> price <span class="op">=</span> models.DecimalField(max_digits<span class="op">=</span><span class="dv">10</span>, decimal_places<span class="op">=</span><span class="dv">2</span>)</span>
<span><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> is_published <span class="op">=</span> models.BooleanField(default<span class="op">=</span><span class="va">False</span>)</span>
<span><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> published_at <span class="op">=</span> models.DateTimeField(null<span class="op">=</span><span class="va">True</span>, blank<span class="op">=</span><span class="va">True</span>)</span>
<span><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> author_email <span class="op">=</span> models.EmailField()</span>
<span><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> attachment <span class="op">=</span> models.FileField(upload_to<span class="op">=</span><span class="st">&quot;files/&quot;</span>, blank<span class="op">=</span><span class="va">True</span>)</span>
<span><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> cover <span class="op">=</span> models.ImageField(upload_to<span class="op">=</span><span class="st">&quot;covers/&quot;</span>, blank<span class="op">=</span><span class="va">True</span>)</span></code></pre></div>
<h3>Отношения</h3>
<ul>
<li><code>ForeignKey(Target, on_delete=...)</code> — связь “один ко
многим”: у одной записи <code>Target</code> может быть много связанных
записей текущей модели.</li>
<li><code>OneToOneField(Target, on_delete=...)</code> — связь “один к
одному”: каждой записи соответствует максимум одна связанная
запись.</li>
<li><code>ManyToManyField(Target)</code> — связь “многие ко многим”:
записи обеих моделей могут ссылаться на несколько записей другой
модели.</li>
</ul>
<p><code>on_delete</code> задаёт, что делать с текущей записью, если
удаляется связанная запись <code>Target</code>: - <code>CASCADE</code>
удалить текущую запись вместе со связанной. - <code>PROTECT</code>
запретить удаление связанной записи, пока на неё есть ссылки. -
<code>SET_NULL</code> — записать <code>NULL</code> в поле связи; требует
<code>null=True</code>. - <code>SET_DEFAULT</code> — записать значение
по умолчанию; требует <code>default</code>. - <code>DO_NOTHING</code>
ничего не делать на уровне Django, ответственность остаётся на БД и
разработчике.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Author(models.Model):</span>
<span><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> name <span class="op">=</span> models.CharField(max_length<span class="op">=</span><span class="dv">100</span>)</span>
<span><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Profile(models.Model):</span>
<span><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> author <span class="op">=</span> models.OneToOneField(Author, on_delete<span class="op">=</span>models.CASCADE)</span>
<span><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> bio <span class="op">=</span> models.TextField()</span>
<span><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Book(models.Model):</span>
<span><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> title <span class="op">=</span> models.CharField(max_length<span class="op">=</span><span class="dv">200</span>)</span>
<span><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> author <span class="op">=</span> models.ForeignKey(Author, on_delete<span class="op">=</span>models.PROTECT)</span>
<span><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a> co_authors <span class="op">=</span> models.ManyToManyField(Author, related_name<span class="op">=</span><span class="st">&quot;coauthored_books&quot;</span>, blank<span class="op">=</span><span class="va">True</span>)</span></code></pre></div>
<hr />
<h2>Миграции</h2>
<p>Инструмент для синхронизации моделей с БД. -
<code>python manage.py makemigrations</code> — создать файлы миграций. -
<code>python manage.py migrate</code> — применить изменения в БД. -
<code>python manage.py showmigrations</code> — список статусов
миграций.</p>
<hr />
<h2>Работа с QuerySet (Запросы)</h2>
<p>Django QuerySet ленивы (вычисляются только при обращении) и
поддерживают цепочки методов.</p>
<h3>Базовые CRUD операции</h3>
<ul>
<li><strong>Create:</strong>
<code>obj = Person.objects.create(name="Ivan")</code> или
<code>obj.save()</code>.</li>
<li><strong>Read:</strong>
<ul>
<li><code>Person.objects.all()</code> (все),</li>
<li><code>Person.objects.get(id=1)</code> (один),</li>
<li><code>Person.objects.filter(age__gt=20)</code> (фильтрация).</li>
</ul></li>
<li><strong>Update:</strong>
<code>Person.objects.filter(id=1).update(age=25)</code>.</li>
<li><strong>Delete:</strong> <code>obj.delete()</code> или
<code>QuerySet.delete()</code>.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Create</span></span>
<span><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>person <span class="op">=</span> Person.objects.create(first_name<span class="op">=</span><span class="st">&quot;Ivan&quot;</span>, age<span class="op">=</span><span class="dv">25</span>)</span>
<span><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Read</span></span>
<span><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>people <span class="op">=</span> Person.objects.<span class="bu">all</span>()</span>
<span><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>person <span class="op">=</span> Person.objects.get(<span class="bu">id</span><span class="op">=</span><span class="dv">1</span>)</span>
<span><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>adults <span class="op">=</span> Person.objects.<span class="bu">filter</span>(age__gte<span class="op">=</span><span class="dv">18</span>)</span>
<span><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Update</span></span>
<span><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(<span class="bu">id</span><span class="op">=</span><span class="dv">1</span>).update(age<span class="op">=</span><span class="dv">26</span>)</span>
<span><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="co"># Delete</span></span>
<span><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>person.delete()</span></code></pre></div>
<h3>Поиск по полям (Field
Lookups)</h3>
<ul>
<li><code>exact</code>, <code>iexact</code> (регистронезависимо).</li>
<li><code>gt</code> (&gt;), <code>gte</code> (&gt;=), <code>lt</code>
(&lt;), <code>lte</code> (&lt;=).</li>
<li><code>contains</code>, <code>icontains</code>.</li>
<li><code>in</code> (список), <code>range</code> (диапазон).</li>
<li><code>isnull=True</code>.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(first_name__exact<span class="op">=</span><span class="st">&quot;Ivan&quot;</span>)</span>
<span><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(first_name__iexact<span class="op">=</span><span class="st">&quot;ivan&quot;</span>)</span>
<span><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(age__gt<span class="op">=</span><span class="dv">18</span>)</span>
<span><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(age__range<span class="op">=</span>(<span class="dv">18</span>, <span class="dv">30</span>))</span>
<span><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(first_name__icontains<span class="op">=</span><span class="st">&quot;iv&quot;</span>)</span>
<span><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(id__in<span class="op">=</span>[<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>])</span>
<span><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>Person.objects.<span class="bu">filter</span>(bio__isnull<span class="op">=</span><span class="va">True</span>)</span></code></pre></div>
<h3>Оптимизация запросов</h3>
<ul>
<li><code>select_related('field')</code> — заранее подтягивает связанную
запись через <code>JOIN</code>. Подходит для <code>ForeignKey</code> и
<code>OneToOneField</code>, когда у объекта есть одна связанная
запись.</li>
<li><code>prefetch_related('field')</code> — делает отдельный запрос за
связанными объектами и собирает их в Python. Подходит для
<code>ManyToManyField</code> и обратных связей, где связанных записей
может быть много.</li>
<li><strong>Проблема N+1</strong> — ситуация, когда Django делает один
запрос за списком объектов, а потом ещё по одному запросу на каждый
объект в цикле.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co"># select_related: книга + её автор загружаются одним SQL-запросом</span></span>
<span><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>books <span class="op">=</span> Book.objects.select_related(<span class="st">&quot;author&quot;</span>)</span>
<span><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> book <span class="kw">in</span> books:</span>
<span><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(book.title, book.author.name)</span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># prefetch_related: авторы загружаются отдельно, затем Django связывает их с книгами</span></span>
<span><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>books <span class="op">=</span> Book.objects.prefetch_related(<span class="st">&quot;authors&quot;</span>)</span>
<span><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> book <span class="kw">in</span> books:</span>
<span><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(book.title, [author.name <span class="cf">for</span> author <span class="kw">in</span> book.authors.<span class="bu">all</span>()])</span></code></pre></div>
<h3>Агрегация и аннотация</h3>
<ul>
<li><code>aggregate(Avg('rating'))</code> — считает одно итоговое
значение по всей выборке: среднее, сумму, минимум, максимум, количество.
Возвращает словарь, а не QuerySet.</li>
<li><code>annotate(total=Count('entries'))</code> — добавляет
вычисляемое поле к каждому объекту в выборке.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.db.models <span class="im">import</span> Avg, Count</span>
<span><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="co"># aggregate: одно итоговое значение по всей таблице книг</span></span>
<span><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>result <span class="op">=</span> Book.objects.aggregate(avg_rating<span class="op">=</span>Avg(<span class="st">&quot;rating&quot;</span>))</span>
<span><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(result[<span class="st">&quot;avg_rating&quot;</span>])</span>
<span><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a><span class="co"># annotate: вычисляемое поле для каждого автора</span></span>
<span><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>authors <span class="op">=</span> Author.objects.annotate(total_books<span class="op">=</span>Count(<span class="st">&quot;books&quot;</span>))</span>
<span><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> author <span class="kw">in</span> authors:</span>
<span><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(author.name, author.total_books)</span></code></pre></div>
<h3>Логические условия</h3>
<ul>
<li><code>Q-объекты</code> нужны для сложных условий в
<code>filter()</code>: <code>OR</code> (<code>|</code>),
<code>AND</code> (<code>&amp;</code>), <code>NOT</code>
(<code>~</code>).</li>
<li><code>F-выражения</code> позволяют сравнивать и изменять поля прямо
на стороне БД, не загружая значение в Python.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.db.models <span class="im">import</span> F, Q</span>
<span><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Q: рейтинг выше 4 или название содержит Django</span></span>
<span><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>books <span class="op">=</span> Book.objects.<span class="bu">filter</span>(</span>
<span><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> Q(rating__gt<span class="op">=</span><span class="dv">4</span>) <span class="op">|</span> Q(title__icontains<span class="op">=</span><span class="st">&quot;Django&quot;</span>)</span>
<span><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>)</span>
<span><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a><span class="co"># F: увеличить счётчик просмотров прямо в БД</span></span>
<span><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>Book.objects.<span class="bu">filter</span>(<span class="bu">id</span><span class="op">=</span><span class="dv">1</span>).update(views<span class="op">=</span>F(<span class="st">&quot;views&quot;</span>) <span class="op">+</span> <span class="dv">1</span>)</span></code></pre></div>
</div>
<p class="back">Назад: <a href="#s-django-models">Django models</a> · <a href="#s-django-queries">Django queries</a></p>
</article>
<article class="note full-note" id="d-django-views">
<h3>Django: представления, URL, шаблоны и CBV</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Представления, шаблоны и CBV">
<h1>Django: Представления,
шаблоны и CBV</h1>
<h2>Маршрутизация (URL Routing)</h2>
<ul>
<li>Описывается в <code>urlpatterns</code> с помощью <code>path()</code>
и <code>re_path()</code>.</li>
<li>Конвертеры: <code>&lt;int:pk&gt;</code>,
<code>&lt;slug:slug&gt;</code>, <code>&lt;uuid:id&gt;</code>.</li>
<li>Включение других файлов:
<code>path('blog/', include('blog.urls'))</code>.</li>
<li>Реверсирование: <code>{% url 'name' %}</code> в шаблонах или
<code>reverse('name')</code> в Python.</li>
</ul>
<p>Конвертеры вытаскивают часть URL, проверяют её тип и передают во view
как аргумент: - <code>&lt;str:name&gt;</code> — непустая строка без
<code>/</code>; используется по умолчанию. - <code>&lt;int:pk&gt;</code>
— целое положительное число. - <code>&lt;slug:slug&gt;</code> — строка
для человекочитаемого URL: буквы, цифры, <code>_</code>, <code>-</code>.
- <code>&lt;uuid:id&gt;</code> — UUID, например
<code>075194d3-6885-417e-a8a8-6c931e272f00</code>. -
<code>&lt;path:subpath&gt;</code> — строка, которая может содержать
<code>/</code>.</p>
<p>Маршрутизация связывает URL с view-функцией или view-классом. Имя
маршрута (<code>name</code>) нужно, чтобы не хардкодить URL в коде и
шаблонах.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># project/urls.py</span></span>
<span><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.urls <span class="im">import</span> include, path</span>
<span><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>urlpatterns <span class="op">=</span> [</span>
<span><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;blog/&quot;</span>, include(<span class="st">&quot;blog.urls&quot;</span>)),</span>
<span><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>]</span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># blog/urls.py</span></span>
<span><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.urls <span class="im">import</span> path</span>
<span><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> . <span class="im">import</span> views</span>
<span><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>app_name <span class="op">=</span> <span class="st">&quot;blog&quot;</span></span>
<span><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>urlpatterns <span class="op">=</span> [</span>
<span><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;&quot;</span>, views.post_list, name<span class="op">=</span><span class="st">&quot;post_list&quot;</span>),</span>
<span><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;&lt;int:pk&gt;/&quot;</span>, views.post_detail, name<span class="op">=</span><span class="st">&quot;post_detail&quot;</span>),</span>
<span><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;tag/&lt;slug:slug&gt;/&quot;</span>, views.posts_by_tag, name<span class="op">=</span><span class="st">&quot;posts_by_tag&quot;</span>),</span>
<span><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;files/&lt;path:subpath&gt;/&quot;</span>, views.file_detail, name<span class="op">=</span><span class="st">&quot;file_detail&quot;</span>),</span>
<span><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>]</span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> post_detail(request, pk):</span>
<span><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> <span class="co"># pk уже int</span></span>
<span><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> ...</span>
<span><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> posts_by_tag(request, slug):</span>
<span><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> <span class="co"># slug — строка из URL</span></span>
<span><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> ...</span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.urls <span class="im">import</span> reverse</span>
<span><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>url <span class="op">=</span> reverse(<span class="st">&quot;blog:post_detail&quot;</span>, kwargs<span class="op">=</span>{<span class="st">&quot;pk&quot;</span>: <span class="dv">1</span>})</span></code></pre></div>
<hr />
<h2>Представления (Views)</h2>
<p>Views принимают <code>HttpRequest</code> и возвращают
<code>HttpResponse</code>.</p>
<p>View — это точка входа для запроса. В ней обычно читают данные из БД,
проверяют условия и возвращают HTML, JSON, редирект или ошибку.</p>
<h3>Функциональные представления
(FBV)</h3>
<p>FBV — обычная функция. Удобна, когда логика короткая и
нестандартная.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.shortcuts <span class="im">import</span> render, get_object_or_404</span>
<span><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> index(request):</span>
<span><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> latest_items <span class="op">=</span> Model.objects.<span class="bu">all</span>()[:<span class="dv">5</span>]</span>
<span><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> render(request, <span class="st">&quot;app/index.html&quot;</span>, {<span class="st">&quot;items&quot;</span>: latest_items})</span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.shortcuts <span class="im">import</span> get_object_or_404, render</span>
<span><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> .models <span class="im">import</span> Post</span>
<span><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> post_detail(request, pk):</span>
<span><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> post <span class="op">=</span> get_object_or_404(Post, pk<span class="op">=</span>pk)</span>
<span><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> render(request, <span class="st">&quot;blog/detail.html&quot;</span>, {<span class="st">&quot;post&quot;</span>: post})</span></code></pre></div>
<h3>Классовые представления (CBV)</h3>
<p>Абстрагируют типичные задачи. Вызываются в URL через
<code>.as_view()</code>.</p>
<ul>
<li><strong>TemplateView:</strong> Отображение статического
шаблона.</li>
<li><strong>ListView:</strong> Автоматический список объектов (данные в
<code>object_list</code>).</li>
<li><strong>DetailView:</strong> Детальная информация об одном объекте
(ищет по <code>pk</code> или <code>slug</code>).</li>
</ul>
<p>CBV — класс с готовым поведением. Django сам вызывает нужные методы
класса: например, <code>get()</code> для GET-запроса или
<code>get_queryset()</code> для получения списка объектов.</p>
<p><strong>Пример ListView:</strong></p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.views.generic <span class="im">import</span> DetailView, ListView, TemplateView</span>
<span><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> .models <span class="im">import</span> Post</span>
<span><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> AboutView(TemplateView):</span>
<span><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> template_name <span class="op">=</span> <span class="st">&quot;blog/about.html&quot;</span></span>
<span><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> PostListView(ListView):</span>
<span><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span> Post</span>
<span><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> template_name <span class="op">=</span> <span class="st">&quot;blog/list.html&quot;</span></span>
<span><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> context_object_name <span class="op">=</span> <span class="st">&quot;posts&quot;</span></span>
<span><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> paginate_by <span class="op">=</span> <span class="dv">10</span></span>
<span><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> get_queryset(<span class="va">self</span>):</span>
<span><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> Post.objects.<span class="bu">filter</span>(is_published<span class="op">=</span><span class="va">True</span>).order_by(<span class="st">&quot;-created_at&quot;</span>)</span>
<span><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> PostDetailView(DetailView):</span>
<span><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span> Post</span>
<span><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a> template_name <span class="op">=</span> <span class="st">&quot;blog/detail.html&quot;</span></span>
<span><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a> context_object_name <span class="op">=</span> <span class="st">&quot;post&quot;</span></span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="co"># blog/urls.py</span></span>
<span><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.urls <span class="im">import</span> path</span>
<span><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> .views <span class="im">import</span> AboutView, PostDetailView, PostListView</span>
<span><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>app_name <span class="op">=</span> <span class="st">&quot;blog&quot;</span></span>
<span><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>urlpatterns <span class="op">=</span> [</span>
<span><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;about/&quot;</span>, AboutView.as_view(), name<span class="op">=</span><span class="st">&quot;about&quot;</span>),</span>
<span><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;&quot;</span>, PostListView.as_view(), name<span class="op">=</span><span class="st">&quot;post_list&quot;</span>),</span>
<span><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> path(<span class="st">&quot;&lt;int:pk&gt;/&quot;</span>, PostDetailView.as_view(), name<span class="op">=</span><span class="st">&quot;post_detail&quot;</span>),</span>
<span><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>]</span></code></pre></div>
<hr />
<h2>Шаблоны (Templates)</h2>
<p>Используют <strong>Django Template Language (DTL)</strong>.</p>
<h3>Синтаксис</h3>
<ul>
<li><strong>Переменные:</strong> <code>{{ var.attr }}</code>.</li>
<li><strong>Теги:</strong> <code>{% if %}</code>,
<code>{% for %}</code>, <code>{% url %}</code>,
<code>{% csrf_token %}</code>.</li>
<li><strong>Фильтры:</strong> <code>{{ date|date:"Y-m-d" }}</code>,
<code>{{ list|length }}</code>, <code>{{ text|lower }}</code>.</li>
</ul>
<p>Шаблон получает контекст из view. Например, если view передала
<code>{"posts": posts}</code>, в шаблоне доступна переменная
<code>posts</code>.</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>{% for post in posts %}</span>
<span><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">article</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">h2</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;{% url &#39;blog:post_detail&#39; post.pk %}&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a> {{ post.title|upper }}</span>
<span><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">h2</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> {% if post.is_published %}</span>
<span><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span>{{ post.created_at|date:&quot;Y-m-d&quot; }}<span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> {% endif %}</span>
<span><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">article</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>{% empty %}</span>
<span><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">p</span><span class="dt">&gt;</span>Постов нет.<span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a>{% endfor %}</span></code></pre></div>
<h3>Наследование шаблонов</h3>
<p>Позволяет избежать дублирования HTML-кода. - <strong>Базовый шаблон
(<code>base.html</code>):</strong> Определяет блоки
<code>{% block content %}{% endblock %}</code>. - <strong>Дочерний
шаблон:</strong> Использует <code>{% extends "base.html" %}</code> и
переопределяет блоки.</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co">&lt;!-- templates/base.html --&gt;</span></span>
<span><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;!doctype</span> html<span class="dt">&gt;</span></span>
<span><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">html</span><span class="ot"> lang</span><span class="op">=</span><span class="st">&quot;ru&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">title</span><span class="dt">&gt;</span>{% block title %}Сайт{% endblock %}<span class="dt">&lt;/</span><span class="kw">title</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">head</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">main</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> {% block content %}{% endblock %}</span>
<span><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">main</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">body</span><span class="dt">&gt;</span></span>
<span><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">html</span><span class="dt">&gt;</span></span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">&lt;!-- templates/blog/list.html --&gt;</span></span>
<span><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>{% extends &quot;base.html&quot; %}</span>
<span><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>{% block title %}Посты{% endblock %}</span>
<span><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>{% block content %}</span>
<span><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">h1</span><span class="dt">&gt;</span>Посты<span class="dt">&lt;/</span><span class="kw">h1</span><span class="dt">&gt;</span></span>
<span><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> {% for post in posts %}</span>
<span><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">a</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;{% url &#39;blog:post_detail&#39; post.pk %}&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> {{ post.title }}</span>
<span><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">a</span><span class="dt">&gt;</span></span>
<span><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a> {% endfor %}</span>
<span><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>{% endblock %}</span></code></pre></div>
<hr />
<h2>Статические файлы (Static)</h2>
<ul>
<li>Подключаются через <code>{% load static %}</code>.</li>
<li>Настройки в <code>settings.py</code>: <code>STATIC_URL</code>,
<code>STATICFILES_DIRS</code>.</li>
<li>В продакшне используется команда
<code>python manage.py collectstatic</code>.</li>
</ul>
<p>Статика — это CSS, JavaScript, изображения и другие файлы, которые не
генерируются view.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="co"># settings.py</span></span>
<span><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>STATIC_URL <span class="op">=</span> <span class="st">&quot;static/&quot;</span></span>
<span><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>STATICFILES_DIRS <span class="op">=</span> [BASE_DIR <span class="op">/</span> <span class="st">&quot;static&quot;</span>]</span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>{% load static %}</span>
<span><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">link</span><span class="ot"> rel</span><span class="op">=</span><span class="st">&quot;stylesheet&quot;</span><span class="ot"> href</span><span class="op">=</span><span class="st">&quot;{% static &#39;css/app.css&#39; %}&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">script</span><span class="ot"> src</span><span class="op">=</span><span class="st">&quot;{% static &#39;js/app.js&#39; %}&quot;</span><span class="dt">&gt;&lt;/</span><span class="kw">script</span><span class="dt">&gt;</span></span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode bash"><code class="sourceCode bash"><span><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="ex">python</span> manage.py collectstatic</span></code></pre></div>
</div>
<p class="back">Назад: <a href="#s-fbv">Django FBV</a> · <a href="#s-cbv-types">Django CBV</a> · <a href="#s-django-urls">Django URLs</a> · <a href="#s-django-templates">Django templates</a></p>
</article>
<article class="note full-note" id="d-django-forms">
<h3>Django: Работа с формами и CUD</h3>
<p class="note-source">Полный текст исходной заметки.</p>
<div class="embedded-note" data-note-title="Работа с формами и CUD">
<h1>Django: Работа с формами и
CUD</h1>
<p><strong>CUD (Create, Update, Delete)</strong> — операции изменения
данных: создать, обновить, удалить.</p>
<p>Форма в Django — это слой между HTML-формой и Python-кодом. Она
принимает данные от пользователя, проверяет их, приводит к нормальным
типам и помогает сохранить результат.</p>
<h2>Типы форм</h2>
<ul>
<li><strong><code>forms.Form</code></strong> — обычная форма, не
привязанная к модели. Подходит для поиска, обратной связи, фильтров,
отправки email.</li>
<li><strong><code>forms.ModelForm</code></strong> — форма на основе
модели. Django сам создаёт поля формы по полям модели и умеет сохранять
объект через <code>form.save()</code>.</li>
</ul>
<p>Если данные не нужно сохранять в таблицу напрямую — чаще подходит
<code>forms.Form</code>. Если форма создаёт или редактирует модель —
проще использовать <code>forms.ModelForm</code>.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django <span class="im">import</span> forms</span>
<span><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> .models <span class="im">import</span> Blog</span>
<span><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> BlogForm(forms.ModelForm):</span>
<span><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">class</span> Meta:</span>
<span><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span> Blog</span>
<span><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> fields <span class="op">=</span> [<span class="st">&quot;name&quot;</span>, <span class="st">&quot;tagline&quot;</span>]</span>
<span><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> widgets <span class="op">=</span> {</span>
<span><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;name&quot;</span>: forms.TextInput(attrs<span class="op">=</span>{<span class="st">&quot;class&quot;</span>: <span class="st">&quot;form-control&quot;</span>})</span>
<span><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> }</span></code></pre></div>
<hr />
<h2>Жизненный цикл формы в
представлении</h2>
<p>Обычно view работает так: 1. При <code>GET</code> создаёт пустую
форму и показывает страницу. 2. При <code>POST</code> создаёт форму из
<code>request.POST</code>. 3. Проверяет данные через
<code>is_valid()</code>. 4. Если данные корректные — выполняет действие
и делает <code>redirect</code>. 5. Если есть ошибки — снова показывает
форму с ошибками.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.shortcuts <span class="im">import</span> redirect, render</span>
<span><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> contact_view(request):</span>
<span><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> request.method <span class="op">==</span> <span class="st">&quot;POST&quot;</span>:</span>
<span><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> form <span class="op">=</span> ContactForm(request.POST)</span>
<span><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> form.is_valid():</span>
<span><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> data <span class="op">=</span> form.cleaned_data</span>
<span><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> send_email(data[<span class="st">&quot;email&quot;</span>], data[<span class="st">&quot;message&quot;</span>])</span>
<span><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> redirect(<span class="st">&quot;success&quot;</span>)</span>
<span><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> form <span class="op">=</span> ContactForm()</span>
<span><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> render(request, <span class="st">&quot;contact.html&quot;</span>, {<span class="st">&quot;form&quot;</span>: form})</span></code></pre></div>
<hr />
<h2>Валидация и очистка данных</h2>
<ul>
<li><strong><code>is_valid()</code></strong> — запускает проверку формы.
После него появляются <code>form.cleaned_data</code> и
<code>form.errors</code>.</li>
<li><strong>Автоматическая очистка</strong> — Django превращает строки
из HTML-формы в Python-типы: число в <code>int</code>, дату в
<code>date</code>, email в строку после проверки формата.</li>
<li><strong><code>clean_&lt;fieldname&gt;()</code></strong> — проверка
одного конкретного поля.</li>
<li><strong><code>clean()</code></strong> — проверка всей формы, когда
нужно сравнить несколько полей между собой.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django <span class="im">import</span> forms</span>
<span><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> RegisterForm(forms.Form):</span>
<span><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> username <span class="op">=</span> forms.CharField(max_length<span class="op">=</span><span class="dv">50</span>)</span>
<span><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> password <span class="op">=</span> forms.CharField(widget<span class="op">=</span>forms.PasswordInput)</span>
<span><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> password_repeat <span class="op">=</span> forms.CharField(widget<span class="op">=</span>forms.PasswordInput)</span>
<span><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> clean_username(<span class="va">self</span>):</span>
<span><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> username <span class="op">=</span> <span class="va">self</span>.cleaned_data[<span class="st">&quot;username&quot;</span>]</span>
<span><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> username.lower() <span class="op">==</span> <span class="st">&quot;admin&quot;</span>:</span>
<span><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> forms.ValidationError(<span class="st">&quot;Это имя нельзя использовать&quot;</span>)</span>
<span><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> username</span>
<span><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">def</span> clean(<span class="va">self</span>):</span>
<span><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a> cleaned_data <span class="op">=</span> <span class="bu">super</span>().clean()</span>
<span><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a> password <span class="op">=</span> cleaned_data.get(<span class="st">&quot;password&quot;</span>)</span>
<span><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a> password_repeat <span class="op">=</span> cleaned_data.get(<span class="st">&quot;password_repeat&quot;</span>)</span>
<span><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> password <span class="kw">and</span> password_repeat <span class="kw">and</span> password <span class="op">!=</span> password_repeat:</span>
<span><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">raise</span> forms.ValidationError(<span class="st">&quot;Пароли не совпадают&quot;</span>)</span>
<span><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> cleaned_data</span></code></pre></div>
<hr />
<h2>Generic Editing Views (CBV
для CUD)</h2>
<p>Django предоставляет готовые классы для типовых страниц
редактирования: - <strong><code>CreateView</code></strong> — создать
объект. - <strong><code>UpdateView</code></strong> — изменить объект;
обычно ищет его по <code>pk</code> из URL. -
<strong><code>DeleteView</code></strong> — удалить объект; требует
подтверждения через <code>POST</code>.</p>
<p>Главная идея: вместо ручной обработки <code>GET</code>,
<code>POST</code>, <code>is_valid()</code> и <code>save()</code> можно
описать модель, форму, шаблон и URL после успеха.</p>
<div class="sourceCode"><pre
class="sourceCode python"><code class="sourceCode python"><span><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.urls <span class="im">import</span> reverse_lazy</span>
<span><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> django.views.generic <span class="im">import</span> CreateView, DeleteView, UpdateView</span>
<span><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> .forms <span class="im">import</span> BlogForm</span>
<span><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> .models <span class="im">import</span> Blog</span>
<span><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> BlogCreateView(CreateView):</span>
<span><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span> Blog</span>
<span><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> form_class <span class="op">=</span> BlogForm</span>
<span><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> template_name <span class="op">=</span> <span class="st">&quot;blog/blog_form.html&quot;</span></span>
<span><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> success_url <span class="op">=</span> reverse_lazy(<span class="st">&quot;blog_list&quot;</span>)</span>
<span><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> BlogUpdateView(UpdateView):</span>
<span><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span> Blog</span>
<span><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a> form_class <span class="op">=</span> BlogForm</span>
<span><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> template_name <span class="op">=</span> <span class="st">&quot;blog/blog_form.html&quot;</span></span>
<span><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> success_url <span class="op">=</span> reverse_lazy(<span class="st">&quot;blog_list&quot;</span>)</span>
<span><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> BlogDeleteView(DeleteView):</span>
<span><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a> model <span class="op">=</span> Blog</span>
<span><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> template_name <span class="op">=</span> <span class="st">&quot;blog/blog_confirm_delete.html&quot;</span></span>
<span><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a> success_url <span class="op">=</span> reverse_lazy(<span class="st">&quot;blog_list&quot;</span>)</span></code></pre></div>
<h2>Отображение в шаблоне</h2>
<ul>
<li><code>{{ form.as_p }}</code> — быстро вывести поля формы в
<code>&lt;p&gt;</code>.</li>
<li><code>{{ form.as_table }}</code> — вывести поля как строки
таблицы.</li>
<li>Ручное управление нужно, когда важна HTML-разметка, CSS-классы или
отдельный вывод ошибок.</li>
</ul>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> method</span><span class="op">=</span><span class="st">&quot;post&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> {% csrf_token %}</span>
<span><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> {{ form.as_p }}</span>
<span><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;submit&quot;</span><span class="dt">&gt;</span>Сохранить<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">form</span><span class="ot"> method</span><span class="op">=</span><span class="st">&quot;post&quot;</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> {% csrf_token %}</span>
<span><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> {% for field in form %}</span>
<span><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> {{ field.label_tag }}</span>
<span><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> {{ field }}</span>
<span><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> {{ field.errors }}</span>
<span><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> {% endfor %}</span>
<span><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span>
<span><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&lt;</span><span class="kw">button</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;submit&quot;</span><span class="dt">&gt;</span>Сохранить<span class="dt">&lt;/</span><span class="kw">button</span><span class="dt">&gt;</span></span>
<span><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">form</span><span class="dt">&gt;</span></span></code></pre></div>
<h3>CSRF-токен</h3>
<p><strong>CSRF (Cross-Site Request Forgery)</strong> — атака, при
которой чужой сайт пытается отправить запрос от имени уже
авторизованного пользователя.</p>
<p>Пример: пользователь залогинен в Django-приложении. Он открывает
вредную страницу, а она незаметно отправляет <code>POST</code>-запрос на
удаление объекта. Без защиты браузер приложит cookies пользователя, и
сервер может принять запрос как настоящий.</p>
<p><code>{% csrf_token %}</code> добавляет в форму скрытое поле с
защитным токеном:</p>
<div class="sourceCode"><pre
class="sourceCode html"><code class="sourceCode html"><span><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> type</span><span class="op">=</span><span class="st">&quot;hidden&quot;</span><span class="ot"> name</span><span class="op">=</span><span class="st">&quot;csrfmiddlewaretoken&quot;</span><span class="ot"> value</span><span class="op">=</span><span class="st">&quot;...&quot;</span><span class="dt">&gt;</span></span></code></pre></div>
<p>При <code>POST</code> Django сверяет токен из формы с токеном
пользователя. Если токена нет или он неправильный, запрос
отклоняется.</p>
<p>Короткое правило: во все HTML-формы с <code>method="post"</code>
добавлять <code>{% csrf_token %}</code>.</p>
</div>
<p class="back">Назад: <a href="#s-django-forms">Django forms</a></p>
</article>
</section>
<p class="back"><a href="#top">Наверх</a></p>
</main>
</body>
</html>