Flaskでウェブアプリケーション作成①
今回はFlaskのアプリケーションになります。
以前にlineの自動応答を作る際にFlaskのコードで書いてGit Actionで動かすということをやったことがあるのですが、Flaskでのウェブアプリケーション作成はこれが一つ目です。やりたいことやプロジェクトなどを簡単に管理できるものを作りたいと思い作ってみました。もう少し凝ったものにしたいのですが、一旦最低限の機能は完成したので公開します。
Flask でウェブサービス① やりたいこと管理アプリ
例えばこんな状況で
今回は、単純に自分のゴールを管理したり、日々やっていることをプロジェクト単位で管理したり、簡単な備忘録を登録したり、あとは業務以外でやりたいことを一覧にしたりするというウェブサービスになります。
イメージはこんな感じで出来上がってます。
このガジェットでできることはこちら
- 自分のプロジェクトを管理できます。
- 備忘録程度のナレッジを管理できます。
- やりたいことを管理できます。
- プロジェクトは新規登録から編集、そして終了した項目などをチェックできます。
- 備忘録はタグを1つつけることができるので、後からタグで記事をソートできます。
- herokuでウェブに公開しスマホからでもチェック、作成、編集できます。
※未実装の機能(今後実装します)
評価(自己)
役立ち度 ★★★★(良し)
効率化 ★★★(平均)
ミス防止 ★★★(平均)
楽しさ ★★★★(良し)
操作 ★★★★(良し)
※基本的に自分の使いたい機能は実装できましたが、ハンバーガーメニューなど実装できていない部分があるのとデザイン的にイマイチかなと。。
実行環境
使用環境 heroku
使用言語 python
使用ライブラリ flask、SQLAlchemyなど
その他 bootstrap
コード
フォルダの構成は下記のようになっています。
やりたいこと管理 > static > css + styles.css > img > template + base.html + index.html + allprojects.html + allknowledge.html + alldesire.html + createproject.html + createknowledge.html + createdesire.html + showknowledge.html + showproject.html + knowledgetags.html + updateproject.html + updateknowledge.html + updatedesire.hmtl > app.py > blog.db > Procfile > requirement.txt
代表的なファイルは下記です。
app.py
from flask import Flask from flask import render_template, request, redirect # from flask_bootstrap import Bootstrap from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from datetime import datetime import pytz app = Flask(__name__) # ローカルでDBを使う際はこちら # app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db' # heroku用DB app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://ヘロクではpostgresqlを使用。ターミナルでheroku config --app アプリ名 で取得したもの' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db = SQLAlchemy(app) migrate = Migrate(app, db) class Project(db.Model): id = db.Column(db.Integer, primary_key=True) projecttitle = db.Column(db.String(100), nullable=False) overview = db.Column(db.Text(), nullable=False) todo1 = db.Column(db.String(100)) todo2 = db.Column(db.String(100)) todo3 = db.Column(db.String(100)) todo4 = db.Column(db.String(100)) todo5 = db.Column(db.String(100)) todo6 = db.Column(db.String(100)) todo7 = db.Column(db.String(100)) todo8 = db.Column(db.String(100)) todo9 = db.Column(db.String(100)) todo10 = db.Column(db.String(100)) todo11 = db.Column(db.String(100)) todo12 = db.Column(db.String(100)) todo13 = db.Column(db.String(100)) todo14 = db.Column(db.String(100)) todo15 = db.Column(db.String(100)) status1 = db.Column(db.Integer) status2 = db.Column(db.Integer) status3 = db.Column(db.Integer) status4 = db.Column(db.Integer) status5 = db.Column(db.Integer) status6 = db.Column(db.Integer) status7 = db.Column(db.Integer) status8 = db.Column(db.Integer) status9 = db.Column(db.Integer) status10 = db.Column(db.Integer) status11 = db.Column(db.Integer) status12 = db.Column(db.Integer) status13 = db.Column(db.Integer) status14 = db.Column(db.Integer) status15 = db.Column(db.Integer) completedrate = db.Column(db.Integer) created_at = db.Column(db.DateTime, nullable=False, default=datetime.now(pytz.timezone('Asia/Tokyo'))) class Knowledge(db.Model): id = db.Column(db.Integer, primary_key=True) knowledgetitle = db.Column(db.String(100), nullable=False) article = db.Column(db.Text(), nullable=False) tags = db.Column(db.String(100)) created_at = db.Column(db.DateTime, nullable=False, default=datetime.now(pytz.timezone('Asia/Tokyo'))) class Desire(db.Model): id = db.Column(db.Integer, primary_key=True) desiretitle = db.Column(db.String(100), nullable=False) desirecontent = db.Column(db.Text(), nullable=False) status = db.Column(db.Integer) created_at = db.Column(db.DateTime, nullable=False, default=datetime.now(pytz.timezone('Asia/Tokyo'))) @app.route('/', methods=['GET', 'POST']) def index(): projects = Project.query.all() progress_project = [project.projecttitle for project in projects if project.completedrate < 100][:3] # 下記データベースをソートして取り出す方法 knowledges = db.session.query(Knowledge).order_by(Knowledge.created_at.desc()).all() latest_knowledge = [knowledge.knowledgetitle for knowledge in knowledges][:3] desires = Desire.query.all() not_completeds = [desire.desiretitle for desire in desires if desire.status==0][:10] return render_template('index.html', progress_project=progress_project, latest_knowledge=latest_knowledge, not_completeds=not_completeds) # 以下プロジェクトページ @app.route('/allprojects', methods=['GET', 'POST']) def allprojects(): if request.method == 'GET': projects = db.session.query(Project).order_by(Project.created_at.desc()).all() return render_template('allprojects.html', projects=projects) @app.route('/createproject', methods=['GET', 'POST']) def createproject(): if request.method == 'POST': projecttitle = request.form.get('projecttitle') overview = request.form.get('overview') todo1 = request.form.get('todo1') todo2 = request.form.get('todo2') todo3 = request.form.get('todo3') todo4 = request.form.get('todo4') todo5 = request.form.get('todo5') todo6 = request.form.get('todo6') todo7 = request.form.get('todo7') todo8 = request.form.get('todo8') todo9 = request.form.get('todo9') todo10 = request.form.get('todo10') todo11 = request.form.get('todo11') todo12 = request.form.get('todo12') todo13 = request.form.get('todo13') todo14 = request.form.get('todo14') todo15 = request.form.get('todo15') status1 = 0 status2 = 0 status3 = 0 status4 = 0 status5 = 0 status6 = 0 status7 = 0 status8 = 0 status9 = 0 status10 = 0 status11 = 0 status12 = 0 status13 = 0 status14 = 0 status15 = 0 completedrate = 0 project = Project(projecttitle=projecttitle, overview=overview, todo1=todo1, todo2=todo2, todo3=todo3, todo4=todo4, todo5=todo5, todo6=todo6, todo7=todo7, todo8=todo8, todo9=todo9, todo10=todo10, todo11=todo11, todo12=todo12, todo13=todo13, todo14=todo14, todo15=todo15, status1=status1, status2=status2, status3=status3, status4=status4, status5=status5, status6=status6, status7=status7, status8=status8, status9=status9, status10=status10, status11=status11, status12=status12, status13=status13, status14=status14, status15=status15, completedrate=completedrate) db.session.add(project) db.session.commit() return redirect('/') else: return render_template('createproject.html') @app.route('/<int:id>/showproject', methods=['GET']) def showproject(id): project = Project.query.get(id) if request.method == 'GET': # リストをあらかじめ作って渡すこと可能 li =[project.todo1, project.todo2, project.todo3, project.todo4, project.todo5, project.todo6, project.todo7, project.todo8, project.todo9, project.todo10, project.todo11, project.todo12, project.todo13, project.todo14, project.todo15] li2 = [project.status1, project.status2, project.status3, project.status4, project.status5, project.status6, project.status7, project.status8, project.status9, project.status10, project.status11, project.status12, project.status13, project.status14, project.status15] # htmlでzipを使用したいときは先にzip化して渡すこと return render_template('showproject.html', project=project, l=zip(li, li2)) @app.route('/<int:id>/updateproject', methods=['GET', 'POST']) def updateproject(id): project = Project.query.get(id) if request.method == 'GET': return render_template('updateproject.html', project=project) else: project.projecttitle = request.form.get('projecttitle') project.overview = request.form.get('overview') project.todo1 = request.form.get('todo1') project.todo2 = request.form.get('todo2') project.todo3 = request.form.get('todo3') project.todo4 = request.form.get('todo4') project.todo5 = request.form.get('todo5') project.todo6 = request.form.get('todo6') project.todo7 = request.form.get('todo7') project.todo8 = request.form.get('todo8') project.todo9 = request.form.get('todo9') project.todo10 = request.form.get('todo10') project.tpdo11 = request.form.get('tpdo11') project.todo12 = request.form.get('todo12') project.todo13 = request.form.get('todo13') project.todo14 = request.form.get('todo14') project.todo15 = request.form.get('todo15') checks = request.form.getlist('check') if '0' in checks: project.status1 = 1 if '1' in checks: project.status2 = 1 if '2' in checks: project.status3 = 1 if '3' in checks: project.status4 = 1 if '4' in checks: project.status5 = 1 if '5' in checks: project.status6 = 1 if '6' in checks: project.status7 = 1 if '7' in checks: project.status8 = 1 if '8' in checks: project.status9 = 1 if '9' in checks: project.status10 = 1 if '10' in checks: project.status11 = 1 if '11' in checks: project.status12 = 1 if '12' in checks: project.status13 = 1 if '13' in checks: project.status14 = 1 if '14' in checks: project.status15 = 1 li =[project.todo1, project.todo2, project.todo3, project.todo4, project.todo5, project.todo6, project.todo7, project.todo8, project.todo9, project.todo10, project.todo11, project.todo12, project.todo13, project.todo14, project.todo15] li2 = [project.status1, project.status2, project.status3, project.status4, project.status5, project.status6, project.status7, project.status8, project.status9, project.status10, project.status11, project.status12, project.status13, project.status14, project.status15] completed = [elem for elem in li2 if elem == 1] todos = [elem for elem in li if elem] project.completedrate = round(len(completed)/len(todos)*100) db.session.commit() return redirect('/') # return render_template('showproject.html', project=project) @app.route('/<int:id>/deleteproject', methods=['GET']) def deleteproject(id): project = Project.query.get(id) db.session.delete(project) db.session.commit() return redirect('/') # 以下知識用ページ @app.route('/allknowledge', methods=['GET', 'POST']) def allknowledge(): if request.method == 'GET': knowledges = db.session.query(Knowledge).order_by(Knowledge.created_at.desc()).all() tag_list = list(set(knowledge.tags for knowledge in knowledges)) return render_template('allknowledge.html', knowledges=knowledges, tag_list=tag_list) @app.route('/createknowledge', methods=['GET', 'POST']) def createknowledge(): if request.method == 'POST': knowledgetitle = request.form.get('knowledgetitle') article = request.form.get('article') tags = request.form.get('tags') knowledge = Knowledge(knowledgetitle=knowledgetitle, article=article, tags=tags) db.session.add(knowledge) db.session.commit() return redirect('/') else: return render_template('createknowledge.html') @app.route('/<int:id>/showknowledge', methods=['GET']) def showknowledge(id): knowledge = Knowledge.query.get(id) if request.method == 'GET': return render_template('showknowledge.html', knowledge=knowledge) @app.route('/<int:id>/updateknowledge', methods=['GET', 'POST']) def updateknowledge(id): knowledge = Knowledge.query.get(id) if request.method == 'GET': return render_template('updateknowledge.html', knowledge=knowledge) else: knowledge.knowledgetitle = request.form.get('knowledgetitle') knowledge.article = request.form.get('article') db.session.commit() return render_template('showknowledge.html', knowledge=knowledge) @app.route('/<int:id>/deleteknowledge', methods=['GET']) def deletekowledge(id): knowledge = Knowledge.query.get(id) db.session.delete(knowledge) db.session.commit() return redirect('/') @app.route('/<tags>/knowledgetags', methods=['GET']) def sort_knowledge(tags): if request.method == 'GET': # フィルターでソートする方法 knowledges = Knowledge.query.filter(Knowledge.tags == tags).all() return render_template('knowledgetags.html', knowledges=knowledges, tag=tags) @app.route('/alldesire', methods=['GET', 'POST']) def alldesire(): if request.method == 'GET': desires = Desire.query.all() return render_template('alldesire.html', desires=desires) @app.route('/createdesire', methods=['GET', 'POST']) def createdesire(): if request.method == 'POST': desiretitle = request.form.get('desiretitle') desirecontent = request.form.get('desirecontent') status = 0 desire = Desire(desiretitle=desiretitle, desirecontent=desirecontent, status=status) db.session.add(desire) db.session.commit() return redirect('/') else: return render_template('createdesire.html') @app.route('/<int:id>/updatedesire', methods=['GET', 'POST']) def updatedesire(id): desire = Desire.query.get(id) if request.method == 'GET': return render_template('updatedesire.html', desire=desire) else: desire.desiretitle = request.form.get('desiretitle') desire.desirecontent = request.form.get('desirecontent') check = request.form.get('check') print(check) if check is not None: desire.status = 1 db.session.commit() return redirect('/') @app.route('/<int:id>/deletedesire', methods=['GET']) def deletedesire(id): desire = Desire.query.get(id) db.session.delete(desire) db.session.commit() return redirect('/')
base.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- <link rel="stylesheet" href="{{url_for('static', filename='css/bootstrap.min.css')}}"> --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <link rel="stylesheet" href="{{url_for('static', filename='css/styles.css')}}"> <title>やりたいこと管理アプリ</title> </head> <body> <nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-5"> <div class="container"> <a class="navbar-brand" href="/">やりたいこと管理アプリ</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse navbarSupportedContent" id="navbarSupportedContent"> <ul class="navbar-nav"> <li class="nav-item"><a class="nav-link" href="/">ホーム</a></li> <li class="nav-item"><a class="nav-link" href="/allprojects">プロジェクト一覧</a></li> <li class="nav-item"><a class="nav-link" href="/createproject">プロジェクト作成</a></li> <li class="nav-item"><a class="nav-link" href="/allknowledge">ナレッジ一覧</a></li> <li class="nav-item"><a class="nav-link" href="/createknowledge">ナレッジ作成</a></li> <li class="nav-item"><a class="nav-link" href="/alldesire">やりたい事一覧</a></li> <li class="nav-item"><a class="nav-link" href="/createdesire">やりたい事登録</a></li> </ul> </div> </div> </nav> {% block content %} {% endblock %} <div class="container mt-5"> <ul class="footer_ul"> <li class="footer_li"><a href="/">ホーム</a></li> <li class="footer_li"><a href="/allprojects">プロジェクト一覧</a></li> <li class="footer_li"><a href="/createproject">プロジェクト作成</a></li> <li class="footer_li"><a href="/allknowledge">ナレッジ一覧</a></li> <li class="footer_li"><a href="/createknowledge">ナレッジ作成</a></li> <li class="footer_li"><a href="/alldesire">やりたい事一覧</a></li> <li class="footer_li"><a href="/createdesire">やりたい事登録</a></li> </ul> </div> </body> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <!-- <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script> --> </html>
index.html
{% extends 'base.html' %} {% block content %} <div class="container"> <h1 class="title"><em>Wanna</em></h1> {% for not_completed in not_completeds%} <ul class="top_list"> <li class="list_item">{{not_completed}}</li> </ul> {% endfor %} </div> <div class="container mt-5"> <h1><em>Doing</em></h1> <div class="row"> <div class="col-12 col-md-6 mt-3"> <div class="card me-10" style="height: 43rem;" > <img src="static/img/project.jpg" alt="プロジェクト画像" class="card-img-top"> <div class="card-body"> <h4 class="card-title border-bottom">プロジェクト</h4> <div> <p class="card-text text-success">進行中</p> {% for progress in progress_project %} <ul class="list-group list-group-flush"> <li class="list-group-item">{{progress}}</li> </ul> {% endfor %} </div> <div class="text-center"> <a href="/createproject" class="btn btn-outline-secondary position-absolute bottom-0 start-50 translate-middle-x mb-3">新規作成</a> </div> </div> </div> </div> <div class="col-12 col-md-6 mt-3"> <div class="card ms-10" style="height: 43rem;" > <img src="static/img/konwledge.jpg" alt="知識画像" class="card-img-top"> <div class="card-body"> <h4 class="card-title border-bottom"">ナレッジ</h4> <div> <p class="card-text text-success" >最新</p> {% for latest in latest_knowledge %} <ul class="list-group list-group-flush"> <li class="list-group-item">{{latest}}</li> </ul> {% endfor %} </div> <div class="text-center"> <a href="/createknowledge" class="btn btn-outline-secondary position-absolute bottom-0 start-50 translate-middle-x mb-3">新規投稿</a> </div> </div> </div> </div> </div> </div> {% endblock %}
その他のhtmlファイルは同様にして作っています。