I happened to find an article I wrote two and a half years ago in my sophomore year. I recorded how to build a website in ten days. Much of the content now seems outdated, and there are various flaws in the style and code (do not imitate!). , but still want to release it, after all, a record of the struggle of their own 🙂


Pkyx is a comparison site developed in Flask+MongoDB.

Day 1: Configure the remote development environment

First, Paralles Desktop installed 64-bit Ubuntu 15.04 with Nginx and VirtualEnv.

1. Create a new directory in Ubuntu, create a virtual environment with VirtualEnv, install flask with PIP, and test it out.


2. Create a project under Pycharm and the compiler selects the Python interpreter from VirtualEnv.


3. Run the ifconfig command on the VM to view the IP address and configure SSH options. Before connecting to the VM, remember that the Ssh-server has been installed on the VM, because it has only ssh-client by default. Install openssh-server apt-get install openssh-server apt-get install openssh-server


3. After the project is created, configure the SFTP option in tools-deployment-configuation. Note here that Root Path refers to the top-level Path on the remote host (Ubuntu).


On the Mapping TAB, configure the project path to map to the project path of the remote host.


4. In tools-deployment-option, configure Upload changed files automatically to the default server. Here is the synchronization time between the project selected host and the remote host project. Always means Always in sync, Ctrl+ S means save in sync.


5. Write code that returns a route to Hello World.


Move the code to the remote host and Upload to push the code directly into the project path of the VIRTUAL machine Sync with.. View the status of the files deployed by the project and select the files to synchronize.



Now that the configuration is basically complete, let’s just Run the code.

7. Run code.


Flask’s application has been started, but it is important to note that you cannot directly access the vm’s localhost on the local computer, so http://127.0.0.1:5000/ refers to the service in the VM, and you cannot view the service in the host through this path. So what? Flask: Nginx is the nginx proxy for the vm. The flask is the nginx proxy for the vm.

(There is also a Paralles method that uses a port mapping in the Paralles network Settings so that the Paralles host can access the vm’s localhost.)

8. Modify the nginx configuration file (/etc/nginx/site-available/[conf]).


Our nginx server listens on port 80 of the VM and forwards path-related requests to port 5000 bound to the flask, while static file path (/static) requests bypass the flask and go directly to the vm’s file directory, effectively reducing the load of flask applications.

9. Obtain the IP address of the VM and use a browser to access the web program.


perfect

10. Don’t forget to synchronize your project to Git.

Day 2: Write application configurations and views

Before writing the configuration and views, first plan the directory structure for your application.

Code/Pkyx / ├─ app │ ├─ config.py │ ├─ forms. Py │ ├─ init.py │ ├─ main │ ├─ Errors. Py (wrong view) │ │ ├ ─ ─ init. Py │ │ └ ─ ─ views. Py (subject view) │ ├ ─ ─ the static (static file directory) │ │ └ ─ ─ style.css. CSS │ ├ ─ ─ templates │ (template file directory) │ │ ├ ─ ─ 404 HTML │ ├ ─ ─ 500 HTML │ │ ├ ─ ─ index. The HTML │ │ └ ─ ─ pk. HTML │ └ ─ ─ the users (user module) │ ├ ─ ─ init. Py │ └ ─ ─ views. Py ├ ─ ─ manage.pyCopy the code

Let’s do some more preparatory work by installing the following extensions with PIP.

  • Flask-mail: Flask-mail extension that allows you to send Mail to users quickly and easily.
  • Flask-wtf: Flask forms extension that allows you to write form classes and base properties in code and render forms in templates.
  • Flask-pymongo: A PyMongo extension for Flask that makes deploying MongoDB connections for Flask applications faster and easier.

1. Next, write the form file.

First import the Form class from the WTF extension, and the Form class we are going to define will inherit from this Form class, then simply define three fields for the Form, two text input fields, add a DataRequired instance of wtForms in Validators, It will set the two text boxes as required. Finally, there is a submit field, which is the button to submit the form.


