Flask – компактный фреймворк для быстрой разработки веб-приложений. Он предоставляет минимальную необходимую функциональность и не навязывает никаких строгих правил в отношении структуры и архитектуры приложения (как это делает Django).
Flask универсален – на его основе можно создавать сложные приложения и API, и в то же время он идеально подходит для разработки небольших проектов. Самый большой плюс Flask – на нем очень просто реализовать генератор статических сайтов.
Основные преимущества Flask:
- Минималистичность. Flask отличается небольшим размером – в нем есть все самое необходимое и нет ничего лишнего.
- Гибкость. Фреймворк не диктует определенных правил и позволяет разработчику сохранить полный контроль над структурой приложения.
- Простота в использовании. Он имеет несколько встроенных функций, которые позволяют сразу начать создавать полноценные веб-приложения, даже если у вас нет опыта в веб-разработке на Python. Например, у Flask есть встроенный сервер, поддержка сессий, обработчик форм, шаблонизатор.
- Интеграция с дополнительными библиотеками. Фреймворк очень просто интегрируется с многочисленными библиотеками, которые расширяют его функциональность. Это позволяет создать гибкий, масштабируемый проект для любой сферы.
- Простота тестирования. У Flask есть встроенный тестовый клиент, который максимально упрощает тестирование и отладку.
Установка
Flask лучше всего устанавливать в виртуальное окружение – это позволяет избежать появления ошибок, связанных с конфликтами версий различных библиотек и модулей. Выполните в cmd:
python -m venv fproject\venv
Перейдите в только что созданную директорию:
cd fproject
Активируйте окружение:
venv\scripts\activate
И установите Flask:
pip install flask
Активировать виртуальное окружение нужно перед каждым
сеансом работы с Flask.
Напишем приложение, которое будет выводить традиционное
приветствие Hello, World! в браузере. Сохраните
этот код в файле app.py в
директории fproject:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
Этот код создает объект приложения Flask с помощью класса Flask и присваивает его переменной app. Декоратор @app.route('/')
устанавливает маршрут для
главной страницы нашего приложения, а метод def hello()
определяет, что будет отображаться на этой странице.
if
проверяет,
__name__ == '__main__':
запускается ли данный файл как самостоятельное приложение, или импортируется как модуль. В
нашем случае он запускается как независимое приложение, поэтому вызывается метод app.run(), который запускает веб-сервер Flask.
Запустите приложение в командой строке:
(venv) C:\Users\User\fproject>app.py
Откройте адрес http://localhost:5000/
в браузере:
Flask по умолчанию использует порт 5000. При желании его можно
изменить на более привычный 8000:
app.run(port=8000)
Кроме того, можно включить режим отладки – тогда все
возникающие ошибки будут отображаться на странице браузера, а при внесении
любых изменений в файлы проекта сервер будет автоматически перезагружаться:
app.run(debug=True)
Для остановки сервера нажмите Ctrl+C.
Маршруты в Flask
Маршруты – это URL-адреса, по которым пользователи могут открывать определенные
страницы (разделы) веб-приложения. Маршруты в Flask определяются с помощью декоратора
@app.route()
. Для каждого маршрута можно
написать отдельную функцию представления, которая будет выполнять какие-то действия при переходе по определенному адресу. Рассмотрим пример:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return 'Это главная страница.'
@app.route('/about')
def about():
return 'Здесь будет информация об авторе сайта.'
@app.route('/blog')
def blog():
return 'Это блог с заметками о работе и увлечениях.'
if __name__ == '__main__':
app.run()
Сохраните код, запустите приложение, последовательно
откройте адреса:
- http://localhost:5000/
- http://localhost:5000/about
- http://localhost:5000/blog
Переменные в маршрутах
В URL можно передавать различные значения. Запустите этот
код и перейдите по адресу, например, http://localhost:5000/user/EvilAdmin
from flask import Flask
app = Flask(__name__)
@app.route('/user/<username>')
def user_profile(username):
return f"Это профиль пользователя {username}"
if __name__ == '__main__':
app.run()
Имя пользователя, переданное в качестве переменной, будет
показано на странице:
А так можно передать в маршруте целое число:
from flask import Flask
app = Flask(__name__)
@app.route('/user/<int:user_id>')
def user_profile(user_id):
return f"Это профиль пользователя с ID {user_id}"
if __name__ == '__main__':
app.run()
Перейдите по адресу, например, http://localhost:5000/user/5:
GET- и POST-запросы
GET и POST – это HTTP-запросы, которые используются для
отправки данных между клиентом и сервером.
GET-запрос применяют для получения данных от сервера. При
выполнении GET-запроса клиент отправляет запрос на сервер, а сервер возвращает
запрошенную информацию в ответ. GET-запросы могут содержать параметры в URL-адресе,
которые используются для передачи дополнительных данных.
POST-запрос используют для отправки данных на сервер. При
выполнении POST-запроса клиент отправляет данные на сервер, а сервер их обрабатывает. POST-запросы обычно применяют для отправки форм, с данными из которых нужно что-то сделать на бэкенде.
Рассмотрим простейший пример обработки формы авторизации.
Базы данных для хранения учетных записей у нас пока нет, поэтому в приведенном
ниже коде мы пропустим всю функциональность для проверки корректности логина и
пароля (мы рассмотрим этот вопрос позже, в одном из заданий):
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# проверка логина и пароля
return 'Вы вошли в систему!'
else:
return render_template('login.html')
if __name__ == '__main__':
app.run()
Маршрут @app.route('/login', methods=['GET', 'POST'])
обрабатывает и POST, и GET-запросы: в первом случае он отправит
данные на сервер, во втором – просто выведет страницу с формой авторизации.
Для вывода формы на странице сделаем простейший шаблон. Этот
код нужно сохранить в файле login.html, в директории templates (в этой папке Flask по
умолчанию ищет шаблоны):
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="row justify-content-center mt-5">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h1 class="text-center">Вход на сайт</h1>
</div>
<div class="card-body">
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger">
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
<form method="post">
<div class="mb-3">
<label for="username" class="form-label">Логин:</label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Пароль:</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary">Войти</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
Никакие CSS
стили к шаблону не подключены, поэтому он выглядит не слишком привлекательно.
Но шаблон работает, а форма получает логин и пароль:
Шаблонизатор Jinja2
Шаблоны в Flask
используются для динамического формирования веб-страниц. Шаблоны представляют
собой HTML страницы, в
которые можно передавать любые данные с бэкенда. К шаблонам можно подключать
любые CSS-фреймворки
типа Bootstrap и Tailwind,
и любые JS-скрипты.
Поведением шаблонов управляет шаблонизатор Jinja2 – он предоставляет
функциональность для создания условий, циклов, макросов, наследования и блоков.
Главные преимущества шаблонизатора:
- Может проводить различные операции с контентом самостоятельно, не обращаясь к бэкенду.
- Обеспечивает наследование дизайна и стилей от базового шаблона.
Наследование работает так:
- Базовый шаблон, который обычно называется base.html, содержит общую разметку для сайта.
- В base.html подключаются локальные и CDN-фреймворки (CSS, JS), задаются фоновые изображения и фавикон.
- Дочерние шаблоны наследуют этот базовый шаблон и дополняют его своим собственным контентом.
Продемонстрируем наследование на примере. Сохраните в папке templates два файла. Это
содержимое файла base.html – в нем
подключается CSS-фреймворк
Bootstrap, кастомные
стили custom.css из
статической папки static,
иконки Font Awesome:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %}{% endblock %}</title>
<!-- Bootstrap стили -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- иконки fontawesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- кастомные стили -->
<link rel="stylesheet" href="{{ url_for('static', filename='custom.css') }}">
</head>
<body>
<!-- навигация -->
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container">
<a class="navbar-brand" href="#">Мой личный сайт</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNav" aria-controls="navbarNav"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end"
id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Обо мне</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- контент дочерних шаблонов -->
<div class="container my-3">
{% block content %}
{% endblock %}
</div>
</body>
</html>
Шаблон base.html также содержит верхнее меню для навигации по сайту – это меню будут наследовать все дочерние шаблоны. Вот пример дочернего шаблона about.html:
{% extends 'base.html' %}
{% block title %}Мое резюме{% endblock %}
{% block content %}
<div class="container my-5">
<h1 class="text-center mb-4">Мое резюме</h1>
<div class="row">
<div class="col-md-6">
<h2>Образование</h2>
<h4>Московский политехнический институт</h4>
<p class="mb-0">Бакалавр Computer Science</p>
<p class="text-muted">2016 - 2020</p>
</div>
<div class="col-md-6">
<h2>Опыт работы</h2>
<h4>Web Developer - XYZ компания</h4>
<p class="mb-0">2019 - н.в.</p>
<p class="text-muted">- Разработка и поддержка веб-приложений</p>
<p class="text-muted">- Работа с Python, Django, HTML/CSS, JavaScript, MySQL</p>
</div>
</div>
<div class="row mt-5">
<div class="col-md-6">
<h2>Навыки</h2>
<ul class="list-group">
<li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> Python</li>
<li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> Django</li>
<li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> HTML/CSS</li>
<li class="list-group-item border-0 py-1"><i class="fa fa-cog"></i> JavaScript</li>
</ul>
</div>
<div class="col-md-6">
<h2>Проекты</h2>
<h4>Сайт для продажи автомобилей</h4>
<p class="text-muted mb-0">- Разработка сайта с использованием Django</p>
<p class="text-muted">- Интеграция с API маркетплейса для получения данных об автомобилях</p>
<h4>Игровой блог</h4>
<p class="text-muted mb-0">- Разработка блога с использованием Django</p>
<p class="text-muted">- Возможность создавать учeтные записи пользователей и писать комментарии</p>
</div>
</div>
<div class="row mt-5">
<div class="col-md-6">
<h2>Контакты</h2>
<p class="mb-0"><i class="fa fa-phone"></i> Телефон: +990123456789</p>
<p class="mb-0"><i class="fa fa-envelope"></i> Email: example@example.com</p>
<p class="mb-0"><i class="fa fa-github"> GitHub: <a href="https://github.com/example"></i>example</a></p>
</div>
<div class="col-md-6">
<h2>Языки</h2>
<ul class="list-group">
<li class="list-group-item border-0 py-1"><i class="fa fa-check-circle"></i> Английский (C1)</li>
<li class="list-group-item border-0 py-1"><i class="fa fa-check-circle"></i> Немецкий (B2)</li>
<li class="list-group-item border-0 py-1"><i class="fa fa-check-circle"></i> Русский (родной)</li>
</ul>
</div>
</div>
</div>
{% endblock %}
Фон страницы шаблонизатор берет из файла static/customs.css:
body {
background-color: #e5e5e5;
}
А код для вывода страницы выглядит так:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/about')
def about():
return render_template('about.html')
if __name__ == '__main__':
app.run(debug=True)
Запустите приложение, откройте адрес http://localhost:5000/about:
Работа с базой данных
Для работы с базами данных в Flask удобно использовать ORM SQLAlchemy.
Как уже упоминалось в предыдущей главе о SQLite, ORM играет роль своеобразной прослойки между приложением и СУБД SQLite, и позволяет работать
с базами без использования языка SQL. Надо заметить, что работать с базами данных в SQLAlchemy
немного сложнее, чем в Django ORM,
но гораздо проще, чем в чистом Python.
Начнем с установки SQLAlchemy в виртуальное окружение:
pip install flask-sqlalchemy
В SQLAlchemy основой для создания таблиц в базе данных
служат модели (специальные классы). Поля классов определяют структуру таблицы, которая будет
использоваться для хранения информации в базе данных. В полях классов можно
задавать типы данных, которые соответствуют типам данных в БД,
например, String для хранения строк, Integer для целых чисел, Float для
плавающих чисел и т.д.
SQLAlchemy, как и другие ORM, очень упрощает создание связей между таблицами. В
приведенном ниже примере используется связь один ко многим (ForeignKey),
поскольку у одного исполнителя может быть несколько альбомов, а в одном альбоме
всегда будет несколько треков:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Artist(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
class Album(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
year = db.Column(db.String(4), nullable=False)
artist_id = db.Column(db.Integer, db.ForeignKey('artist.id'), nullable=False)
artist = db.relationship('Artist', backref=db.backref('albums', lazy=True))
class Song(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
length = db.Column(db.String(4), nullable=False)
track_number = db.Column(db.Integer, nullable=False)
album_id = db.Column(db.Integer, db.ForeignKey('album.id'), nullable=False)
album = db.relationship('Album', backref=db.backref('songs', lazy=True))
Сохраните этот код в файле models.py – мы будем импортировать модели из него в главный файл приложения app.py и в скрипт create_db.py, который создает базу данных и заполняет ее тестовой информацией.
Код для create_db.py будет следующим:
from flask import Flask
from models import Artist, Album, Song, db
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///music.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
if __name__ == '__main__':
with app.app_context():
db.create_all()
# создаем тестовых исполнителей
artist1 = Artist(name='The Rolling Stones')
artist2 = Artist(name='Jefferson Airplane')
artist3 = Artist(name='Nine Inch Nails')
artist4 = Artist(name='Tool')
db.session.add_all([artist1, artist2, artist3, artist4])
db.session.commit()
# создаем тестовые альбомы
album1 = Album(title='Aftermath', year='1966', artist=artist1)
album2 = Album(title='Beggars Banquet', year='1968', artist=artist1)
album3 = Album(title='Surrealistic Pillow', year='1967', artist=artist2)
album4 = Album(title='Broken', year='1992', artist=artist3)
album5 = Album(title='The Fragile', year='1999', artist=artist3)
album6 = Album(title='Lateralus', year='2001', artist=artist4)
album7 = Album(title='AEnima', year='1996', artist=artist4)
album8 = Album(title='10,000 Days', year='2006', artist=artist4)
# создаем тестовые песни
song1 = Song(title='Paint it Black', length='4:20', track_number=1, album=album1)
song2 = Song(title='Sympathy For The Devil', length='3:53', track_number=2, album=album1)
song3 = Song(title='White Rabbit', length='3:42', track_number=5, album=album3)
song4 = Song(title='Wish', length='3:46', track_number=6, album=album4)
song5 = Song(title='Starfuckers, Inc.', length='5:00', track_number=1, album=album5)
song6 = Song(title='Schism', length='6:46', track_number=7, album=album6)
song7 = Song(title='Eulogy', length='8:29', track_number=3, album=album7)
song8 = Song(title='Vicarious', length='7:07', track_number=5, album=album8)
db.session.add_all([album1, album2, album3, album4, album5, album6, album7, album8, song1, song2, song3, song4, song5, song6, song7, song8])
db.session.commit()
Этот код наглядно демонстрирует, как именно создаются
записи, и какие между ними существуют связи. Чтобы создать и заполнить базу,
запустите файл в активированном виртуальном окружении:
(venv) C:\Users\User\fproject>create_db.py
В реальных приложениях базу данных удобнее заполнять,
например, с помощью модуля csv – мы рассмотрим этот метод ниже, в одном из заданий.
Главный файл приложения app.py
выглядит так:
from flask import Flask, render_template
from models import Artist, Album, Song, db
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///music.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# связываем приложение и экземпляр SQLAlchemy
db.init_app(app)
@app.route('/songs')
def songs():
songs_list = Song.query.all()
return render_template('songs.html', songs=songs_list)
if __name__ == '__main__':
app.run(debug=True)
Шаблон songs.html использует
тот же самый базовый base.html, что и предыдущий
пример:
{% extends 'base.html' %}
{% block title %}
Мои любимые песни
{% endblock %}
{% block content %}
<h1 class="mb-5">Мои любимые песни</h1>
<div class="card-columns">
<div class="row">
{% for song in songs %}
<div class="col-md-3">
<div class="card mb-3">
<div class="card-header fw-bold">{{ song.title }}</div>
<div class="card-body">
<p class="badge bg-primary text-wrap">{{ song.album.artist.name }}</p>
<p class="card-text">Альбом:
<strong>{{ song.album.title }}</strong></p>
<p class="card-text">Длина: {{ song.length }} минут</p>
<p class="card-text">Номер трека: {{ song.track_number }}</p>
<p class="card-text">Дата релиза: {{ song.album.year }}</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
После создания шаблона можно запустить приложение:
Практика
Задание 1
Напишите Flask-приложение,
которое выводит в шаблон index.html приветствие для
пользователя. Приветствие зависит от времени суток:
- С 6:00 до 12:00 – «Доброе утро»
- С 12:00 до 18:00 – «Добрый день»
- С 18:00 до 24:00 – «Добрый вечер»
- С 00:00 до 6:00 – «Доброй ночи»
Пример:
Решение:
Код для app.py выглядит
так:
from flask import Flask, render_template
import datetime
app = Flask(__name__)
@app.route('/')
def index():
now = datetime.datetime.now()
if now.hour >= 6 and now.hour < 12:
greeting = 'Доброе утро'
elif now.hour >= 12 and now.hour < 18:
greeting = 'Добрый день'
elif now.hour >= 18 and now.hour < 24:
greeting = 'Добрый вечер'
else:
greeting = 'Доброй ночи'
return render_template('index.html', greeting=greeting)
if __name__ == '__main__':
app.run(debug=True)
Для вывода приветствия используются шаблоны base.html и
index.html.
Задание 2
Напишите Flask-приложение, которое с помощью шаблона выводит пронумерованный список дел.
Пример:
Решение:
Это код приложения app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def task_list():
tasks = ["Выгулять собаку", "Погладить рубашку", "Зайти в супермаркет",
"Убрать на кухне", "Дописать статью", "Позвонить тимлиду"]
return render_template('task_list.html', tasks=tasks)
if __name__ == '__main__':
app.run()
Нумерацию в Jinja2
легко реализовать с помощью loop.index
:
<!DOCTYPE html>
<html>
<head>
<title>Мой список важных дел</title>
<style>
ul.no-bullets {
list-style-type: none;
}
</style>
</head>
<body>
<h1>Список дел на сегодня:</h1>
<ul class="no-bullets">
{% for task in tasks %}
<li>{{loop.index}}. {{ task }}</li>
{% endfor %}
</ul>
</body>
</html>
Задание 3
Напишите app.py и
шаблон welcome.html, которые выводят
различный контент для пользователей с разными правами доступа:
- Админ имеет полный доступ.
- Модератор может редактировать записи и комментарии.
- Рядовой пользователь может создавать записи от своего имени и просматривать френдленту.
Пример:
Решение:
В приложении app.py можно
определить только маршрут, вся функциональность по определению уровней доступа
находится в шаблоне:
<!DOCTYPE html>
<html>
<head>
<title>Личный кабинет</title>
</head>
<body>
{% if user_level == 'admin' %}
<h1>Привет, админ!</h1>
<p>У тебя есть доступ ко всем настройкам.</p>
<a href="#">Редактирование профилей</a>
<a href="#">Создание учетных записей</a>
<a href="#">Публикация статей</a>
{% elif user_level == 'moderator' %}
<h1>Привет, модератор!</h1>
<p>У тебе есть доступ к редактированию записей.</p>
<a href="#">Редактирование записей</a>
<a href="#">Модерация комментариев</a>
{% else %}
<h1>Привет, пользователь!</h1>
<p>У тебя нет доступа к редактированию контента и настроек.</p>
<a href="#">Новая запись</a>
<a href="#">Записи друзей</a>
{% endif %}
</body>
</html>
Задание 4
Напишите скрипт для создания и заполнения базы данных SQLite данными о книгах из
файла books.json, а также app.py и
шаблоны, которые выводят:
- Карточки с информацией о книгах.
- Карточку отдельной книги.
Пример:
Решение:
Напишем модель
Book и скрипт,
который создает и заполняет базу из json-файла. Затем создадим app.py с
двумя маршрутами – для вывода всех
книг, и для вывода отдельной
книги:
Задание 5
Для онлайн-магазина нужно написать модуль, который поможет
сотрудникам сделать инвентаризацию. Приложение состоит из базы данных, в
которой таблицы связаны сложными отношениями:
- Каждый производитель (Manufacturer) поставляет несколько типов товаров (Category) – ноутбуки, наушники, смартфоны и так далее.
- Одну и ту же категорию товаров могут производить несколько компаний.
- В каждой категории может быть множество товаров (Item).
Нужно реализовать вывод всех товаров по поставщикам и по
категориям. Все данные о товарах находятся в файле info.csv.
Пример:
Решение:
Файл models.py, описывающий структуру
базы данных, выглядит так:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
manufacturer_items = db.Table('manufacturer_items',
db.Column('manufacturer_id', db.Integer, db.ForeignKey('manufacturer.id'), primary_key=True),
db.Column('item_id', db.Integer, db.ForeignKey('item.id'), primary_key=True)
)
class Manufacturer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
items = db.relationship('Item', secondary=manufacturer_items, backref=db.backref('manufacturers', lazy=True))
def __repr__(self):
return '<Manufacturer %r>' % self.name
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
items = db.relationship('Item', backref='category')
def __repr__(self):
return '<Category %r>' % self.name
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
description = db.Column(db.String(255), nullable=False)
price = db.Column(db.Float, nullable=False)
quantity = db.Column(db.Integer, nullable=False, default=0)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False)
def __repr__(self):
return '<Item %r>' % self.
Для заполнения базы данными из файла info.csv напишем скрипт.
Приложение использует 4 шаблона:
- base.html
- index.html
- categories.html
- items.html
За вывод всех товаров определенного производителя в
отдельной категории отвечает эта функция:
@app.route('/<manufacturer>/<category>')
def show_items(manufacturer, category):
items = Item.query.join(Item.manufacturers).join(Item.category).\
filter(Manufacturer.name == manufacturer).\
filter(Category.name == category).all()
return render_template('items.html', manufacturer=manufacturer, category=category, items=items)
Задание 6
Для супермаркета нужно написать веб-приложение, которое
выводит список товаров на складе и позволяет добавлять новые.
Пример:
Решение:
Приложение состоит из файлов app.py, create_db.py и models.py.
Для добавления новых товаров используется шаблон add.html и
маршрут/функция add:
Задание 7
Для ветеринарной клиники нужно написать модуль учета
пациентов. В приложении должны быть функции добавления, редактирования и
удаления карточек.
Пример:
Решение:
Начнем с создания базы данных на основе моделей.
Функции add_patient,
edit_patient, delete_patient реализованы в приложении app.py.
Шаблоны add.html и
edit.html обеспечивают
добавление и редактирование карточек:
Задание 8
Напишите модуль аутентификации для Flask-приложения. Необходимо
реализовать:
- регистрацию;
- проверку учетных данных при входе;
- перенаправление на страницу профиля.
Пример:
Решение:
Проект включает в себя файлы app.py, create_db.py и models.py. Кроме того, модуль
использует шаблоны:
- base.html
- index.html
- login.html
- profile.html
- register.html
После регистрации и входа пользователь перенаправляется на страницу
своего профиля:
Задание 9
Напишите веб-приложение для хранения заметок. Необходимо
реализовать набор CRUD
операций – вывод, добавление, редактирование и удаление заметок. При создании
базы данных следует учесть, что заметка может относиться только к одной
категории, а в категории может быть множество заметок. На главной странице
выводятся все заметки, причем текст ограничивается первыми 300 символами.
Нажатие на ссылку «Далее» открывает страницу с полным текстом заметки.
Пример:
Решение:
База данных для приложения создается с помощью скрипта create_db.py на основе моделей.
CRUD операции
описаны в app.py. При нажатии на
название категории шаблон и маршрут category
выводят все заметки, относящиеся
к данной теме.
Задание 10
Напишите Flask приложение для
ведения блога. Каждая запись может иметь несколько тегов, но должна относиться
к одной категории. Как и в предыдущем задании, нужно реализовать просмотр отдельных
записей, добавление, редактирование и удаление постов. При выводе всех записей,
а также записей по категориям и тегам посты должны отображаться в порядке
убывания даты, т.е. самые новые находятся сверху.
Пример:
Решение:
Прежде всего разработаем
модели Tag, Category и
Post, а затем создадим
на их основе базу данных при помощи скрипта.
Приложение использует следующие шаблоны:
- base.html
- category.html
- delete_post.html
- edit_post.html
- index.html
- new_post.html
- post.html
- tag.html
CRUD операции реализованы в главном файле приложения app.py. Чтобы самые свежие
записи выводились первыми, в models.py мы
определили метод newest_first
. При нажатии на ссылку «Читать»
выводится полный текст записи:
Подведем итоги
Мы рассмотрели основные приемы разработки простых
веб-приложений на основе фреймворка Flask:
- Создание маршрутов и функций представления.
- Получение и обработку данных с фронтенда.
- CRUD операции.
- Основные возможности шаблонизатора Jinja2.
При создании приложений Flask для получения данных с фронтенда обычно используются формы WTF Forms.
В этой статье при решении заданий намеренно использовались HTML-формы – так процесс передачи и
обработки данных выглядит максимально понятно.
В следующей статье будем изучать NumPy.
Содержание самоучителя
- Особенности, сферы применения, установка, онлайн IDE
- Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
- Типы данных: преобразование и базовые операции
- Методы работы со строками
- Методы работы со списками и списковыми включениями
- Методы работы со словарями и генераторами словарей
- Методы работы с кортежами
- Методы работы со множествами
- Особенности цикла for
- Условный цикл while
- Функции с позиционными и именованными аргументами
- Анонимные функции
- Рекурсивные функции
- Функции высшего порядка, замыкания и декораторы
- Методы работы с файлами и файловой системой
- Регулярные выражения
- Основы скрапинга и парсинга
- Основы ООП: инкапсуляция и наследование
- Основы ООП: абстракция и полиморфизм
- Графический интерфейс на Tkinter
- Основы разработки игр на Pygame
- Основы работы с SQLite
- Основы веб-разработки на Flask
- Основы работы с NumPy
- Основы анализа данных с Pandas
***
Материалы по теме
- 🐍🥤 Flask за час. Часть 1: создаем адаптивный сайт для GitHub Pages
- 🐍📚 Создаем аналог LiveLib.ru на Flask. Часть 1: основы работы с SQLAlchemy
- 🐍⛩️🥤 Руководство для начинающих по шаблонам Jinja в Flask
Язык Python — это лёгкая дорога в программирование. А Flask помогает проложить путь в веб-разработку и научиться писать сайты с помощью Python. Получается, он такой же лёгкий, как и Python? Да, но есть нюансы.
В этой статье мы расскажем, что собой представляет фреймворк Flask, чем он лучше других и когда стоит выбрать именно его. А параллельно напишем свой первый небольшой сайт, где даже сможем публиковать посты.
Всё, что нужно знать о Flask в Python:
- Что это такое
- Чем он лучше других фреймворков
- Как установить Flask
- Как написать простой сайт
- Как создать блог
- Что нужно запомнить
Flask — это легковесный веб-фреймворк для языка Python, который предоставляет минимальный набор инструментов для создания веб-приложений. На нём можно сделать и лендинг, и многостраничный сайт с кучей плагинов и сервисов. Не фреймворк, а мечта!
У Flask много преимуществ, которые выделяют его среди других фреймворков:
- простой синтаксис — это всё-таки Python;
- удобные шаблоны — можно быстро создавать прототипы веб-приложений;
- куча инструментов для гибкой настройки сайтов под любые нужды.
Изображение: Wikimedia Commons
Ещё под Flask написаны сотни расширений и плагинов, которые добавляют дополнительные возможности — разные виды аутентификации, управление базами данных и работу с формами. И всё это — бесплатно и с открытым кодом.
Скачать фреймворк можно на официальном сайте. А если появятся вопросы по установке или настройке, то на помощь всегда придёт огромное сообщество Flask-разработчиков.
Flask входит в топ-15 самых популярных фреймворков для веб-разработки среди опытных программистов. Рядом с ним в рейтинге находятся Django, Express.js, Laravel, Ruby on Rails, Spring и ASP.NET. И, конечно, на Flask написано немало популярных сайтов. Вот лишь несколько примеров:
- Pinterest — одна из крупнейших социальных сетей для обмена изображениями и идеями;
- Netflix — один из крупнейших сервисов видеостриминга в мире;
- Uber — сервис вызова такси и автомобильного транспорта;
- Reddit — один из самых популярных новостных UGC-агрегаторов;
- Twilio — платформа для разработки приложений для обмена сообщениями.
В общем, его используют везде: и в малом, и в крупном бизнесе, и на частных предприятиях, и в госучреждениях.
У Flask есть ряд особенностей, за которые его любят веб-разработчики. Давайте их перечислим:
- Минимальный набор инструментов из коробки. Причём они не навязывают какую-то архитектуру или жёсткую структуру проектов. Разработчики сами решают, как и что они будут создавать.
- Гибкость. Работая с Flask, программист может выбирать только необходимые встроенные инструменты и подключать дополнительные внешние, не перегружая проект лишними модулями.
- Расширяемость. У Flask много расширений и плагинов, которые помогают быстро добавить новую функциональность. Например, авторизацию, управление базами данных и работу с формами.
- Простота. У Flask простой синтаксис, что делает изучение этого фреймворка более простым, а также позволяет быстрее создавать прототипы веб-приложений.
- Поддержка сообщества. Flask запустили в 2010 году, и почти по любому связанному с ним вопросу в интернете уже есть ответы.
В общем, Flask как будто бы создан для новичков. Он несложен, в нём есть все необходимые базовые функции и возможности для расширения. Но при этом Flask может показаться слабеньким фреймворком, непригодным для крупных проектов. Кстати, это тоже можно исправить сторонними плагинами и библиотеками.
Чтобы освоить азы Flask, в этой статье мы создадим небольшой сайт-блог и объясним главные концепции фреймворка в деле. Начнём с установки.
Чтобы установить Flask, сначала нужно установить Python. Приступим.
Flask требует наличия Python версии 3.5 или выше. Если у вас нет Python, его можно загрузить с официального сайта Python. Подробную инструкцию можно посмотреть в нашем гайде по установке Python для всех операционных систем.
Если вы скачиваете официальную версию Python или пакет Anaconda, у вас автоматически установится PIP. Это менеджер пакетов для Python, который позволяет управлять сторонними библиотеками. Нам он понадобится, чтобы установить Flask.
Чтобы проверить, есть ли у вас PIP, введите в консоли:
pip --version
или
python3 -m pip --version
В ответ на экран выведется версия PIP. Если ничего не происходит, значит, PIP у вас не установлен. Исправим это:
python -m ensurepip --default-pip
Снова проверим, появился ли в системе менеджер пакетов. Если всё равно что-то не получается, попробуйте найти решение проблемы на Stack Overflow — или обратитесь к астрологу
Теперь поставим сам Flask. Делается это очень просто:
pip install Flask
Начнётся процесс загрузки Flask, после которого он будет готов к использованию. Если вам нужна конкретная версия Flask, установить её можно, указав её номер с помощью дополнительного параметра ==<version>.
pip install Flask==<version>
Например, мы можем установить версию 2.0.1:
pip install Flask==2.0.1
Чтобы проверить, работает ли Flask, введём следующую команду:
pip show flask
или создадим Python-файл и впишем туда такую строку:
import flask
Теперь запустим интерпретатор и убедимся, что программа исполняется без ошибок.
Приступим к коду. Для начала нам понадобится основа для приложения. Создадим новый файл с именем app.py. Это и будет наше Flask-приложение.
На первом этапе импортируем класс Flask из библиотеки Flask:
from flask import Flask
Затем создадим экземпляр класса Flask:
app = Flask(__name__)
Здесь мы передаем аргумент __name__ конструктору класса, этот аргумент скажет Flask, где находится наше приложение. Так Flask сможет определить местоположение шаблонов и статических файлов, о которых речь пойдёт дальше. Если вы ещё не особо знакомы с классами в Python, советуем прочитать нашу статью об объектно-ориентированном программировании на Python.
Весь бэкенд строится на маршрутах — или URL-адресах. Они помогают задавать удобную структуру и понятное поведение веб-приложениям.
Для пользователя маршруты — это отдельные «вкладки» на сайте. Например, если зайти на сайт Skillbox, откроется его главная страница www.skillbox.ru. А если кликнуть на любой курс, мы перейдём на другую страницу сайта с другим URL-адресом, таким как www.skillbox.ru/course/profession-python. Видим, что к адресу нашего сайта добавился текст: /course/profession-python/. Эта «приписка» и перенесла нас на другую страницу с другим содержимым. Получается, маршруты позволяют создавать разные страницы с разным наполнением в рамках одного сайта.
Чтобы задать маршрут во Flask, нужно написать следующее:
@app.route('/') def hello_world(): return 'Hello, World!'
Так мы создали URL-адрес главной страницы сайта. Например, для Skillbox главной страницей будет www.skillbox.ru. Тут мы мысленно можем дописать слеш: www.skillbox.ru/.
Сам маршрут задаётся в строке @app.route(‘/’). Внутрь круглых скобочек мы по ходу статьи будем вписывать разные маршруты, а пока нам хватит стандартного.
Внутрь маршрута мы поместили функцию hello_world(), которая будет выполняться при обращении к корневому URL, или главной странице нашего сайта (ведь наш маршрут ведёт именно на неё). Функция возвращает строку Hello, World! в браузере.
Теперь нам нужно запустить приложение:
if __name__ == '__main__': app.run()
Этот код гарантирует, что сервер Flask будет запущен только в том случае, если файл app.py был запущен напрямую, а не импортирован как модуль.
Сохраняем файл app.py и запускаем его с помощью команды в консоли:
python app.py
После запуска вы должны увидеть сообщение о том, что сервер Flask был запущен:
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Чтобы взглянуть на работу нашего приложения, нужно перейти по адресу, который был указан в консоли — http://127.0.0.1:5000/. Вот что мы там увидим:
Ликуем — у нас всё получилось, сайт работает. Дальше будем усложнять наше приложение и начнём создавать блог.
Чтобы создать блог, одним простым приложением уже не обойтись, придётся научиться использовать HTML-шаблоны и подключать базу данных.
В дальнейшем мы будем использовать наш базовый код из предыдущего раздела. Полностью он выглядит так:
app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
if __name__ == '__main__':
app.run()
HTML-шаблоны — это файлы, которые задают структуру и содержимое страниц сайта. Шаблоны упрощают жизнь программистам — им не приходится десятки раз писать один и тот же HTML-код, ведь его можно просто взять и… шаблонизировать.
Ещё немного об HTML-шаблонах
HTML-шаблоны позволяют создавать динамические веб-страницы с помощью заготовленных блоков, которые мы можем настраивать и использовать повторно. Шаблоны помогают избежать дублирования кода и облегчают поддержку приложения, так как мы можем легко изменять отображение страниц, не затрагивая внутреннюю логику. Например, в нашем медиа о программировании оформление всех статей очень похоже: структура, внешние по отношению к статье элементы и тому подобное — это возможно как раз благодаря шаблонам.
Например, можно использовать один и тот же базовый шаблон для всех страниц сайта и переопределять блоки контента для каждой страницы. В таком случае, если нам понадобится изменить дизайн всего сайта, мы можем изменить только базовый шаблон, а все страницы обновятся автоматически, без дополнительных усилий.
Если вы не знаете, как писать HTML-код, советуем прочитать нашу статью об HTML. А теперь создадим HTML-шаблоны: выделим под них папку templates и добавим в неё файл base.html со следующим содержимым:
<!DOCTYPE html> <html> <head> <title>{% block title %}{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body> </html>
Этот шаблон будет отправной точкой для всех остальных HTML-страниц. В нём мы прописали заголовок:
<head> <title>{% block title %}{% endblock %}</title> </head>
и основной контент (тело) страницы:
<body> {% block content %}{% endblock %} </body>
Вы, наверное, уже обратили внимание на странные элементы, а точнее теги {% block %} и {% endblock %}. Они как раз нужны, чтобы динамически добавлять туда новые элементы: другие HTML-блоки, JavaScript-код и тому подобное.
Теперь давайте создадим второй шаблон и назовём его index.html. Он будет наследовать элементы базового шаблона:
{% extends "base.html" %} {% block title %}Home{% endblock %} {% block content %} <h1>Welcome to my website!</h1> <p>This is the homepage.</p> {% endblock %}
Этот шаблон переопределяет заголовок страницы и определяет контент, который будет отображаться на домашней странице: заголовок страницы Home и немного текста.
Пришло время воспользоваться шаблонами. Изменим файл app.py и импортируем функцию render_template из библиотеки Flask, которая позволяет работать с шаблонами:
from flask import Flask, render_template
Изменим маршрут главной страницы и используем в нём новую функцию, чтобы отобразить шаблон index.html:
@app.route('/') def index(): return render_template('index.html')
Сохраним изменения в файлах app.py, index.html и base.html, а затем снова запустим наше приложение:
python app.py
Открываем браузер и переходим по адресу http://127.0.0.1:5000/. У нас отобразится содержимое шаблона index.html:
Чтобы вести блог, нужно куда-то сохранять все посты, картинки, видео и тому подобное. Куда-то — это в базу данных. Баз данных существует немало, мы даже написали про них отдельную статью. Но если коротко — база данных нужна, чтобы удобно хранить, обрабатывать и сохранять данные.
Мы будем использовать базу данных SQLite, потому что она занимает совсем немного места, легка в освоении и вообще клёвая. А главный плюс — её не нужно скачивать отдельно, потому что она сразу есть в Python.
Займёмся привычным делом — импортируем модуль SQLite3:
import sqlite3
Теперь мы можем создать функцию, которая будет подключать нас к базе данных и возвращать объект подключения:
def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn
Эта функция создаст подключение к базе данных database.db и установит режим получения результатов запросов в виде словаря Row, что позволит обращаться к столбцам базы данных по их именам.
Другими словами — наши данные будут храниться в виде таблицы, где каждая строка — уникальный пост, а каждый столбец (или ячейка) — информация об этом посте, например, сам текст, количество лайков, автор и так далее.
Но ещё нам понадобится функция, которая будет закрывать подключение к базе данных:
def close_db_connection(conn):
conn.close()
Теперь мы можем использовать эти функции для выполнения запросов к базе данных внутри нашего приложения. Обновим функцию index(), чтобы она получала все записи из таблицы posts:
@app.route('/') def index(): conn = get_db_connection() posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts)
Здесь мы получаем подключение к базе данных с помощью функции get_db_connection(), выполняем запрос к таблице posts и получаем все записи, используя метод fetchall(). Затем мы закрываем подключение с помощью функции close_db_connection() и передаём полученные записи в шаблон index.html с помощью функции render_template().
Но перед тем как использовать базу данных, её нужно инициализировать и создать таблицу posts.
У нашего учебного поста будет три поля с данными: уникальный идентификатор (ID), заголовок и текст поста. Назовём их соответственно — id, title и content:
def init_db(): conn = get_db_connection() conn.execute('CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)') conn.close()
Функция execute() создаст нам таблицу posts с полями id, title и content. У каждого из этих полей будет собственный тип данных: целое число, строка и строка соответственно.
Мы задали полю id специальный параметр AUTOINCREMENT, чтобы при добавлении нового поста его айдишник автоматически увеличивался на 1. А параметр PRIMARY KEY нужен для уточнения, что строки в таблице уникальные и не пустые.
Инициализировать базу данных нужно в самом начале. Во Flask мы можем сделать это перед первым запросов к базе данных с помощью следующего кода:
@app.before_first_request
def before_first_request():
init_db()
Здесь мы используем декоратор @app.before_first_request, который указывает, что функция before_first_request() вызывается перед тем, как запустится рендер шаблонов.
Готово — наше приложение подключено к базе данных SQLite, а мы можем переходить дальше. Но сперва убедимся, что всё работает. Сохраняем изменения и запускаем приложение:
python app.py
Если перейти по адресу http://127.0.0.1:5000/, то может показаться, что ничего не изменилось. Но если посмотреть в папку с файлами, то можно увидеть там новый файл — database.db. Это и есть наша база данных.
Настало время сделать новый и прекрасный шаблон — для постов. Он будет простым: заголовок, текст и ссылка на главную страницу. Создадим в папке templates файл post.html и добавим следующий код:
<!DOCTYPE html> <html> <head> <title>{{ post.title }}</title> </head> <body> <h1>{{ post.title }}</h1> <p>{{ post.content }}</p> <a href="{{ url_for('index') }}">Back to index</a> </body> </html>
В момент рендеринга в эту HTML-страницу мы будем передавать пост, который достали из базы данных. У этого поста будут заголовок (title) и основной текст (content), а всё вместе будет лежать внутри объекта post.
Пока всё просто, но обратите внимание на новую функцию — url_for(). Она позволяет перейти на другой маршрут и отрисовать другую HTML-страницу. В нашем случае, если мы нажмём на ссылку Back to index, запустится функция index(), которая перенаправит нас на главную страницу (маршрут /) и отрисует шаблон index.html.
Теперь нам нужно сделать новый маршрут под один из постов в файле app.py:
@app.route('/<int:post_id>') def get_post(post_id): conn = get_db_connection() post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post)
Тут уже всё немного сложнее, но обо всём по порядку:
- @app.route(‘/<int:post_id>’) — задаём новый маршрут. Он будет выглядеть так: www.oursite.com/1. Единица будет указывать на индекс поста из базы данных. <int:post_id> — это ещё одно указание на то, что индекс поста должен быть целым числом (int), а не, например, строкой.
- get_post(post_id) — передаём айдишник поста, который как раз и «встанет» в URL-адрес и добавится к запросу к базе данных.
- post = conn.execute (‘SELECT * FROM posts WHERE id =? ‘, (post_id,)).fetchone() — запрашиваем из базы данных пост по нашему айдишнику и берём одну строку функцией fetchone().
- return render_template (‘post.html’, post=post) — рендерим HTML-шаблон и передаём туда полученный пост.
Давайте убедимся, что всё работает корректно и страница отрисовывается. Временно напишем «костыль» и вручную добавим пост (исключительно для проверки):
@app.route('/<int:post_id>') def get_post(post_id): conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES ("Random Title", "Lorem ipsum dolor sit amet consectetur adipiscing elit")') post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone() conn.close() return render_template('post.html', post=post)
Наш «костыль» — это четвёртая строка. Здесь мы сами вставляем новую строку в базу данных с помощью запроса INSERT. Теперь перейдём по адресу http://127.0.0.1:5000/1:
Круто — всё отрисовалось, кнопка «Назад» работает! Идём дальше и не забываем удалить «костыль».
Дополним файл index.html и создадим список, в котором будут находиться все посты из базы данных:
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html>
Главный движ у нас происходит в теге <ul>: мы идём по всем элементам списка posts и для каждого создаём заголовок со ссылкой на пост. При нажатии на ссылку вызывается функция url_for(), а ей передаётся функция-рендер get_post() и ID поста.
Ещё мы немного декорировали заголовок самой страницы. Можете сравнить с прошлой версией.
Если перезапустить приложение, то мы не увидим новых постов, потому что их просто нет в базе данных. Но можно опять заполнить всё вручную. Изменим функцию index():
@app.route('/') def index(): conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES ("Why I love Flask", "This is so cool!!!")') conn.execute('INSERT INTO posts (title, content) VALUES ("Cats >> Dogs", "It was a joke because they are all so adorable.")') posts = conn.execute('SELECT * FROM posts').fetchall() conn.close() return render_template('index.html', posts=posts)
Видим результат:
Правда, если перейти по ссылке, то ничего не отобразится, но и это мы со временем исправим.
Отображение постов работает, осталось добавить возможность создавать новые посты. Для этого создадим новый HTML-шаблон и метод для рендеринга.
В папке templates создаём новый файл add_post.html:
{% extends 'base.html' %} {% block content %} <h1>Add New Post</h1> {% with messages = get_flashed_messages() %} {% if messages %} <ul class="flashes"> {% for message in messages %} <li>{{ message }}</li> {% endfor %} </ul> {% endif %} {% endwith %} <form method="post"> <label for="title">Title</label><br> <input type="text" id="title" name="title"><br> <label for="content">Content</label><br> <textarea id="content" name="content"></textarea><br> <input type="submit" value="Submit"> </form> {% endblock %}
Он будет наследовать файл base.html, а внутри содержать форму, где пользователь указывает заголовок и тело поста. Если он вдруг введёт некорректные данные, то мы покажем ему сообщение об ошибке с помощью флеш-сообщений (когда некорректно заполненное поле подсвечивается красным и у вас нет возможности отправить заполненную форму).
Теперь добавим новый маршрут в файл app.py:
@app.route('/new', methods=['GET', 'POST']) def new_post(): if request.method == 'POST': title = request.form['title'] content = request.form['content'] conn = get_db_connection() conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)', (title, content)) conn.commit() conn.close() return redirect(url_for('index')) return render_template('add_post.html')
Этот маршрут обрабатывает GET- и POST-запросы по адресу /new. Если запрос выполняется методом GET, то функция просто отображает форму для ввода данных с помощью шаблона add_post.html. А если запрос выполняется методом POST, мы достаём заголовок и тело поста из формы, а затем добавляем их в базу данных. В конце — редиректим (то есть перенаправляем) пользователя обратно на главную.
Подробнее об HTTP-запросах вы можете прочитать в нашей статье.
Чтобы все эти функции работали, мы также должны их импортировать в начале файла:
from flask import Flask, render_template, request, redirect, url_for
Теперь давайте сохраним свои наработки и запустим приложение:
python app.py
Переходим по адресу http://127.0.0.1:5000/ и видим, что опять ничего не изменилось:
Попробуем перейти по адресу http://127.0.0.1:5000/new:
Ура! Мы видим нашу форму! Давайте впишем туда что-нибудь и нажмём Submit:
Нас перекинуло на главную страницу — и на ней находится наш новый пост. Ура! Всё наконец-то работает!
К тому же теперь мы можем перейти по ссылке и посмотреть пост целиком:
Но всё ещё есть проблема — нужно добавить кнопку Add new post, чтобы каждый раз не добавлять пост, вбивая в адресную строку наш URL. Для этого нам нужно добавить всего одну строчку в файл index.html:
<!DOCTYPE html> <html> <head> <title>Blog</title> </head> <body> <h1>Blog</h1> <a href="{{ url_for('new_post') }}"><button>Add New Post</button></a> <ul> {% for post in posts %} <li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li> {% endfor %} </ul> </body> </html>
Мы создали кнопку, нажав которую, мы перейдём на страницу со ссылкой для создания нового поста. Проверяем:
Кликаем — переходим куда нужно:
Добавили новый пост:
Наше приложение приобрело следующую структуру:
Blog Flask/ |-- templates/ | |-- index.html | |-- base.html | |-- post.html | |-- add_post.html |-- app.py |-- database.db
В папке templates хранятся HTML-шаблоны: index.html, add_post.html, post.html и base.html. Файл app.py содержит основной код приложения Flask, в котором определены маршруты и функции для работы с базой данных. А файл database.db — это база данных SQLite.
Полный код каждого файла можно посмотреть ниже:
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Blog</title>
</head>
<body>
<h1>Blog</h1>
<a href="{{ url_for('new_post') }}"><button>Add New Post</button></a>
<ul>
{% for post in posts %}
<li><a href="{{ url_for('get_post', post_id=post['id']) }}">{{ post['title'] }}</a></li>
{% endfor %}
</ul>
</body>
</html>
base.html:
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
post.html:
<!DOCTYPE html>
<html>
<head>
<title>{{ post.title }}</title>
</head>
<body>
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
<a href="{{ url_for('index') }}">Back to index</a>
</body>
</html>
add_post.html:
{% extends 'base.html' %}
{% block content %}
<h1>Add New Post</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<form method="post">
<label for="title">Title</label><br>
<input type="text" id="title" name="title"><br>
<label for="content">Content</label><br>
<textarea id="content" name="content"></textarea><br>
<input type="submit" value="Submit">
</form>
{% endblock %}
app.py:
from flask import Flask, render_template, request, redirect, url_for
import sqlite3
app = Flask(__name__)
def get_db_connection():
conn = sqlite3.connect('database.db')
conn.row_factory = sqlite3.Row
return conn
def close_db_connection(conn):
conn.close()
def init_db():
conn = get_db_connection()
conn.execute('CREATE TABLE IF NOT EXISTS posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL)')
conn.close()
@app.route('/')
def index():
conn = get_db_connection()
posts = conn.execute('SELECT * FROM posts').fetchall()
conn.close()
return render_template('index.html', posts=posts)
@app.route('/<int:post_id>')
def get_post(post_id):
conn = get_db_connection()
post = conn.execute('SELECT * FROM posts WHERE id = ?', (post_id,)).fetchone()
conn.close()
return render_template('post.html', post=post)
@app.route('/new', methods=['GET', 'POST'])
def new_post():
if request.method == 'POST':
title = request.form['title']
content = request.form['content']
conn = get_db_connection()
conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)', (title, content))
conn.commit()
conn.close()
return redirect(url_for('index'))
return render_template('add_post.html')
@app.before_first_request
def before_first_request():
init_db()
if __name__ == '__main__':
app.run()
Наш блог полностью готов к работе. В него можно добавлять новые функции (например, редактирование постов или систему авторизации), а также применять CSS-стили, чтобы он выглядел красивее. Но главное — мы заложили основу для бэкенда и разобрались, как это сделать на Flask.
Вот некоторые важные вещи, которые стоит помнить при работе с фреймворком Flask:
- Flask — это микрофреймворк для создания веб-приложений на языке Python.
- Flask использует декораторы для связывания функций с URL-адресами и методами HTTP.
- Чтобы удобно отображать HTML-страницы, можно использовать шаблоны, которые упрощают разработку.
- Flask не имеет встроенной поддержки баз данных, но к нему всегда можно подключить сторонние — например, SQLite.
- Flask использует объект request для доступа к данным, отправленным пользователем через формы и URL-адреса.
- Flask использует флеш-сообщения для отображения сообщений об ошибках или на веб-странице.
Рассказываем про один из самых популярных и лаконичных микрофреймворков для Python — Flask. Как написать простое приложение, подключить к нему Bootstrap и базу данных, и есть ли вообще у Flask минусы.
Flask — это микрофреймворк для создания веб-приложений на Python. В нем из коробки доступен только минимальный инструментарий, но при этом он поддерживает расширения так, как будто они реализованы в самом Flask. Расширения для микрофреймворка позволяют коммуницировать с базами данных, проверять формы, контролировать загрузку на сервер, работать с аутентификацией и многое другое.
Первая публичная версия Flask вышла 16 апреля 2010 года. Автор проекта — Армин Ронахер, который возглавлял команду энтузиастов в Python-разработке Poocco. Flask основан на быстром и расширяемом механизме шаблонов Jinja и наборе инструментов Werkzeug. Кроме того, Flask использует одну из самых передовых служебных библиотек WSGI (Web Server Gateway Interface — стандарт взаимодействия между Python-программой, выполняющейся на стороне сервера, и самим веб-сервером).
При этом WSGI тоже разработал Армин Ронахер. По его словам, идея Flask изначально была первоапрельской шуткой, которая стала популярной и превратилась в серьезное приложение.
Изучите Python на Хекслете
Пройдите нашу профессию «Python-разработчик», чтобы поменять свою жизнь и стать бэкенд-программистом.
Плюсы и минусы Flask
Практически все плюсы и минусы Flask появились именно из-за того, что он является микрофреймворком.
Среди достоинств:
- Простота. Flask легко установить и настроить.
- Гибкость. Микрофреймворк позволяет разработчикам самостоятельно выбирать технологии и инструменты, которые они хотят применять в своих проектах
- Расширяемость. Flask позволяет расширять функциональность с помощью плагинов и модулей, которые можно легко интегрировать в проект.
- Активное сообщество. Flask является одним из самых используемых фреймворков для Python, поэтому имеет большое комьюнити разработчиков.
При этом у Flask есть и свои недостатки:
- Отсутствие готовых решений. Разработчики изначально могут использовать во Flask только минимальный набор функциональности. Если же программисту нужны более широкие возможности, такие как аутентификация пользователя, ему придется добавить дополнительные библиотеки или реализовать это самостоятельно.
- Нет встроенной многопоточности. Flask был разработан как однопоточный фреймворк. И чтобы управлять многопоточными веб-приложениями, придется установить дополнительные библиотеки.
- Ограниченные возможности для масштабирования. Если проект начинает расти и усложняться, то могут появиться сложности в поддержке нужной функциональности.
То есть Flask можно удобно использовать в небольших проектах — он идеален для макетирования идей и быстрого прототипирования. При этом его редко используют в крупных проектах, и он плохо подходит для асинхронного программирования.
Как создать проект на Flask
Для начала работы с микрофреймворком нужно скачать последнюю версию Flask:
pip install Flask
Для примера мы напишем на Flask тестовое веб-приложение с минимальным функционалом. Как работают приложения такого типа:
- Пользователь вводит в браузере url, например — hexlet.io. В нашем тестовом приложении пользователь не будет вводить url, потому что мы будем работать с локальным сервером по адресу
http://127.0.0.1:5000
. - Браузер получает у DNS IP-адрес нужного нам сервера. DNS — это Domain Name System, распределенная системе серверов. Она работает как общая «контактная книга» в интернете.
- Браузер отправляет запрос по этому адресу и получает ответ. Как правило — в виде HTML-страницы.
- Браузер отображает содержимое страницы.
Итак, создадим файл hello.py
и запишем в него следующий код:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index() -> str:
return '<p>Hello Flask!</p>'
if __name__ == '__main__':
app.run(debug=True)
Давайте подробно разберем, что делает код, который мы написали.
Первой строкой мы импортируем класс Flask
. После этого мы создаем объект этого класса, передав первым аргументом имя модуля, — это и будет наше приложение для общения с веб-cервером. __name__
— это удобный способ передать именно то приложение, из которого запущен Flask.
Декоратор route()
сообщает Flask, при обращении к какому URL-адресу запустится декорируемая разработчиком функция — в нашем примере это index
. Последней строчкой мы открываем локальный веб-сервер с параметром debug=True
— это позволит следить за всеми ошибками в логе программы.
Читайте также:
Программирование на Python: особенности обучения, перспективы, ситуация на рынке труда
Запускаем веб-приложение через терминал:
python hello.py
Если мы все сделали правильно, то в терминале появятся эти сообщения:
В основном тут отображается служебная информация. Единственное, что нас интересует — сообщение, что наш локальный сервер запущен по адресу http://127.0.0.1:5000/
. В нем красными буквами указывается, что локальный сервер не подходит для продакшена. Но, так как мы реализовали тестовое приложение, то не будем деплоить его на реальный сервер.
Вернемся к коду. С помощью переменной части маршрута Flask может передавать в функцию аргументы.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
greet_with_link = """<h1>Привет, Мир!</h1>
<p><a href="user/Аникин/Георгий">Нажми на меня</a></p>"""
return greet_with_link
@app.route('/user/<surname>/<name>')
def get_user(name, surname):
personal_instruc = f"""<h1>Привет, {surname} {name}!</h1>
<p>Измени имя пользователя в адресной строке и перезагрузи страницу</p>"""
return personal_instruc
if __name__ == '__main__':
app.run(debug=True)
В нашем примере значения просто появятся в браузере как часть строки. На стартовой странице нашего сайта будет запускаться функция index()
. В ней пользователю, помимо приветствия, будет предлагаться нажать на ссылку, при клике на которую он перейдет на user/Аникин/Георгий
. Этот URL-маршрут будет обрабатываться уже функцией get_user
.
Функция get_user
декорируется @app.route('/<surname>/<name>’)
, а в адресной строке у нас /user/Аникин/Георгий
. То есть наша функция получает аргументы из URL-адреса, эти значения лежат между косых скобок. По умолчанию тип таких значений string
принимает любой текст без косой черты. Но переменные маршрутов могут быть и иных типов: int
, float
, path
и других. Типы указываются в формате <тип:имя переменной>
.
Структура приложения на Flask
Создадим подкаталог flask_app
с такой структурой файлов и папок:
Чтобы написать приложение сложнее одной строки, в директории проекта должны находиться папки static
и templates
. Директория static
содержит ресурсы, которые используются шаблонами. В том числе включая файлы CSS, JavaScript и картинки. Папка templates
содержит только шаблоны с расширением *.html
.
Заполним наши файлы кодом. Сначала — наш основной файл проекта app.py
:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/about")
def get_page_about():
return render_template("about.html")
if __name__ == "__main__":
app.run(debug=True)
После этого — index.html
:
<!DOCTYPE html>
<html>
<head>
<title>Main page</title>
</head>
<body>
<h1>Главная страница</h1>
</body>
</html>
И файл about.html
:
<!DOCTYPE html>
<html>
<head>
<title>About</title>
</head>
<body>
<h1>О приложении</h1>
</body>
</html>
Для отображения HTML-шаблонов мы используем функцию render_template()
. В нашем коде она принимает только имя шаблона и возвращает строку с результатом рендеринга шаблона.
Однако render_template()
может принимать неограниченное количество именованных аргументов, которые можно использовать в этом шаблоне. Это позволит решить проблему нашего тестового проекта — сейчас у нас две функции, две страницы, и очень много дублированного кода.
Напишем базовый шаблон base.html
и пару его наследников. При этом блоки {% block smth %} … {% endblock %}
— это части базового шаблона, которые можно заменить в наследнике. Переменные передаются по именам в конструкции {{ variable }}
.
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Приложение Flask{% endblock %}</title>
</head>
<body>
<h1>{{h1}}</h1>
</body>
</html>
После появления файла с базовым HTML-шаблоном можем поправить наши остальные HTML-файлы:
about.html:
{% extends 'base.html' %}
{% block title %}About{% endblock %}
index.html:
{% extends 'base.html' %}
{% block title %}Main page{% endblock %}
Кроме того, нужно поправить и основной файл Flask-проекта app.py
:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html", h1 = "Главная страница")
@app.route("/about")
def get_page_about():
return render_template("about.html", h1 = "О приложении")
if __name__ == "__main__":
app.run(debug=True)
Подключаем Bootstrap
Bootstrap — это открытый и бесплатный набор инструментов для создания сайтов и веб-приложений.
В нашем проекте в папке templates
у нас есть подкаталог bootstrap
, а в нем файл base.html
— это немного модифицированная заготовка сайта-документации Bootstrap-Flask:
<!doctype html>
<html lang="en">
<head>
{% block head %}
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% block styles %}
<!-- Bootstrap CSS -->
{{ bootstrap.load_css() }}
{% endblock %}
<title>{% block title %}Приложение Flask{% endblock %}</title>
{% endblock %}
</head>
<body>
<!-- Your page content -->
{% block content %}
<div class="jumbotron text-center">
<h1>{{h1}}</h1>
</div>
{% endblock %}
{% block scripts %}
<!-- Optional JavaScript -->
{{ bootstrap.load_js() }}
{% endblock %}
</body>
</html>
В файлах index.html
и about.html
заменим строку наследования на:
{% extends 'bootstrap/base.html' %}
Второй путь подключения Bootstrap к проекту на Flask — через CDN. Подробнее об этом можно почитать в документации фреймворка.
Читайте также:
Как создатель Python Гвидо ван Россум устроился в Microsoft и теперь работает над развитием CPython
После подключения Bootstrap нужно будет немного поправить основной файл нашего проекта app.py
:
from flask_bootstrap import Bootstrap4
from flask import Flask, render_template
app = Flask(__name__)
bootstrap = Bootstrap4(app)
@app.route("/")
def index():
return render_template("index.html", h1 = "Главная страница")
@app.route("/about")
def get_page_about():
return render_template("about.html", h1 = "О приложении")
if __name__ == "__main__":
app.run(debug=True)
Последним элементом нашего веб-приложения будет форма отправки. Для этого нужно немного модифицировать index.html
:
{% extends 'bootstrap/base.html' %}
{% block title %}Main page{% endblock %}
{% block content %}
{{super()}}
<div class="container text-center">
<form class="d-inline-block" style="max-width: 33%;">
<div class="form-group">
<label for="eventDate">Дата</label>
<input type="date" name="eventDate" class="form-control" placeholder="Дата события">
</div>
<div class="form-group">
<label for="eventName">Событие</label>
<input type="text" name="eventName" class="form-control" placeholder="Название события">
</div>
<div class="form-group">
<label for="eventDuration">Продолжительность</label>
<input type="number" name="eventDuration" class="form-control" placeholder="Продолжительность" min="1" max="24">
</div>
<button type="submit" class="btn btn-primary">Записать</button>
</form>
</div>
{% endblock %}
Вообще, Bootstrap может добавить огромное количество элементов в приложение буквально в несколько кликов. Мы ограничились четырьмя — три поля и одна кнопка. Ключевой элемент здесь — это {{ super() }}
.
Подключаем базу данных
Итак, у нас есть форма отправки, но она пока ничего не делает с данными. Для нас было бы неплохо хранить, обрабатывать и в будущем легко извлекать данные этих форм. Обычно такие задачи решают с помощью реляционных баз данных (далее БД).
Есть большое количество способов работы с SQL-запросами во Flask. Мы можем использовать, например, sqlite3 и чистый SQL, а можем — библиотеку sqlite3 для Python. Кроме того, можно обернуть чистые SQL-запросы в код, либо использовать Psycopg2 для работы с PostgresSQL в Python (мы рекомендуем делать именно так и вот почему). Для примера в этом тексте мы используем библиотеку Flask SQLAlchemy (расширение для Flask), которая предлагает технологию ORM для взаимодействия с БД.
Подключаем базу данных к нашему проекту через файл app.py
:
from datetime import datetime
from flask import Flask, redirect, render_template, request
from flask_bootstrap import Bootstrap4
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
bootstrap = Bootstrap4(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///events.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
date = db.Column(db.Date, nullable=False)
name = db.Column(db.String(255), nullable=False)
duration = db.Column(db.Integer, nullable=False)
def __str__(self):
return (
f"Название: {self.name}\n"
f"Дата: {self.date}\n"
f"Продолжительность {self.duration}ч"
)
@app.route('/', methods=['POST'])
def add_event():
date = datetime.strptime(request.form['eventDate'], '%Y-%m-%d').date()
name = request.form['eventName']
duration = int(request.form['eventDuration'])
print(date, name, duration, sep='\n')
event = Event(date=date, name=name, duration=duration)
db.session.add(event)
db.session.commit()
return redirect('/')
@app.route("/")
def index():
return render_template("index.html", h1 = "Главная страница")
@app.route("/about")
def get_page_about():
return render_template("about.html", h1 = "О приложении")
if __name__ == "__main__":
with app.app_context():
db.create_all()
app.run(debug=True)
В нашей БД появился класс Event
c атрибутами, который наследуется от db.Model
. Это позволяет с помощью SQLAlchemy создать таблицу event
, а поля нашего класса сделать колонками этой таблицы. Кроме того, мы определили магический метод __str__
для строкового отображения экземпляров класса — это пригодится для отображения в HTML.
Для создания таблицы в блок if __name__ == ‘__main__’
мы добавили команду db.create_all()
, а для обработки отправленной формы — метод add_event
. Он работает с методом POST
, который указывает Flask, что данные будут отправлены на сервер.
В методе POST
мы считываем данные отправленной формы и создаем для каждой строки временную переменную. После этого мы создаем объект event
класса Event
, передаем наши временные переменные как именованные аргументы, добавляем event
в БД и фиксируем изменения.
Нам осталось лишь немного поправить форму: в файле index.html
в открывающем теге <form>
добавим атрибуты action="{{ url_for('add_event') }}" method="POST"
. Теперь форма отправки по нажатию на кнопку «Записать»
будет отправлять данные в базу данных.
Добавим страницу отображения наших записей в новый файл Events.html
:
{% extends 'bootstrap/base.html' %}
{% block title %}Events{% endblock %}
{% block content %}
{{super()}}
<div class="container text-center">
<a href="{{ url_for('index') }}"><h2>Добавить событие</h2></a>
</div>
<div class="mt-4">
<ul class="list-group">
{% for event in events %}
<li class="list-group-item">
<p>{{ event }}</p>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}
В файл app.py
добавим view
:
@app.route("/events")
def view_events():
events = Event.query.order_by(Event.date).all()
return render_template("events.html", h1 = "События", events=events)
А в основном контейнере index.html
добавим ссылку на эту страницу:
<a href="{{ url_for('view_events') }}"><h2>Посмотреть события</h2></a>
Наш тестовый проект на Flask готов! Его можно запустить на локальном сервере через команду python app.py
(в некоторых случаях надо будет написать название директории перед названием файла app.py
).
Что еще почитать про Flask
- Большой курс по Flask на Хекслете
- Документация Flask
- Цикл статей на Real Python
- Проектирование RESTful API с помощью Python и Flask
Изучите Python на Хекслете
Пройдите нашу профессию «Python-разработчик», чтобы поменять свою жизнь и стать бэкенд-программистом.
Стать бэкенд-разработчиком
Хотите создать сайт самостоятельно? У вас нет навыков программирования и ни малейшего понимания о том, что такое HTML, CSS, JavaScript и язык программирования Python (или PHP на худой конец)? Тогда данная публикация точно не для вас! Для создания сайта на Python-Django потребуется определенный уровень понимания как работает сайт, какие технологии обеспечивают его функционирование и хотя бы базовое понимание языка программирования Python. И это я еще не говорю о током важном элементе как дизайн! Если у вас нет указанных навыков, то создание и раскрутка сайта от Seo Evolution поможет вам решить задачу открытия Интернет-представительства для вашего бизнеса или персонального бренда. Для тех же, кто хочет попробовать свои силы и создать свой сайт самостоятельно я предлагаю нижеследующую информацию.
Почему Python?
Python является одним из наиболее динамично развивающихся языков программирования в мире благодаря своей простоте и универсальности. Он достаточно прост в изучении и понимании синтаксиса. При этом достаточно мощный по своим возможностям. На нем можно писать как простенькие скрипты, так и сложные приложения и даже некоторые виды игр. Еще одним плюсом языка Python является его кросс-платформенность, т.е. написанный код будет работать на любой операционной системе и на устройствах различного типа – от компьютера до смартфона. Главное – установить интерпретатор языка Python на устройство в нужной версии и можно выполнять код. Если вы новичок в программировании и разработке программного обеспечения, то изучение языка Python будет отличным вариантом для применения получаемых знаний на практике, а мощь и универсальность языка позволит легко переключаться между проектами различного назначения без необходимости кардинального переобучения.
Почему Django?
Django – это веб-фреймворк. Что такое фреймворк? Говоря проще – набор стандартного инструментария для создания сайтов. Фреймворк может включать реализацию стандартных функций, которые часто используются при создании сайта и служит для ускорения процесса разработки. При этом фреймворк – это не конструктор, а всего лишь база, позволяющая быстро запустить сайт и обеспечить его работу. Чтобы сделать даже простой сайт с использованием Django необходимо понимание HTML/CSS и немного Python. Благодаря отличной документации создать сайт на Django можно даже без глубоких знаний программирования, но если планируете развиваться в профессиональном плане и создавать качественные сайты, лучше уделить достаточно времени изучению HTML, CSS, JavaScript и Python. После продолжить развитие изучением дополнительных технологий. Выбор Django в качестве основы сайта оправдан, если вы планируете постоянное его усовершенствование, т.к. он позволяет не тратить время на рутину и реализацию базового функционала, а сосредоточиться на главном.
Как начать создание сайта на Python Django?
Всего несколько простых шагов и ваш компьютер будет готов к созданию сайтов с использованием Python и Django.
Шаг 1. Установка интерпретатора Python. Его можно скачать с сайта python.org. Если вы не знаете, какая версия Python вам нужна, то скачивайте последнюю для вашей версии операционной системы и следуйте инструкции по установке на указанном сайте.
Шаг 2. Установка редактора. В принципе, можно ограничиться и простым текстовым редактором, но для удобства лучше установить IDE (интегрированную среду разработки). Я рекомендую использовать PyCharm Community Edition. Это бесплатная версия PyCharm с широкими возможностями для программирования на языке Python. Есть версии для Windows, Linux и macOS.
Шаг 3. Начинаем создание сайта на Django. Для новичков на сайте djangoproject.com есть понятное руководство для знакомства и старта разработки с использованием данного фреймворка. Документация на английском, так что знание языка крайне желательно. Да и вовсе обязательно, если хотите добиться успехов в этой сфере.
Ну что же, старт разработки на Python Django не так уж и сложен, но и нет так уж и прост… Главное поставить цель и идти к ней. Для этого читайте следующий пост серии о разработке сайта на Django и Python – Установка Django и создание проекта.
Удачи в разработке сайтов!
Я решила создать собственный сайт, который позволял бы вести блог, отображать информационные панели и запускать блокноты Jupyter.
Разобралась, как создать само приложение Dash. Однако мне не удалось найти способ, который описывал бы все 3 нужные функциональности. И в данной статье мы займемся его разработкой.
С кодом шаблона, лежащего в основе сайта, можно ознакомиться по ссылке.
Введение
Изначально сайт был разработан в Dash. Он представлял собой информационную панель, как показано ниже:
Информационные панели создавались с помощью Dash и Plotly.
Но тут выяснилось, что я не могу отображать посты блога в разметке Markdown. Вот почему мне пришлось искать другой способ создания сайта.
Я начала изучать информацию в интернете. Мне нужно было найти подход для разработки такого сайта, который бы отображал разметку Markdown и использовал преимущества Dash для создания информационных панелей. Однако поиски не увенчались успехом.
Зато я натолкнулась на описание практического опыта разработки Джеймса Хардинга. Он состоял в создании простого статического блога на основе Markdown с помощью Flask. Я воспользовалась им для настройки основного сайта и с помощью пары приемов согласовала его работу с информационной панелью.
Структура каталога
Основная структура выглядит следующим образом:
.
|-- virtualenv/
|-- main/
| |-- content
| | |-- posts
| | | `-- about_website.md
| |-- dash
| |-- data
| |-- static
| |-- templates
| |-- __init__.py
|-- app.py
|-- Dockerfile
|-- .pylintrc
`-- requirements.txt
В __init__.py
происходит настройка сервера Flask, а также регистрация Flatpages и компонентов Dash.
Сайт разделен на статические файлы и файлы HTML. Выводимые на сайте изображения или файлы CSS и JavaScript располагаются в папке static
, а файлы HTML — в папке templates
.
Регистрация приложения Flask
Приложение вызывается из файла app.py
. Сам он вызывает функцию create_app()
, находящуюся в файле __init__.py
.
def create_app():
app = Flask(__name__, static_url_path='/static')
app.config.from_object(__name__)
app.config['FLATPAGES_HTML_RENDERER'] = my_renderer
register_dashapps(app)
register_routes(app)
return app
Эта функция включает настройки сервера и указание места, в котором статические файлы наделяют сайт уникальными характеристиками.
Примечание. Если не добавить static_url_path
в качестве параметра, сервер Flask не сможет определить, где искать файлы CSS и JavaScript. Столкнувшись с ошибкой 404 при вызове данных файлов, знайте, что проблема может быть именно в этом.
После регистрации сервера Flask добавляем компоненты Dash и страницы разметки Markdown Flatpages.
Добавление информационной панели
Файл __init__.py
включает функцию register_dashapps
, которая представлена ниже:
def register_dashapps(app):
from .main.layout import html_layout
# Метатеги для отзывчивости viewport
meta_viewport = {"name": "viewport",
"content": "width=device-width, initial-scale=1, shrink-to-fit=no"}dash_app = dash.Dash(
server=app,
routes_pathname_prefix="/projects/dashapp/",
external_stylesheets=[
"https://fonts.googleapis.com/css?family=Lato",
],
)with app.app_context():
dash_app.index_string = html_layout
dash_app.layout = init_dashboard()
Главное отличие между обычным Dash и данной функцией заключается в дополнительном контексте Flask. Теперь информационная панель запускается через app_context()
Flask.
Добавление блогов
Большинство блогов придерживаются общепринятого подхода и хранят контент в реляционной базе данных.
Простые и “плоские” посты моего блога взяты из файлов в исходном коде. Flatpages отлично подходит для данного случая использования и быстро добавляет программу визуализации в конфигурацию приложения Flask, которая преобразует или переводит Markdown в HTML.
FLATPAGES_EXTENSION = '.md'
FLATPAGES_ROOT = 'content'
POST_DIR = 'posts'def my_renderer(text):
body = render_template_string(markdown2.markdown(
text, extras=['fenced-code-blocks']))
return pygmented_markdown(body)def create_app():
app = Flask(__name__, static_url_path='/static')
app.config.from_object(__name__)
app.config['FLATPAGES_HTML_RENDERER'] = my_renderer
return app
Кроме того, нам нужно зарегистрировать маршруты сайта, указывающие на посты.
def register_routes(app):
pages = FlatPages(app)
freezer = Freezer(app)
Добавление блокнотов Jupyter
Заключительный этап предусматривает добавление блокнотов Jupyter.
Прежде чем добавить переадресацию в файл __init__.py
, убедимся в наличии доступа к блокноту Jupyter с сайта. Я задействовала инструмент nbviewer
, позволяющий бесплатно разместить этот блокнот. Затем данный URL можно использовать в переадресации (<URL>), как показано ниже:
@app.route('/<title_for_redirect>')
def project_jupyter_notebook():
return redirect('https://mybinder.org/<your_binder_url>')
Теперь вы знаете, как объединить вместе Flask, Dash и Jupyter Notebook и создать прекрасный сайт.
Читайте также:
- Бэкенд-разработчик: какие знания нужны для трудоустройства
- Управляем интернетом с помощью Python
- Овладей Python, создавая реальные приложения. Часть 6
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Sarah Floris: Create an Analytical Website From Scratch Using Python