当前位置: 首页 > 工具软件 > flask-base > 使用案例 >

如何使用Python和Flask构建Web应用程序—深入的教程

俞俊逸
2023-12-01

Python is an incredibly versatile language. It’s considered to be a staple of modern development. It’s used for the simplest of scripts to complex machine learning and neural network training algorithms.

Python是一种难以置信的通用语言。 它被认为是现代发展的主要内容。 它用于最简单的脚本到复杂的机器学习和神经网络训练算法。

But perhaps the less-known usage of Python is its use as a web server. Overshadowed by more popular frameworks like as Node/Express and Ruby on Rails, Python is often overlooked as a web server choice for most developers.

但是,也许鲜为人知的Python用法是将其用作Web服务器。 Python被Node / Express和Ruby on Rails等更流行的框架所掩盖,对于大多数开发人员而言,Python通常被视为Web服务器的选择。

Having a backend written in Python is really useful for several reasons, among which are:

使用Python编写后端确实很有用,原因有以下几个:

  • It’s easy to step up from learning Python as a regular scripting language to using it to make a backend.

    从学习将Python作为一种常规脚本语言升级到使用它作为后端很容易。
  • It’s best to use if you plan on serving parts of your application that are already written in Python (For example - submitting a form, evaluating input via a Tensorflow model, and returning the output to a use)

    如果计划服务已用Python编写的应用程序部分,则最好使用(例如-提交表单,通过Tensorflow模型评估输入,并将输出返回给用户)
  • It has a diverse ecosystem of packages and tools to help you with development, not to mention a great community of developers (since the language has been around so long)

    它具有多样化的软件包和工具生态系统,可以帮助您进行开发,更不用说庞大的开发人员社区了(因为这种语言已经存在了很长时间)

The purpose of this article is to demonstrate how Python can be used to create a full stack web application. In this tutorial, I will be using Flask, a Python “microframework” to developing a web application.

本文的目的是演示如何使用Python创建完整的Web应用程序。 在本教程中,我将使用Flask(Python“微框架”)开发Web应用程序。

I would be remiss not to mention that there are other more popular Python frameworks out there such as Django, but Flask is useful for the budding developer since it is bare bones and requires developers to create/utilize the components they need within the App based on their requirement (rather than calling some command line tool that generates 20 files automatically… lookin’ at you Ruby on Rails). Of course, I won’t be going through how to start a Web App completely from scratch, rather I’ll give you an intro to Flask and then move onto how you can use a project called flask-base to get upto speed quickly in the future.

不用说我还有其他更流行的Python框架,例如Django,但是Flask对于刚起步的开发人员很有用,因为它是基础知识,并且要求开发人员根据以下内容在App中创建/利用他们需要的组件他们的要求(而不是调用某些会自动生成20个文件的命令行工具,而是在Ruby on Rails上查找)。 当然,我不会讲解如何从头开始完全启动Web应用程序,而是向您介绍Flask,然后继续介绍如何使用名为flask-base的项目来快速入门。未来。

烧瓶简介 (Intro to Flask)

Flask is a microframework (read as: It doesn’t come with much) for web development in Python. Before we do a deep(ish) dive, let’s cover some basic concepts of backend development.

Flask是用Python进行Web开发的微框架(读作:不多)。 在深入探讨之前,让我们介绍一些后端开发的基本概念。

路线 (Routes)

Let’s imagine you’re visiting apple.com and want to go to the Mac section at apple.com/mac/. How do Apple’s servers know to serve you the specific page that shows the details about Mac devices. It is most likely because they have a web app running on a server that knows when someone looks up apple.com and goes to the /mac/ section of the website, handle that request and send some pages back. The logic behind figuring out what to do when someone goes to /mac/ is done by a route.

假设您要访问apple.com并想转到apple.com/mac/的Mac部分。 Apple的服务器如何知道如何为您提供特定页面,该页面显示有关Mac设备的详细信息。 这很可能是因为他们有一个运行在服务器上的Web应用程序,该应用程序知道何时有人查找apple.com并转到网站的/mac/部分,处理该请求并发回一些页面。 弄清楚当有人去/mac/时该怎么做的逻辑是由一条路线完成的。

So when I visit apple.com (implied apple.com/), the / route handles what is shown. If I go to apple.com/purchase, there is a /purchase route. If I go to apple.com/purchase/1 where 1 is some item identifier, there most likely is a generic route handler /purchase/<int:item-id> that handles that request. Routes can handle both GET and POST requests as well.

因此,当我访问apple.com (暗示apple.com/ )时, /路由会处理显示的内容。 如果我转到apple.com/purchase ,则有一条/purchase路线。 如果我去apple.com/purchase/1 ,其中1是某些项目标识符,则很可能是通用路由处理程序/purchase/<int:item- id>处理该请求。 路由也可以处理GET和POST请求。

基本应用 (Basic App)

So how do we make a basic Flask app that has routes? Well, let’s take a look at the docs. Create a Python file called hello.py that contains the following.

那么我们如何制作具有路线的基本Flask应用程序呢? 好吧,让我们看一下文档。 创建一个名为hello.py的Python文件,其中包含以下内容。

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
	return "Hello World"

Let’s break down what’s happening here.

让我们分解一下这里发生的事情。

  1. We import our Flask dependency

    我们导入Flask依赖项
  2. We create an instance of a Flask App. The argument passed into the Flask instantiator (__name__) evaluates to a string that "names" the Flask App. When run from the command line, __name__ == "__main__". You can set the first argument to whatever you want.

    我们创建一个Flask应用的实例。 传递给Flask实例化器( __name__ )的参数的计算结果为一个为Flask应用程序“命名”的字符串。 从命令行运行时, __name__ == "__main__" 。 您可以将第一个参数设置为所需的任何值。

  3. We set up a route / on our App that executes the hello() function immediately below it when that route is visited. Note that the function must return a string or a rendered template.

    我们在应用程序上设置了一个路由/ ,当访问该路由时,将在其下方立即执行hello( )函数。 请注意,该函数必须返回字符串或渲染的模板。

