When creating a web application, it is convenient to have a template. This time, I would like to introduce the template of the Flask app that I usually use.
It is a web application that creates and manages a simple PRJ with.
The main specifications of the site are as follows.
As mentioned above, it is a simple application, so it is a scale that can be written in one file, but I want to organize my folders neatly for extensibility and readability.
In particular, the folders were organized with the following points in mind.
The actual folder structure is as follows.
tree
root root folder
│ .gitignore
│ LICENSE
│ main.py First application server body to be executed
│ Procfile Used when running on Heroku
│ README.md
│ requirements.txt Used when running on Heroku
│
├─.vscode VSCode configuration file (for gitignore)
│ launch.Define json debug configuration
│ settings.json Python virtual environment specification etc.
│
├─biz Business logic storage
│ prj.Write business logic such as py DB operations and API calls
│
├─mw middleware (filter) storage
│ prj.Write the overall processing of the middle layer that connects py route and biz
│
├─route Routing definition (blueprint) storage
│ prj.Write routing definitions for each py resource
│ top.Write routing definitions for each py resource
│
├─static Static content storage
│ ├─common Static content used on all pages
│ │ ├─css
│ │ │ page.css
│ │ │
│ │ └─js
│ │ page.js
│ │
│ ├─prj Static content storage by resource
│ │ ├─css
│ │ │ page.css
│ │ │
│ │ └─js
│ │ page.js
│ │
│ └─top Static content storage by resource
│ ├─css
│ │ page.css
│ │
│ └─js
│ page.js
│
└─ templates Jinja Template storage
│ footer.html common footer
│ header.html common header
│ layout.html page layout definition
│ top.Individual page for each html resource (1 file complete system)
│
└─prj Individual page by resource (multi-page system)
entry.html
search.html
show.html
When you start the Flask server, run main.py.
When the Flask server accepts a request from a user to the TOP page, it processes according to the following flow. There is no particular logic in the processing when the TOP page is displayed, and the corresponding template is simply returned, so the processing flow is as follows.
https://my-site.com/
with a browserroute / top.py
route / top.py
returns therender_template ('top')
methodrender_template ('top')
method was returned, use template / top.html
to configure the TOP page and return the result to the browser.The processing flow of the PRJ screen is roughly explained using the PRJ detailed screen as an example.
Unlike the TOP screen, the PRJ screen needs to implement multiple actions such as registration, search, and reference. Therefore, each layer has the following roles.
By designing in this way, the process can be divided into each layer and managed in an easy-to-understand manner. It also makes it easier to see where to make corrections when adding new features.
※たとえば、新しいアクションdeleteを追加したければ、route、mw、biz、templateの各prj.py/prj.htmlにdeleteアクションに対応するメソッドを追加する、など。
As for the processing relationship between layers, we will implement it with the following policy.
Import * .py in the route folder and ʻApp.register_blueprint ()` so that various requests can be routed properly.
main.py
from flask import Flask, redirect, url_for
app = Flask(__name__)
from route.top import top
from route.prj import prj
app.register_blueprint(top)
app.register_blueprint(prj)
if __name__ == '__main__':
app.debug = True
app.run(host='127.0.0.1',port=5000)
route/top.py
Only the process to display the TOP screen.
Because it is the routing when the site route (https://my-site.com/
) is accessed
Blueprint's url_prefix sets ''
(empty string).
route/top.py
from flask import Flask, render_template, request, Blueprint
top = Blueprint('top', __name__, url_prefix='')
@top.route('/')
def index():
return render_template('top.html', title='TOP')
route/prj.py
prj routing definition.
Set the Blueprint url_prefix to '/ prj'
. By doing this, you will be able to accept requests for https://my-site.com/prj/~
.
By defining the routing like @ prj.route ('/ search', methods = ['POST'])
, various actions such as https://my-site.com/prj/search
are accepted. Will be able to be.
route/prj.py
from flask import Flask, render_template, request, Blueprint
from mw.prj import parse, search_prj, get_prj, create_prj, update_prj
prj = Blueprint('prj', __name__, url_prefix='/prj')
@prj.route('/')
def index():
return render_template('prj/search.html', title='Project search', prj={})
@prj.route('/search', methods=['POST'])
def search():
q = parse(request)
if q is None:
#Early return
return jsonify({'code': 'W00100','message': 'The value entered is invalid.'})
#Search DB based on the entered conditions
data = search_prj(q)
#Display search results
return jsonify({'data': data})
@prj.route('/show/<prj_id>', methods=['GET'])
def show(prj_id):
if prj_id is None:
#Early return
return render_template('prj/show.html', title='Project details', data={})
# prj_Search DB by id
data = get_prj(prj_id)
#View details page
return render_template('prj/show.html', title='Project details', data=data)
@prj.route('/entry', methods=['GET'])
def entry():
#Initial display of PRJ input screen
return render_template('prj/entry.html', title='Project creation')
@prj.route('/create', methods=['POST'])
def create():
#Create PRJ
data = create_prj(parse(request))
#View details page
return render_template('prj/show.html', title='Project details', data=data, message='I have created a project.')
mw/prj.py Write a check process and Biz call process to process each action.
mw/prj.py
from flask import request
import biz.prj as prj
def parse(request):
#Omitted
def search_prj(dto):
#Omitted
def get_prj(prj_id):
"""
Get the project with the specified project ID
"""
return prj.get(prj_id)
def create_prj(dto):
#Omitted
def update_prj(data, dto):
#Omitted
biz/prj.py Write API access process and DB access process. The sample below describes a get process that calls the API over HTTP. Also, since API endpoints and API keys are confidential information as a system, they are set to be read from environment variables.
biz/prj.py
import requests
import os
api_endpoint = os.environ.get('API_ENDPOINT')
headers = {
'x-api-key':os.environ.get('API_KEY')
}
def get(prj_id):
r_get = requests.get(api_endpoint + '/prj/' + prj_id, headers=headers)
return r_get.json()
template/layout.html Template for all HTML pages. CSS and JS common to all pages can be read and defined in this layout.html. I try not to write it on each page.
{% Block css%}
and {% block js%}
are defined so that CSS and JS individually for each page can be read.
The contents of the body tag are defined to be a common header, content of each page, and a common footer.
<link rel="stylesheet" href="/static/common/css/page.css" />
<script src="/static/common/js/page.js"></script>
When reading static content under static, href and src are defined with a slash start like / static
.
This is the same layout.html for common static content under static / common /
even if the page hierarchy is different like template / top.html
and template / prj / show.html
. This is so that it can be read using.
template/layout.html
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0" />
<!--Common style-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.8.2/css/bulma.min.css">
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<link rel="stylesheet" href="/static/common/css/page.css" />
<!--Individual page css-->
{% block css %}
{% endblock %}
</head>
<body class="has-navbar-fixed-top">
<!--header-->
{% include "header.html" %}
<!--content-->
{% block content %}
{% endblock %}
<!--footer-->
{% include "footer.html" %}
<!--Common JS-->
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="/static/common/js/page.js"></script>
<!--Individual page JS-->
{% block js %}
{% endblock %}
</body>
</html>
template/top.html
template/top.html
{% extends "layout.html" %}
{% block css %}
<link rel="stylesheet" href="/static/top/css/page.css" type="text/css" />
{% endblock %}
{% block content %}
<section class="hero is-medium is-primary is-bold">
<div class="hero-body">
<div class="container">
<div class="columns is-centered">
<div class="column is-narrow">
<h1 class="title is-1">
Site Concept
</h1>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block js %}
<script async src="/static/top/js/page.js"></script>
{% endblock %}
template/prj/show.html
template/prj/show.html
{% extends "layout.html" %}
{% block css %}
<link rel="stylesheet" href="/static/prj/css/page.css" type="text/css" />
{% endblock %}
{% block content %}
<section class="hero is-medium is-primary is-bold">
<div class="hero-body">
<div class="container">
<div class="columns is-centered">
<div class="column is-narrow">
<h1 class="title is-1">
{{ data['name'] }}
</h1>
</div>
</div>
</div>
</div>
</section>
{% endblock %}
{% block js %}
<script async src="/static/prj/js/page.js"></script>
{% endblock %}
The application code being created using this template is published on Github. https://github.com/KeitaShiratori/ripple