PyCharm 2019.3ヘルプ

FlaskによるWebアプリケーションの作成

このチュートリアルでは、Flask Webフレームワークを使用してWebベースのアプリケーションを開発する方法について説明します。当社のMeteoMasterアプリケーションは、データベースに保存されている流星データを処理し、それを次のチャートの形式で表示します。

  • 散布図—プラハ、サンクトペテルブルク、サンフランシスコ、パリ、シンガポールの年間平均気温と湿度の累積レポート。

  • 折れ線グラフ—各都市の月間平均気温と湿度。

Staying Above the Weather: アプリケーションの概要

以下のアプリケーション機能を実装するためにさまざまなWebテクノロジを使用します。

関数

技術

流星データ操作

データを格納するためのSQLite(英語)データベース、Pythonコードでデータベースとの操作を実行するためのSQLAlchemy(英語)パッケージ。

グラフ表示

グラフをプロットするmatplotlib(英語)パッケージ。

コンテンツの表示と編集

ページビューを作成するためのHTML(英語)とスマートテンプレートを作成するためのJinja(英語)

コンテンツを管理する

アプリケーションの内容を調整するFlask(英語)

MeteoMasterは、以下のコンポーネントで構成されるWebアプリケーションです。

Key modules of the Flask Web application
メインページ

散布図を表示し、特定の都市の気候の詳細な概要へのリンクを提供するアプリケーションエントリポイント。

シティ

各都市の気候に関する詳細情報を含む一連のページ。

ログイン

認証ページ気象データを編集するには、有効な認証情報を入力する必要があります。

編集

都市固有のデータを編集するための一連のページ。

各HTMLページには、Pythonコードで実装された対応するFlaskビューがあります。

PyCharmでFlaskアプリケーションを作成する

Flaskプロジェクトの作成の説明に従って基本的なFlaskプロジェクトを作成し、アプリケーションのプロトタイプ作成を開始します。

  1. 新規プロジェクトダイアログでFlaskを選択します。

    Creating a new Flask project

  2. ロケーションフィールドにプロジェクトの場所へのパスを入力し、プロジェクト名として流星マスターを入力します。残りの設定はデフォルトのままにして、変更を保存します。

  3. デフォルトのアプリケーションを実行するには、Shift+F10 をクリックします。実行ツールウィンドウで、ハイパーリンクをクリックしてターゲットページをプレビューします。

    Preview the Hello World app

  4. MeteoMasterアプリケーションに必要なすべてのパッケージをインストールしてください。最も簡単な方法は、プロジェクトの依存関係を使用することです(requirements.txtを使用するを参照)。プロジェクトのルートを右クリックして新規 | ファイルを選択し、次にファイル名としてrequirements.txtを指定して、それに以下の依存関係のリストを追加します。

    Click==7.0 cycler==0.10.0 Flask==1.0.2 Jinja2==2.10 kiwisolver==1.0.1 MarkupSafe==1.1.0 matplotlib==3.0.2 numpy==1.15.4 pyparsing==2.3.0 python-dateutil==2.7.5 six==1.11.0 SQLAlchemy==1.2.14 Werkzeug==0.14.1

    パッケージのインストールを続行するには、インストール要件リンクをクリックしてください。

    Installing packages required for the application

データベースの設定

