[PYTHON] Personal best practice template to use when you want to make MVP with Flask

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.

Premise

Site overview

image.png

It is a web application that creates and manages a simple PRJ with.

The main specifications of the site are as follows.

Folder structure

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

Outline of processing

Start Flask server

When you start the Flask server, run main.py.

Online processing (display of TOP screen)

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.

image.png

Online processing (display of PRJ screen)

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.

image.png

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.

Implementation details

maiy.py: Flask server startup process

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 %}

Full code for sample app

The application code being created using this template is published on Github. https://github.com/KeitaShiratori/ripple

Recommended Posts

Personal best practice template to use when you want to make MVP with Flask
Solution when you want to use cv_bridge with python3 (virtualenv)
Use aggdraw when you want to draw beautifully with pillow
When you want to send an object with requests using flask
When you want to use it as it is when using it with lambda memo
Gist repository to use when you want to try a little with ansible
Memorandum of means when you want to make machine learning with 50 images
If you want to make a discord bot with python, let's use a framework
When you want to filter with Django REST framework
[AWS] What to do when you want to pip with Lambda
When you want to register Django's initial data with relationships
When you want to use python2.x on modern Gentoo Linux
I know? Data analysis using Python or things you want to use when you want with numpy
If you want to use field names with hyphens when updating firestore data in python
When you want to print to standard output with print while testing with pytest
[Python] When you want to use all variables in another file
When you want to adjust the axis scale interval with APLpy
When you want to replace a column with a missing value (NaN) column by column
Personal best practices when fine-tuning with Chainer
How to build an environment when you want to use python2.7 after installing Anaconda3
[OpenCV] When you want to check if it is read properly with imread
I want to use MATLAB feval with python
I want to make a game with Python
When you want to update the chrome driver.
I want to use Temporary Directory with Python2
I don't want to use -inf with np.log
I want to use ip vrf with SONiC
[Python] I want to add a static directory with Flask [I want to use something other than static]
[Python] I want to use only index when looping a list with a for statement
When the variable you want to superscript with matplotlib is two or more characters
What to do if you don't want to use Japanese column names when using ortoolpy.logistics_network
If you want to use Cython, also include python-dev
Try to make RESTful API with MVC using Flask 1.0.2
Links to do what you want with Sublime Text
When you want to play a game via Proxy
Things to do when you start developing with Django
When you want to plt.save in a for statement
Site notes to help you use NetworkX with Python
The programming language you want to be able to use
Make a note of what you want to do in the future with Raspberry Pi
Useful operation when you want to solve all problems in multiple programming languages with Codewars
[Python] When you want to import and use your own package in the upper directory
What to do if you get an Undefined error when trying to use pip with pyenv
Easy to use Flask
How to make a Cisco Webex Teams BOT with Flask
[Django] A memorandum when you want to communicate asynchronously [Python3]
I want to use R functions easily with ipython notebook
Knowledge you need to know when programming competitive programming with Python2
I want to make a blog editor with django admin
[python] A note when trying to use numpy with Cython
I want to make a click macro with pyautogui (desire)
Understanding how to use Jinja2 makes development with Flask smarter
I want to make a click macro with pyautogui (outlook)
[Python] I want to use the -h option with argparse
I want to use a virtual environment with jupyter notebook!
If you want your colleagues to use the same language
When you want to hit a UNIX command on Python
When you want to use multiple versions of the same Python library (virtual environment using venv)