原文: http://www.catonlinepy.tech/
聲明: 原創不易,未經許可,不得轉載css
今天的教程主要給你們介紹,如何在Flask應用中添加我的主頁以及在我的主頁中如何上傳用戶頭像。教程中的代碼都會託管到github上,貓姐一如既往的強調,在學習本課內容時必定要親自動手實現代碼,遇到問題再到github上查看代碼,若是實在不知道如何解決,能夠在日誌下方留言。html
在建立我的主頁以前,先來建立今天的項目目錄,貓姐直接將第5天的day5目錄複製後改爲day6,而後程序裏面的userauth_demo目錄改爲userprofile_demo目錄,並將代碼中的userauth_demo改成userprofile_demo。在此基礎上,咱們還須要建立以下文件和目錄:前端
# 注意:如下全部的操做都必須在虛擬環境中進行 # 在userprofile_demo新建文件utils.py文件,此文件用來保存一些功能獨立的小函數 (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo$ touch utils.py # 在userprofile_demo目錄下新建static目錄,此目錄用來保存css,js及圖片文件 (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo$ mkdir static # cd到static目錄 (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo$ cd static # 在static目錄中新建profile目錄,用戶上傳的頭像圖片將保存到該目錄 (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo/static$ mkdir profile # 在templates目錄中新建account.html文件 (miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6/userprofile_demo/templates$ touch account.html
最終,咱們獲得今天項目的目錄結構以下(使用tree命令獲得):python
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ tree day6 day6 ├── run.py └── userprofile_demo ├── config.py ├── database.db ├── forms.py ├── __init__.py ├── models.py ├── routes.py ├── static │ └── profile │ └── default.jpg ├── templates │ ├── account.html │ ├── index.html │ ├── layout.html │ ├── login.html │ └── register.html └── utils.py
一般,在用戶登陸後,在主頁導航欄中會有一個用戶名的超連接,當用戶點擊這個超連接時,就會跳轉到用戶的我的主頁。下面,咱們在layout.html文件的導航欄中添加我的主頁的入口:git
<html> <head> {% if title %} <title>{{ title }}-喵星在線</title> {% else %} <title>喵星在線</title> {% endif %} {% block js %} {% endblock js%} </head> <header> <div> <a href="{{ url_for('index') }}">主頁</a> {% if current_user.is_authenticated %} <a href="{{ url_for('logout') }}">註銷</a> <a href="#">{{current_user.username}}</a> {% else %} <a href="{{ url_for('login') }}">登錄</a> {% endif %} </div> </header> <body> <!-- 渲染flash消息 --> {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} <div class="alert alert-{{ category }}"> {{ message }} </div> {% endfor %} {% endif %} {% endwith %} {% block content %} {% endblock %} </body> </html>
此時用戶在登陸狀態下,訪問http://127.0.0.1:5005/時,能夠獲得以下效果:github
完成了導航中我的主頁的入口後,咱們須要完成我的主頁中呈現的內容。這裏account.html一樣須要繼承layout.html的導航欄。在account.html文件中添加以下代碼:sql
{% extends "layout.html" %} {% block content %} <hr> <div> <h1>用戶:{{html_user.username}}</h1> </div> {% endblock %}
當點擊頂部的miaojie時,它會將用戶帶到我的主頁頁面,服務器爲了響應這一請求,還須要添加相應的路由函數,在routes.py文件中添加以下代碼:數據庫
#... @app.route("/account") def account(): if not current_user.is_authenticated: return redirect(url_for('index')) return render_template("account.html", title="第六天", html_user=curent_user) #...
此時,還須要修改基模板layout.html文件中我的主頁的入口連接,才能實現正常的url跳轉,以下使用url_for函數完成我的主頁url的渲染:flask
#.. <header> <div> <a href="{{ url_for('index') }}">主頁</a> {% if current_user.is_authenticated %} <a href="{{ url_for('logout') }}">註銷</a> <a href="{{ url_for('account') }}">{{current_user.username}}</a> {% else %} <a href="{{ url_for('login') }}">登錄</a> {% endif %} </div> </header>
此時刷新主頁後,在主頁中點擊miaojie,就能夠跳轉到用戶我的頁面了,效果以下:服務器
上面只是在我的主頁中顯示了用戶的用戶名,這裏咱們再添加顯示用戶頭像的代碼,只需在account.html中增長以下img標籤便可:
<!--繼承基模板--> {% extends "layout.html" %} {% block content %} <hr> <div> <h1>用戶:{{html_user.username}}</h1> <img alt="" style="border-radius: 50%;" src='/static/profile/default.jpg' width="60" height="60"> </div> {% endblock %}
同時在img標籤中增長了一點css效果,style="border-radius:50%",長和高都爲60px,radius會將圖片渲染成圓形,src是圖片所在的位置。此時咱們只須要在static/profile目錄下放置一張用戶頭像的圖片,而後刷新我的主頁,就能看到用戶的頭像了:
上文中只是讓圖像簡單的顯示在主頁中,咱們並不能對其進行編輯,爲了實現改換用戶頭像的功能,咱們還須要增長圖片文件的上傳功能。在models.py文件中的user數據庫中增長profile_image字段,該字段用來保存用戶頭像圖片的文件名:
#.. class User(UserMixin, db.Model): __tablename__ = 'user' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(20), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password = db.Column(db.String(60), nullable=False) profile_image = db.Column(db.String(20), nullable=True) def __repr__(self): return f"User('{self.username}','{self.email}','{self.password}')"
因爲表明數據庫中表的models.py文件發生了變化,因此須要進行數據庫的遷移,可是在進行遷移以前,咱們先將config.py文件的位置放到與run.py同級目錄中,第4課中已經講了數據庫的遷移,你們能夠直接按照第4課的5.3小結內容進行操做便可,一樣在操做以前,須要設置環境變量,環境變量在第1課的3節最後已經講過如何設置了。遷移完成後database.db會在run.py同級目錄中生成。
數據庫的用戶表更新後,須要在forms.py文件中增長更新用戶信息使用的表單UpdateAccountForm:
#.. from flask_wtf.file import FileField, FileAllowed #.. class UpdateAccountForm(FlaskForm): username = StringField(u'用戶名', validators=[DataRequired(), Length(min=2,max=20)]) email = EmailField(u"郵箱", validators=[DataRequired()]) password = StringField(u'密碼', validators=[DataRequired()]) profile_image = FileField(u"更新頭像", validators=[FileAllowed(["png", "jpg"])]) submit = SubmitField(u'更新')
在表單中FileField字段使用了flask_wtf提供的FileAllowed驗證函數,它確保上傳的圖像只能是png和jpg兩種格式,FileField字段會被Jinja2渲染生成type="file"的<input>標籤。
在模板account.html中添加渲染前端表單的內容(html_form對象是經過路由函數傳到這裏的):
<!--繼承基模板--> {% extends "layout.html" %} {% block content %} <hr> <div> <h1>用戶:{{html_user.username}}</h1> <img alt="" style="border-radius: 50%;" src='/static/profile/default.jpg' width="60" height="60"> </div> <hr> <div> <form method="POST" action="{{ url_for('account') }}" enctype="multipart/form-data"> {{ html_form.hidden_tag() }} <fieldset> <div class="form-group"> {{ html_form.username.label(class="form-control-label") }} <br> {{ html_form.username(class="form-control form-control-lg" ) }} </div> <div class="form-group"> {{ html_form.email.label(class="form-control-label") }} <br> {{ html_form.email(class="form-control form-control-lg") }} </div> <div class="form-group"> {{ html_form.profile_image.label() }} <br> {{ html_form.profile_image(class="form-control-file") }} </div> </fieldset> <div class="form-group"> {{ html_form.submit(class="btn btn-outline-info") }} </div> </form> </div> {% endblock %}
上面已經完成account.html頁面前臺form表單的顯示(渲染)工做,這時就須要在視圖函數中(python文件)將表明表單的類傳遞到前端模板文件(html文件)中,下面在routes.py中完成視圖函數的編寫:
#.. # 從userprofile_demo.forms中導入UpdateAccountForm from userprofile_demo.forms import LoginForm, UpdateAccountForm # .. @app.route("/account") def account(): if not current_user.is_authenticated: return redirect(url_for('index')) form = UpdateAccountForm() form.email.data = user.email form.username.data = user.username return render_template("account.html", html_user=current_user, html_form=form) #..
這裏,視圖函數默認收到的是get請求,而且將當前登陸用戶的信息傳遞到form表單,最終用戶的信息將會在前端顯示出來(user.email是從數據庫中讀取email的值顯示在郵箱輸入框中,user.username表示從數據庫中讀取username的值顯示在用戶名輸入框中)。再次刷新我的主頁頁面,效果以下:
在上面的account.html文件中,img標籤中的src顯示的是固定的圖像,若是咱們須要顯示其它的用戶頭像,每次都須要修改account.html文件中的img標籤,可是咱們並不但願這麼作。爲了可以方便的更新用戶的頭像信息,咱們須要使用更加聰明的方法。咱們首先須要在config.py文件中配置用戶頭像保存的目錄,以下:
import os basedir = os.path.abspath(os.path.dirname(__file__)) class Config(object): SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'database.db') # 使用表單,對Flask_WTF進行配置 SECRET_KEY = 'miaojie is great!' # 配置上傳用戶頭像的目錄 PROFILE_PATH = "profile/"
而後咱們修改模板文件account.html中的img標籤,將src屬性的值更改成視圖函數傳過來的變量,以下:
<!--繼承基模板--> {% extends "layout.html" %} {% block content %} <hr> <div> <h1>用戶:{{html_user.username}}</h1> <img alt="" style="border-radius: 50%;" src='{{url_for("static", filename = html_user_image)}}' width="60" height="60"> </div> #.. {% endblock %}
下面咱們在routes.py文件中,對用戶的頭像圖片變量profile_image進行處理,使其能向前端傳入正確的用戶頭像文件:
#.. # 從flask中導入current_app from flask import current_app #.. @app.route("/account") def account(): if not current_user.is_authenticated: return redirect(url_for('index')) form = UpdateAccountForm() profile_image = current_user.profile_image if profile_image: profile_image = current_app.config["PROFILE_PATH"] + profile_image else: profile_image = current_app.config["PROFILE_PATH"] + "default.jpg" form.email.data = current_user.email form.username.data = current_user.username return render_template("account.html", title="第六天", html_user=current_user, html_form=form, html_user_image=profile_image) #..
當咱們尚未上傳新的用戶頭像文件時,profile_image變量爲None值,因此會使用default.jpg圖片文件做爲用戶的默認頭像。
上面只是實現了用戶頭像的顯示,這裏將介紹如何對用戶的頭像進行更新。當用戶選擇了新的頭像文件後,點擊「更新」按鈕時,會觸發post請求,此時account視圖函數須要對該post進行正確的處理,才能實現用戶頭像文件的上傳:
#.. @app.route("/account", methods=["GET", "POST"]) def account(): if not current_user.is_authenticated: return redirect(url_for('index')) form = UpdateAccountForm() profile_image = current_user.profile_image if profile_image: profile_image = current_app.config["PROFILE_PATH"] + profile_image else: profile_image = current_app.config["PROFILE_PATH"] + "default.jpg" if request.method == "POST": username = form.username.data email = form.email.data if form.profile_image.data: picture_file = save_user_face_image(form.profile_image.data) current_user.profile_image = picture_file current_user.username = username current_user.email = email db.session.commit() return redirect(url_for("account")) form.email.data = current_user.email form.username.data = current_user.username return render_template("account.html", title="第六天", html_user=current_user, html_form=form, html_user_image=profile_image) #..
上面代碼中,當account視圖函數拿到post請求後,從表單中獲取用戶輸入的用戶名和郵箱,並保存到username和email變量中。這裏,若是form.profile_image.data爲True,則表明用戶上傳了新的頭像文件,此時咱們須要將用戶上傳的文件保存至profile目錄,並使用新的用戶頭像文件名更新數據庫中的profile_image字段。
在上面的post請求中,尚未實現save_user_face_image()函數,該函數的定義,咱們將它單獨放在utils.py文件中。因爲該函數在處理圖片文件時,須要用到PIL模塊,因此咱們先使用pip命令來安裝pillow庫:
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day6$ pip install pillow
如今,在utils.py文件中實現save_user_face_image()函數:
import os,secrets from flask import current_app # 從PIL中導入Image類 from PIL import Image def save_user_face_image(image_data): random_hex = secrets.token_hex(8) f_name, f_ext = os.path.splitext(image_data.filename) new_image_name = random_hex + f_ext new_image_path = os.path.join(current_app.root_path, "static/profile", new_image_name) output_size = (125,125) i = Image.open(image_data) i.thumbnail(output_size) i.save(new_image_path) return new_image_name
在route.py文件中還須要導入save_user_face_image:
#.. from userprofile_demo.utils import save_user_face_image #..
OK!大功告成,此時,咱們瀏覽http://127.0.0.1:5005/account頁面,點擊「Browse」按鈕,選擇圖片後點擊「更新」按鈕,就能夠實現用戶頭像的更新了,效果以下:
學習完今天的內容,咱們實現了以下功能:
下一課的教程,貓姐將帶領你們一塊兒學習建立、更新和刪除日誌。今天的內容就到這裏,喜歡的同窗們能夠在下面點贊留言,或是訪問個人博客地址:http://www.catonlinepy.tech/ 加入咱們的QQ羣進一步交流學習!
你們能夠到github上獲取今天教程的全部代碼:https://github.com/miaojie19/...
具體下載代碼的命令以下:
# 使用git命令下載flask-course-primary倉庫全部的代碼 git clone https://github.com/miaojie19/flask-course-primary.git # 下載完成後,進入day6目錄下面,便可看到今天的代碼 cd flask-course-primary cd day6