Share This
Связаться со мной
Крути в низ
Categories
//🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Изучаем основные принципы работы фреймворка, разрабатывая личный сайт с резюме, портфолио, блогом и контактной формой.

flask za chas chast 1 sozdaem adaptivnyj sajt dlja github pages d51bf88 - 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Flask – микрофреймворк: это означает, что в стандартную поставку входит только самое необходимое, а все остальное при необходимости легко подключается. Поэтому приставка «микро» не должна вводить в заблуждение – при желании на Flask можно реализовать серьезный, масштабируемый проект. А для реализации таких небольших веб-приложений, как наш сайт, Flask подходит как нельзя лучше.

Обзор проекта

Готовый сайт находится здесь. У сайта несколько секций:

  • Главная
  • Резюме
  • Портфолио
  • Блог
  • Контакты

Переключение между секциями создает иллюзию многостраничности, но в «живой» версии сайт – одностраничный. Бэкенд включает в себя модуль Frozen Flask, который превращает приложение в генератор статических сайтов (SSG): все страницы, необходимые для адекватного представления сайта в статической версии, создаются автоматически.

flask za chas chast 1 sozdaem adaptivnyj sajt dlja github pages 19702cf - 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Готовый сайт на GitHub Pages

Фронтенд сделан на Bootstrap с несколькими дополнительными JS скриптами – например, записи в блоге фильтруются (без перезагрузки страницы) по тегам с помощью скрипта isotope.js, при этом теги для фильтра скрипт получает из расширения Flask – FlatPages. Записи в блоге и карточки в портфолио можно перелистывать свайпом, без перезагрузки страницы. Bootstrap обеспечивает адаптивность: сайт одинаково хорошо смотрится на широкоформатном мониторе и на смартфоне.

Первый этап

На этом этапе мы установим Flask вместе со всеми нужными расширениями и зависимостями, напишем первый вариант кода для блога и сделаем два простейших шаблона.

Установка Flask

Сначала нужно создать папку для проекта и активировать виртуальное окружение:

         mkdir flask_project cd flask_project mkdir .venv pipenv shell      

Папка .venv – служебная: менеджер pipenv автоматически разместит все нужные зависимости там, и они не будут загромождать корневую директорию проекта. Виртуальное окружение активируется командой pipenv shell, для выхода нужно выполнить exit.

Установим Flask и все необходимые зависимости. Для этого сохраните этот список в файле requirements.txt:

         Click==7.0 Flask==1.1.1 Flask-FlatPages==0.7.1 Frozen-Flask==0.15 itsdangerous==1.1.0 Jinja2==2.10.3 Markdown==3.1.1 MarkupSafe==1.1.1 Pygments==2.4.2 PyYAML==5.1.2 Werkzeug==0.16.0      

Поместите файл в директорию проекта и выполните команду:

         pipenv install -r requirements.txt     

Для быстрого знакомства с принципами работы Flask мы сначала создадим тестовый блог, а затем перейдем к реализации нашего проекта.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» Интересно, перейти к каналу

Структура Flask проекта

Начнем с создания структуры проекта:

         ├── mysite.py ├── content │   └── posts │   	 ├── static └── templates      

В папке content/posts будут размещаться Markdown файлы, в templates – шаблоны, в static – CSS стили, изображения и JS-скрипты. Весь код приложения мы напишем в файле mysite.py – сначала импортируем нужные модули, затем определим основные параметры, после пропишем маршруты к шаблонам и запустим сервер. Простейший вариант кода mysite.py выглядит так:

         import sys from flask import Flask, render_template from flask_flatpages import FlatPages, pygments_style_defs from flask_frozen import Freezer DEBUG = True FLATPAGES_AUTO_RELOAD = DEBUG FLATPAGES_EXTENSION = '.md' FLATPAGES_ROOT = 'content' POST_DIR = 'posts'   app = Flask(__name__) flatpages = FlatPages(app) freezer = Freezer(app) app.config.from_object(__name__)   @app.route("/") def index(): 	posts = [p for p in flatpages if p.path.startswith(POST_DIR)] 	posts.sort(key=lambda item: item['date'], reverse=True) 	return render_template('index.html', posts=posts, bigheader=True)   @app.route('/posts/<name>/') def post(name): 	path = '{}/{}'.format(POST_DIR, name) 	post = flatpages.get_or_404(path) 	return render_template('post.html', post=post)   if __name__ == "__main__": 	if len(sys.argv) > 1 and sys.argv[1] == "build":     	freezer.freeze() 	else:     	app.run(host='127.0.0.1', port=8000, debug=True)      

