Python是一种通用编程语言,可以用于多种场合。在本节中,我们将看到如何使用Python进行网络开发。Python有许多Web框架。Django和Flask是最流行的框架。今天,我们将学习如何使用Flask进行Web开发。
Flask是用Python编写的Web开发框架。Flask使用Jinja2模板引擎。Flask也可以与其他现代前端库(如React)一起使用。
如果你还没有安装virtualenv包,请先安装它。虚拟环境将允许隔离项目依赖与本地机器依赖。
完成所有步骤后,你的项目文件结构应该如下所示:
├── Procfile ├── app.py ├── env │ ├── bin ├── requirements.txt ├── static │ └── css │ └── main.css └── templates ├── about.html ├── home.html ├── layout.html ├── post.html └── result.html
按照以下步骤开始使用Flask。
步骤1:使用以下命令安装virtualenv。
pip install virtualenv
步骤2:
asabeneh@Asabeneh:~/Desktop$ mkdir python_for_web asabeneh@Asabeneh:~/Desktop$ cd python_for_web/ asabeneh@Asabeneh:~/Desktop/python_for_web$ virtualenv venv asabeneh@Asabeneh:~/Desktop/python_for_web$ source venv/bin/activate (env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze (env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip install Flask (env) asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze Click==7.0 Flask==1.1.1 itsdangerous==1.1.0 Jinja2==2.10.3 MarkupSafe==1.1.1 Werkzeug==0.16.0 (env) asabeneh@Asabeneh:~/Desktop/python_for_web$
我们创建了一个名为python_for_web的项目目录。在项目中,我们创建了一个虚拟环境venv,它可以是任何名称,但我喜欢称它为_venv_。然后我们激活了虚拟环境。我们使用pip freeze检查项目目录中安装的包。pip freeze的结果是空的,因为还没有安装包。
现在,让我们在项目目录中创建app.py文件并编写以下代码。app.py文件将是项目中的主文件。以下代码包含flask模块和os模块。
主页路由。
# 导入flaskfromflaskimportFlaskimportos# 导入操作系统模块app=Flask(__name__) @app.route('/') # 这个装饰器创建主页路由defhome (): return'<h1>欢迎</h1>'@app.route('/about')defabout(): return'<h1>关于我们</h1>'if__name__=='__main__': # 部署时我们使用环境变量# 使其同时适用于生产和开发环境port=int(os.environ.get("PORT", 5000)) app.run(debug=True, host='0.0.0.0', port=port)
要运行flask应用程序,在主flask应用程序目录中输入python app.py。
运行_python app.py_后,检查本地主机5000端口。
让我们添加额外的路由。 创建关于路由
# 导入flaskfromflaskimportFlaskimportos# 导入操作系统模块app=Flask(__name__) @app.route('/') # 这个装饰器创建主页路由defhome (): return'<h1>欢迎</h1>'@app.route('/about')defabout(): return'<h1>关于我们</h1>'if__name__=='__main__': # 部署时我们使用环境变量# 使其同时适用于生产和开发环境port=int(os.environ.get("PORT", 5000)) app.run(debug=True, host='0.0.0.0', port=port)
现在,我们在上面的代码中添加了关于路由。如果我们想渲染HTML文件而不是字符串呢?使用render_template函数可以渲染HTML文件。让我们在项目目录中创建一个名为templates的文件夹,并在其中创建home.html和about.html。让我们也从flask导入render_template函数。
在templates文件夹内创建HTML文件。
home.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><title>主页</title></head><body><h1>欢迎回家</h1></body></html>
about.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><title>关于</title></head><body><h1>关于我们</h1></body></html>
app.py
# 导入flaskfromflaskimportFlask, render_templateimportos# 导入操作系统模块app=Flask(__name__) @app.route('/') # 这个装饰器创建主页路由defhome (): returnrender_template('home.html') @app.route('/about')defabout(): returnrender_template('about.html') if__name__=='__main__': # 部署时我们使用环境变量# 使其同时适用于生产和开发环境port=int(os.environ.get("PORT", 5000)) app.run(debug=True, host='0.0.0.0', port=port)
正如你所看到的,为了访问不同的页面或进行导航,我们需要一个导航。让我们为每个页面添加一个链接,或者创建一个我们用于每个页面的布局。
<ul><li><ahref="/">主页</a></li><li><ahref="/about">关于</a></li></ul>
现在,我们可以使用上面的链接在页面之间导航。让我们创建一个额外的页面来处理表单数据。你可以给它取任何名字,我喜欢称它为post.html。
我们可以使用Jinja2模板引擎向HTML文件注入数据。
# 导入flaskfromflaskimportFlask, render_template, request, redirect, url_forimportos# 导入操作系统模块app=Flask(__name__) @app.route('/') # 这个装饰器创建主页路由defhome (): techs= ['HTML', 'CSS', 'Flask', 'Python'] name='30天Python编程挑战'returnrender_template('home.html', techs=techs, name=name, title='主页') @app.route('/about')defabout(): name='30天Python编程挑战'returnrender_template('about.html', name=name, title='关于我们') @app.route('/post')defpost(): name='编程语言文章'path=request.pathreturnrender_template('post.html', name=name, path=path, title='文章') if__name__=='__main__': # 部署时我们使用环境变量# 使其同时适用于生产和开发环境port=int(os.environ.get("PORT", 5000)) app.run(debug=True, host='0.0.0.0', port=port)
home.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><title>{{title}}</title></head><body><ul><li><ahref="/">主页</a></li><li><ahref="/about">关于</a></li><li><ahref="/post">文章</a></li></ul><h1>欢迎回到{{name}}</h1><ul> {% for tech in techs %} <li>{{tech}}</li> {% endfor %} </ul></body></html>
about.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><title>{{title}}</title></head><body><ul><li><ahref="/">主页</a></li><li><ahref="/about">关于</a></li><li><ahref="/post">文章</a></li></ul><h1>关于{{name}}</h1></body></html>
post.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><title>{{title}}</title></head><body><ul><li><ahref="/">主页</a></li><li><ahref="/about">关于</a></li><li><ahref="/post">文章</a></li></ul><h1>{{name}}</h1><p>当前路径: {{path}}</p><formaction="/result" method="POST"><div><inputtype="text" name="first_name" placeholder="第一名字" required/></div><div><inputtype="text" name="last_name" placeholder="姓氏" required/></div><div><inputtype="text" name="old_job" placeholder="旧工作" /></div><div><inputtype="text" name="current_job" placeholder="当前工作" /></div><div><inputtype="text" name="country" placeholder="国家" /></div><div><buttontype="submit">提交</button></div></form></body></html>
现在,让我们添加一个接收表单数据的路由。我们使用POST方法,因为我们将收到表单数据。
# 导入flaskfromflaskimportFlask, render_template, request, redirect, url_forimportos# 导入操作系统模块app=Flask(__name__) @app.route('/') # 这个装饰器创建主页路由defhome(): techs= ['HTML', 'CSS', 'Flask', 'Python'] name='30天Python编程挑战'returnrender_template('home.html', techs=techs, name=name, title='主页') @app.route('/about')defabout(): name='30天Python编程挑战'returnrender_template('about.html', name=name, title='关于我们') @app.route('/post')defpost(): name='文章'returnrender_template('post.html', name=name, title='文章') @app.route('/result', methods=['POST'])defresult(): first_name=request.form['first_name'] last_name=request.form['last_name'] old_job=request.form['old_job'] current_job=request.form['current_job'] country=request.form['country'] print(first_name, last_name, old_job, current_job, country) result_data= { 'first_name':first_name, 'last_name':last_name, 'old_job': old_job, 'current_job': current_job, 'country':country } returnrender_template('result.html', result_data=result_data, title='结果') if__name__=='__main__': # 部署时我们使用环境变量# 使其同时适用于生产和开发环境port=int(os.environ.get("PORT", 5000)) app.run(debug=True, host='0.0.0.0', port=port)
result.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><title>{{title}}</title></head><body><ul><li><ahref="/">主页</a></li><li><ahref="/about">关于</a></li><li><ahref="/post">文章</a></li></ul><h1>表单数据</h1><ul><li>第一名字: {{result_data.first_name}}</li><li>姓氏: {{result_data.last_name}}</li><li>旧工作: {{result_data.old_job}}</li><li>当前工作: {{result_data.current_job}}</li><li>国家: {{result_data.country}}</li></ul></body></html>
在现实世界中,我们不会在所有页面中重复HTML代码。而是创建一个布局并将其继承到其他文件中。让我们使用继承(模板)。现在,我们需要创建的不是三个不同的文件,而是一个名为layout.html的布局文件。然后其他文件将继承它。
layout.html
<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8" /><metaname="viewport" content="width=device-width, initial-scale=1.0" /><linkhref="https://fonts.googleapis.com/css?family=Lato:300,400|Nunito:300,400|Raleway:300,400&display=swap" rel="stylesheet" /><linkrel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}" /> {% if title %} <title>30天Python - {{ title}}</title> {% else %} <title>30天Python</title> {% endif %} </head><body><header><divclass="menu-container"><div><aclass="brand-name nav-link" href="/">30天Python</a></div><ulclass="nav-lists"><liclass="nav-list"><aclass="nav-link active" href="{{ url_for('home') }}">主页</a></li><liclass="nav-list"><aclass="nav-link active" href="{{ url_for('about') }}">关于</a></li><liclass="nav-list"><aclass="nav-link active" href="{{ url_for('post') }}">文章</a></li></ul></div></header><main> {% block content %} {% endblock %} </main></body></html>
在上面的布局中,我们创建了一个可以被所有继承布局的页面使用的公共布局。在布局内部,我们可以看到导航链接。我们使用{% block content %}{% endblock %}标记以允许子模板添加内容。
home.html
{% extends 'layout.html' %} {% block content %} <divclass="container"><h1>欢迎回到{{name}}</h1><p> 这个项目是通过使用以下技术构建的: <spanclass="tech">Flask</span>, <spanclass="tech">Python</span> and <spanclass="tech">HTML</span> CSS</span></p><ul> {% for tech in techs %} <liclass="tech">{{tech}}</li> {% endfor %} </ul></div> {% endblock %}
about.html
{% extends 'layout.html' %} {% block content %} <divclass="container"><h1>关于{{name}}</h1><p> 这个挑战是一个30天编程挑战,旨在帮助你学习Python编程语言,通过每天解决一个Python问题。 </p></div> {% endblock %}
post.html
{% extends 'layout.html' %} {% block content %} <divclass="container"><h1>{{name}}</h1><p>{{path}}</p><formaction="/result" method="POST"><div><inputtype="text" name="first_name" placeholder="第一名字" required/></div><div><inputtype="text" name="last_name" placeholder="姓氏" required/></div><div><inputtype="text" name="old_job" placeholder="旧工作" /></div><div><inputtype="text" name="current_job" placeholder="当前工作" /></div><div><inputtype="text" name="country" placeholder="国家" /></div><div><buttontype="submit">提交</button></div></form></div> {% endblock %}
result.html
{% extends 'layout.html' %} {% block content %} <divclass="container"><h1>表单数据</h1><ul><li>第一名字: {{result_data.first_name}}</li><li>姓氏: {{result_data.last_name}}</li><li>旧工作: {{result_data.old_job}}</li><li>当前工作: {{result_data.current_job}}</li><li>国家: {{result_data.country}}</li></ul></div> {% endblock %}
以下是main.css文件,我们将把它放在static/css目录中:
/* === GENERAL === */body { margin:0; padding:0; font-family:"Lato", sans-serif; background-color:#f0f8ea; } .container { max-width:80%; margin: auto; padding:30px; } ul { list-style-type: none; padding:0; } .tech { color:#5bbc2e; } /* === HEADER === */header { background-color:#5bbc2e; } .menu-container { display: flex; justify-content: space-between; padding:20px30px; } .brand-name { color: white; font-weight:800; font-size:24px; } .nav-lists { display: flex; } .nav-list { margin-right:15px; } .nav-link { text-decoration: none; color: white; font-weight:300; } /* === FORM === */form { margin:30px0; border:1px solid #ddd; padding:30px; border-radius:10px; } form>div { margin-bottom:15px; } input { width:100%; padding:10px; border:1px solid #ddd; border-radius:5px; outline:0; font-size:16px; box-sizing: border-box; margin-top:5px; } button { padding:12px24px; border:0; background-color:#5bbc2e; color: white; border-radius:10px; font-size:16px; outline:0; cursor: pointer; } button:hover { background-color:#4b9c25; }
Heroku提供了一个免费的托管服务。如果你想要部署一个应用程序,你应该有一个Heroku账户。
asabeneh@Asabeneh:~/Desktop/python_for_web$ heroku login heroku: Press any key to open up the browser to login or q to exit: Opening browser to https://cli-auth.heroku.com/auth/cli/browser/ec0972d5-d8c6-4adf-b004-a42a22dd09a8 Logging in... done Logged in as asabeneh@gmail.com asabeneh@Asabeneh:~/Desktop/python_for_web$
在部署应用程序之前,我们需要告诉Heroku哪些依赖包需要安装,以及如何运行应用程序。Heroku使用requirements.txt文件获取应用程序依赖包的信息。使用pip freeze命令列出所有依赖包及其版本,并将其写入requirements.txt。
asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze Click==7.0 Flask==1.1.1 itsdangerous==1.1.0 Jinja2==2.10.3 MarkupSafe==1.1.1 Werkzeug==0.16.0 asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze > requirements.txt
Procfile告诉Heroku如何运行应用程序。在本例中,我们使用Gunicorn作为WSGI HTTP服务器,用于运行Python Web应用程序。我们需要将Gunicorn添加到我们的依赖包中。
asabeneh@Asabeneh:~/Desktop/python_for_web$ pip install gunicorn asabeneh@Asabeneh:~/Desktop/python_for_web$ pip freeze > requirements.txt
现在,让我们创建一个Procfile,并添加以下内容:
web: gunicorn app:app
asabeneh@Asabeneh:~/Desktop/python_for_web$ heroku create 30-days-of-python-app Creating ⬢ 30-days-of-python-app... done https://30-days-of-python-app.herokuapp.com/ | https://git.heroku.com/30-days-of-python-app.git asabeneh@Asabeneh:~/Desktop/python_for_web$ git init Initialized empty Git repository in /home/asabeneh/Desktop/python_for_web/.git/ asabeneh@Asabeneh:~/Desktop/python_for_web$ heroku git:remote -a 30-days-of-python-app set git remote heroku to https://git.heroku.com/30-days-of-python-app.git asabeneh@Asabeneh:~/Desktop/python_for_web$ echo -e "venv\n.vscode"> .gitignore asabeneh@Asabeneh:~/Desktop/python_for_web$ git add . asabeneh@Asabeneh:~/Desktop/python_for_web$ git commit -m "first python web app" [master (root-commit) 9dfcc6a] first python web app 9 files changed, 403 insertions(+) create mode 100644 .gitignore create mode 100644 Procfile create mode 100644 app.py create mode 100644 requirements.txt create mode 100644 static/css/main.css create mode 100644 templates/about.html create mode 100644 templates/home.html create mode 100644 templates/layout.html create mode 100644 templates/post.html create mode 100644 templates/result.html asabeneh@Asabeneh:~/Desktop/python_for_web$ git push heroku master Enumerating objects: 14, done. Counting objects: 100% (14/14), done. Delta compression using up to 2 threads Compressing objects: 100% (12/12), done. Writing objects: 100% (14/14), 6.08 KiB | 1.52 MiB/s, done. Total 14 (delta 2), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected remote: -----> Installing python-3.6.10 remote: -----> Installing pip remote: -----> Installing dependencies with Pipenv 2018.5.18… remote: Installing dependencies from Pipfile.lock (872ae5)… remote: -----> Installing SQLite3 remote: -----> $ python manage.py collectstatic --noinput remote: Traceback (most recent call last): remote: File "manage.py", line 10, in<module> remote: from app import app remote: ModuleNotFoundError: No module named 'app' remote: remote: ! Error while running '$ python manage.py collectstatic --noinput'. remote: See traceback above for details. remote: remote: You may need to update application code to resolve this error. remote: Or, you can disable collectstatic for this application: remote: remote: $ heroku config:set DISABLE_COLLECTSTATIC=1 remote: remote: https://devcenter.heroku.com/articles/django-assets remote: -----> Discovering process types remote: Procfile declares types -> web remote: remote: -----> Compressing... remote: Done: 55.7M remote: -----> Launching... remote: Released v3 remote: https://30-days-of-python-app.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/30-days-of-python-app.git * [new branch] master -> master asabeneh@Asabeneh:~/Desktop/python_for_web$
如你所见,我们已经成功地创建了第一个网络应用程序、对其进行部署,并将其托管在Heroku上。您可以使用此链接尝试本应用程序。
事不宜迟,让我们做一些练习,巩固所学到的知识。
创建一个名为"成绩计算器"的Flask应用程序。用户可以输入他们的分数、科目名称,然后应用程序应根据分数显示不同的消息:
- 如果分数≥90,显示"优秀!你的[科目]成绩是[分数]"。
- 如果80≤分数<90,显示"很好!你的[科目]成绩是[分数]"。
- 如果70≤分数<80,显示"一般!你的[科目]成绩是[分数]"。
- 如果60≤分数<70,显示"及格!你的[科目]成绩是[分数]"。
- 如果分数<60,显示"你需要更加努力!你的[科目]成绩是[分数]"。
创建一个"体重指数计算器"应用程序,计算体重指数(BMI),公式为体重(kg)/(身高(m)²)。根据BMI值显示不同的健康状态:
- BMI<18.5:"体重过轻"
- 18.5≤BMI<24.9:"健康体重"
- 25≤BMI<29.9:"超重"
- BMI≥30:"肥胖"
创建一个博客应用程序,用户可以添加、编辑和删除博客文章。
创建一个"任务管理器"应用程序,用户可以添加、查看和删除任务。
🎉 恭喜!🎉