それでは、アプリケーション用のデータソースを設定しましょう。PyCharmを使うと、非常に簡単です。

  1. 次の場所から、5都市の流星データを含む事前定義データベースをダウンロードします。

    https://github.com/allaredko/flask-tutorial-demo/blob/master/user_database(英語)

    user_database ファイルをプロジェクトのルートディレクトリーに保存します。

  2. 追加したファイルをダブルクリックしてデータベースツールウィンドウを開きます。

    Meteo database

    SQLiteデータベースドライバーをまだインストールしていない場合は、configure database をクリックし、警告メッセージをクリックして不足しているドライバーをインストールします。

    user_database データベースに citymeteoの表が表示されます。各テーブルをダブルクリックしてデータをプレビューします。 city テーブルには3つの列があります。city_id , city_namecity_climate (都市気候の簡単な説明)です。 meteo テーブルには、city_id , month , average_humidityaverage_temperatureの4つの列があります。2つのテーブル間の関係を設定するために、tablecity_id 列に外部キーが定義されています。

    >
  3. 必要に応じて、データベースツールウィンドウのuser_databaseを右クリックしてプロパティーを選択します。データ・ソースおよびドライバーダイアログで、接続のテストをクリックしてデータソースが正しく設定されていることを確認します。

    Adding a data source
  4. Pythonファイルを作成するuser_database.py、新しく作成されたデータベースを操作します。データベースを記述するためにSQLAlchemy宣言ベース(英語)構文を使用してください。

    metadata = MetaData() engine = create_engine('sqlite:///user_database', connect_args={'check_same_thread': False}, echo=False) # echo=False Base = declarative_base() db_session = sessionmaker(bind=engine)() # Table city class City(Base): __tablename__ = 'city' city_id = Column(Integer, primary_key=True) city_name = Column(String) city_climate = Column(String) city_meteo_data = relationship("Meteo", backref="city") # Table meteo class Meteo(Base): __tablename__ = 'meteo' id = Column(Integer, primary_key=True) city_id = Column(ForeignKey('city.city_id')) month = Column(String) average_humidity = Column(Integer) average_temperature = Column(Float)

    コードフラグメントのインポートステートメントを手動で追加する代わりに、提案されている簡単な修正を適用します。電球アイコンをクリックするか、Alt+Enterを押します。

    Applying quick fixes for the missing import statements

  5. テーブルとそれらの関係を定義したため、データベースからデータを取得するために次の関数を追加します。

    # Retrieving data from the database def get_cities(): return db_session.query(City) # Generating the set of average temperature values for a particular city def get_city_temperature(city): return [month.average_temperature for month in city.city_meteo_data] # Generating the set of average humidity values for a particular city def get_city_humidity(city): return [month.average_humidity for month in city.city_meteo_data] data = get_cities() MONTHS = [record.month for record in data[0].city_meteo_data] CITIES = [city.city_name for city in data]

散布図のプロット

データを取得し、最初のグラフ、つまり各都市の年間平均気温と湿度を示す散布図を作成する準備が整いました。matplotlib(英語)ライブラリーを使用して、グラフを設定し、値を割り当てます。

  1. 次のコードをさらに別のPythonファイルを作成するcharts.py、および貼り付けます。

    import matplotlib.pyplot as plt from user_database import data, get_city_temperature, get_city_humidity, CITIES yearly_temp = [] yearly_hum = [] for city in data: yearly_temp.append(sum(get_city_temperature(city))/12) yearly_hum.append(sum(get_city_humidity(city))/12) plt.clf() plt.scatter(yearly_hum, yearly_temp, alpha=0.5) plt.title('Yearly Average Temperature/Humidity') plt.xlim(70, 95) plt.ylabel('Yearly Average Temperature') plt.xlabel('Yearly Average Relative Humidity') for i, txt in enumerate(CITIES): plt.annotate(txt, (yearly_hum[i], yearly_temp[i])) plt.show()
  2. グラフをプレビューする最も簡単な方法は、Shift+F10 ショートカットを使用することです。PyCharmは、散布図をSciViewツールウィンドウに表示します。プロットタブを使用して、生成されたグラフをプレビューすることができます。

    Previewing a scatter chart

  3. グラフをイメージに保存して、アプリケーションのメインページに追加できるようにします。 plt.show()savefig(img) 関数を利用するフラグメントで置き換えます。

    from io import BytesIO import matplotlib.pyplot as plt from user_database import data, MONTHS, get_city_temperature, get_city_humidity, CITIES def get_main_image(): """Rendering the scatter chart""" yearly_temp = [] yearly_hum = [] for city in data: yearly_temp.append(sum(get_city_temperature(city))/12) yearly_hum.append(sum(get_city_humidity(city))/12) plt.clf() plt.scatter(yearly_hum, yearly_temp, alpha=0.5) plt.title('Yearly Average Temperature/Humidity') plt.xlim(70, 95) plt.ylabel('Yearly Average Temperature') plt.xlabel('Yearly Average Relative Humidity') for i, txt in enumerate(CITIES): plt.annotate(txt, (yearly_hum[i], yearly_temp[i])) img = BytesIO() plt.savefig(img) img.seek(0) return img

