Djangoで天気アプリを作成する方法
序章
この記事では、さまざまな都市の現在の天気を表示するDjangoアプリを作成します。
現在の気象データは、 Open Weather MapAPIによって提供されます。
データベースを操作してフォームを作成します。 このチュートリアルで学んだことは、後でより複雑なプロジェクトに適用できます。
前提条件
- このプロジェクトにはPythonがインストールされている必要があります。詳細については、このチュートリアルシリーズを参照できるはずです。
この記事のコードはPython3とDjango3.0で記述されているため、このチュートリアルに従うには、両方にある程度精通している必要があります。
ステップ1—プロジェクトの設定
Djangoのインストールは、他のPythonライブラリのインストールと似ています。
- 仮想環境を開始し、
pipを実行してDjangoをインストールできます。 - または、プロジェクトディレクトリを作成し、
pipenvを実行してから、pipenvシェルをアクティブ化することもできます。
どちらの方法でも機能しますが、この記事ではpipenvを使用します。
注:別のDjangoインストール方法については、このチュートリアルシリーズを参照して追加情報を入手できるはずです。
公式ドキュメントには、HomebrewまたはLinuxbrewを使用してpipenvをインストールする手順が記載されています。 pipと一緒にpipenvをインストールすることも可能です。
ターミナルで、環境ディレクトリを作成します。
mkdir the_weather_env
次に、環境ディレクトリに移動します。
cd the_weather_env
次に、pipenvを使用してDjangoをインストールします。
pipenv install django
これにより、最新バージョンのDjangoがインストールされます。 この記事を書いている時点では、Djangoはバージョン3.0.5です。
この瞬間に、pipenvを使用して、後で使用するRequestsライブラリをインストールします。
pipenv install requests
ターミナルで次のコマンドを実行して、プロジェクトのvirtualenvをアクティブにします。
pipenv shell
これにより、新しいシェルサブプロセスが生成されます。
ステップ2—Djangoプロジェクトを開始する
Djangoをインストールしたら、まだ作成していない場合は、このプロジェクトのディレクトリを作成して移動します。
Djangoがプロジェクトを生成するために提供するstartprojectコマンドを実行できます。
django-admin startproject the_weather
Djangoはあなたのディレクトリにいくつかの新しいファイルを作成しているはずです。
開発サーバーを起動してみましょう。 これを行うには、ターミナルの新しいディレクトリに移動します。
cd the_weather
次に、manage.pyを使用して、ターミナルでrunserverコマンドを実行します。
python manage.py runserver
ターミナルを確認すると、アプリのURLが表示されます。 デフォルトでは、127.0.0.1:8000になっているはずです。
次に、Webブラウザーを開き、そのURLにアクセスします。
「おめでとうございます」ページが表示された場合は、Djangoが正しく設定されていることがわかります。
ステップ3—管理ダッシュボードにログインする
次に、Djangoが提供する管理ダッシュボードにログインします。 これを実現するには、まずデータベースを移行する必要があります。つまり、Djangoはデフォルトのアプリに必要な事前定義されたテーブルを作成します。
まず、サーバーを停止する必要があります。 環境に応じて、これはキーボードコマンドCONTROL+CまたはCTRL+Cで実行できます。
次に、ターミナルでmigrateコマンドを実行します。
python manage.py migrate
そのコマンドを実行することで、Djangoは設定のデフォルトデータベースであるSQLiteデータベースを作成し、そのデータベースにいくつかのテーブルを追加しました。 プロジェクトディレクトリに新しいdb.sqlite3ファイルが表示されれば、データベースが作成されたかどうかがわかります。
Djangoが提供するテーブルの1つは、ユーザーテーブルです。これは、アプリ内のすべてのユーザーを格納するために使用されます。 作成しているアプリにはユーザーは必要ありませんが、管理者ユーザーがいると、管理ダッシュボードにアクセスできます。
管理者ユーザーを作成するには、ターミナルでcreatesuperuserコマンドを実行します。
python manage.py createsuperuser
管理者ユーザーのユーザー名、電子メールアドレス、およびパスワードを入力して、指示に従います。 終了したら、ターミナルでサーバーを再起動する必要があります。
python manage.py runserver
Webブラウザーで、127.0.0.1:8000/adminに移動して管理ダッシュボードにアクセスします。
このページに移動できるのは、urls.pyにadminが設定されているためです。
作成したユーザー名とパスワードでログインすると、Django管理ダッシュボードが表示されます。
グループとユーザーは、Djangoがアクセスできる2つのモデルを表しています。 モデルは、データベース内のテーブルの単なるコード表現です。 Djangoはさらに多くのテーブルを作成しましたが、残りのテーブルに直接アクセスする必要がないため、モデルは作成されませんでした。
[ユーザー]をクリックすると、ユーザーテーブルの詳細が表示され、作成したユーザーが表示されます。 ダッシュボードのさまざまなリンクをクリックして、何が利用できるかを確認してください。 ユーザーを削除しないように注意してください。削除しないと、createsuperuserを再度実行する必要があります。
とりあえず管理ダッシュボードを離れて、コードに取り組みましょう。 天気予報アプリ用にプロジェクト内にアプリを作成する必要があります。
ステップ4—アプリを作成する
Djangoでは、 apps を使用して、プロジェクトの機能を分離できます。 Djangoの場合、アプリはプロジェクトの特定の機能を指します。
たとえば、settings.pyファイルを見ると、INSTALLED_APPSリストが表示されます。
インストールされた最初のアプリ(django.contrib.admin)は、今使用したものです。 すべての管理機能を処理し、それ以外は処理しません。 プロジェクトのもう1つのアプリは、デフォルトでdjango.contrib.authです。これにより、管理ダッシュボードにログインできます。
あなたの場合、天気の表示に関連するすべてを処理する新しいアプリを作成する必要があります。
まず、サーバーを停止する必要があります。
次に、ターミナルでstartappコマンドを実行します。
python manage.py startapp weather
startappを実行することにより、Djangoは新しいディレクトリとより多くのファイルをプロジェクトに追加しました。
最新のファイルを生成したら、weatherアプリディレクトリにurls.pyという新しいファイルを作成しましょう。
the_weather / Weather / urls.py
from django.urls import path urlpatterns = [ ]
このファイルは、the_weatherディレクトリのurls.pyに似ています。 違いは、このurls.pyファイルには、アプリ自体に関連するすべてのURLが含まれていることです。
まだURLを指定していませんが、アプリを認識し、アプリに固有のURLをアプリurls.pyファイルにルーティングするようにプロジェクトを設定できます。
まず、settings.pyのINSTALLED_APPSリストに移動し、weatherをリストに追加します。
the_weather / the_weather / settings.py
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'weather',
]
...
これにより、Djangoはプロジェクトでweatherアプリを使用することを認識できます。 これを行うことで、Djangoは移行とURLを探す場所を知ることができます。
次に、元のurls.pyを変更して、アプリのurls.pyファイルを指すようにする必要があります。 これを実現するには、管理ダッシュボードの既存のパスの下に行を追加します。 また、アプリのurls.pyファイルをポイントできるように、includeをインポートする必要があります。
the_weather / the_weather / urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('weather.urls')),
]
空の文字列は、アプリへのエントリポイントにエンドポイントを使用する必要がないことを意味します。 代わりに、アプリに特定のエンドポイントを処理させます。 path('weather/', ...)のようなものを配置することもできます。つまり、天気アプリに関連付けられているものを取得するには、127.0.0.1:8000/weather/と入力する必要があります。 ただし、プロジェクトは単純なので、ここではそれを行いません。
ステップ5—テンプレートとビューを追加する
次に、テンプレートをプロジェクトに追加する必要があります。
Djangoのtemplateは、テンプレートを動的にする追加の構文を可能にするHTMLファイルです。 変数の追加、ifステートメント、ループなどの機能を処理できるようになります。
ターミナルで、weatherアプリディレクトリに移動します。
cd weather
次に、templatesディレクトリを作成します。
mkdir templates
そしてそれにナビゲートします:
cd templates
また、アプリと同じ名前の別のディレクトリを作成します。 これは、Djangoがさまざまなアプリのすべてのテンプレートディレクトリを組み合わせているためです。 ファイル名が重複しないようにするには、アプリの名前を使用して重複を防ぐことができます。
mkdir weather
このweatherディレクトリ内に、index.htmlという名前の新しいファイルを作成します。 これがメインテンプレートになります。
テンプレートに使用するHTMLは次のとおりです。
the_weather /weather/templates/weather/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>What's the weather like?</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.css" />
</head>
<body>
<section class="hero is-primary">
<div class="hero-body">
<div class="container">
<h1 class="title">
What's the weather like?
</h1>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-offset-4 is-4">
<form method="POST">
<div class="field has-addons">
<div class="control is-expanded">
<input class="input" type="text" placeholder="City Name">
</div>
<div class="control">
<button class="button is-info">
Add City
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-offset-4 is-4">
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-50x50">
<img src="http://openweathermap.org/img/w/10d.png" alt="Image">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<span class="title">Las Vegas</span>
<br>
<span class="subtitle">29° F</span>
<br> thunderstorm with heavy rain
</p>
</div>
</div>
</article>
</div>
</div>
</div>
</div>
</section>
<footer class="footer">
</footer>
</body>
</html>
注:舞台裏では、ブルマを使用してスタイリングとレイアウトを処理しています。 ブルマとCSSフレームワークの詳細については、ブルマを知る:私の現在のお気に入りのCSSフレームワークを読むことを検討してください。
テンプレートを作成したので、ビューとURLの組み合わせを作成して、アプリで実際にこれを確認できるようにします。
DjangoのViewsは、関数またはクラスのいずれかです。 この場合、単純なビューを作成しているので、関数を作成します。 この関数をviews.pyファイルに追加します。
the_weather / Weather / views.py
from django.shortcuts import render
def index(request):
return render(request, 'weather/index.html') #returns the index.html template
ビューにindexという名前を付けているのは、ルートURLであるアプリのインデックスにあるためです。 テンプレートをレンダリングするには、render関数に必要なrequestと、レンダリングするテンプレートファイルの名前(この場合はweather/index.html)を返します。 。
このビューにリクエストを送信するURLを追加しましょう。 アプリのurls.pyファイルで、urlpatternsリストを更新します。
the_weather / Weather / urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index), #the path for our index view
]
これにより、作成したばかりのビューを参照できます。
Djangoは、エンドポイントのない任意のURLと一致し、作成したビュー関数にルーティングします。
次に、ターミナルを使用してプロジェクトルート(the_weather)に戻ります。
次に、サーバーを起動します。
python manage.py runserver
次に、Webブラウザーを開き、127.0.0.1:8000に再度アクセスします。
index.htmlファイルのレンダリングされたHTMLを観察します。 都市を追加するための入力があります。 そして、ラスベガスのハードコードされた天気の表示があります。 ただし、この時点でのフォームは機能せず、天気は単なるプレースホルダーです。 次はそれに取り組みましょう。
ステップ6—WeatherAPIを使用する
ここで実行したいのは、 Open Weather MapAPIにサインアップすることです。 これにより、アプリに追加した都市の天気をリアルタイムで取得できます。
サイトに移動し、アカウントを作成してから、ダッシュボードのAPIキーに移動します。 提供されているデフォルトキーを使用することも、新しいAPIキーを作成することもできます。 このキーを使用すると、APIを使用して天気を取得できます。
注: APIキーを秘密にして、他のユーザーが使用できないようにすることが重要です。 APIキーをGitHubなどのリモートリポジトリにコミットしないようにする必要があります。
使用するエンドポイントの1つは以下のとおりです。そのため、APIキーを使用して次のURLを変更し、ブラウザでURLに移動すると、返されるデータを確認できます。
http://api.openweathermap.org/data/2.5/weather?q=las%20vegas&units=imperial&appid=YOUR_APP_KEY
APIキーがアクティブになるまでに数分かかる場合があるため、最初に機能しない場合は、数分後に再試行してください。
座標、温度、気象条件を含むJSON形式の応答が表示されます。
それでは、データをアプリに取り込むためのリクエストを追加しましょう。
indexビューを更新して、お持ちのURLにリクエストを送信しましょう。
the_weather / Weather / views.py
from django.shortcuts import render
import requests
def index(request):
url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'
city = 'Las Vegas'
city_weather = requests.get(url.format(city)).json() #request the API data and convert the JSON to Python data types
return render(request, 'weather/index.html') #returns the index.html template
import requests、url、city、およびcity_weatherを追加します。
これらの新しい行を使用して、リクエストの送信先となるURLを追加します。
このURLは、以前にブラウザでテストしたURLとは少し異なることに注意してください。 都市はURLの一部ではなく、変数に移動されました。 このパターンにより、将来的に他の都市名に置き換えることができます。
今のところ、都市を「ラスベガス」に設定しますが、後でデータベースから都市に設定します。
最後に、都市を使用してURLにリクエストを送信し、その都市のJSON表現を取得します。
printをコンソールに表示すると、アドレスバーにURLを入力したときに表示されたのと同じデータが表示されます。
the_weather / Weather / views.py
...
def index(request):
...
print(city_weather) #temporarily view output
return render(request, 'weather/index.html') #returns the index.html template
Webブラウザーでページをリロードすると、データがコンソールに出力されます。
これが真であることが確認されたら、コードからprintステートメントを削除できます。
ステップ7—テンプレートにデータを表示する
次に、データをテンプレートに渡して、ユーザーに表示できるようにする必要があります。
必要なすべてのデータを保持する辞書を作成しましょう。 返されるデータのうち、temp、description、およびiconが必要になります。
the_weather / Weather / views.py
...
def index(request):
...
weather = {
'city' : city,
'temperature' : city_weather['main']['temp'],
'description' : city_weather['weather'][0]['description'],
'icon' : city_weather['weather'][0]['icon']
}
return render(request, 'weather/index.html') #returns the index.html template
必要な情報がすべて揃ったので、それをテンプレートに渡すことができます。 テンプレートに渡すには、contextという変数を作成します。 これは、テンプレート内でその値を使用できるようにする辞書になります。
次に、renderで、3番目の引数としてcontextを追加します。
the_weather / Weather / views.py
...
def index(request):
...
context = {'weather' : weather}
return render(request, 'weather/index.html', context) #returns the index.html template
context内に気象データを追加したら、テンプレートに移動してデータを追加しましょう。
index.htmlテンプレート内で行う必要があるのは、ハードコードされた値の代わりに変数を使用するようにHTMLを変更することだけです。 変数は{{}}タグを使用し、コンテキストディクショナリ内のすべてを参照します。
Djangoは辞書キーを変換するため、ドット表記を使用してのみアクセスできることに注意してください。 たとえば、weather.cityは都市名を示します。 Pythonのようにweather['city']を使用することはありません。
「ボックス」<div>を見つけて、変数を使用するように更新します。
the_weather /weather/templates/weather/index.html
...
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-50x50">
<img src="http://openweathermap.org/img/w/{{ weather.icon }}.png" alt="Image">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<span class="title">{{ weather.city }}</span>
<br>
<span class="subtitle">{{ weather.temperature }}° F</span>
<br> {{ weather.description }}
</p>
</div>
</div>
</article>
</div>
...
すべての変数が置き換えられると、都市の現在の天気が表示されます。
ただし、都市は現在もハードコーディングされています。 次に実行したいのは、データベースからプルして、データベースにある都市を表示することです。
これを実現するには、データベースにテーブルを作成して、天気を知りたい都市を保持します。 このためのモデルを作成します。
weatherアプリのmodels.pyファイルに移動し、次を追加します。
the_weather / Weather / models.py
from django.db import models
class City(models.Model):
name = models.CharField(max_length=25)
def __str__(self): #show the actual city name on the dashboard
return self.name
class Meta: #show the plural of city as cities instead of citys
verbose_name_plural = 'cities'
これにより、都市の名前であるnameという列を持つテーブルがデータベースに作成されます。 この都市はcharfieldになります。これは単なる文字列です。
データベースでこれらの変更を取得するには、makemigrationsを実行して、データベースを更新するコードを生成し、移行してこれらの変更を適用する必要があります。
サーバーを停止してから、ターミナルで移行を実行しましょう。
python manage.py makemigrations
そして移行します:
python manage.py migrate
管理ダッシュボードでこのモデルを表示できるようにする必要があります。 これを行うには、admin.pyファイルに登録する必要があります。
the_weather / Weather / admin.py
from django.contrib import admin from .models import City admin.site.register(City)
次に、サーバーを再起動し、Webブラウザーで管理ダッシュボードを表示します。
Cityがオプションになりました。
次に、管理ダッシュボードに移動して、いくつかの都市を追加できます。 例:「ロンドン」、「東京」、「ラスベガス」。
データベース内のエントリを使用して、ビューでこれらのエントリをクエリする必要があります。 Cityモデルをインポートしてから、そのモデルにすべてのオブジェクトを照会することから始めます。
the_weather / Weather / views.py
from django.shortcuts import render import requests from .models import City
次に、requestをcitiesで更新します。
the_weather / Weather / views.py
...
def index(request):
url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'
cities = City.objects.all() #return all the cities in the database
...
都市のリストがあるので、それらをループして各都市の天気を取得し、最終的にテンプレートに渡されるリストに追加する必要があります。
これは、前のステップで行ったことのバリエーションにすぎません。 違いは、各辞書をループしてリストに追加することです。
まず、[X21X]ごとにweatherを保持するweather_dataリストを作成します。
次に、元のcity変数を、cities上のループに置き換えます。
次に、各cityのweather応答をweather_dataに追加する必要があります。
また、単一の辞書の代わりにこのリストを渡すには、contextを更新する必要があります。
この時点で、views.pyは次のようになります。
the_weather / Weather / views.py
...
def index(request):
...
cities = City.objects.all() #return all the cities in the database
weather_data = []
for city in cities:
city_weather = requests.get(url.format(city)).json() #request the API data and convert the JSON to Python data types
weather = {
'city' : city,
'temperature' : city_weather['main']['temp'],
'description' : city_weather['weather'][0]['description'],
'icon' : city_weather['weather'][0]['icon']
}
weather_data.append(weather) #add the data for the current city into our list
context = {'weather_data' : weather_data}
return render(request, 'weather/index.html', context) #returns the index.html template
次に、index.htmlテンプレート内で、このリストをループして、リスト内のcityごとにHTMLを生成する必要があります。 これを実現するために、city用の単一の「ボックス」<div>を生成するHTMLの周りにforループを配置できます。
the_weather /weather/templates/weather/index.html
...
<div class="column is-offset-4 is-4">
{% for weather in weather_data %}
<div class="box">
<article class="media">
<div class="media-left">
<figure class="image is-50x50">
<img src="http://openweathermap.org/img/w/{{ weather.icon }}.png" alt="Image">
</figure>
</div>
<div class="media-content">
<div class="content">
<p>
<span class="title">{{ weather.city }}</span>
<br>
<span class="subtitle">{{ weather.temperature }}° F</span>
<br> {{ weather.description }}
</p>
</div>
</div>
</article>
</div>
{% endfor %}
</div>
...
これで、データベースにあるすべての都市のデータを調べることができます。
ステップ8—フォームの作成
最後のステップは、ユーザーがフォームから直接都市を追加できるようにすることです。
そのためには、フォームを作成する必要があります。 フォームは手動で作成できますが、フォームにはモデルとまったく同じフィールドがあるため、ModelFormを使用できます。
weatherアプリでforms.pyという名前の新しいファイルを作成します。
the_weather / Weather / forms.py
from django.forms import ModelForm, TextInput
from .models import City
class CityForm(ModelForm):
class Meta:
model = City
fields = ['name']
widgets = {
'name': TextInput(attrs={'class' : 'input', 'placeholder' : 'City Name'}),
} #updates the input class to have the correct Bulma class and placeholder
フォームを表示するには、ビューでフォームを作成してテンプレートに渡す必要があります。
そのために、index.htmlを更新してフォームを作成しましょう。 また、フォームがテンプレートに渡されるように、contextを更新する必要があります。
the_weather / Weather / views.py
...
from .forms import CityForm
def index(request):
...
form = CityForm()
weather_data = []
...
context = {'weather_data' : weather_data, 'form' : form}
次に、index.htmlテンプレートで、フォームセクションを更新して、ビューからのフォームと、DjangoでのPOSTリクエストに必要なcsrf_tokenを使用します。
the_weather /weather/templates/weather/index.html
...
<form method="POST">
{% csrf_token %}
<div class="field has-addons">
<div class="control is-expanded">
{{ form.name }}
</div>
<div class="control">
<button class="button is-info">
Add City
</button>
</div>
</div>
</form>
...
注: CSRFは、 Cross-Site RequestForgeryの略です。 これは、フォームデータが予想される信頼できるソースから送信されていることを確認するためのセキュリティ対策です。
HTMLのフォームが機能している状態で、フォームデータを受信時に処理する必要があります。 そのために、POSTリクエストをチェックするifブロックを作成します。 追加した都市のデータをすぐに取得できるように、天気データの取得を開始する前に、リクエストのタイプのチェックを追加する必要があります。
the_weather / Weather / views.py
...
def index(request):
url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'
cities = City.objects.all() #return all the cities in the database
if request.method == 'POST': # only true if form is submitted
form = CityForm(request.POST) # add actual request data to form for processing
form.save() # will validate and save if validate
form = CityForm()
...
request.POSTを渡すことで、フォームデータを検証できます。
これで、都市の名前を入力し、[追加]をクリックして、都市が表示されるのを確認できるはずです。
たとえば、次の都市として「マイアミ」を追加します。
ifブロックからドロップアウトすると、フォームが再作成されるため、必要に応じて別の都市を追加できます。 残りのコードは同じように動作します。
これで、アプリで複数の都市の天気を追跡する方法ができました。
結論
この記事では、これを機能させるためにDjangoのさまざまな部分(ビュー、モデル、フォーム、テンプレート)を操作する必要がありました。 また、実際の気象データを取得するには、Pythonライブラリrequestsを使用する必要がありました。 そのため、アプリは単純ですが、より複雑なアプリでも同じ概念の多くを使用します。