[PYTHON] Modèle de bonnes pratiques personnelles à utiliser lorsque vous souhaitez créer MVP avec Flask

Lors de la création d'une application Web, il est pratique d'avoir un modèle. Cette fois, je voudrais vous présenter le modèle de l'application Flask que j'utilise habituellement.

supposition

Aperçu du site

image.png

C'est une application web qui crée et gère un simple PRJ avec.

Les principales spécifications du site sont les suivantes.

Structure des dossiers

Comme mentionné ci-dessus, c'est une application simple, c'est donc une échelle qui peut être écrite dans un seul fichier, Je souhaite organiser soigneusement les dossiers pour garantir l'évolutivité et la lisibilité.

En particulier, les dossiers ont été organisés en tenant compte des points suivants.

La structure réelle des dossiers est la suivante.

tree


dossier racine
│  .gitignore         
│  LICENSE
│  main.py Le premier serveur d'applications à être exécuté
│ Procfile Utilisé lors de l'exécution sur Heroku
│  README.md
│  requirements.txt Utilisé lors de l'exécution sur Heroku
│
├─.fichier de configuration vscode VSCode (pour gitignore)
│      launch.Définir la configuration de débogage json
│      settings.json Spécification de l'environnement virtuel Python, etc.
│
├─biz Business Logic Storage
│     prj.Écrire une logique métier telle que les opérations py DB et les appels d'API
│
├─mw Middleware (filtre) stockage
│     prj.Écrivez le traitement global de la couche intermédiaire qui relie la route py et le biz
│
├─route Stockage de la définition d'itinéraire
│     prj.Ecrire une définition de routage pour chaque ressource py
│     top.Ecrire une définition de routage pour chaque ressource py
│
├─ Stockage statique de contenu statique
│ ├─common Contenu statique utilisé sur toutes les pages
│  │  ├─css
│  │  │      page.css
│  │  │
│  │  └─js
│  │         page.js
│  │
│ ├─prj Stockage de contenu statique par ressource
│  │  ├─css
│  │  │      page.css
│  │  │
│  │  └─js
│  │          page.js
│  │
│ └─top Stockage de contenu statique par ressource
│      ├─css
│      │      page.css
│      │
│      └─js
│              page.js
│
└─ Modèles Stockage de modèles Jinja
   │  footer.Pied de page commun html
   │  header.en-tête commun html
   │  layout.définition de la mise en page html
   │  top.Page individuelle pour chaque ressource html (1 fichier système complet)
   │
└─prj Page individuelle par ressource (système multi-pages)
           entry.html
           search.html
           show.html

Aperçu du traitement

Démarrer le serveur Flask

Lorsque vous démarrez le serveur Flask, exécutez main.py.

