[Из песочницы] Мега-Учебник Flask, Часть 6: Страница профиля и аватарка
Это шестая статья в серии, где я буду документировать мой опыт написания веб-приложения на Python, используя микрофреймворк Flask.Цель данного руководства — разработать довольно функциональное приложение-микроблог, которое я за полным отсутствием оригинальности решил назвать microblog.
Краткое повторение В прошлой статье мы создали систему авторизации, сейчас пользователи могут авторизоваться на сайте используя OpenID.Сегодня мы будем работать с профилем пользователя. Сперва, создадим страницу профиля, на которой будет показываться информация о пользователе, и его постах, так же мы научимся показывать аватарку. А потом мы создадим форму редактирования личных данных.
Страница профиля. На самом деле, создание страницы профиля не требует никаких новых концепций. Мы просто создадим новое представление и HTML-шаблон к ней.Функция в представлении. (файл app.views.py):
@app.route ('/user/
Как только у нас появится пользователь, мы вызываем render_template, вместе с тестовым сообщением. Обращаю ваше внимание, что на странице пользователя нужно показываться только сообщения этого пользователя, поэтому нужно правильно заполнить поле author.
Наш первоначальный шаблон выглядит достаточно просто (файл app/templates/user.html):
{% extends «base.html» %}
{% block content %}
User: {{user.nickname}}!
{% for post in posts %}
{{post.author.nickname}} says: {{post.body}}
{% endfor %} {% endblock %} Мы закончили со страницей профиля, но на неё нигде нет ссылки. Что бы пользователю было легко добраться до своего профиля, мы добавим ссылку на него в верхней панели навигации (файл app/templates/base.html): Обращаю ваше внимание, что в функцию url_for мы добавили обязательный параметр nickname.Посмотрим что у нас получилось. Нажав на You Profile мы должны перейти на страницу пользователя. Так как у нас нет ни одной ссылки на страницы других пользователей, вам придётся ввести URL вручную, что бы посмотреть на профиль другого пользователя. Например, наберите http://localhost:5000/user/miguel что бы посмотреть профиль пользователя Miguel.Аватарки. Сейчас наши страницы с профилем, достаточно унылые. Давайте добавим аватарку, что бы сделать их более интересными.Теперь напишем метод, который будет возвращать аватарку, и положим его в класс (app/models.py)
from hashlib import md5 # … class User (db.Model): # … def avatar (self, size): return 'http://www.gravatar.com/avatar/' + md5(self.email).hexdigest () + '? d=mm&s=' + str (size) Метод avatar вернёт путь до аватарки, сжатой до указанных размеров.Gravatar поможет сделать это очень легко. Вам просто нужно создать MD5-хэш от емейла, а затем добавить его в специально сформированный URL, который был выше. После хэша добавим в URL другие параметры. d=mm указывает, что нужно вернуть изображение по умолчанию, когда пользователь не имеет Gravatar аккаунт. Параметр mm возвращает изображение с серым силуэтом человека. Параметр s = N указывает до каких размеров следует масштабировать аватарку.
Документация для Gravatar.
Теперь класс User знает как вернуть изображение, мы можем добавить его на страницу профиля (файл app/templates/user.html):
{% extends «base.html» %}
{% block content %}
User: {{user.nickname}} |
{% for post in posts %}
{{post.author.nickname}} says: {{post.body}}
{% endfor %} {% endblock %} Примечательно, что класс User отвечает за возвращение аватарки, и если в один прекрасный момент мы решили что Gravatar не то что мы хотим, мы просто перепишем метод avatar, так что он будет возвращать другой путь (даже те которые укажем на нашем собственном сервере), все наши шаблоны будут представлены с новыми аватарками автоматически.Мы добавили аватарку в верхнюю часть страницы профиля, но в нижней части страницы у нас есть сообщения, рядом с которыми хорошо бы показывать аватарку маленького размера. Для страницы профиля мы, конечно, будем показывать ту же самую аватарку для всех сообщений, но потом, когда мы переведём эту функциональность на главную страницу мы будем иметь каждое сообщение украшенное аватаркой автора сообщения, и это будет действительно хорошо.Для показа аватарки для поста мы внесём небольшие изменения в шаблон (файл app/templates/user.html): {% extends «base.html» %}{% block content %}
User: {{user.nickname}} |
{% for post in posts %}
{{post.author.nickname}} says: {{post.body}} |
{{post.author.nickname}} says: {{post.body}} |
{% extends «base.html» %}
{% block content %}
User: {{user.nickname}} |
{% for post in posts %} {% include 'post.html' %} {% endfor %} {% endblock %} Как только мы сделаем работающую главную страницу, мы будем ссылать на тот же подшаблон, пока мы не готовы к этому, поэтому оставим для следующей главы.Более интересные профили Теперь когда у нас есть хорошая страница профиля, нам не хватает информации для показа. Пользователи любят добавлять на свои страницы немного информации о себе, поэтому мы дадим им эту возножость, и так же будем отображать на странице профиля. Ещё будем отслеживать когда пользователь заходил последний раз най сайт и тоже будем показывать на странице профиля.Что бы сделать задуманное, мы должны изменить базу данных. Нужно добавить два новых поля для нашего класса User (файл app/models.py):
class User (db.Model): id = db.Column (db.Integer, primary_key = True) nickname = db.Column (db.String (64), unique = True) email = db.Column (db.String (120), index = True, unique = True) role = db.Column (db.SmallInteger, default = ROLE_USER) posts = db.relationship ('Post', backref = 'author', lazy = 'dynamic') about_me = db.Column (db.String (140)) last_seen = db.Column (db.DateTime) Каждый раз когда мы изменяем базу данных, мы создаём новую миграцию. Помните как в части про базы данных мы прошли через муки для настройки миграционной системы БД. Теперь мы пожинаем плоды этих усилий. Для добавления новых полей в нашу БД, просто выполняем сценарий: ./db_migrate.py
И получаем ответ: New migration saved as db_repository/versions/003_migration.pyCurrent database version: 3
И наши два новых поля добавлены в БД. Не забыли, что если вы на Windows то путь запуска скрипта разные.Если мы не имеем миграционной системы, вам необходимо редактировать БД в ручную, или хуже того, удалить её и создать заново.Теперь давайте изменим шаблон профиля, с учетом этих полей (файл app/templates/user.html):
{% extends «base.html» %}
{% block content %}
User: {{user.nickname}}{% if user.about_me %}{{user.about_me}} {% endif %} {% if user.last_seen %}Last seen on: {{user.last_seen}} {% endif %} |
{% for post in posts %} {% include 'post.html' %} {% endfor %} {% endblock %} Как вы могли заметить, мы используем Jijna2 что бы показывать эти поля, потому что мы будем их показывать, только когда в них есть данные.Сейчас два новых поля пустые для всех пользователей, так что ничего не отобразится.
Поле Last_seen легко поддерживать. Помните, как в предыдущей главе мы создали обработчик before_request. Хорошее место для добавленя времени входа пользователя (файл app/views.py):
from datetime import datetime # … @app.before_request def before_request (): g.user = current_user if g.user.is_authenticated (): g.user.last_seen = datetime.utcnow () db.session.add (g.user) db.session.commit () Если вы войдёте на ваше страницу профиля, то увидите когда последний раз заходили на сайт и каждый раз, обновляя страницу, время будет обновлятся, потому что каждый раз, когда браузер делает запрос breforerequest, обработчик будет обновлять время в БД.Обратите внимание, что мы пишем время в стандартном часовом поясе UTC. Мы обсуждали это в предыдущей главе, что мы пишем все временные метки в UTC, что бы они соответствовали друг другу. Есть побочный эффект, время на странице профиля то же отображается в UTC. Мы исправим это в одной из следующих глав, которая будет посвещенна временным меткам.
Теперь нужно выделить место, для показа поля «обо мне», и правильней было бы разместить его в странице редактирования профиля.
Редактирование профиля. Добавить форму редактирования профиля на удивление легко. Начнём с создания веб-формы (файл app/forms.py) from flask.ext.wtf import Form from wtforms import TextField, BooleanField, TextAreaField from wtforms.validators import Required, Length
class EditForm (Form): nickname = TextField ('nickname', validators = [Required ()]) about_me = TextAreaField ('about_me', validators = [Length (min = 0, max = 140)]) И шаблон (файл app/templates/edit.html):
{% extends «base.html» %}
{% block content %}
Edit Your Profile
{% endblock %} И наконец напишем функцию обработчик (файл app/views.py): from forms import LoginForm, EditForm@app.route ('/edit', methods = ['GET', 'POST']) @login_required def edit (): form = EditForm () if form.validate_on_submit (): g.user.nickname = form.nickname.data g.user.about_me = form.about_me.data db.session.add (g.user) db.session.commit () flash ('Your changes have been saved.') return redirect (url_for ('edit')) else: form.nickname.data = g.user.nickname form.about_me.data = g.user.about_me return render_template ('edit.html', form = form) Так же добавим ссылку на него со страницы профиля пользователя, что бы можно было легко добраться до редактирования (файл app/templates/user.html): {% extends «base.html» %}
{% block content %}
User: {{user.nickname}}{% if user.about_me %}{{user.about_me}} {% endif %} {% if user.last_seen %}Last seen on: {{user.last_seen}} {% endif %} {% if user.id == g.user.id %}{% endif %} |
{% for post in posts %} {% include 'post.html' %} {% endfor %} {% endblock %} Мы используем условные операторы, что убедится что ссылки на редактировния профиля не появлялись когда вы читаете чужой профиль.Вот так выглядит новый скриншот страницы пользователя, с небольшим описанием о себе.
Заключение… и домашнее задание! Мы проделали большую работу с профилем пользователя, не так ли? Но у нас есть одна неприятная ошибка и мы должны её исправить.Сможете её найти?
Подсказка. Мы допустили ошибку в предыдущей главе, когда делали авторизацию. И сегодня мы написали новый кусок кода, который имеет ту же ошибку.
Попробуйте её найти, и если найдёте, то не стесняйтесь и пишите в комментариях. Я обьясню ошибку и как её исправть в следующей главе.
Как всегда вот ссылка для загрузки приложения с сегодняшними изменениями.
Я не включил базу данных в архив. Если у вас есть база данных предыдущей главы, просто положите её в нужное место и запустите db_upgrade.py. Ну, а если у вас нет предыдущей базы данных, слздайте новую с помощью db_create.py.
Спасибо что читаете мой учебник.Надеюсь увидеть вас в следующем выпуске.
P.S. Автор оригинала статьи Miguel Grinberg