В это трудно поверить, но основной код блога действительно занимает всего 28 строк. Это возможно благодаря модулям FlatPages и Flask Frozen: первый избавляет от необходимости хранить посты в базе данных, проводит рендеринг Markdown-файлов в html, обеспечивает вывод записей и обращение к их свойствам, что будет заметно при создании шаблонов. Flask Frozen в действии мы увидим чуть позже: этот модуль берет на себя создание статической копии сайта – экспортирует html-файлы и все нужные ассеты (скрипты, CSS, изображения) в папку build.

Добавим в папку posts два-три тестовых поста – в YAML части обязательно должны быть метаданные title, date, description, потому что Jinja будет вызывать их в шаблонах. Markdown посты можно писать в любом редакторе – хоть в Блокноте, хоть в Sublime Text; можно обзавестись и специальным редактором – MarkdownPad для Windows, Mou для macOS.

Теперь создадим два простейших шаблона. Это код для index.html:

         {% block content %} <h2>Блог - тестовый запуск</h2>             {% for post in posts %}         	<small>{{ post.date }}</small>         	<p>           	<h3>             	{{ post.title }}           	</h3>         	<p>           	<i>{{ post.description }}</i>         	</p>          	<p>{{ post.html[:100]|safe }}</p>             	<a href="{{ url_for('post', name=post.path.replace('posts/', '')) }}"><span>Читать</span></a>         	</p>         	{% endfor %} {% endblock %}      

А это шаблон для вывода записи на отдельной странице post.html:

         {{ post.date }} {{ post.title }} {{ post.dеscription }} {{ post.html|safe }}      

Оба шаблона мы доработаем на следующем этапе, а пока запустим приложение python mysite.py и посмотрим на результат.

flask za chas chast 1 sozdaem adaptivnyj sajt dlja github pages 973cf33 - 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Главная страница блога

flask za chas chast 1 sozdaem adaptivnyj sajt dlja github pages 441d53b - 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Отдельная запись

Весь код и тестовый контент для этого этапа есть здесь.

Второй этап

На этом этапе мы сделаем первые шаблоны и подключим файл с настройками.

Шаблонизатор Jinja2

Flask использует шаблонизатор Jinja2. Синтаксис Jinja2 идентичен шаблонизатору Django и напоминает Python. Если вам еще не приходилось работать с Django, на этом этапе достаточно знать, что логика в Jinja2 заключается в такие фигурные скобки {% %}, а переменные – в такие {{ }}.

Шаблон Jinja2 представляет собой обычный html-файл, в котором блоки с логикой и переменными размещаются в уже упомянутых скобках. К шаблону можно подключать любые JS-скрипты, иконки, шрифты. Большое количество переменных можно передать в шаблон в виде словаря:

         @app.route("/") def index(): 	variables = {"title":"Это мой сайт",              	"description":"Разработчик, дизайнер, автор ИТ-курсов"              	"keywords":"Разработка на Python, курсы по Django"  	            }	 return render_template('index.html', **variables)      

В шаблоне index.html, в свою очередь, эти переменные можно вставить в нужные теги:

         <title>{{ title }}</title>   <meta content="{{ description }}" name="description">   <meta content="{{ keywords }}" name="keywords">      

Если же переменных очень много, имеет смысл вынести словарь в отдельный файл – в дальнейшем мы воспользуемся именно этим вариантом.

Jinja2 поддерживает наследование и включение шаблонов – это позволяет разбить шаблон объемной страницы на несколько секций, которые проще редактировать по отдельности. Наш сайт состоит из одной страницы с несколькими разделами, которые целесообразно вынести в отдельные шаблоны:

  • base.html
  • index.html
  • header.html
  • resume.html
  • counters.html
  • skills.html
  • interests.html
  • portfolio.html
  • card.html
  • blog.html
  • post.html
  • contacts.html

Jinja2 не диктует каких-то жестких правил: при желании можно обойтись всего двумя шаблонами – index.html и post.html. И, конечно, можно не выносить переменные в отдельный файл, а вписать весь текст резюме и портфолио прямо в index.html. Но поддерживать сайт проще, если хранить всю потенциально изменяемую информацию в Markdown-файлах и текстовом файле конфигурации – в этом случае для изменения данных нужно будет внести поправки только один раз: переменные в шаблонах обеспечат обновление текста во всех разделах сайта. Кроме того, ненужные разделы сайта очень просто удалить, если они находятся в отдельных шаблонах.

Первый шаблон, который мы создадим – base.html. Он будет получать переменные из бэкенда. Для передачи данных в шаблон создайте файл settings.txt и сохраните в нем словарь:

         {"site_url":"http://localhost:8000", "site_title": "John Doe: Python разработчик и автор контента", "description": "Джон Доу - портфолио, резюме и блог Python разработчика", "keywords": "Веб-разработка на Python, бэкенд на Django и Flask"}      