Traitement en ligne (affichage de l'écran TOP)

Lorsque le serveur Flask accepte une requête de l'utilisateur vers la page TOP, il traite selon le flux suivant. Il n'y a pas de logique particulière dans le traitement lorsque la page TOP est affichée et que le modèle correspondant est simplement renvoyé, de sorte que le flux de traitement est le suivant.

image.png

Traitement en ligne (affichage de l'écran PRJ)

Le flux de traitement de l'écran PRJ sera expliqué grossièrement en utilisant l'écran détaillé PRJ comme exemple.

Contrairement à l'écran TOP, l'écran PRJ doit implémenter plusieurs actions telles que l'enregistrement, la recherche et la référence. Par conséquent, chaque couche a les rôles suivants.

image.png

En concevant de cette manière, le traitement peut être divisé en chaque couche et géré d'une manière facile à comprendre. Cela permet également de voir plus facilement où apporter des corrections lors de l'ajout de nouvelles fonctionnalités.

※たとえば、新しいアクションdeleteを追加したければ、route、mw、biz、templateの各prj.py/prj.htmlにdeleteアクションに対応するメソッドを追加する、など。

Quant à la relation de traitement entre les couches, nous la mettrons en œuvre avec la politique suivante.

Détails d'implémentation

maiy.py: processus de démarrage du serveur Flask

Importez * .py dans le dossier d'itinéraire et ʻApp.register_blueprint () `afin que diverses requêtes puissent être acheminées correctement.

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 Seul le processus pour afficher l'écran TOP. Parce que c'est le routage lorsque la route du site (https: // mon-site.com /) est accédée L'url_prefix de Blueprint définit '' (caractère vide).

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 définition de routage système prj. Définissez `` / prj'dans le préfixe url_prefix de Blueprint. En faisant cela, vous serez en mesure d'accepter les demandes dehttps: // mon-site.com / prj / ~. En définissant une route comme @ prj.route ('/ search', methods = ['POST']), diverses actions telles que https: // mon-site.com / prj / search` sont acceptées. Pourront.

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='Recherche de projet', prj={})

@prj.route('/search', methods=['POST'])
def search():
  q = parse(request)
  if q is None:
    #Retour anticipé
    return jsonify({'code': 'W00100','message': 'La valeur saisie n'est pas valide.'})
  
  #Rechercher DB avec les conditions saisies
  data = search_prj(q)

  #Afficher les résultats de la recherche
  return jsonify({'data': data})

@prj.route('/show/<prj_id>', methods=['GET'])
def show(prj_id):
  if prj_id is None:
    #Retour anticipé
    return render_template('prj/show.html', title='Détails du projet', data={})
  
  # prj_Rechercher DB par identifiant
  data = get_prj(prj_id)

  #Afficher la page de détails
  return render_template('prj/show.html', title='Détails du projet', data=data)

@prj.route('/entry', methods=['GET'])
def entry():
  #Affichage initial de l'écran de saisie PRJ
  return render_template('prj/entry.html', title='Création de projet')

@prj.route('/create', methods=['POST'])
def create():
  #Créer PRJ
  data = create_prj(parse(request))
  #Afficher la page de détails
  return render_template('prj/show.html', title='Détails du projet', data=data, message='J'ai créé un projet.')

mw/prj.py Écrivez un processus de vérification et un processus d'appel Biz pour traiter chaque action.

mw/prj.py


from flask import request
import biz.prj as prj

def parse(request):
  #Omis

def search_prj(dto):
  #Omis

def get_prj(prj_id):
  """
Obtenez le projet avec l'ID de projet spécifié
  """
  return prj.get(prj_id)

def create_prj(dto):
  #Omis

def update_prj(data, dto):
  #Omis

biz/prj.py Traitement de l'accès à l'API et traitement de l'accès à la base de données. L'exemple ci-dessous décrit un processus get qui appelle une API via HTTP. De plus, étant donné que les points de terminaison d'API et les clés d'API sont des informations confidentielles en tant que système, ils sont définis pour être lus à partir des variables d'environnement.

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 Modèle pour toutes les pages HTML. CSS et JS communs à toutes les pages peuvent être lus et définis dans ce layout.html. J'essaye de ne pas l'écrire sur chaque page.

{% Block css%} et {% block js%} sont définis pour que CSS et JS individuellement pour chaque page puissent être lus.

Le contenu de la balise body est défini comme étant un en-tête commun, le contenu de chaque page et un pied de page commun.

<link rel="stylesheet" href="/static/common/css/page.css" /> <script src="/static/common/js/page.js"></script> Lors de la lecture de contenu statique sous static, href et src sont définis par une barre oblique comme / static. C'est le même layout.html pour le contenu statique commun sous static / common / même si la hiérarchie des pages est différente comme template / top.html et template / prj / show.html. C'est pour qu'il puisse être lu en utilisant.

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" />
<!--Style commun-->
<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" />
<!--Page individuelle CSS-->
{% block css %}
{% endblock %}
</head>
<body class="has-navbar-fixed-top">

  <!--entête-->
  {% include "header.html" %}

  <!--contenu-->
  {% block content %}
  {% endblock %}

  <!--bas de page-->
  {% include "footer.html" %}

  <!--JS commun-->
  <script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
  <script src="/static/common/js/page.js"></script>

  <!--Page individuelle 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 %}

Code complet pour l'exemple d'application

Le code d'application créé à l'aide de ce modèle est publié sur Github. https://github.com/KeitaShiratori/ripple

Recommended Posts

Modèle de bonnes pratiques personnelles à utiliser lorsque vous souhaitez créer MVP avec Flask
Utilisez aggdraw lorsque vous voulez dessiner magnifiquement avec un oreiller
Lorsque vous souhaitez envoyer un objet avec des requêtes à l'aide de flask
Lorsque vous souhaitez l'utiliser tel quel lorsque vous l'utilisez avec lambda memo
Un référentiel essentiel à utiliser lorsque vous souhaitez l'essayer avec ansible
Signifie mémo lorsque vous essayez de faire de l'apprentissage automatique avec 50 images
Si vous voulez créer un bot discord avec python, utilisons un framework
Lorsque vous souhaitez filtrer avec le framework Django REST
[AWS] Que faire lorsque vous souhaitez piper avec Lambda
Lorsque vous souhaitez enregistrer les données initiales de Django avec des relations
Lorsque vous souhaitez utiliser python 2.x sur Gentoo Linux moderne
Je connais? Analyse de données à l'aide de Python ou de choses que vous souhaitez utiliser quand vous le souhaitez avec numpy
[Python] Lorsque vous souhaitez utiliser toutes les variables dans un autre fichier
Lorsque vous souhaitez ajuster l'intervalle de l'échelle de l'axe avec APLpy
Lorsque vous souhaitez remplacer une colonne par une valeur manquante (NaN) colonne par colonne
Meilleures pratiques personnelles lors de la mise au point avec Chainer
Comment créer un environnement lorsque vous souhaitez utiliser python2.7 après l'installation d'Anaconda3
[OpenCV] Lorsque vous voulez vérifier s'il est lu correctement avec imread
Je veux utiliser MATLAB feval avec python
Je veux faire un jeu avec Python
Lorsque vous souhaitez mettre à jour le pilote Chrome.
Je souhaite utiliser le répertoire temporaire avec Python2
Je ne veux pas utiliser -inf avec np.log
Je souhaite utiliser ip vrf avec SONiC
[Python] Je veux ajouter un répertoire statique avec Flask [Je veux utiliser autre chose que statique]
[Python] Je souhaite utiliser uniquement l'index lors de la mise en boucle d'une liste avec une instruction for
Lorsque la variable que vous souhaitez utiliser comme exposant dans matplotlib comporte deux caractères ou plus
Que faire si vous ne souhaitez pas utiliser de noms de colonnes japonais lors de l'utilisation d'ortoolpy.logistics_network
Si vous souhaitez utiliser Cython, incluez également python-dev
Essayez de créer une API RESTful avec MVC à l'aide de Flask 1.0.2
Liens pour faire ce que vous voulez avec Sublime Text
Choses à faire lorsque vous commencez à développer avec Django
Lorsque vous voulez plt.save dans l'instruction for
Notes de site pour vous aider à utiliser NetworkX avec Python
Le langage de programmation que vous souhaitez pouvoir utiliser
Notez ce que vous voulez faire à l'avenir avec Razpai
Opération utile lorsque vous souhaitez résoudre tous les problèmes dans plusieurs langages de programmation avec Codewars
[Python] Lorsque vous souhaitez importer et utiliser votre propre package dans le répertoire supérieur
Que faire si vous obtenez une erreur non définie lorsque vous essayez d'utiliser pip avec pyenv
Flacon facile à utiliser
Comment créer un BOT Cisco Webex Teams à l'aide de Flask
[Django] Mémorandum lorsque vous souhaitez communiquer de manière asynchrone [Python3]
Je souhaite utiliser facilement les fonctions R avec le notebook ipython
Connaissances à connaître lors de la programmation de concours avec Python2
Je veux créer un éditeur de blog avec l'administrateur de django
[python] Remarques lors de la tentative d'utilisation de numpy avec Cython
Je veux faire une macro de clic avec pyautogui (désir)
Comprendre comment utiliser Jinja2 rend le développement avec Flask plus intelligent
Je veux faire une macro de clic avec pyautogui (Outlook)
[Python] Je souhaite utiliser l'option -h avec argparse
Je souhaite utiliser un environnement virtuel avec jupyter notebook!
Si vous souhaitez que vos collègues utilisent la même langue
Lorsque vous souhaitez lancer une commande UNIX sur Python
Lorsque vous souhaitez utiliser plusieurs versions de la même bibliothèque Python (environnement virtuel utilisant venv)