メインページを作成する

アプリケーションのメインページを設定し、散布図のビューを作成します。

  1. app.py ファイルの app.route() コードフラグメントを次のコードに置き換えます。

    @app.route('/') def main(): """Entry point; the view for the main page""" cities = [(record.city_id, record.city_name) for record in data] return render_template('main.html', cities=cities) @app.route('/main.png') def main_plot(): """The view for rendering the scatter chart""" img = get_main_image() return send_file(img, mimetype='image/png', cache_timeout=0)
  2. 足りないimport文を追加するために提案された素早い修正を適用してください。

  3. このファイルはまだ作成されていないため、PyCharmは main.html を強調表示しています。

    Code inspection

    PyCharm インテンションアクションを使えば、足りないテンプレートファイルを素早く作成できます。 Alt+Enter を押して、コンテキストメニューからテンプレートmain.htmlを作成するを選択します。テンプレートファイルの名前と場所を確認して、OKをクリックします。その結果、main.htmltemplates ディレクトリーに追加されます。新しく追加したファイルを開き、そのファイルに次のコードを貼り付けます。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="../static/style.css" rel="stylesheet" type="text/css"> <title>Typical Climate</title> </head> <body> <div id="element1"> <img src="{{ url_for('main_plot') }}" alt="Image"> </div> <div id="element2"> <p>What city has the best climate?</p> <p>When planning your trip or vacation you often check weather data for humidity and temperature.</p> <p>Below is the collection of the yearly average values for the following cities: </p> <ul> {% for city_id, city_name in cities %} <li><a href="">{{ city_name }}</a></li> {% endfor %} </ul> </div> </body> </html>

    このフラグメントの {{ city_name }} は、city_name Python変数をHTMLテンプレートに渡すために使用されるJinja2(英語)テンプレート変数です。

  4. また、スタイルシートを作成して、アプリケーション内のすべてのHTMLページのフォントとレイアウトを設定します。静的ディレクトリーを右クリックして新規 | スタイルシートを選択し、css ファイルの名前 style.cssを指定して、次のスタイル定義を貼り付けます。

    body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } #element1 { display: inline-block; } #element2 { display: inline-block; vertical-align: top; margin-top: 90px; alignment: left; width: 25%; }
  5. 初めてアプリケーションを実行すると、PyCharmは自動的に実行/デバッグ構成を生成しました。結果を評価したいときはいつでも、それを使用して変更されたアプリケーションを実行することができます。

    Run/Debug configuration

    the Run button をクリックして、実行ツールウィンドウの http://127.0.0.1:5000/ リンクをたどります。次のページが表示されるはずです。

    The main page of the MeteoMaster application

折れ線グラフ

