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 и пагинация

  • 0 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован.

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

Свежие комментарии

    Рубрики

    About Author 01.

    blank
    Roman Spiridonov

    Моя специальность - Back-end Developer, Software Engineer Python. Мне 39 лет, я работаю в области информационных технологий более 5 лет. Опыт программирования на Python более 3 лет. На Django более 2 лет.

    Categories 05.

    © Speccy 2022 / All rights reserved

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