# Модели и шаблоны --- ## Словарь терминов ---- Проект - совокупность всего программного кода, составляющего разрабатываемый сайт. Физически он представляет собой папку, в которой находятся папки и файлы с исходным кодом. ---- папка Конфигурации (название условное) - пакет языка Python, содержащий модули, которые относятся к проекту целиком и задают его конфигурацию (в частности, ключевые настройки). Название этого пакета совпадает с названием проекта, и менять его не стоит — в противном случае придется вносить в код обширные правки. ---- Пакет в Python – это каталог, включающий в себя другие каталоги и модули, но при этом дополнительно содержащий файл `__init__.py`. Пакеты используются для формирования пространства имен, что позволяет работать с модулями через указание уровня вложенности (через точку). ---- Приложение в терминологии Django — это отдельный фрагмент функциональности разрабатываемого сайта, более или менее независимый от других таких же фрагментов и входящий в состав проекта. Приложение может реализовывать работу всего сайта, его раздела или же какой-либо внутренней подсистемы сайта, используемой другими приложениями. Любое приложение представляется обычным пакетом Python. ---- Контроллер Django (он же view или представление) — это код, запускаемый при обращении по интернет-адресу определенного формата и в ответ выводящий на экран определенную веб-страницу. ---- Контроллер Django может представлять собой как функцию (контроллер-функция), так и класс (контроллер-класс). Первые более универсальны, но зачастую трудоемки в программировании, вторые позволяют выполнить типовые задачи, наподобие вывода списка каких-либо позиций, минимумом кода. ---- Путь — это часть интернет-адреса, находящаяся между адресом хоста и набором GET-параметров (например, интернет-адрес http://localhost:8000/bboard/34/edit/ содержит путь bboard/34/edit/). ---- Маршрутизатор - Важнейшей частью любого Web-фреймворка является механизм, отвечающий за маршрутизацию. В Django для этого используется свой небольшой eDSL, описывающий urlpatterns — набор образцов, с которыми сопоставляются пути из каждого входящего запроса. ---- Каждый образец состоит из описания статических и динамических частей пути в виде строки или регулярного выражения. Статические части пути в образце просто проверяются на равенство соответствующим участкам пути в запросе. Динамические же участки пути позволяют захватывать значения и передавать во view в качестве аргументов. ---- Как только выясняется, что путь или его начало совпали с образцом, происходит либо вызов view, либо передача оставшейся части пути во вложенный блок urlpatterns. В большинстве больших Django-проектов urlpatterns вложены друг в друга и представляют собой дерево. --- ## Модели ---- Модель — это класс, описывающий определенную таблицу в базе данных, в частности набор имеющихся в ней полей. Отдельный экземпляр класса модели представляет отдельную запись таблицы, позволяет получать значения, хранящиеся в полях записи, и заносить в них новые значения. Модель никак не привязана к конкретному формату базы данных. ---- Объявим модель Bb, представляющую объявление, со следующими полями: ``` * title — заголовок объявления с названием продаваемого товара (тип — строковый, длина — 50 символов). Поле, обязательное к заполнению; * content — сам текст объявления, описание товара (тип — текст); * price — цена (тип — вещественное число); * published— дата публикации (тип— временная отметка, значение по умолчанию — текущие дата и время, индексированное). ``` ---- Завершим работу отладочного веб-сервера. Откроем модуль models.py пакета приложения bboard и запишем в него следующий код ``` from django.db import models class Bb(models.Model): title = models.CharField(max_length=50) content = models.TextField(null=True, blank=True) price = models.FloatField(null=True, blank=True) published = models.DateTimeField(auto_now_add=True, db_index=True) ``` ---- Рассмотрим использованные нами классы полей и их параметры: * CharField * TextField * FloatField * DateTimeField ---- Миграция — это программа, сгенерированная на основе заданной модели и создающая в базе данных все описанные этой моделью структуры: таблицу, поля, индексы, правила и связи. ---- Чтобы сгенерировать миграцию на основе модели `Bb`, переключимся в командную строку, проверим, остановлен ли отладочный веб-сервер и находимся ли мы в папке проекта, и дадим команду: `python manage.py makemigrations bboard` ---- Модуль с кодом нашей первой миграции будет иметь имя `OOO1_initial.py.` ``` from django.db inport migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Bb’, fields=[ ... ], ) , ] ``` ---- ``` fields=[ (’id’, models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose name=’ID’)), (’title’, models.CharField(max_length=50)), (’content', models.TextField(blank=True, null=True)), ('price', models.FloatField(blank=True, null=True)), ('published', models.DateTimeField(auto_now_add=True, db_index=True)),], ``` ---- Миграция при выполнении порождает команды на языке SQL, создающие в базе необходимые структуры. Посмотрим на SQL-код, создаваемый нашей миграцией, задав в командной строке команду: `python manage.py sqlmigrate bboard 0001` ---- После команды sqlmigrate, выводящей SQL-код, мы поставили имя приложения и числовую часть имени модуля с миграцией: ``` BEGIN; — Create model Bb CREATE TABLE "bboard_bb" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(50) NOT NULL, "content" text NULL, "price" real NULL, "published" datetime NOT NULL ); CREATE INDEX "bboard_bb_published_58fde1b5" ON "bboard_bb" ("published"); COMMIT; ``` ---- Для выполнения первой миграции наберем в командной строке команду: `python manage.py migrate` ---- ![](https://i.imgur.com/ow1dx2b.png) ---- Создадим первое объявление — первую запись модели Bb: :::info Для операций дальше нужно активировать консоль Django командой `python manage.py shell` ::: ``` from bboard.models import Bb bl = Bb(title='Дача', content='Общество "Двухэтажники". Два этажа, кирпич, свет, газ, канализация', price=500000) ``` ---- Чтобы сохранить запись в базу данных, достаточно вызвать у нее метод `save()` о без параметров: `bl.save()` ---- Проверим, сохранилось ли наше первое объявление, получив значение ключевого поля: `bl.pk` ---- Мы можем обратиться к любому полю записи, воспользовавшись соответствующимему атрибутом класса модели: ``` bl.title ’Дача’ bl.content 'Общество "Двухэтажники". Два этажа, кирпич, свет, газ, канализация' bl.price 500000 bl.published datetime.datetime(2019, 11, 21, 15, 17, 31, 695200, tzinfo=<UTC>) bl.id 1 ``` ---- Создадим еще одно объявление: ``` b2 = Bb() b2.title = 'Автомобиль' b2.content = "Жигули" b2.save () b2.рк 2 ``` ---- Давайте дополним ее: ``` b2.content = '"Жигули", 1980 года, ржавая, некрашеная, сильно битая' b2.save() ``` ---- Добавим еще одно объявление: ``` Bb.objects.create(title='Дом', content='Трехэтажный, кирпич', price=50000000) <Bb: Bb obj ect (3)> ``` ---- Все классы моделей поддерживают атрибут класса objects. Он хранит диспетчер записей — объект, представляющий все имеющиеся в модели записи и являющийся экземпляром класса Manager. Метод `create()` диспетчера записей создает новую запись модели, принимая в качестве набора именованных параметров значения ее полей, сразу же сохраняет ее ивозвращает в качестве результата. ---- Выведем ключи и заголовки всех объявлений, имеющихся в модели Bb: ``` for b in Bb.objects.all(): print(b.pk, ': ', b.title) ``` ---- Метод `all` диспетчера записей возвращает набор записей— последовательность из всех записей модели, которую можно перебрать в цикле. ---- Отсортируем записи модели по заголовку: ``` for b in Bb.objects.order_by('title'): print(b.pk, ': ', b.title) ``` ---- Метод `order_by()` диспетчера записей сортирует записи по значению поля, имя которого указано в параметре, и сразу же возвращает набор записей, получившийся в результате сортировки. Извлечем объявления о продаже домов: ``` for b in Bb.objects.filter(title='Дом'): print(b.pk, ': ', b.title) ``` ---- Метод `filter()` диспетчера записей фильтрует записи по заданным критериям. ---- Объявление о продаже автомобиля имеет ключ 2. Отыщем его: ``` b = Bb.objects.get(pk=2) b.title ``` ---- Метод `get()` диспетчера записей имеет то же назначение, что и метод `filter()`, и вызывается аналогичным образом. Однако он ищет не все подходящие записи, а лишь одну и возвращает ее в качестве результата. ---- Давайте удалим эту запись: `b.delete()` Метод delete () модели, как уже понятно, удаляет текущую запись и возвращает сведения о количестве удаленных записей, обычно малополезные. :::info Чтобы выйти из консоли, наберите команду exit() ::: ---- Откроем модуль views.py пакета приложения bboard и исправим хранящийся в нем код: ``` from django.http import HttpResponse from .models import Bb def index(request): s = 'Список объявлений\r\n\r\n\r\n' for bb in Bb.objects.order_by('-published'): s += bb.title + '\r\n' + bb.content + '\r\n\r\n' return HttpResponse(s, content_type='text/plain; charset=utf-8') ``` ---- ![](https://i.imgur.com/HF2Kd5f.png) ---- Рекомендации при создании базы данных: * Каждая модель должна представлять только один упрощенный объект реального мира. * При создании связей таблиц (об этом поговорим на будующих занятиях), убедитесь, что эта связь не заставляет вас дублировать записи. * В случае, если связь требует дублировать записи таблицы, то выделите "разводящую" таблицу. --- ## Шаблоны ---- Шаблон — это образец для генерирования веб-страницы, отправляемой клиенту в составе ответа. Генерированием страниц на основе шаблонов занимается подсистема Django, называемая шаблонизатором. ---- Шаблон Django — это файл с HTML-кодом страницы, содержащий особые команды шаблонизатора: директивы, теги и фильтры. Директивы указывают поместить в заданное место HTML-кода какое-либо значение, теги управляют генерированием содержимого, а фильтры выполняют какие-либо преобразования указанного значения перед выводом. ---- В файле `settings.py` нахдим строку `from pathlib import Path` и заменяем её на `from pathlib import Path, os` ---- Далее нам неоюнодимо добавить строку `os.path.join(BASE_DIR, 'templates')` в параметр `'DIRS': []`. Это позволит задать папку для шаблонов по умолчанию. ``` TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': {...}, }, ] ``` ---- Создадим в папке пакета приложения bboard папку templates, а в ней — вложенную папку bboard. Сохраним в этой папке наш первый шаблон index.html ``` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Главная :: Доска объявлений</title> </head> <body> <h1>0бъявления</h1> {% for bb in bbs %} <div> <h2>{{ bb.title }}</h2> <p>{{ bb.content }}</p> <p>{{ bb.published|date:"d.m.Y H:i:s" }}</p> </div> {% endfor %} </body> </html> ``` ---- Рассмотрим первый тег шаблонизатора: ``` {% for bb in bbs %} ... {% endfor %} ``` ---- Теперь познакомимся с директивой шаблонизатора: `{{ bb.title }}` ---- И, наконец, фильтр date: `<р>{{ bb.published|date: "d.m.Y H:i:s" }}</p>` ---- Откроем модуль `views.py` пакета приложения bboard и внесем исправления в его код. ``` from django.http import HttpResponse from django.template import loader from .models import Bb def index(request) : template = loader.get_template('bboard/index.html') bbs = Bb.objects.order_by('-published') context = {'bbs': bbs} return HttpResponse(template.render(context, request)) ``` ---- ![](https://i.imgur.com/G2KFTcx.png) ---- В коде контроллера `index()` для рендеринга мы использовали низкоуровневые инструменты, несколько усложнив код. Но Django предоставляет средства более высокого уровня — функции-сокращения. Так, функция-сокращение `render()` из модуля django.shortcuts выполняет и загрузку, и рендеринг шаблона. ---- Попробуем ее в деле, исправив код модуля `views.py`: ``` from django.shortcuts import render from .models import Bb def index(request): bbs = Bb.objects.order_by('-published') return render(request, 'bboard/index.html', {'bbs': bbs}) ```
{"metaMigratedAt":"2023-06-16T11:11:32.292Z","metaMigratedFrom":"Content","title":"Модели и шаблоны","breaks":true,"contributors":"[{\"id\":\"0d39d5a3-691d-488c-8f1e-1a0fb0be4f13\",\"add\":15521,\"del\":2656}]"}
    429 views