2. Write the blueprint document.

Flask-blueprint has many uses, one of which is to partition urls for application modules.

Registers a blueprint on an application's URL prefix and/or subdomain. URL prefixes and/or subdomain parameters become common view parameters for all views in the blueprint (by default).Copy the code

Details about blueprint: http://dormousehole.readthedocs.org/en/latest/blueprints.html#blueprints

You can also specify the static file path for the module as well as the template file path in the parameters of Blueprint.


3. Write an error response view.

It is important to write an error response view for your application. Here we simply define two views, one for the response to error 404 and one for error 500. Note that main is used as the template object defined in the previous example. It prefixes the function endpoint with the name used to build Blueprint (simple_page in this case).


4. Write the body view.

Here we define the corresponding views for the home page (/) and comparison page (/ PK). In index, we instantiate the PkForm we defined earlier as a context parameter to render the template function render_template. In the PK view, it accepts data submitted by POST from the index form, so add the ‘POST ‘attribute to its decorator’s methods argument (default is only GET). Make sure you pass Request. form as an argument to the form’s constructor, otherwise the form will not receive any data. We then use the form to validate_on_submit to determine if the form’s data is valid. If it is, we take the two data from the input field and render it into the comparison page. Otherwise, we simply return an error response.


5. Write templates.

Here, we simply define a form in the template that points to the pk view we defined earlier. Of course, this simple display is not enough. The href attribute of the link tag in the header of the page specifies the static file path (thanks to the powerful Jinjia2 template) from url_for() reverse parsing.


Here it looks something like this:


Next, write the template for the comparison page.


6. Define the configuration file.

So far, the framework of the application is pretty clear, but we need more flexibility to start and run the application, writing global configuration files in the app directory.

BaseConfig defines some of the basic configuration for the application, such as the secret key, mailbox configuration, etc. All the following other configurations inherit from BaseConfig and extend other configurations. Here we define a DevConfig (development configuration), which as the name implies is the configuration under development. You can also define other types of configurations (such as production configurations, test configurations, etc.). In DevConfig we have extended the configuration of MongoDB connections (flask-pymongo) and a static method, init_app, which applies some configuration initialization (such as establishing database connections).


7. Write functions to create applications.

Here you write a function that creates and initializes the application, which takes care of initializing the incoming configuration for the application, initializing itself with the init_app of the configuration, and registering the blueprint you wrote earlier.


8. Create the management application script (manage.py).

Finally, the application also needs a global management script, which for now only needs to add the code to start the application.


9. Start and run the application.

Complete the above steps and the application is ready to run.


Day 3: Write RESTful apis and test databases

The view, template, and form work is done, and the application is ready to run.

The REST (Presentation layer State Transitions) design style is currently the most popular design pattern, and the REST style API will be written for the application.

About RESTful

1. Again, create a directory for the API module and define Blueprint with init.py.


2. Test the database

In the app module initialization, we create a connection instance of MongoDB, but this instance is not bound to the current application context. Therefore, we need to create a connection instance of mongo in create_app and add it to the init_app method. MongoDB can work in the application, just need to import the app module mongo instance in another file (from app import Mongo).


Next,, create a new collection of entries in the Mongo shell and insert a single piece of data.

3. Write utility functions (utils).

The two utility functions (bson_to_json and bson_obj_id) that we will use in API views convert MongoDB’s BSON (document data format) to JSON and id to the ObjectId form in MongoDB, respectively. Because these two functions are difficult to implement with python built-in functions, pyMongo provides a very good json_util for the Bson module we provide, which makes it easy to implement the data format conversion between MongoDB and Python.


A picture can explain the process.


4. Write REST apis.

In the Flask, it’s easy to implement REST apis in Python’s Web framework, the Python community has many REST packages (EVE, REST Framework, etc.), and in Flask, Flask provides a Pluggable View for developers by default. There is a MethodView, which is designed specifically for developers to design REST style views. This class View has an as_view() method, which can be used to convert the class View directly into a normal View. When there is a need to reuse views, they are more flexible than normal functional views.