On the command line, let’s set up something called a virtual environment (it will help us isolate our development environment and package installations from the rest of our system).

在命令行上,让我们设置一个称为虚拟环境的东西(它将帮助我们将开发环境和软件包安装与系统的其余部分隔离开)。

  1. If you haven’t done so already, install pip via easy_install pip (you may need to run sudo in front of this if you are on a Mac.

    如果尚未安装,请通过easy_install pip安装pip(如果您使用的是Mac,则可能需要在此之前运行sudo

  2. Run pip install virtualenv to install virtualenv using pip

    运行pip install virtualenv以使用pip安装virtualenv

  3. In the directory of your App, create your virtual environment by running virtualenv venv (this creates a virtual environment in a folder called venv of the current directory).

    在您应用程序的目录中,通过运行virtualenv venv创建虚拟环境(这会在当前目录的venv文件夹中创建一个虚拟环境)。

  4. Run source venv/bin/activate to activate the virtual environment. This is specifically required to install packages into it. You can deactivate the virtual environment by running deactivate from the command line. Pretty simple.

    运行source venv/bin/activate以激活虚拟环境。 将软件包安装到其中特别需要此操作。 您可以通过从命令行运行deactivate来停用虚拟环境。 很简单

Now that our virtual environment is installed and activated, let’s install Flask. It’s really simple, just run pip install Flask. We can then run the example from earlier by writing the following in our command line.

现在已经安装并激活了我们的虚拟环境,让我们安装Flask。 这非常简单,只需运行pip install Flask 。 然后,我们可以通过在命令行中编写以下代码来运行该示例。

FLASK_APP=hello.py flask run

You should see something like * Running on http://localhost:5000/ in your terminal. And if you visit that link in your browser, you'll see a page with just Hello World.

您应该在终端中看到* Running on http://localhost:5000/ 。 而且,如果您在浏览器中访问该链接,则会看到仅包含Hello World的页面。

示例应用程序:Penn Club评论 (Example App: Penn Club Review)

Note: The code for this project can be found at this repository on GitHub.

注意:该项目的代码可以在GitHub的此存储库中找到

Now let’s figure out some project to create in order to demonstrate the full capabilities of Flask. One recent project I came up with is a club rating app called “PennClubReview”.

现在,让我们找出一些要创建的项目,以演示Flask的全部功能。 我最近提出的一个项目是一个名为“ PennClubReview”的俱乐部评级应用程序。

I’m currently attending the University of Pennsylvania. One of the most common problems that freshmen on campus face is choosing which clubs to join on campus. This process is further complicated by the fact that some clubs are very competitive to get into, have multiple interview rounds, and require a large time commitment. Often, none of these aspects of clubs are discussed during club information sessions.

我目前正在宾夕法尼亚大学上学。 大学新生面对的最普遍的问题之一是选择哪些俱乐部加入校园。 由于有些俱乐部进入竞争非常激烈,需要进行多次面试,而且需要大量时间投入,因此这一过程变得更加复杂。 通常,在俱乐部信息会议期间不会讨论俱乐部的这些方面。

So, in order to combat this issue, we can create an app where

因此,为了解决这个问题,我们可以创建一个应用

  • An administrator can set survey questions for users to answer about clubs.

    管理员可以设置调查问题,以供用户回答有关俱乐部的问题。
  • Users can view average ratings for each survey question for each club

    用户可以查看每个俱乐部的每个调查问题的平均评分
  • Users can view individual responses for clubs. If a user chooses to submit another review, their previous response is overwritten.

    用户可以查看俱乐部的个人回复。 如果用户选择提交其他评论,则其先前的答复将被覆盖。
  • Users can suggest clubs for administrators to edit/approve to show in public (administrators should be notified via email when this happens)

    用户可以建议俱乐部供管理员编辑/批准以在公共场所显示(发生这种情况时,应通过电子邮件通知管理员)
  • A user or admin needs to be able to edit their own account information.

    用户或管理员需要能够编辑自己的帐户信息。
  • An administrator should have the ability to add/remove users, survey questions, club categories and clubs from the system.

    管理员应具有从系统中添加/删除用户,调查问题,俱乐部类别和俱乐部的能力。

分解应用程序组件 (Breaking down App components)

In order to develop this app, we’ll need to have some more components in addition to Flask, such as a backing database, a login management system, some way to organize routes, and handle emailing. We could code this from scratch. But, there is already an amazing boilerplate that can give you a great starting place.

为了开发该应用程序,除了Flask之外,我们还需要其他一些组件,例如后备数据库,登录管理系统,一些组织路线和处理电子邮件的方法。 我们可以从头开始编写代码。 但是,已经有一个惊人的样板可以为您提供一个很好的起点。

输入烧瓶瓶 (Enter Flask-Base)

Flask-base is a project that my friends and I developed as part of a student run nonprofit called Hack4Impact. We work with nonprofits over a semester to develop technical projects that help them accomplish their mission.

Flask-base是我和我的朋友们在一个学生组织的非营利组织Hack4Impact中开发的一个项目。 我们在一个学期内与非营利组织合作,开发可帮助他们完成任务的技术项目。

While working on so many projects, we found out that we would often be repeating the same code across all of our applications. So we decided to create a single code base containing the most common parts that any App we made would need. This code base would include:

在进行许多项目时,我们发现我们经常在所有应用程序中重复相同的代码。 因此,我们决定创建一个代码库,其中包含我们制作的任何App所需的最常见部分。 该代码库将包括:

  • User authentication scheme

    用户认证方案
  • Account management

    帐户管理
  • Blueprints (to handle routes)

    蓝图(处理路线)
  • Backing database

    支持数据库
  • Emailing (with a redis queue)

    电子邮件发送(带有Redis队列)

It recently became fairly popular, garnering 1200+ GitHub stars over the course of a few months. This codebase is perfect for what we are trying to set up. You can find the GitHub repo containing the code for Flask base here.

它最近变得相当流行,在几个月的时间内获得了1200多个GitHub星。 该代码库非常适合我们尝试设置的内容。 您可以在此处找到包含Flask base代码的GitHub存储库。

应用开发设置 (App dev setup)

First let’s clone flask-base. Follow the instructions on the README.md page. In a nutshell run the following.

首先,让我们克隆flask-base。 请按照README.md页上的说明进行操作。 简而言之,运行以下命令。

git clone https://github.com/hack4impact/flask-base.git

cd flask-base

virtualenv  venv

source venv/bin/activate

pip install -r requirements.txt

python manage.py recreate_db

python manage.py setup_dev

OK. I’ll elaborate on what we’ve done here.

好。 我将在这里详细说明我们所做的。

  • Clone the repository from GitHub (i.e. download it) and then go into its directory.

    从GitHub克隆存储库(即下载它),然后进入其目录。
  • Create a new virtual environment and activate it.

    创建一个新的虚拟环境并激活它。
  • Read the package dependencies in the requirements.txt file and install all of them via pip.

    阅读requirements.txt文件中的软件包依赖项,然后通过pip安装所有依赖项。

  • Instantiate the database (recreate it) and also insert an administrator rule (via setup_dev).

    实例化数据库(重新创建数据库),并插入管理员规则(通过setup_dev)。

Additionally, let’s create a running database migration. This will keep track of changes in our database models without needing to recreate our database (i.e. remove all the information and then rebuild the database from scratch). Migrations allow us to preserve information. We can do this via the command below.

另外,让我们创建一个正在运行的数据库迁移。 这将跟踪数据库模型中的更改,而无需重新创建数据库(即删除所有信息,然后从头开始重建数据库)。 迁移使我们能够保留信息。 我们可以通过下面的命令来做到这一点。

To run the App, run honcho start -f Local (you'll need to install Honcho if you haven't already). If you have any issues, chances are they have been addressed in the README of flask-base already. Now you can visit localhost:5000 and pull up a running flask-base application.

要运行该应用程序,请运行honcho start -f Local (如果尚未安装,则需要安装Honcho)。 如果您有任何问题,很可能已经在flask-base的自述文件中解决了。 现在您可以访问localhost:5000并启动一个正在运行的flask-base应用程序。

To log into the App as an administrator, go to the login link and type in for the username flask-base-admin@example.com with a password password. You can then invite new users into the application from the administrator screen. Note that before you do so, you'll need to create a config.env file that contains the following two variables:

要以管理员身份登录该应用,请转到登录链接,然后输入用户名flask-base-admin@example.com和密码password 。 然后,您可以从管理员屏幕邀请新用户加入应用程序。 请注意,在执行此操作之前,您需要创建一个包含以下两个变量的config.env文件:

MAIL_PASSWORD=someSendGridPasswordMAIL_USERNAME=someSendGridUsername

Upon creation of a user account, the account remains unconfirmed until the new invited user clicks a link sent to their email. Additionally, a user can register for the App and will go through a similar authentication flow with regards to confirmation.

创建用户帐户后,该帐户将一直未确认,直到新邀请的用户单击发送到其电子邮件的链接为止。 此外,用户可以注册该应用程序,并将通过类似的身份验证流程进行确认。

Look through the flask-base documentation to get a better sense of some of the capabilities of flask-base out of the box. For now, we’re going to move on to how we can use it to make our App.

查看烧瓶瓶文档,以更好地了解烧瓶瓶的某些功能。 现在,我们将继续介绍如何使用它来制作我们的App。

数据库! (Databases!)

All our database logic is wrapped by the SQLAlchemy ORM so we don’t have to make very verbose database statements to run queries or add / delete records. All the database models (think of them as classes) are contained within the app/models folder. Let's think of some models that are needed for the application itself.

我们所有的数据库逻辑都由SQLAlchemy ORM封装,因此我们不必制作非常冗长的数据库语句即可运行查询或添加/删除记录。 所有数据库模型 (将它们视为类)都包含在app/models文件夹中。 让我们考虑一下应用程序本身所需的一些模型。

So we need to have a Club model that contains the name of the club (Datatype: String), a club description (Datatype: Text) and a variable is_confirmed (Datatype: Boolean) to keep track of whether a club that is suggested has been approved by an administrator to be shown. Additionally, we want some way to refer to the categories of a club, and another way to refer to the question answers that belong to a club.

因此,我们需要一个Club模型,其中包含Clubname (数据类型:字符串),俱乐部description (数据类型:文本)和变量is_confirmed (数据类型:布尔值),以跟踪所建议的俱乐部是否已被由管理员批准显示。 此外,我们需要某种方式来引用俱乐部的类别,以及另一种方式来引用属于俱乐部的问题答案。

Let’s think about how Clubs and Club Categories should relate to each other. We can think of it as follows. A club has many categories (e.g. a club can be a Social Impact and Tech club) and a club category can belong to many clubs (e.g. there can be many Tech clubs on campus). The only attribute this ClubCategory has a category_name (Datatype: String).

让我们考虑一下“俱乐部”和“俱乐部类别”应该如何相互关联。 我们可以这样认为。 俱乐部具有许多类别(例如,一个俱乐部可以是“ Social ImpactTech俱乐部),而俱乐部类别可以属于许多俱乐部(例如,校园中可以有很多Tech俱乐部)。 该ClubCategory的唯一属性具有category_name (数据类型:字符串)。

We can create this relationship (a many to many relationship), via an association table.

我们可以通过关联表创建此关系(多对多关系)。

俱乐部和俱乐部类别(多对多) (Club and Club Categories (Many to Many))

Now how do we encode that logic into flask-base? First, create a file called club.py in app/models. First let's create the Club and ClubCategory models.

现在,我们如何将这种逻辑编码为flask-base? 首先,在app/models创建一个名为club.py的文件。 首先,我们创建ClubClubCategory模型。

So now we have two models, but they aren’t connected to each other. Each of them have individual attributes, but neither can be explicitly connected to each other. We make the connection via an association as I mentioned earlier. After the db import, add the following lines.

因此,现在我们有两个模型,但是它们没有相互连接。 它们每个都具有各自的属性,但是两者都不能明确地相互连接。 如前所述,我们通过关联建立连接。 db导入后,添加以下行。

What this does is create a new association table (an intermediary between the Club and ClubCategory model). There are two columns in this table club_id and club_category_id which refer to the respective id’s of their respective models (note that the id attribute is a Primary Key within each model, i.e. the thing that is unique for each record). But within the association table, we refer to these Primary Keys as Foreign Keys (because they are refering to other tables). Additionally, we need to add a line to the Club model at the bottom.

这样做是创建一个新的关联表(Club和ClubCategory模型之间的中介)。 该表中有两列club_idclub_category_id ,它们引用各自模型的各自ID(请注意, id属性是每个模型内的主键,即每个记录的唯一属性)。 但是在关联表中,我们将这些主键称为外键(因为它们正在引用其他表)。 此外,我们需要在底部的Club模型中添加一条线。

categories = db.relationship(        'ClubCategory', secondary=club_category_assoc, backref='clubs')

And this actually creates the bidirectional relationship between the Club and ClubCategory models and sets up a relationship between Club and ClubCategory using the club_category_assoc association table. The backref tells the ClubCategory model how to refer to the Club models. So, with a given club club, you can run club.categories to get an array of category object backs. With a given ClubCategory called category, you can get all the clubs in that category by doing category.clubs.

这实际上创建之间的双向关系, ClubClubCategory达之间的关系模型和集ClubClubCategory使用club_category_assoc关联表。 backref告诉ClubCategory模型如何引用Club模型。 因此,对于给定的club club ,您可以运行club.categories以获得类别对象club.categories阵列。 对于给定的ClubCategory称为category ,你可以通过执行获得该类别所有的俱乐部category.clubs

You can see this in action by doing the following:

您可以通过执行以下操作来查看此操作:

In app/models/__init__.py add the line

app/models/__init__.py添加以下行

And then run python manage.py shell. Run the following commands to interact with your database models (note that >;>> indicates an input you put in).

然后运行python manage.py shell 。 运行以下命令与数据库模型进行交互(请注意, > ; >>表示您输入的输入)。

问题与答案(多对一) (Questions and Answers (Many to One))

Great! We now have a working Club and ClubCategory model. Now let’s move onto the Question and Answer models. For a question, we need to keep track of the content of the question which will be a String containing the text of the question itself. We will also include a max_rating attribute that will contain the maximum rating an individual can give for the question. For example, if the question content is "Rate the community of the club 10 is the best", we could set max_rating to be 10. Additionally, we'll keep track of a boolean free_response to determine whether we will allow people to include an optional extra response that is long form. Lastly, we will need to have a relation to the Answer model. This will be a one to many relation because a question can have multiple answers but an answer can only have one question.

大! 现在,我们有了一个有效的Club和ClubCategory模型。 现在,让我们走上了QuestionAnswer的机型。 对于问题,我们需要跟踪问题的content ,该content将是包含问题本身文本的字符串。 我们还将包括一个max_rating属性,该属性将包含个人可以对问题给出的最高评分。 例如,如果问题内容为“ max_rating俱乐部10的社区为最佳”,则可以将max_rating设置为10。此外,我们将跟踪布尔值free_response以确定是否允许人们加入长格式的可选额外响应。 最后,我们需要与Answer模型有关系。 这将是一对多的关系,因为一个问题可以有多个答案,但是一个答案只能有一个问题。

The Answer model will have the following attributes:

Answer模型将具有以下属性:

  • an answer attribute corresponding the the free text response of an answer (if the question allows a free text response)

    对应于answer的自由文本响应的answer属性(如果问题允许自由文本响应)

  • a rating ranging from 1 to whatever is the max rating for the question

    rating范围从1到问题的最高评分

  • a user_id relating to the user who wrote the question (once again a user can have many answers, but an answer can only have one user)

    与写问题的用户有关的user_id (再次,一个用户可以有很多答案,但是一个答案只能有一个用户)

  • a question_id referring to the question that the answer belongs to

    一个question_id指的是question的答案属于

  • a club_id referring to the club the answer belongs to

    引用答案所属clubclub_id

Let’s create a file question.py

让我们创建一个文件question.py

Most of the stuff in here is fairly straightforward except for the last line. The last line connects the Question and Answer models. It says to set up a relationship with the Answer model which can refer to the Question model via the keyword question. Given an answer a, you can get the question via a.question and given a question q, you can get the answer associated with it via q.answers. Let's now set up the Answer model. Create a new file called answer.py in the models folder and paste in the following.

除了最后一行,这里的大多数内容都非常简单。 最后一行连接QuestionAnswer模型。 它说要与Answer模型建立关系,该模型可以通过关键字question引用Question模型。 给定答案a ,您可以通过a.question获得问题,给定问题q ,您可以通过q.answers获得与其相关的答案。 现在,我们来建立Answer模型。 在models文件夹中创建一个名为answer.py的新文件,然后粘贴以下内容。

So this file is much longer, but recall that there are many things an answer is related to. Let’s start at the beginning, note that question_id refers to the Question model via the foreign key questions.id (the id column of the questions table (which contains records of instances of the Question model).

因此,该文件要长得多,但是请记住,答案与很多事情有关。 让我们从头开始,需要注意的是question_idQuestion通过外交重点型号questions.id (该id的列questions表(其中包含的实例记录Question模型)。

Note that we also have a user_id column that refers to a user. Let's go into user.py within the app/models folder and add the following line after the role_id declaration.

请注意,我们还有一个user_id列,它引用了一个用户。 让我们进入app/models文件夹中的user.py ,并在role_id声明之后添加以下行。

This statement uses very similar syntax to that of the Question model.

该语句使用与Question模型非常相似的语法。

Also note that there is a club_id attribute that refers to the club the answer is associated with. Edit the club.py file to include the following line as the last attribute of the Club model.

还要注意,有一个club_id属性指向与答案相关联的俱乐部。 编辑club.py文件,以club.py下行作为Club模型的最后一个属性。

Finally, add these two lines to __init__.py in app/models

最后,将这两行添加到app/models __init__.py

And now we should be able to play around with our databases as follows.

现在,我们应该可以按如下方式使用我们的数据库了。

Lastly, let’s address the newAnswer method. This method is used to insert new answers into the database while making sure that if a user has already answered that question, we delete it and insert the new response.

最后,让我们解决newAnswer方法。 此方法用于将新答案插入数据库,同时确保如果用户已经回答了该问题,我们将其删除并插入新答案。

Once again, we can run python manage.py shell

再一次,我们可以运行python manage.py shell

There, we are now done with the models :)

在那里,我们现在完成了模型:)

观看次数 (Views)

Now the database stuff is out of way, let’s create the way for users to interact with the application itself. First let’s set up some blueprints.

现在数据库内容已不合时宜,让我们为用户创建与应用程序本身进行交互的方式。 首先,让我们设置一些蓝图。

蓝图 (Blueprints)

Blueprints are a great way to organize you flask application. It allows you to mount all routes that are associated with each other in a single file. For example, for all actions associated with an account, such as account management, user password reset, forgot password, etc. would be included in the account blueprint.

蓝图是组织烧瓶应用程序的好方法。 它允许您在单个文件中装载彼此关联的所有路由。 例如,对于与帐户关联的所有操作,例如帐户管理,用户密码重置,忘记密码等,都将包括在account蓝图中。

Each blueprint has a folder associated with it under app. For example, there is an account/ folder and a folder under templates containing the actual html templates that will be rendered to the user.

每个蓝图在app下都有一个与之关联的文件夹。 例如,在templates下有一个account/文件夹和一个文件夹,其中包含将呈现给用户的实际html模板。

Let’s add some blueprints. Before the return app line of app/__init__.py add the following.

让我们添加一些蓝图。 在app/__init__.pyreturn app行之前,添加以下内容。

These calls create blueprints mounted at the url prefixes /club, /question, and /category respectively. Let's create the folders club, question, and category for each of the blueprints. Within each of the folders create the files __init__.py, forms.py, and views.py.

这些调用分别创建在URL前缀/club/question/category挂载的蓝图。 让我们为每个蓝图创建文件夹clubquestioncategory 。 在每个文件夹中创建文件__init__.py . forms.pyforms.pyviews.py

俱乐部形式和意见 (Club Forms and Views)

I’ll walk through how to set up the views/templates for club blueprint. The other views are fairly easy to understand from the code.

我将逐步介绍如何为club蓝图设置视图/模板。 其他视图从代码中很容易理解。

So within the club view, we want to have a few different things to show

因此,在俱乐部视图中,我们想展示一些不同的东西

  1. If you are an administrator, you should be able to create a club and give it a name, description, and categories.

    如果您是管理员,则应该能够创建俱乐部,并为其指定名称,描述和类别。
  2. If you are an administrator, you should be able to view all the clubs, including ones that aren’t confirmed.

    如果您是管理员,则应该可以查看所有俱乐部,包括未确认的俱乐部。
  3. If you are an administator or user, you should be able to view an individual club’s information.

    如果您是管理员或用户,则应该能够查看单个俱乐部的信息。
  4. If you are an administator, you should be able to edit a club’s information and delete a club.

    如果您是管理员,则应该能够编辑俱乐部的信息并删除俱乐部。

Let’s first create a couple of forms within forms.py that we will then pass to our views, specifically the view that handles create a new club and the one that edits club information.

让我们首先在forms.py中创建几个表单,然后将它们传递到视图,特别是处理创建新俱乐部和编辑俱乐部信息的视图。

In forms.py for club add the following lines:

forms.pyclub增加下面几行:

Flask-base uses wtforms to create forms. wtforms allows us to create forms in an object oriented manner where each form is a class.

Flask-base使用wtforms创建表单。 wtforms允许我们以面向对象的方式创建表单,其中每个表单都是一个类。

So we create two forms, one called NewClubForm that extends the base wtforms Form class, and has 3 fields - name (Datatype: Text), desc (Datatype: Text) containing the description of the club, and categories (a multiple select dropdown). With the categories field, we query the ClubCategory model with a Lambda function (which is basically an anonymous function) for the category names and populate the category select field options with the results from that query.

因此,我们创建了两种形式,一种名为NewClubForm ,用于扩展基础wtforms Form类,并具有3个字段- name (数据类型:文本), desc (包含俱乐部说明)的desc (数据类型:文本)和categories (多选下拉列表) 。 使用categories字段,我们使用Lambda函数(基本上是一个匿名函数)查询ClubCategory模型的类别名称,并使用该查询的结果填充类别选择字段选项。

Lastly, we have a submit field, so the submit button can be rendered.

最后,我们有一个submit字段,因此可以渲染Submit按钮。

Next, we have an EditClubForm which extends the NewClubForm field set by adding a new field called is_confirmed. Recall that is_confirmed in our Club model determines whether the given club instance can be shown or not shown to the public. We will be adding the function for a club to be suggested by users, and by default, suggested clubs are hidden until approved by an admin. We also overwrite the submit field to display the text "Edit Club".

接下来,我们有一个EditClubForm ,它通过添加一个称为is_confirmed的新字段来扩展NewClubForm字段集。 回想一下,我们Club模型中的is_confirmed决定了是否可以向公众显示给定的俱乐部实例。 我们将为用户建议的俱乐部添加功能,默认情况下,建议的俱乐部在管理员批准之前是隐藏的。 我们还将覆盖submit字段以显示文本“编辑俱乐部”。

In views.py under club/, we create a few routes.

club/下的views.py ,我们创建了一些路线。

  • /new-club (GET, POST) LOGIN PROTECTED: The renders and accepts data from form for creating a new club.

    /new-club (获取,发布)登录保护:渲染并接受来自表单的数据以创建新俱乐部。

  • /clubs (GET) ADMIN PROTECTED: Renders all the clubs

    /clubs (GET)受保护:渲染所有俱乐部

  • /<int:club_id>/(:info) (GET) LOGIN PROTECTED: Will render out info for a given club instance with id = club_id and can access the route at /club/1 or /club/1/info.

    /<int:club_id>/( :info)(获取)登录保护:将呈现with id = c lub_id的给定俱乐部实例的信息,并可以访问/ club / 1或/ club / 1 / info的路由。

  • /<int:club_id>/change-club-details (GET, POST) ADMIN PROTECTED: Render and accept data from form for editing club information.

    /<int:club_id>/change-club-d细节(GET,POST)受保护的管理员:渲染并接受来自表单的数据以编辑俱乐部信息。

  • /<int:club_id>/delete (GET) ADMIN PROTECTED: Render page to delete club

    /<int:club_id>/删除(GET)受保护:呈现页面以删除俱乐部

  • /<int:club_id>/_delete (GET) ADMIN PROTECTED: Delete club with club id.

    /<int:club_id>/_删除(GET)受保护:删除具有俱乐部ID的俱乐部。

For the first route /new-club, we want to also allow regular users to create a new club, which is why we only login protect it. Let's see how we can make a route for this.

对于第一个路线/new-club ,我们还希望允许普通用户创建一个新的俱乐部,这就是我们仅登录保护它的原因。 让我们看看如何为此做一个路线。

Breaking down the code. In line 1, we declare where the route will be accessible. For example, it will be on the club blueprint at the sub-route /new-club. The full URL it can be accessed at is basedomain.com/club/new-club.

分解代码。 在第1行中,我们声明可在何处访问该路由。 例如,它将位于子路线/new-club.club蓝图上/new-club. 可以访问的完整URL是basedomain.com/club/new-club

We then put a route decorator @login_required on the route, this decorator will throw a 403 error if the user isn't logged in but will also allow the user to view the route if they are logged in.

然后,我们在路由上放置一个路由装饰器@login_required ,如果用户未登录,该装饰器将引发403错误,但如果用户登录,还将允许用户查看路由。

Next, we define a method to handle requests to the route (note that this name must be unique). This method can be referred to by club.new_club in Jinja templating.

接下来,我们定义一个方法来处理对路由的请求(请注意,该名称必须是唯一的)。 该方法可以由Jinja模板中的club.new_club

We then instantiate our NewClubForm we created earlier. In the following line, we check to see if the form submission was valid (note that this route will also accept POST requests to it) via the form.validate_on_submit() method. If it is, then we create a new Club instance with name, description, and categories corresponding to the form fields. Note for is_confirmed we set it equal to whether the current user is an administrator or not (because if a regular user submits to this form, we want the new club to not appear to everyone, hence we set is_confirmed to False). We then add the new club instance to the database session and commit the session.

然后,我们实例化我们NewClubForm创建的NewClubForm 。 在下一行中,我们通过form.validate_on_submit()方法检查表单提交是否有效(请注意,此路由还将接受对其的POST请求form.validate_on_submit() 。 如果是,那么我们将创建一个新的Club实例,其namedescriptioncategories与表单字段相对应。 请注意,对于is_confirmed我们将其设置为等于当前用户是否为管理员(因为如果常规用户提交此表单,我们希望新俱乐部不会出现在所有人is_confirmed ,因此将is_confirmed设置为False)。 然后,我们将新的club实例添加到数据库会话中并提交该会话。

Lastly, if the user submitting the form is not an admin, we generate a link to send to the administrator of the form via email. This link should go directly to the admin change_club_details route which will allow the admin to toggle is_confirmed. We then look through the database for all users with an administrator role and add an emailing task to our redis queue. Within the get_queue() method, we enqueue the send_email job specifically, setting the recipient to the admin email, the subject equal to

最后,如果提交表单的用户不是管理员,我们将生成一个链接,以通过电子邮件发送给表单管理员。 该链接应直接转到admin change_club_details路由,这将允许管理员切换is_confirmed 。 然后,我们通过数据库查找具有管理员角色的所有用户,并将电子邮件任务添加到我们的Redis队列中。 在get_queue()方法中,我们专门使send_email作业入队,将收件人设置为管理员电子邮件,主题等于

add the club instance (to be used as a templating variable), and the link (also to be used as a templating variable).

添加club实例(用作模板变量)和链接(也用作模板变量)。

We also pass the template which we create in app/templates/club/email/suggested_club.html and .txt. The content is as follows for the html file:

我们还将在app/templates/club/email/suggested_club.html.txt创建的template传递给我们。 html文件的内容如下:

and for the .txt file

和.txt文件

Next we will take care of the /clubs route that renders all the clubs in a table. For the route handler, we can just pass in all the clubs into a template.

接下来,我们将处理/clubs路由,该路由将所有俱乐部显示在表中。 对于路由处理程序,我们可以将所有俱乐部都传递到模板中。

And the club template we render is located at app/templates/club/clubs.html with the following content.

我们渲染的俱乐部模板位于app/templates/club/clubs.html具有以下内容。

Most of this is fairly straightforward if you know Jinja (or any templating language). Basically, the for loop {% for c in clubs %} ... {% endfor %} will go through all the clubs and for each club, it will render the club name {{ c.name }} and the club categories.

如果您了解Jinja(或任何模板语言),那么大多数内容都非常简单。 基本上,for循环{% for c in clubs %} ... {% endfor %}将遍历所有俱乐部,并且对于每个俱乐部,它将呈现俱乐部名称{{ c.name }}和俱乐部类别。

Note that for each of the clubs rendered, we also include a line:

注意,对于每个渲染的俱乐部,我们还包括一行:

This links to the individual club info page for the given club instance that is rendered. Let’s move on to making that route.

这将链接到呈现的给定俱乐部实例的各个俱乐部信息页面。 让我们继续前进。

Note that for this view we only need to pass in the club instance information to the manage_club view. We can do this easily via:

请注意,对于此视图,我们仅需要将俱乐部实例信息传递到manage_club视图。 我们可以通过以下方法轻松完成此操作:

We can also set up a few other routes because our manage_club.html page actually does display multiple routes.

我们还可以设置其他一些路由,因为我们的manage_club.html页面实际上确实显示了多个路由。

Let’s set up the /change-club-details route which just renders and accepts info from the EditClubForm form.

让我们设置/change-club-details路由,该路由仅呈现并接受EditClubForm表单中的信息。

Note that when saving the club.is_confirmed field, we need to convert the string True and False values to their boolean counterparts as stated in the forms.py specification for EditClubForm. We do this via a custom defined function bool which is defined as follows:

请注意,在保存club.is_confirmed字段时,我们需要将字符串TrueFalse值转换为布尔值,如forms.py规范中EditClubForm 。 我们通过定义如下的自定义函数bool来做到这一点:

The python default bool will return True if any string is defined, including False', hence we need to define our own function.

如果定义了任何字符串(包括False' ,则python default bool将返回True ,因此我们需要定义自己的函数。

We also define the delete to render the delete page and the _delete function that actually deletes the club instance.

我们还定义了delete以呈现删除页面,并定义了_delete函数,该函数实际上删除了Club实例。

Note that for the _delete route, we have a redirect towards the clubs route that lists all the club instances.

请注意,对于_delete路线,我们有一个指向clubs路线的重定向,其中列出了所有club实例。

Now we move to the manage_club.html template at app/templates/club/manage_club.html. The content of that is as follows:

现在,我们转到app/templates/club/manage_club.html上的manage_club.html模板。 其内容如下:

Let’s break down this file. On the first line we are just extending our base layout and then we import form macros. Macros are basically methods in Jinja.

让我们分解这个文件。 在第一行,我们只是扩展基本布局,然后导入表单宏。 宏基本上是Jinja中的方法。

We have a endpoints variable that will contain links to the different parts of the management page. In the navigation macro, we render all the individual elements of the list in the endpoints.

我们有一个endpoints变量,它将包含指向管理页面不同部分的链接。 在navigation宏中,我们在endpoints呈现列表的所有单个元素。

We also create a club_info macro that will contain the information related to the club and all the answers associated with the club by doing the following

我们还通过执行以下操作,创建了一个club_info宏,其中包含与俱乐部有关的信息以及与俱乐部相关的所有答案

Lastly, we actually write the logic for rendering the page within the {% block content %} ... {% endblock %} tags. We switch between the subpages to render by checking the request.endpoint to see if it is the deletion endpoint or if there is a form (in which case render the form). Otherwise, we just call the club_info macro. And we’re done with the club routes and views. Most of the other routes for category and questions follow a similar type of logic.

最后,我们实际上编写了在{% block content %} ... {% endblock %}标签内呈现页面的逻辑。 我们通过检查request.endpoint在子页面之间进行渲染,以查看它是否是删除终结点或是否存在表单(在这种情况下,呈现表单)。 否则,我们仅调用club_info宏。 我们已经完成了俱乐部的路线和景观。 关于类别和问题的其他大多数路线都遵循类似的逻辑类型。

主要观点和形式 (Main Views and Forms)

The main route is the public facing part of the application. The route behaviors are as follows

主要途径是应用程序面向公众的部分。 路由行为如下

  • / (GET): display all clubs, associated questions, and average ratings for each club per question in a table.

    /(GET):在表格中显示所有俱乐部,相关问题以及每个问题的每个俱乐部的平​​均评分。
  • /submit-review/<int:club_id> (GET, POST): Dynamically generate the form to submit a club review based on the questions in the `Question` db. Also accept data for the form and save as answers fo the club matching club_id.

    / submit-review / <int:club_id>(获取,发布):根据“问题”数据库中的问题动态生成表单以提交俱乐部评论。 还接受表格的数据,并保存为与club_id相匹配的俱乐部的答案。

The first route is very straightforward and matches the `/clubs` route we implemented earlier. The only difference is that the `questions` must also be passed in.

第一条路线非常简单,并且与我们之前实现的`/ clubs`路线相匹配。 唯一的区别是还必须传递“问题”。

The most interesting part here is how to calculate the average rating and pass that into the route. I create a list called all_c and for each of the clubs, I create a club_obj containing basic information for the club.F or each of the answers for a club, I add a new property of the club_obj correponding to the question content, if one doesn't exist already. I append each of the ratings to a list and then iterate through each of the properties of the club_obj. If the property has a value that is of type list, then I replace that list with the average of the ratings in that list. I then append club_obj to all_c and pass that into the template.

这里最有趣的部分是如何计算平均评分并将其传递给路线。 我创建一个名为all_c的列表,并为每个俱乐部创建一个club_obj其中包含该俱乐部的基本信息。F或俱乐部的每个答案,如果要添加一个与问题内容相对应的club_obj的新属性还不存在。 我将每个等级附加到列表中,然后遍历club_obj每个属性。 如果该属性具有列表类型的值,那么我将该列表替换为该列表中评分的平均值。 然后,我将club_obj附加到all_c并将其传递到模板中。

动态形式生成 (Dyanamic Form Generation)

For the submit-review route, I need to create a form dynamically based on the questions that I have in my Question model. The code is as follows:

对于submit-review路线,我需要根据我的Question模型中存在的问题动态创建一个表单。 代码如下:

We first create a dummy form class that inherits from the base Form class. Then for each of the questions, we ceate new form fields setattr(F, ...) on the dummy form F. The setattr method takes as its second argument the name of the form field. We set this to the id of the question with _q appended to it corresponding to the rating and _resp corresponding to a free response if indicated. For the rating form field, we create a SelectField with choices from 1 to the max_rating.

我们首先创建一个从基础Form类继承的虚拟Form类。 然后针对每个问题,在虚拟表单F上创建新的表单字段setattr(F, ...)setattr方法将表单字段的名称作为第二个参数。 我们将其设置为问题的ID,并在其ID后面附加_q对应等级,并在_resp后面附加免费响应(如果有指示)。 对于评级表单字段,我们创建一个SelectField ,其选择范围为1到max_rating

To handle a form submission, we use the same if statement form.validate_on_submit() but instead of looking for specific named fields of the form, we instead iterate through all the fields of the form and create a new answer using the newAnswer method. This method will delete any previous response before adding a new one for the user if they responded for this club.

为了处理表单提交,我们使用if语句相同form.validate_on_submit()但不是寻找方式的具体命名字段,我们不是通过迭代所有领域的form ,并使用了新的答案newAnswer方法。 如果用户对此俱乐部做出了响应,则此方法将删除之前的所有响应,然后再为用户添加新的响应。

发射 (Launching)

Now that most of the App is done, we can launch this app on Heroku.

现在,大多数应用程序都已完成,我们可以在Heroku上启动该应用程序。

If you’ve never signed up with Heroku

如果您从未注册过Heroku

  1. Go to Heroku.com and sign up for an account

    转到Heroku.com并注册一个帐户
  2. Install the CLI

    安装CLI

If you haven’t set up a git repo initially, run git init and log in with your Heroku account.

如果您最初没有设置git repo,请运行git init并使用您的Heroku帐户登录。

Then, git add all the relevant files (i.e. anything but config.env and venv/) and run pip freeze > requirements.txt to make sure that all the dependencies you have installed are included.

然后, git add所有相关文件(即config.envvenv/任何文件)并运行pip freeze > requirements. venv/ pip freeze > requirements. txt以确保包括所有已安装的依赖项。

Run heroku create to make a new Heroku instance and run git push heroku master to add your files to the Heroku repository.

运行heroku create一个新的Heroku实例,并运行git push heroku master将文件添加到Heroku存储库。

After that is done running, you’ll need to set some environment variables with the following command

运行完之后,您需要使用以下命令设置一些环境变量

Once that is done, run the following which will create the database on Heroku

完成后,运行以下命令,这将在Heroku上创建数据库

and then the following command will create the administrator account.

然后以下命令将创建管理员帐户。

You’ll also need to create a Redis togo instance to handle the task queue

您还需要创建一个Redis togo实例来处理任务队列

and lastly run the following command which will tell Heroku to spin up a dyno (read as sub-server) that handles our Redis queue.

最后运行以下命令,该命令将告诉Heroku启动处理我们Redis队列的dyno(读取为子服务器)。

You can then run heroku open to open your running Heroku app in a separate window.

然后,您可以运行heroku open来在单独的窗口中打开正在运行的Heroku应用程序。

扩展应用程序和结论 (Extending the App & Conclusion)

It’s pretty easy to copy the current application structure and extend it to add more information/routes to the App. Just view any of the previous routes that have been implemented. If, for some reason, you want to include a file upload of some type, you’ll need to integrate the App with Amazon S3 if you plan to run the app on Heroku (since it has an ephemeral file system).

复制当前应用程序结构并对其进行扩展以向该应用程序添加更多信息/路由非常容易。 只需查看之前已实施的任何路由即可。 如果出于某种原因想要包括某种类型的文件上传,那么如果您计划在Heroku上运行该应用程序(因为它具有临时文件系统),则需要将该应用程序与Amazon S3集成。

Overall, flask-base provides a great starting point for making your flask application. Of course the backend may be fairly verbose to code, but as a result, it gives you very granular control over your app.

总体而言,烧瓶基可以为您的烧瓶应用提供一个很好的起点。 当然,后端的代码可能相当冗长,但是结果是,它使您可以非常精细地控制应用程序。

翻译自: https://www.freecodecamp.org/news/how-to-use-python-and-flask-to-build-a-web-app-an-in-depth-tutorial-437dbfe9f1c6/

 类似资料: