python(Flaskベースのタスクリスト管理システム)

19896 ワード

1.目標
本プロジェクトでは、データベースのバックエンドとしてMariadb、フロントエンドのテクノロジースタックとしてBootstrapを学習し、インベントリアプリケーションを実現します.ここからFlask WebアプリケーションフレームワークとMariadbリレーショナルデータベースとBootStrap web開発フレームワークを学ぶことができます.
2.プロジェクト紹介
本アプリケーションは、TodoMVCのtodo listアプリケーションから変更され、Mariadbをデータベースのバックエンドとして使用し、BootstrapをフロントエンドのFlaskアプリケーションとして使用します.まずいい名前をつけましょう.便利になったら呼んでください.
todo list=>(カスタマイズ、任意の名前)=>todoest
一般的なtodo listアプリケーションのように、todoestは以下の機能を実現しています.
  • 管理データベース接続
  • すべてのtodoエントリ
  • がリストされます.
  • 新しいtodo
  • を作成
  • 検索単一todo
  • 単一todoを編集するか、完了したとマークする
  • 単一todo
  • を削除
    3.プロジェクト効果
  • ラベルページを新規作成し、todoest
  • を起動します.
  • ブラウザアクセスを開くhttp://localhost:8000/

  • 4.技術分析
  • なぜFlaskを選んだのですか?
  • FlaskはPythonを用いて作成された軽量級Webアプリケーションフレームワークである.WSGIツールボックスはWerkzeug、テンプレートエンジンはJinja 2を使用します.FlaskはBSDライセンスを使用します.
  • Flaskは、単純なコアを使用し、extensionで他の機能を追加するため、「microframework」とも呼ばれています.
  • Flaskには、デフォルトで使用されるデータベース、フォーム検証ツールはありません.したがってFlaskはPythonを用いて作成された軽量級Webアプリケーションフレームワークである.軽くて拡張しやすく、主流で、質問があっても見つからないことを恐れず、todoestという軽応用に最適です.

  • なぜMariadbを選んだのですか?MariaDBデータベース管理システムはMySQLの1つの分岐で、主にオープンソースコミュニティによって維持されており、GPLライセンスMariaDBを採用する目的はMySQLと完全に互換性があり、APIとコマンドラインを含め、簡単にMySQLの代替品になることができるようにすることです.MariaDBはMySQLデータベースの代替品と見なされていますが、拡張機能、ストレージエンジン、およびいくつかの新しい機能の改善においてMySQLよりも優れています.MySQLからMariaDBへの移行も簡単です.
  • なぜBootstrapを選んだのですか?
  • Bootstrapは、アメリカのTwitter会社のデザイナーMark OttoとJacob Thorntonが協力してHTML、CSS、JavaScriptに基づいて開発した簡潔で直感的で強力なフロントエンド開発フレームワークであり、Web開発をより迅速にする.
  • Bootstrapには豊富なWebコンポーネントが含まれており、これらのコンポーネントに基づいて、きれいで機能的なWebサイトを迅速に構築することができます.ドロップダウンメニュー、ボタングループ、ボタンドロップダウンメニュー、ナビゲーション、ナビゲーションバー、パスナビゲーション、ページング、レイアウト、サムネイル、警告ダイアログ、進捗バー、メディアオブジェクトなどの
  • があります.

    プロジェクトの成果:
    appパッケージ:
    __ init __:
    from flask import  Flask
    from flask_sqlalchemy import  SQLAlchemy
    from flask_bootstrap import  Bootstrap
    from flask_script import Manager
    from flask_migrate import  Migrate
    from flask_moment import  Moment
    import  pymysql
    
    app = Flask(__name__)
    
    #        
    pymysql.install_as_MySQLdb()
    
    #            
    app.config.from_pyfile('../config.py')
    db = SQLAlchemy(app)
    manager = Manager(app)
    bt = Bootstrap(app)
    migrate = Migrate(app, db)
    moment = Moment(app)
    
    

    models(データベーステーブルテンプレート):
    from app import db
    from werkzeug.security import  generate_password_hash, check_password_hash
    from datetime import  datetime
    
    
    
    #         :    ,     ,     ,
    #         :   ,    ,    
    #    
    class User(db.Model):
        id = db.Column(db.Integer, autoincrement=True, primary_key=True)
        username = db.Column(db.String(20), unique=True)
        password_hash = db.Column(db.String(100), nullable=False)
        email = db.Column(db.String(30), unique=True)
        add_time = db.Column(db.DateTime, default=datetime.now()) #       
        # 1).  User    todos; 2). Todo    user;
        todos = db.relationship('Todo', backref="user")
        categories = db.relationship('Category', backref='user')
    	
    	
        @property
        def password(self):
            """u.password"""
            raise  AttributeError("         ")
    
        @password.setter
        def password(self, password):
            """u.password = xxxxx """
            self.password_hash = generate_password_hash(password)
    
    
        def verify_password(self, password):
            """        """
            return  check_password_hash(self.password_hash, password)
    
        def __repr__(self):
            return  "" %(self.username)
    
    #         :    
    #     ,     ,         
    #    
    class Todo(db.Model):
        id = db.Column(db.Integer, autoincrement=True, primary_key=True)
        content = db.Column(db.String(100)) #     
        status = db.Column(db.Boolean, default=False) #      
        add_time = db.Column(db.DateTime, default=datetime.now())  #       
        #      ,        id
        category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
        #       ;
        user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    
        def __repr__(self):
            return  "" %(self.content[:6])
    
    
    #    
    class Category(db.Model):
        id = db.Column(db.Integer, autoincrement=True, primary_key=True)
        name = db.Column(db.String(20), unique=True)
        add_time = db.Column(db.DateTime, default=datetime.now())  #       
        # 1). Category      todos, 2). Todo    category;
        todos = db.relationship('Todo', backref='category')
    
        user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    
        def __repr__(self):
            return  "" %(self.name)
    

    form(webフォーム):
    from flask_wtf import FlaskForm
    from wtforms import StringField, PasswordField, SubmitField, \
        ValidationError, SelectField, DateTimeField
    from wtforms.validators import DataRequired, Email, Length, EqualTo
    
    #     
    from app.models import User, Category
    
    
    class RegisterForm(FlaskForm):
        email = StringField(
            label="  ",
            validators=[
                DataRequired(),
                Email(),
    
            ]
        )
        username = StringField(
            label="   ",
            validators=[
                DataRequired(),
    
            ],
        )
        password = PasswordField(
            label='  ',
            validators=[
                DataRequired(),
                Length(6, 12, "     6-12 ")
            ]
        )
    
        repassword = PasswordField(
            label='    ',
            validators=[
                EqualTo("password", "          ")
            ]
        )
    
        submit = SubmitField(
            label="  "
        )
    
        # *****************************************************
        #      validate_username          ,      ,       
        def validate_username(self, field):
            # filed.data ==== username       
            u = User.query.filter_by(username=field.data).first()
            if u:
                raise ValidationError("   %s    " % (u.username))
    
        def validate_email(self, filed):
            u = User.query.filter_by(email=filed.data).first()
            if u:
                raise ValidationError("  %s    " % (u.email))
    
    
    #     
    class LoginForm(FlaskForm):
        username = StringField(
            label="   ",
            validators=[
                DataRequired(),
    
            ],
        )
        password = PasswordField(
            label='  ',
            validators=[
                DataRequired(),
                # Length(6, 12, "     6-12 ")
            ]
        )
        submit = SubmitField(
            label="  "
        )
    
    #        
    class TodoForm(FlaskForm):
        content = StringField(
            label="    ",
            validators=[
                DataRequired()
            ]
        )
        #     
        category = SelectField(
            label="    ",
            coerce=int,
            choices=[(item.id, item.name) for item in Category.query.all()]
        )
    #       (       )
    class AddTodoForm(TodoForm):
        finish_time = DateTimeField(
            label="      "
        )
        submit = SubmitField(
            label="    ",
        )
    #       (       )
    class EditTodoForm(TodoForm):
        submit = SubmitField(
            label="    ",
        )
    

    views(ビュー関数):
    import json
    from functools import wraps
    
    from app import app, db
    
    
    from app.forms import RegisterForm, LoginForm, AddTodoForm, EditTodoForm
    from flask import render_template, flash, redirect, url_for, session, request
    
    from app.models import User, Todo
    
    #      ,        
    def is_login(f):
        """            """
    
        @wraps(f)
        def wrapper(*args, **kwargs):
            #   session      seesion['user'],
            #       ,      ,       ;
            #        ,       ,        ;;
            if session.get('user', None):
                return f(*args, **kwargs)
            else:
                flash("          %s" % (f.__name__))
                return redirect(url_for('login'))
    
        return wrapper
    
    #     
    @app.route('/')
    def index():
        return redirect(url_for('list'))
    
    
    #     
    @app.route('/register/', methods=['POST', 'GET'])
    def register():
        form = RegisterForm()
        if form.validate_on_submit():
            # 1.            ;
            email = form.email.data
            username = form.username.data
            password = form.password.data
    
            # 2.           ?      None,      ;
            u = User.query.filter_by(username=username).first()
            if u:
                flash("  %s    " % (u.username))
                return redirect(url_for('register'))
            else:
                u = User(username=username, email=email)
                u.password = password
                db.session.add(u)
                db.session.commit()
                flash("    %s  " % (u.username))
                return redirect(url_for('login'))
        return render_template('register.html',
                               form=form)
    
    
    #     
    @app.route('/login/', methods=['GET', 'POST'])
    def login():
        form = LoginForm()
        if form.validate_on_submit():
            username = form.username.data
            password = form.password.data
    
            # 1.         ?
            u = User.query.filter_by(username=username).first()
            if u and u.verify_password(password):
                session['user_id'] = u.id
                session['user'] = u.username
                flash("    !")
                return redirect(url_for('index'))
            else:
                flash("         !")
                return redirect(url_for('login'))
    
        return render_template('login.html',
                               form=form)
    
    #     
    @app.route('/logout/')
    @is_login
    def logout():
        session.pop('user_id', None)
        session.pop('user', None)
    
        return redirect(url_for('login'))
    
    
    #     
    @app.route('/todo/add/', methods=['GET', 'POST'])
    @is_login
    def todo_add():
        form = AddTodoForm()
        if form.validate_on_submit():
            #          
            content = form.content.data
            category_id = form.category.data
    
            #        
            #            ,
            todo = Todo(content=content,
                        category_id=category_id,
                        user_id=session.get('user_id'))
            db.session.add(todo)
            db.session.commit()
            flash("      ")
            return redirect(url_for('todo_add'))
        return render_template('todo/add_todo.html',
                               form=form)
    
    
    #     
    @app.route('/todo/edit//', methods=['GET', 'POST'])
    @is_login
    def todo_edit(id):
        form = EditTodoForm()
        # *****  :               ,         ;
        todo = Todo.query.filter_by(id=id).first()
        form.content.data = todo.content
        form.category.data = todo.category_id
        if form.validate_on_submit():
            #               request.form    ,  form.content.data                 ;
            # print(request.form)
            # content = form.content.data   # error
            # category_id = form.category.data   # error
            content = request.form.get('content')
            category_id = request.form.get('category')
    
            #         
            todo.content = content
            todo.category_id = category_id
            db.session.add(todo)
            db.session.commit()
            flash("      ")
            return redirect(url_for('list'))
        return render_template('todo/edit_todo.html',
                               form=form)
    
    
    #     :     id  
    @app.route('/todo/delete//')
    @is_login
    def todo_delete(id):
        todo = Todo.query.filter_by(id=id).first()
        db.session.delete(todo)
        db.session.commit()
        flash("      ")
        return redirect(url_for('list'))
    
    
    #     
    @app.route('/todo/list/')
    @app.route('/todo/list//')
    @is_login
    def list(page=1):
        #         
        # Todo.query.paginate(page, per_page=5)
        todoPageObj = Todo.query.filter_by(user_id=session.get('user_id')).paginate(page, per_page=app.config['PER_PAGE'])  #  config.py      ;
        return render_template('todo/list_todo.html',
                               todoPageObj=todoPageObj,
                               )
    
    
    #          
    @app.route('/todo/done//')
    @is_login
    def done(id):
        todo = Todo.query.filter_by(id=id).first()
        todo.status = True
        db.session.add(todo)
        db.session.commit()
        flash("      ")
        return redirect(url_for('list'))
    
    
    #           
    @app.route('/todo/undo/')
    @is_login
    def undo(id):
        todo = Todo.query.filter_by(id=id).first()
        todo.status = False
        db.session.add(todo)
        db.session.commit()
        flash("      ")
        return redirect(url_for('list'))
    
    
    
    @app.route('/showTodo/')
    @is_login
    def showTodo():
        done_count = Todo.query.filter_by(status=True).count()
        undone_count = Todo.query.filter_by(status=False).count()
        return render_template('todo/show_todo.html',
                               info={
                                   '   ': done_count,
                                   '   ': undone_count
    
                               })
    
    
    #   echarts                
    @app.route('/newShowTodo/')
    @is_login
    def newShowTodo():
        return render_template('todo/new_show_todo.html')
    
    #      ajax                
    #        screpts     ,
    #      python         js    json  
    @app.route('/get_data/')
    @is_login
    def get_data():
        done_count = Todo.query.filter_by(status=True).count()
        undone_count = Todo.query.filter_by(status=False).count()
        info = {
            'info': ["   ", "   "],
            'count': [done_count, undone_count]
        }
        # ensure_ascii=False,        
        return json.dumps(info, ensure_ascii=False)
    
    #   cup   
    @app.route('/disk/')
    def get_disk():
        import psutil
        cpuInfo = json.dumps({'CPU   ', psutil.cpu_percent()},
                             ensure_ascii=False)
    
        return cpuInfo
    

    static(静的ファイル):
    # css  main.css:
    .navbar {
        font-size: 130%;
        background: whitesmoke;
        margin-top: 10px;
        padding-top: 5px;
        box-shadow: 2px 2px 2px 2px lightgray;
        height: 60px;
    }
    # js  echarts.min.js  :
    
    

    templates(htmlページ):対応するビュー関数の機能に基づいて適切なhtmlページを作成し、bootstrap中国語ドキュメント上のテンプレートを通じて美しいページを作成することができます.config(プロファイル):
    #      
    SQLALCHEMY_DATABASE_URI = 'mysql://root:redhat@localhost/Todo'
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    #       ,  csrf(      )
    SECRET_KEY = 'westos'
    #       
    PER_PAGE = 5
    
    

    manage(アプリケーション):
    from app import manager, db
    #         
    from app.views import *
    from flask_migrate import MigrateCommand
    #             ;
    from app.models import User, Category, Todo
    
    #             
    @manager.command
    def dbinit():
        """        """
        db.drop_all()
        db.create_all()
        u = User(username='admin', email="[email protected]")
        u.password = 'admin'
        db.session.add(u)
        db.session.commit()
        print("  %s    ......." % (u.username))
    
        c = Category(name="  ", user_id=1)
        db.session.add(c)
        print("  %s    ...." % (c.name))
    
        t = Todo(content="  Flask", category_id=1, user_id=1)
        db.session.add(t)
        print("  %s    ....." % (t.content))
        db.session.commit()
    
    #          
    manager.add_command('db', MigrateCommand)
    if __name__ == '__main__':
        manager.run()
    

    関連する拡張
    Flask-Mementローカライズ日時
    1.Flask-Mementを使用する理由
  • Webプログラムのユーザが世界各地から来た場合,Webの世界と現地時間をどのように一致させるかを考える.サーバはユーザの地理的位置に関係なく時間単位を統一する必要があるため,協調時間(Coordinated Universal Time,UTC)が一般的に用いられる.しかし、ユーザーにとって見たいのは自分のいる現地時間であり、現地の慣用的なフォーマットを使用しています.

  • 2.Flask-Ment実現原理?
  • 優雅な解決策は、時間単位をWebブラウザに送信し、現地時間に変換してレンダリングすることです.Webブラウザは、コンピュータのタイムゾーンと領域の設定を得ることができるため、このタスクをよりよくすることができます.
  • JavaScriptを使用して開発された優れたクライアントオープンソースライブラリがあります.momentと呼ばれています.jsは、ブラウザで日付と時間をレンダリングできます.Flask-MentはFlaskプログラムの拡張であり、momentを使用することができます.jsはJinja 2テンプレートに統合されています.

  • 3.具体的なコードは?
  • それまでにflask-momentモジュール、
  • をインストールする必要がある.
  • 以降のappパッケージの_init__このモジュールをファイルにインポート:from flask-moment import Moment,
  • momentのオブジェクトをインスタンス化してアプリケーションに関連付ける:moment=Moment(app)
  • テンプレートの作成はmomentを除く.js.Flask_Momentはjqueryにも依存している.jsは、HTMLドキュメントの導入という一連のファイルを選択することで、どのバージョンを使用するか、拡張提供の補助関数を使用するか、コンテンツ配信ネットワークからテストによる追加コンテンツを導入するには、JinJa 2が提供するsuper()関数を使用する必要があります.このライブラリは、たとえば、ベーステンプレートのscriptsブロックに導入できます.
  • #       moment  
    {% block script %}
    {{ super()}}
    {{ monent.include_moment()}}
    {% endblock %}
    
  • ビュー関数を編集して時間を処理するために、Flask-Mementはテンプレートにmontentクラスを開きます.コードは変数current_をtime(すなわちUTC時間がテンプレート処理に伝達される)は、テンプレートに入力されてレンダリングされる.このようにしてローカル時間問題
  • を解決した.
    @app.route('/')
    def index():
        return render_template('index.html',
                               current_time=datetime.utcnow())
    
  • ページロジック
  • を編集する

    The local date and time is {{ moment(current_time).format('LLL') }}.

    That was {{ moment(current_time).fromNow(refresh=True) }}.

  • 参考資料Flask-Mementはmomentを実現した.jsのformat()、fromNow()、fromTime()、calendar()、valueOf()およびunix()メソッド.ドキュメントを参照してください(http://momentjs.com/docs/#/displaying/)を勉強します.jsが提供するすべてのフォーマットオプション.

  • Flaskとpyechartsに基づくグラフィックの可視化
    1.Flask統合Echartsライブラリ
    Flaskはpython webが開発したマイクロフレームワークで、Echartsのクールな機能は主にjavascriptが機能し、両者を結合し、より大きな役割を果たしています.
  • 静的要求実現ステップ1).公式サイトでEcharts 2)をダウンロードします.Echarts公式サイトのドキュメントを参照するチュートリアル
  • を導入
  • 動的要求ステップ:jsonとajax要求1を用いる).ajaxとは?AJAX=Asynchronous JavaScript and XML(非同期JavaScriptとXML).AJAXは、高速動的Webページを作成するための技術です.AJAXは、バックグラウンドでサーバと少量のデータ交換を行うことで、Webページの非同期更新を実現します.これは、Webページ全体を再ロードせずに、Webページの一部を更新できることを意味します.2). jsonって何?JSON:JavaScript Object Notation(JavaScriptオブジェクト表現).JSONはテキスト情報を格納・交換する構文である.XMLに似ています.
  • # ajax url         
    #         
    @app.route('/getdata')
    def get_data():
        language = ['python', 'java', 'c', 'c++', 'c#', 'php']
        value = ['100', '150', '100', '90', '80', '90']
        return json.dumps({'language':language,'value':value},ensure_ascii=False) 
        #       ,   ensure_ascii=False
    
    
    
    #    scripts    ,$(function() {}); $(document).ready(function(){ })    
     $(function () {
                //       dom,   echarts  
                var myChart = echarts.init(document.getElementById('main'));
                $.ajax({
                    url:'/getdata',
                    success:function (data) {
                        #    python    json       ;
                        json_data=JSON.parse(data)
    
                        console.info(json_data['language'])
                        console.info(json_data['value'])
    
                        //            
                        var option = {
                            title: {
                                text: '        '
                            },
                            tooltip: {},
                            legend: {
                                data:['  ']
                            },
                            xAxis: {
                                data: json_data['language']
                            },
                            yAxis: {},
                            series: [{
                                name: '  ',
                                type: 'bar',
                                data: json_data['value']
                            }]
                        };
                        //                 。
                        myChart.setOption(option);
    
                    }
                })
            })