First, write an API class that inherits MethodView and simply implements the four methods GET, POST, PUT, and DELETE, which correspond to the four HTTP methods’ corresponding processing handles. Add routing rules at the end of the code and map them to different methods. Note that the get method has two cases: one is to provide id and return only a specific resource; the other is to return all (or the first N) resources without id; the add_url_route() method dynamically adds routes.

Find () returns the result cursor. Note the use of the bson_to_json() method. As mentioned earlier, this converts the MongoDB document format from BSON to JSON. After getting the list of json data, return it with json.dumps(). The client ends up with an array of jSON-formatted objects.

As a final note, we pass in a params dictionary in the find() method, which holds the key-value pairs of the parameters that follow the GET request. With conditional queries, we build an API that is more flexible.


Other methods are implemented on their own terms.

5. Test the REST API.

Postman is used to send a GET request to the server’s API address, taking the id string of the document previously inserted into MongoDB and getting a result. (If no arguments are added, all results are obtained)


Another GET request is tested, this time with the attributes and values of the data as a Query string, and the result is a record.


Day 4: Optimize your application with Supervisor and Gunicorn

Sueprvisor is a tool on Linux that monitors applications and processes. We use it as a daemon to automatically start and stop applications.

First install it on your system with sudo apt-get Install Supervisor.

Gunicorn is a high performance WSGI server implemented in Python. Flask’s WSGI server is not suitable for production, so we use Gunicorn as the server for flask applications. Improves application throughput and response speed.

Next, create a new gunicorn configuration file in the application directory with four worker processes (wokers) and bind ports.

In addition, you need to create the supervisor configuration file for your application. The main parameters are as follows:

  • [Program :[app]] : specifies the name of the application
  • Command: specifies the command to start the application
  • Directory: indicates the working directory of the application
  • Stdout_logfile: indicates standard output logs
  • Stderr_logfile: standard error log

It’s also easy to run an application with the Supervisor, which is small enough to run by entering commands in the container’s console.

It is important to ensure that the supervisor configuration file is visible when the supervisor is used in the container using the supervisorord-c [conf_path] and supervisorctl -c [conf_path] commands.


Of course, it is not appropriate to start an application with Gunicorn and Supervisor in a debug environment. This is because you need to observe the running status of the application in logs instead of viewing the output and error messages of the application.

Day 5: Write user and authentication modules

Flask-login is an easy extension to implement user sessions and logins for your application.

Install flask-login with PIP install flask-login.

Flask-httpauth will be used in the authentication module, and we will use authentication to manage requests in the user’s REST API.

PIP install Flask-httpauth

1. Speaking of the user module, of course, can not do without the login/registration function, so we first write the login and registration form.

The code includes some of the most commonly used fields, so there’s nothing more to say here.


2. Write user Model.

Although our application does not adopt the form of ORM model but uses PyMongo to interact directly with MongoDB, we still need to write a general user model because of the user model objects used in flask-Login and the convenience of managing users in the authentication module.

Create a new models.py file, define a User class, and add UserMixin in the flask-Login module. This Mixin will declare some generic User state property for the User class we define. Is_anonymous, IS_Authorized, is_active, etc., mixed in with UserMixin, the User class is treated like a flask-Login User model.

There are four methods defined in this user class.

1) gen_passwd_hash (password)

Return the hashed password, since our password cannot be stored in plain text in the database, using the generate_password_hash method in werkzeug.security.

(2) verify_passwd passwd_hash passwd)

Verify that the entered password is correct by comparing it with the encrypted hashed password.

3) gen_auth_token (self, expiration)

Generate an access token with expired validation.

4) verify_auth_token (token)

Verify the access token and, if successful, return user information.


3. Initialize LoginManager.