Теперь добавьте импорт json и загрузку данных из файла в mysite.py:

         @app.route("/") def index(): 	posts = [p for p in flatpages if p.path.startswith(POST_DIR)] 	posts.sort(key=lambda item: item['date'], reverse=True) 	with open('settings.txt', encoding='utf8') as config:     	data = config.read()     	settings = json.loads(data) 	return render_template('index.html', posts=posts, bigheader=True, **settings)      

Сохраните этот код в templates/base.html:

         <!DOCTYPE html> <html lang="ru"> <head>   <meta charset="utf-8">   <meta content="width=device-width, initial-scale=1.0" name="viewport">   <title>{{ site_title }}</title>   <meta content="{{ description }}" name="description">   <meta content="{{ keywords }}" name="keywords">   <link href="{{ url_for('static', filename='img/favicon.png') }}" rel="icon">   <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Raleway:300,300i,400,400i,500,500i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">   <link href="{{ url_for('static', filename='assets/bootstrap/css/bootstrap.min.css') }}" rel="stylesheet">   <link href="{{ url_for('static', filename='assets/bootstrap-icons/bootstrap-icons.css') }}" rel="stylesheet">   <link href="{{ url_for('static', filename='assets/boxicons/css/boxicons.min.css') }}" rel="stylesheet">   <link href="{{ url_for('static', filename='assets/glightbox/css/glightbox.min.css') }}" rel="stylesheet">   <link href="{{ url_for('static', filename='assets/remixicon/remixicon.css') }}" rel="stylesheet">   <link href="{{ url_for('static', filename='assets/swiper/swiper-bundle.min.css') }}" rel="stylesheet">   <script src="https://kit.fontawesome.com/69e2443572.js" crossorigin="anonymous"></script>   <link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet"> </head> <body>   <main id="main"> {% block content %} <!-- вложенные шаблоны --> {% endblock %}   </main>   <!-- Скрипты -->   <script src="{{ url_for('static', filename='assets/purecounter/purecounter.js') }}"></script>   <script src="{{ url_for('static', filename='assets/bootstrap/js/bootstrap.bundle.min.js') }}"></script>   <script src="{{ url_for('static', filename='assets/glightbox/js/glightbox.min.js') }}"></script>   <script src="{{ url_for('static', filename='assets/isotope-layout/isotope.pkgd.min.js') }}"></script>   <script src="{{ url_for('static', filename='assets/swiper/swiper-bundle.min.js') }}"></script>   <script src="{{ url_for('static', filename='assets/waypoints/noframework.waypoints.js') }}"></script>   <script src="{{ url_for('static', filename='js/main.js') }}"></script> </body> </html>      

А этот – в templates/index.html:

         {% extends "base.html" %} {% block content %} <h1>Здесь будет сайт</h1> {% endblock %}      

Сохраните в папке static все эти ассеты. Теперь можно запускать сервер python mysite.py– скелет сайта готов:

flask za chas chast 1 sozdaem adaptivnyj sajt dlja github pages 646f08f - 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Значения тегов title, description и keywords взяты из файла settings.txt

Перейдем к созданию первого шаблона, расширяющего index.html – header.html. Добавьте переменные в файл settings.txt:

         "site_url":"http://localhost:8000", "tag1":"Разработчик и автор", "tag2":"курсов по Django", "sect1":"Главная", "sect2":"Резюме", "sect3":"Портфолио", "sect4":"Блог", "sect5":"Контакты", "telegram":"https://t.me/johndoe", "facebook":"ttps://facebook.com/john.doe", "vk":"https://vk.com/john_doe", "email":"mailto:john_doe@gmail.com"      

И отредактируйте файл index.html – теперь он будет включать в себя header.html с помощью include:

         {% extends "base.html" %} {% block content %} {% block header %} {% include "header.html" %} {% endblock %} {% endblock %}      

Перезагрузите страницу:

flask za chas chast 1 sozdaem adaptivnyj sajt dlja github pages 56d9596 - 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages

Хэдер включен в index.html

Весь код и контент для этого этапа – здесь. Во второй части туториала мы завершим работу над приложением и загрузим статическую копию сайта на GitHub Pages.

***

Материалы по теме

  • 🐍🚀 Django с нуля. Часть 1: пишем многопользовательский блог для клуба любителей задач Python
  • 🐍🚀 Django с нуля. Часть 2: регистрация, авторизация, ограничение доступа
  • 🐍🚀 Django с нуля. Часть 3: создание профилей, сжатие изображений, CRUD и пагинация

  • 20 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close