特定の都市の気候に関する詳細情報をアプリケーションユーザーに提供するには、折れ線グラフを関連情報とともにレンダリングします。

  1. get_city_image 関数を追加してcharts.pyファイルを修正します。

    def get_city_image(city_id): """Rendering line charts with city specific data""" city = data.get(city_id) city_temp = get_city_temperature(city) city_hum = get_city_humidity(city) plt.clf() plt.plot(MONTHS, city_temp, color='blue', linewidth=2.5, linestyle='-') plt.ylabel('Mean Daily Temperature', color='blue') plt.yticks(color='blue') plt.twinx() plt.plot(MONTHS, city_hum, color='red', linewidth=2.5, linestyle='-') plt.ylabel('Average Relative Humidity', color='red') plt.yticks(color='red') plt.title(city.city_name) img = BytesIO() plt.savefig(img) img.seek(0) return img

    この関数は、2つの線形チャートをプロットします。各都市の毎月の1日の平均気温平均相対湿度です。 get_main_image 関数と同様に、チャートをイメージに保存します。

  2. 折れ線グラフを表示するために、もう1つ .html ファイルを作成します。

    プロジェクトルートのtemplatesディレクトリーを右クリックして新規 | HTML ファイルを選択し、ファイル名として city.html と入力して、新しく作成したファイルに次のコードを貼り付けます。

  3. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link href="../static/style.css" rel="stylesheet" type="text/css"> <title>{{ city_name }}</title> </head> <body> <div id="element1"> <img src="{{ url_for('city_plot', city_id=city_id) }}" alt="Image"> </div> <div id="element2"> <p>This graph shows mean daily temperature and average relative humidity in {{ city_name }}.</p> <p> {{ city_climate }}</p> <hr/> <p><a href="/">Back to the main page</a></p> </div> </body> </html>
  4. 残りのステップは、Flask.route 関数を使用してさらに2つのビューを作成することです。 app.py ファイルに次のコードを追加します。

    @app.route('/city/<int:city_id>') def city(city_id): """Views for the city details""" city_record = data.get(city_id) return render_template('city.html', city_name=city_record.city_name, city_id=city_id, city_climate=city_record.city_climate) @app.route('/city<int:city_id>.png') def city_plot(city_id): """Views for rendering city specific charts""" img = get_city_image(city_id) return send_file(img, mimetype='image/png', cache_timeout=0,)

    クイックフィックス Alt+Enter を使用して、欠落しているインポートステートメントを追加することを忘れないでください。

  5. main.html ファイルを修正して、都市のリストに対応するシティ/*ページへのリンクを追加します。 <li><a href="">{{ city_name }}</a></li><li><a href="{{ url_for('city', city_id=city_id) }}">{{ city_name }}</a></li>に交換してください。

実行 / デバッグ設定を再実行してアプリケーションを再起動し、Parisにつながるリンクをクリックします。city/2 ページにナビゲートされることを期待するべきです。

Detailed weather information for Paris

ログインフォームを作成する

これまでに、データベースから気象データを取得してチャートとして表示する、完全に機能的なアプリケーションを作成しました。ただし、実際にはデータを編集したいと思います。合理的には、編集は許可されたユーザーにのみ許可されるべきのため、ログインフォームを作成しましょう。

  1. プロジェクトルートのtemplatesディレクトリーを右クリックして新規 | HTML ファイルを選択し、ファイル名として login.html と入力して、新しく作成したファイルに次のコードを貼り付けます。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../static/style.css"> <title>Login form </title> </head> <body> <p>Login to edit the meteo database: </p> <div class="container"> <form action="" class="form-inline" method="post"> <input type="text" class="form-control" placeholder="Username" name="username" value="{{ request.form.username }}"> <input type="password" class="form-control" placeholder="Password" name="password" value="{{ request.form.password }}"> <input class="btn btn-default" type="submit" value="Login"> </form> <p>{{ error }} </p> </div> </body> </html>

    このコードはユーザー名パスワードフィールドを持つ典型的なログインフォームを実装します。

  2. 次のコードを app.py ファイルに追加して、ログインフォーム用のFlaskビューを作成し、ログインセッションを制御します。

    @app.route('/login/<int:city_id>', methods=["GET", "POST"]) def login(city_id): """The view for the login page""" city_record = data.get(city_id) try: error = '' if request.method == "POST": attempted_username = request.form['username'] attempted_password = request.form['password'] if attempted_username == 'admin' and attempted_password == os.environ['USER_PASSWORD']: session['logged_in'] = True session['username'] = request.form['username'] return redirect(url_for('edit_database', city_id=city_id)) else: print('invalid credentials') error = 'Invalid credentials. Please, try again.' return render_template('login.html', error=error, city_name=city_record.city_name, city_id=city_id) except Exception as e: return render_template('login.html', error=str(e), city_name=city_record.city_name, city_id=city_id) def login_required(f): @wraps(f) def wrap(*args, **kwargs): """login session""" if 'logged_in' in session: return f(*args, **kwargs) else: pass return redirect(url_for('login')) return wrap app.secret_key = os.environ['FLASK_WEB_APP_KEY']

    不足しているインポートステートメントを追加するには、Alt+Enter ショートカットを使用します。このコードでは、USER_PASSWORDFLASK_WEB_APP_KEYという2つの環境変数が導入されています。

  3. 新しく作成された環境変数の値を流星マスター Run / デバッグ構成に記録することができます。これは、app.py ファイルにハードコードするよりもセキュリティ上重要な情報を保管するための安全な方法です。

    エディターの右上にある実行/デバッグ構成リストに移動し、Edit configurationsを選択します。環境変数フィールドの edit environmental variables アイコンをクリックして、2つの変数を追加します。

    Adding environmental variables
  4. ログイン機能に対応するように city.html ファイルの <div id="element2"> 要素を変更します。

    <p>This graph shows mean daily temperature and average relative humidity in {{ city_name }}.</p> <p> {{ city_climate }}</p> {% if session['logged_in'] %} <p>Want to add more data?</p> <p>Go and <a href="{{ url_for('edit_database', city_id=city_id) }}">edit</a> the meteo database.</p> {% else %} <p>Want to edit meteo data?</p> <p>Please <a href="{{ url_for('login', city_id=city_id) }}">login</a> first.</p> {% endif %} <hr/> <p><a href="/">Back to the main page</a></p>
  5. アプリケーションを再起動し、都市のリンクをクリックしてから、「最初にログインしてください」という文の中のログインリンクをクリックします。ログインフォームが表示されるはずです。

    Login form

    当面は、対応するページを実装していないため、このフォームでは編集できません。一方、誤ったパスワードを入力して「無効な認証情報です。もう一度やり直してください」というメッセージで処理されているかどうかを確認することもできます。

データを編集する

最後の残りのステップは流星データの編集を可能にすることです。

  1. プロジェクトルートのtemplatesディレクトリーを右クリックして新規 | HTML ファイルを選択し、ファイル名として edit.html と入力して、新しく作成したファイルに次のコードを貼り付けます。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" type="text/css" href="../static/style.css"> <title>Edit meteo data for {{ city_name }}</title> </head> <body> <p>Edit the data for {{ city_name }} as appropriate:</p> <div class="container"> <form name="meteoInput" action="" class="form-inline" method="post"> <table> <tr> <td>Month</td> <td colspan="2" align="center">Average Temperature</td> <td colspan="2" align="center">Average Humidity</td> </tr> {% for month in months %} <tr> <td>{{ month }}</td> <td> <input placeholder="20" class="form-control" name="temperature{{ loop.index0 }}" value="{{ meteo[0][loop.index0]}}" type="range" min="-50.0" max="50.0" step="0.01" oninput="temp_output{{ loop.index0 }}.value=this.value" > </td> <td> <output name="temp_output{{ loop.index0 }}">{{ '%0.2f' % meteo[0][loop.index0]|float }}</output> <label> C</label> </td> <td> <input placeholder="20" class="form-control" name="humidity{{ loop.index0 }}" value="{{ meteo[1][loop.index0]}}" type="range" min="0" max="100" oninput="hum_output{{ loop.index0 }}.value=this.value"> </td> <td> <output name="hum_output{{ loop.index0 }}">{{ meteo[1][loop.index0]}}</output> <label> %</label> </td> </tr> {% endfor %} </table> <input class="btn btn-default" type="submit" value="Save"> </form> <p>{{ error }}</p> </div> </body> </html>

    このフラグメントはまた、Jinjia2テンプレートを利用して入力データを処理し、それをデータベースへのコミットを実行するPythonコードに渡します。

  2. 編集ページ用のFlaskビューを作成し、入力データを処理し、データベースを更新する app.py ファイルに、もう1つコード・フラグメントを追加します。

    @app.route('/edit/<int:city_id>', methods=["GET", "POST"]) @login_required def edit_database(city_id): """Views for editing city specific data""" month_temperature = [] month_humidity = [] city_record = data.get(city_id) meteo = [get_city_temperature(city_record), get_city_humidity(city_record)] try: if request.method == "POST": # Get data from the form for i in range(12): # In a production application we ought to validate the input data month_temperature.append(float(request.form[f'temperature{i}'])) month_humidity.append(int(request.form[f'humidity{i}'])) # Database update for i, month in enumerate(city_record.city_meteo_data): month.average_temperature = month_temperature[i] month.average_humidity = month_humidity[i] db_session.commit() return redirect(url_for('main', city_id=city_id)) else: return render_template('edit.html', city_name=city_record.city_name, city_id=city_id, months=MONTHS, meteo=meteo) except Exception as error: return render_template('edit.html', city_name=city_record.city_name, city_id=city_id, months=MONTHS, meteo=meteo, error=error)

    足りないimport文を追加することを忘れないでください。プロジェクトリポジトリ内のapp.py(英語)ファイルの完全なコードを参照してください。

このステップで、データベースと対話するFlaskベースのアプリケーションを作成する作業は完了しました。天候に完全な力を持っています。変更がチャートで目立つように、任意の都市の流星データを確認して変更します。次に変更をプレビューします。

最終更新日: 2019年12月10日

関連ページ:

Flaskプロジェクトの作成

Flaskプロジェクトは、Flaskアプリケーションの生産的な開発を目的としています。PyCharmは特定のディレクトリー構造と設定を作成します。Flaskプロジェクトを作成するには、以下の手順に従いますメインメニューからを選択するか、ようこそ画面の新規プロジェクトボタンをクリックします。新規プロジ...

requirements.txtを使用-ヘルプ| PyCharm

PyCharmを使用すると、プロジェクトの不満足な依存関係を追跡することができ、依存性管理の主要手段との統合が可能になります。要件の定義プロジェクトのルートディレクトリーにある新規ファイルの作成。新規ファイルダイアログで、ファイル名を指定してください。要件ファイルの推奨名はrequirements....

データベースツールウィンドウ

概要:データベースツールウィンドウでは、データベースとDDLデータソースを操作できます。データベースのデータ構造を表示および変更し、他の関連タスクを実行できます。使用可能なデータソースは、データソース、スキーマ、テーブル、および列のツリーとして表示されます。現在データソースが定義されていない場合は、...

ステップ 1. 最初のPythonプロジェクトを作成して実行する-ヘルプ| PyCharm

始める前に:次の前提条件が満たされていることを確認してください。PyCharmCEまたはProfessionalで作業しています。Python自体をインストールしました。macOSまたはLinuxを使用している場合、コンピューターにはすでにPythonがインストールされています。python.org...

インテンション・アクション

エディターで作業するときに、PyCharmはコードを分析し、コードを最適化する方法を検索し、潜在的な問題と実際の問題を検出します。IDEがコードを変更する方法を見つけるとすぐに、エディターの現在の行の横に黄色い電球アイコンが表示されます。このアイコンをクリックすると、このコード単位で使用可能なインテ...

実行/デバッグ構成: Flaskサーバー

このダイアログを使用して、Flaskサーバーの実行/デバッグ構成を作成します。本セクション:構成タブ、ログタブ、共通、ツールバー、起動前、構成タブ:モジュール名/スクリプトパス/カスタム変数を作成してFlaskに渡すには、次のいずれかの方法を選択します。モジュール名- Pythonモジュール名とク...