Create an instance of flask-Login’s LoginManager in the application module, use it to bind the application, and specify the view to which the user is logged in (in this case ‘users.login’). You must provide a user_Loader callback. This callback is used to reload the user object from the user ID stored in the session. It should take a user ID as a parameter and return the corresponding user object.

https://flask-login.readthedocs.org/en/latest/


4. Write the main user view.

The four views, register,login,profile and logout, are defined respectively, and the error judgment and user authentication process should be noted here. Flask-login uses the corresponding login_user(user) and logout_user() to login/out, and finally adds the login_required decorator to the view you want to protect to prevent unlogged access.


5. Add or modify the login registration template.


6. Test functions such as login/registration.

Now that you’ve implemented user login, register a user and log in to the test application.

Registration.


Then log in.


Login succeeded.


7. Write user authentication module.

Some authentication methods were already written in the user class definition.

Now we use flask-HttpAuth to build the REST API with user authentication.

Create a new users.py in the API module directory.

Create an instance of HTTPBasicAuth and define the core verify_password function, which will perform user authentication and will be decorated with auth’s verify_password.

Verify_password provides two authentication modes. The token authentication is used first. If the authentication fails, the user + password authentication is used.

A token fetching view and a resource view are then wrapped with login_requried.


Then start testing the API.

If you do not access the resource using authentication, you will get an access denied response.

We access the resource by email (username): password and return a resource successfully.

Then use authentication to request the view where the token is located and get the JSON with the expiration time and token. Instead of using the user name and password, use the token to access the application resources and return the resources correctly.


Day 6: Componentize applications with template inheritance

Today is the day to complete the template for the application.

It is best to add some style to the template before perfecting the page, otherwise the page will not look beautiful, there is no hierarchy, it is not easy to adjust the page elements. Therefore, on the basis of the original, you can add your own style, export it to the front end by link, or directly use the open source CSS framework, in this case using Semantic-UI, use Bower to install into the application’s resources directory, and then you can write templates.

Because the basis of a web site that commonly have some repeated components (such as navigation, the top, general style, etc.), so that we all want to put these on each page file copies of almost the same code, and when want to change the elements of each file to modify, to develop a lot of inconvenience, this time I would use jinjia2 template inheritance function, Using templates, we can encapsulate these common parts as templates, and replace them by adding a block in a specific place and filling in the contents of the block in an inherited child template.

1. Create a base file (base. HTML) in the template directory as a common template to inherit.

The target contains some basic styles and scripts that define three blocks to fill: the head header, the body content, and the JS file.


2. Inherit the parent template.

Modify the home page file (index.html) that was previously defined to extend the parent template with extends. Then you simply add content to the corresponding block. When rendering, the child template exports the content of the block to its own block to construct and implement the page. The top bar (header.html) and the login box (login.html) are separate components that can be used only by specific templates, which provides more flexibility.


3. Write components.

You can think of the component as a part of the page, so when you write the code, you just need to complete the HTML element of the corresponding part.

Top bar component


Login box (modal box) component


4. Integrate templates.

Now the processed components can be loaded onto the app like building blocks, simply applied to a few pages to see the effect.






Day 7: Write main features

Let’s start right where we left off.

1. Create a page to create an entry, a form is done, the server side to the database to insert a document, so easy.


2. After an entry is created, the system redirects to the information page of the entry and prepares the layout of the information page.

As shown below, the item content will be displayed in a table. The newly created item has only one attribute, type, and an Add attribute button at the bottom of the page dynamically inserts attributes into the item.


Since the contents of the table are rendered by the Python side, the attributes of an entry can have multiple types. As shown below:


This creates a problem because attribute types are not necessarily plain text, so you can’t simply pull data from the database and render it directly.

To achieve dynamic rendering, you define a set of rules between ajax and the server side. When adding attributes to items, the specific data format will be constructed according to different types. Then the Python side will use a custom renderer to insert HTML, knowing the format.

With that in mind, start coding immediately.

When you click the Add Properties button, a TAB menu collapses at the bottom, and the input fields change accordingly when you select different types.


Of course, JS should also cooperate accordingly, with Ajax request server side, data update.

Gets the property value based on the selection type.


An Ajax request


3. Write the renderer.

The flexibility of Jinjia2 makes it easy to use Python code in the template. Here we define a simple type renderer that constructs HTML from the [attribute name, attribute value, attribute type] passed in.


Then do this on the page, remembering to use the Safe filter to unescape.


4. Added edit/delete attribute methods.

Add attributes How can there be no methods to edit and delete attributes?

At first, I wanted to use editable table to modify the table data in real time, but found that this would cause some problems, and finally gave up.

Instead, double-click the table column and modify it with the modal box.

After the modification is successful, the page is refreshed to see the modified table.


5. Improve form validation.

For security reasons, form authentication (especially user information) must be improved because the form authentication is too simple.

To edit the form file, we used a Validators dictionary to store rules for different form fields, adding length and regular table expressions to user names and passwords.


The page also prompts the user how to fill out the form.


6. Polish your style.

Give the page a final touch.



Day 8: File upload using GridFS

There are many ways to upload files. Generally, file system IO can be used. Here we use the GridFS system of mongodb, which is recommended by mongodb to save large files.

Here is the first taste of the trial, using gridFS to achieve user profile picture upload.



In the flask side, request. Files is used to receive the uploaded profile picture, and the extension of the image is determined to be in the format. If it is valid, the special characters of the filename are replaced and filtered by werkzurg. It takes a database and a collection as parameters, uploads them to the GridFS using the FS Object put method, and returns the Object Id pointing to the inserted document db.avatar.files, which saves the avatar Id and the user’s information to the database.



In the front page, the url_for() method is used to reverse resolve the address of the image. First, the route for obtaining the image should be written, which accepts the Object Id of the image as a parameter, and then fetch the data of the image from the FS system. The binary data of the image will be stored in db.avatar.chunks. Create a content-type response in the image format. Otherwise, the image data will not be properly parsed by the browser when opening the URL.


User information may be incomplete, use Jinja2 Environment Filter to write a custom Filter, so that the display of data more humanized.


Finally prepare the comparison page, here to pay attention to the order of the attributes, to achieve the correct rendering.


Preliminary completion of this site page end.

Day 9: Configure Celery&Redis to run background tasks

Sometimes, our application will perform some background tasks, such as some actions that do not directly interact with the user and require low real-time performance. Such as user registration, usually send a mail with the authentication token link to the user’s mailbox, email because this action will be more time consuming, if the same time a large number of registered requests, there may be blocked, affect users browsing experience, and this time we would prefer to put the task in the background, Celery then Celery would be a suitable option, Celery is a distributed task queue responsible for task execution and scheduling.


The structure of Celery consists of three parts: Message broker, task execution unit (worker) and Task result Store.

Here Redis is used as the Celery Broker which carries communications messages.

Install tasks with PIP install flask-celery helper and its flask extension. Install tasks with PIP install redis.

Add celery and redis in the configuration file after setup.

Start by refactoring the directory structure, moving the extension to the Extensions file, and initialize the application with factory functions in init.



Create a tasks directory under app which contains tasks files to be processed by Celery.

Create an asynchronous mail task and decorate it with @celery. Task.


2. Encapsulate a function to send mail in the view function and write the view for user authentication.

Authentication is done using time-stamped tokens.


Run Celery.

With the -a parameter, the user defined profile will be identified, followed by the module file in which the Celery instance is located.


After running, go and register an account.


Click on the email to activate the connection and verify the token:


Day 10: Write a Dockerfile

The final step: Script the deployment environment

First, put all the supervisor configuration files used by the project in the conf directory of the project. The following figure shows the supervisor configuration file of the project.


Then write Dockerfile file, convenient and fast deployment of docker project container environment.


Finally, you can test and run the project on a production server.