如何用 Django 构建天气应用程序

介绍

在本文中,您将构建一个 Django应用程序,显示各种城市的当前天气情况。

Weather App Home Page with London, Tokyo, Las Vegas, and Miami weather

目前的天气数据将由 Open Weather Map API提供。

您将使用数据库工作并创建表格,您在本教程中学习的内容可以后来应用于更复杂的项目。

前提条件

*本项目将需要安装Python,您应该能够参阅此教程系列(https://www.digitalocean.com/community/tutorial_series/how-to-install-and-set-up-a-local-programming-environment-for-python-3)以获取更多信息。

本文中的代码是用Python 3和Django 3.0编写的,所以要遵循本教程,你应该熟悉两者。

步骤1 - 设置项目

安装Django就像安装任何其他Python库:

  • 您可以启动虚拟环境并运行pip来安装Django。 * 或者您可以创建项目目录,运行pipenv,然后激活pipenv壳。

两种方法都起作用,但对于本文,你将使用 pipenv)。

注意:对于替代的Django安装方法,您应该能够参阅本教程系列(https://www.digitalocean.com/community/tutorial_series/django-development)以获取更多信息。

官方文档为使用 Homebrew或 Linuxbrew安装「pipenv」提供了说明。

在您的终端中,创建一个环境目录:

1mkdir the_weather_env

接下来,导航到环境目录:

1cd the_weather_env

然后,使用 pipenv 来安装 Django:

1pipenv install django

这将为您安装最新版本的Django. 在写这篇文章时,Django是在版本 3.0.5.

利用此时刻,您还可以使用 pipenv 来安装您以后会使用的 请求库:

1pipenv install requests

通过在您的终端中运行以下命令来激活项目的 virtualenv:

1pipenv shell

这将引发一个新的壳子次进程。

第2步:启动 Django 项目

一旦你安装了Django,创建并导航到该项目的目录,如果你还没有。

你可以运行Django给你的startproject命令来生成这个项目。

1django-admin startproject the_weather

Django应该在您的目录中创建了一些新文件。

让我们尝试启动您的开发服务器. 要做到这一点,请在您的终端中导航到新的目录:

1cd the_weather

接下来,使用manage.py在您的终端中运行runserver命令:

1python manage.py runserver

如果你检查你的终端,你应该看到你的应用程序的URL. 默认情况下,它应该是127.0.0.1:8000:

Terminal window depicting development server up and running

现在,打开您的Web浏览器并访问该URL:

Browser window depicting Django development server Congratulations page

如果你收到一个恭喜页面,你知道你已经正确设置了Django。

步骤 3 – 登录到 Admin 仪表板

接下来,您将登录到 Django 提供的管理仪表板. 要做到这一点,首先,您必须 migrate 您的数据库,这意味着 Django 将创建默认应用程序所需的预定义表。

首先,您需要停止服务器,这取决于您的环境,可以通过键盘命令CONTROL+CCTRL+C完成。

接下来,在您的终端中运行迁移命令:

1python manage.py migrate

通过运行该命令,Django 为您创建了一个 SQLite 数据库,在设置中为您创建了默认数据库,并将多个表添加到该数据库中。

Django为您提供的表之一是用户表,它将用于存储您的应用程序中的任何用户.您正在构建的应用程序不需要任何用户,但有一个管理员用户将允许您访问管理员仪表板。

要创建一个管理员用户,您将在您的终端中运行createsuperuser命令:

1python manage.py createsuperuser

按照指示,为您的管理员用户提供用户名、电子邮件地址和密码. 完成后,您需要在您的终端中重新启动服务器:

1python manage.py runserver

在您的 Web 浏览器中,请访问 admin 仪表板,点击127.0.0.1:8000/admin

您可以访问此页面的原因是因为您的urls.py中设置了admin

如果您使用您刚刚创建的用户名和密码登录,您将收到 Django Admin 仪表板:

Browser window depicting Django Admin Dashboard

GroupsUsers 代表两个模型 Django 给你访问. Models 只是数据库中表的代码表示。

如果你点击用户,你应该看到有关用户表的更多细节,你应该看到你创建的用户。花一会儿去探索,点击仪表板中的不同的链接,看看什么是可用的。

现在让我们离开管理器仪表板,并在代码上工作,您将需要为您的天气应用程序创建项目内部的应用程序。

步骤 4 – 创建应用程序

在Django中,您可以通过使用 apps 来分离项目中的功能,在Django的情况下,应用程序指的是项目中的特定功能。

例如,如果您查看settings.py文件,您将看到INSTALLED_APPS列表。

第一個安裝的應用程式 - django.contrib.admin - 是你剛剛使用的應用程式. 它處理所有管理員功能,而不是其他任何東西。 您的項目中的另一個應用程式是 django.contrib.auth,讓您登入您的管理員仪表板。

在您的情况下,您需要创建一个新的应用程序来处理与显示天气有关的一切。

首先,你需要关闭服务器。

接下来,在您的终端中运行startapp命令:

1python manage.py startapp weather

通过运行startapp,Django 已为您的项目添加了一个新的目录和更多文件。

随着最新文件的生成,让我们在您的天气应用程序目录中创建一个名为urls.py的新文件:

1[label the_weather/weather/urls.py]
2from django.urls import path
3
4urlpatterns = [
5]

这个文件类似于您的the_weather目录中的urls.py。 不同之处在于,这个urls.py文件包含所有与应用程序本身相关的URL。

您尚未指定 URL,但您可以设置项目来识别您的应用程序,并将任何特定于您的应用程序的 URL 路由到应用程序 urls.py 文件。

首先,进入Settings.py中的INSTALLED_APPS列表,并将天气添加到列表中:

 1[label the_weather/the_weather/settings.py]
 2...
 3
 4INSTALLED_APPS = [
 5    'django.contrib.admin',
 6    'django.contrib.auth',
 7    'django.contrib.contenttypes',
 8    'django.contrib.sessions',
 9    'django.contrib.messages',
10    'django.contrib.staticfiles',
11    'weather',
12]
13
14...

这让Django知道你想在你的项目中使用天气应用程序。

接下来,您需要修改原始 urls.py 以指向您的应用程序 urls.py 文件. 要做到这一点,您需要在管理员仪表板的现有路径下添加一条行。

1[label the_weather/the_weather/urls.py]
2from django.contrib import admin
3from django.urls import path, include
4
5urlpatterns = [
6    path('admin/', admin.site.urls),
7    path('', include('weather.urls')),
8]

空串意味着您不需要为应用程序的输入点使用终端点,相反,您将允许应用程序处理任何特定的终端点,您可能已经放置了类似的‘path('weather/',...)’,这意味着您将不得不键入‘127.0.0.1:8000/weather/`以获得与您的天气应用程序相关的任何内容。

步骤 5 – 添加模板和视图

现在,您需要将模板添加到您的项目中。

Django 中的 template 是一个 HTML 文件,允许额外的语法,使模板动态。您将能够处理功能,如添加变量、如果陈述和循环。

在您的终端中,导航到天气应用程序目录:

1cd weather

接下来,创建templates目录:

1mkdir templates

然后导航它:

1cd templates

您还会创建另一个与您的应用程序相同的名称的目录. 这是因为Django将您所拥有的各种应用程序的所有模板目录结合在一起. 为了防止文件名复制,您可以使用您的应用程序的名称来防止复制:

1mkdir weather

在这个天气目录中,创建一个名为index.html的新文件,这是你的主要模板。

以下是您将为模板使用的HTML:

 1[label the_weather/weather/templates/weather/index.html]
 2<!DOCTYPE html>
 3<html lang="en">
 4<head>
 5    <meta charset="UTF-8">
 6    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 7    <meta http-equiv="X-UA-Compatible" content="ie=edge">
 8    <title>What's the weather like?</title>
 9    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.css" />
10</head>
11<body>
12    <section class="hero is-primary">
13        <div class="hero-body">
14            <div class="container">
15                <h1 class="title">
16                    What's the weather like?
17                </h1>
18            </div>
19        </div>
20    </section>
21    <section class="section">
22        <div class="container">
23            <div class="columns">
24                <div class="column is-offset-4 is-4">
25                    <form method="POST">
26                        <div class="field has-addons">
27                            <div class="control is-expanded">
28                                <input class="input" type="text" placeholder="City Name">
29                            </div>
30                            <div class="control">
31                                <button class="button is-info">
32                                    Add City
33                                </button>
34                            </div>
35                        </div>
36                    </form>
37                </div>
38            </div>
39        </div>
40    </section>
41    <section class="section">
42        <div class="container">
43            <div class="columns">
44                <div class="column is-offset-4 is-4">
45                    <div class="box">
46                        <article class="media">
47                            <div class="media-left">
48                                <figure class="image is-50x50">
49                                    <img src="http://openweathermap.org/img/w/10d.png" alt="Image">
50                                </figure>
51                            </div>
52                            <div class="media-content">
53                                <div class="content">
54                                    <p>
55                                        <span class="title">Las Vegas</span>
56                                        <br>
57                                        <span class="subtitle">29° F</span>
58                                        <br> thunderstorm with heavy rain
59                                    </p>
60                                </div>
61                            </div>
62                        </article>
63                    </div>
64                </div>
65            </div>
66        </div>
67    </section>
68    <footer class="footer">
69    </footer>
70</body>
71</html>

<$>[注] 注: 幕后,我们正在使用 Bulma来处理风格和布局。 为了深入了解 Bulma 和 CSS 框架,请考虑阅读 Get to Know Bulma: My Current Favorite CSS Framework <$>

现在你已经创建了模板,让我们创建一个视图和URL组合,以便你实际上可以在你的应用中看到这一点。

Views 在 Django 中是函数或类别. 在这种情况下,因为您正在创建一个简单的视图,您将创建一个函数. 将此函数添加到您的「views.py」文件:

1[label the_weather/weather/views.py]
2from django.shortcuts import render
3
4def index(request):
5    return render(request, 'weather/index.html') #returns the index.html template

您正在命名您的视图为索引,因为它将位于您的应用程序的索引中,即根 URL. 要获得模板渲染,您将返回请求,这对于渲染函数是必要的,并且您想要渲染的模板文件的名称,在这种情况下,是天气/index.html

在应用程序的urls.py文件中,更新urlpatterns列表。

1[label the_weather/weather/urls.py]
2from django.urls import path
3from . import views
4
5urlpatterns = [
6    path('', views.index),  #the path for our index view
7]

这允许您参考您刚刚创建的视图。

Django将匹配任何没有终点的 URL,并将其路由到您创建的视图函数。

现在,使用您的终端返回您的项目根(‘the_weather’)。

接下来,启动服务器:

1python manage.py runserver

然后,打开您的网页浏览器,然后再次访问127.0.0.1:8000:

Browser window depicting View with hard-coded values for Las Vegas

您将观察index.html文件的渲染HTML。有一个输入来添加一个城市。还有一个显示拉斯维加斯的硬代码天气。然而,此时的表格不起作用,天气只是一个位置。

步骤 6 – 使用天气 API

现在你要做的就是注册 Open Weather Map API。这将使你能够获得你添加到你的应用程序的任何城市的实时天气。

去网站,创建一个帐户,然后去他们的仪表板上的API密钥。你可以使用他们提供的默认密钥,或者你可以创建一个新的API密钥。

Open Weather Map Dashboard

<$>[注] 注: 重要的是要保密API密钥,以防止其他方使用它们。

您将使用的一个终端点是下面的,因此您可以通过使用 API 密钥修改下面的 URL 来查看返回的数据并导航到浏览器中的 URL:

1http://api.openweathermap.org/data/2.5/weather?q=las%20vegas&units=imperial&appid=YOUR_APP_KEY

您的 API 密钥可能需要几分钟才能激活,所以如果它不起作用,请在几分钟后再次尝试。

您应该看到 JSON 格式的响应,包括坐标、温度和天气条件。

随着这一点,让我们添加一个请求来将数据输入到您的应用程序中。

让我们更新您的索引视图,将请求发送到您所拥有的 URL。

 1[label the_weather/weather/views.py]
 2from django.shortcuts import render
 3import requests
 4
 5def index(request):
 6    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'
 7
 8    city = 'Las Vegas'
 9
10    city_weather = requests.get(url.format(city)).json() #request the API data and convert the JSON to Python data types
11
12    return render(request, 'weather/index.html') #returns the index.html template

添加导入请求,url,citycity_weather

使用这些新行,您将添加您将发送请求的 URL。

请注意,此 URL 与您在浏览器中测试的 URL 略有不同. 该城市不是 URL 的一部分,它已被移动到变量。

目前,您将将城市设置为拉斯维加斯,但稍后将从数据库中设置为城市。

最后,您将将请求发送到使用该城市的 URL,并获得该城市的 JSON 表示。

如果您将其打印到控制台,您可以看到当您将URL放入地址栏时所看到的相同数据:

1[label the_weather/weather/views.py]
2...
3def index(request):
4    ...
5    print(city_weather) #temporarily view output
6
7    return render(request, 'weather/index.html') #returns the index.html template

如果您在 Web 浏览器中重新加载该页面,您将看到数据被打印到您的控制台。

验证为真实后,您可以从代码中删除打印声明。

步骤 7 – 在模板中显示数据

接下来,您需要将数据传输到模板中,以便向用户显示。

让我们创建一个字典,包含你需要的所有数据. 返回的数据,你将需要temp,描述图标

 1[label the_weather/weather/views.py]
 2...
 3def index(request):
 4    ...
 5    weather = {
 6        'city' : city,
 7        'temperature' : city_weather['main']['temp'],
 8        'description' : city_weather['weather'][0]['description'],
 9        'icon' : city_weather['weather'][0]['icon']
10    }
11
12    return render(request, 'weather/index.html') #returns the index.html template

现在你有你想要的所有信息,你可以将其传递给模板. 要将其传递给模板,你将创建一个名为背景的变量。

然后在渲染中,您将添加背景作为第三个参数:

1[label the_weather/weather/views.py]
2...
3def index(request):
4    ...
5    context = {'weather' : weather}
6
7    return render(request, 'weather/index.html', context) #returns the index.html template

随着在背景中添加的天气数据,让我们去模板添加数据。

index.html模板中,你所需要做的就是修改HTML以使用变量而不是硬编码的值。

请注意,Django 会转换字典密钥,所以你只能使用 dot 标注 来访问它们。例如,‘weather.city’ 会给你城市名称。

查找盒子

,并更新它以使用变量:

 1[label the_weather/weather/templates/weather/index.html]
 2...
 3<div class="box">
 4    <article class="media">
 5        <div class="media-left">
 6            <figure class="image is-50x50">
 7                <img src="http://openweathermap.org/img/w/{{ weather.icon }}.png" alt="Image">
 8            </figure>
 9        </div>
10        <div class="media-content">
11            <div class="content">
12                <p>
13                    <span class="title">{{ weather.city }}</span>
14                    <br>
15                    <span class="subtitle">{{ weather.temperature }}° F</span>
16                    <br> {{ weather.description }}
17                </p>
18            </div>
19        </div>
20    </article>
21</div>
22...

随着所有变量被更换,您现在将被介绍为您的城市的当前天气。

Browser window depicting dynamic weather for Las Vegas

然而,该城市目前仍然是硬编码的,您下一步要做的就是从数据库中拉出并显示数据库中的城市。

要做到这一点,您将在您的数据库中创建一个表格,以保留您想要知道天气的城市。

在你的天气应用程序中,进入models.py文件,然后添加以下内容:

 1[label the_weather/weather/models.py]
 2from django.db import models
 3
 4class City(models.Model):
 5    name = models.CharField(max_length=25)
 6
 7    def __str__(self): #show the actual city name on the dashboard
 8        return self.name
 9
10    class Meta: #show the plural of city as cities instead of citys
11        verbose_name_plural = 'cities'

这将创建一个表在您的数据库中,它将有一个名为名称的列,这是城市的名称。

要在数据库中获得这些更改,您必须运行makemigrations来生成代码来更新数据库,并迁移以应用这些更改。

让我们停止服务器,然后在您的终端中执行迁移:

1python manage.py makemigrations

并移民:

1python manage.py migrate

你需要让它能够在你的管理器仪表板上看到这个模型. 要做到这一点,你需要在你的admin.py文件中注册它。

1[label the_weather/weather/admin.py]
2from django.contrib import admin
3from .models import City
4
5admin.site.register(City)

接下来,重新启动服务器并在您的 Web 浏览器中查看管理仪表板。

Browser window depicting cities on the Admin Dashboard

城市现在是一个选择。

然后您可以进入管理仪表板并添加一些城市,例如:伦敦,东京拉斯维加斯

Browser window depicting three cities added to database

对于数据库中的条目,您需要在视图中查询这些条目,首先导入城市模型,然后对所有对象查询该模型:

1[label the_weather/weather/views.py]
2from django.shortcuts import render
3import requests
4from .models import City

然后更新请求城市:

1[label the_weather/weather/views.py]
2...
3def index(request):
4    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'
5
6    cities = City.objects.all() #return all the cities in the database
7    ...

因为你有城市列表,你会想旋转它们,并获得每个城市的天气,并将其添加到最终传递到模板的列表中。

這將只是你在早期步驟中所做的變化,而不同之處在於你正在循環並將每個字典附加到列表中。

首先,您将创建一个天气_数据列表,以保持每个城市天气

然后,替换原始的城市变量,并用城市的循环。

接下来,每个城市天气响应应应附加到天气_数据

您还需要更新背景,以通过这个列表,而不是单个字典。

在此时,你的views.py应该看起来像:

 1[label the_weather/weather/views.py]
 2...
 3def index(request):
 4    ...
 5    cities = City.objects.all() #return all the cities in the database
 6
 7    weather_data = []
 8
 9    for city in cities:
10
11        city_weather = requests.get(url.format(city)).json() #request the API data and convert the JSON to Python data types
12
13        weather = {
14            'city' : city,
15            'temperature' : city_weather['main']['temp'],
16            'description' : city_weather['weather'][0]['description'],
17            'icon' : city_weather['weather'][0]['icon']
18        }
19
20        weather_data.append(weather) #add the data for the current city into our list
21
22    context = {'weather_data' : weather_data}
23
24    return render(request, 'weather/index.html', context) #returns the index.html template

接下来,在index.html模板中,你需要绕过这个列表并生成列表中的每个城市的HTML。

 1[label the_weather/weather/templates/weather/index.html]
 2...
 3<div class="column is-offset-4 is-4">
 4    {% for weather in weather_data %}
 5    <div class="box">
 6        <article class="media">
 7            <div class="media-left">
 8                <figure class="image is-50x50">
 9                    <img src="http://openweathermap.org/img/w/{{ weather.icon }}.png" alt="Image">
10                </figure>
11            </div>
12            <div class="media-content">
13                <div class="content">
14                    <p>
15                        <span class="title">{{ weather.city }}</span>
16                        <br>
17                        <span class="subtitle">{{ weather.temperature }}° F</span>
18                        <br> {{ weather.description }}
19                    </p>
20                </div>
21            </div>
22        </article>
23    </div>
24    {% endfor %}
25</div>
26...

现在,您可以查看您在数据库中拥有的所有城市的数据。

步骤8 - 创建形式

最后一步是允许用户通过表单直接添加城市。

您可以手动创建表单,但由于您的表单将具有与模型完全相同的字段,您可以使用ModelForm

在你的天气应用程序中创建一个名为forms.py的新文件:

 1[label the_weather/weather/forms.py]
 2from django.forms import ModelForm, TextInput
 3from .models import City
 4
 5class CityForm(ModelForm):
 6    class Meta:
 7        model = City
 8        fields = ['name']
 9        widgets = {
10            'name': TextInput(attrs={'class' : 'input', 'placeholder' : 'City Name'}),
11        } #updates the input class to have the correct Bulma class and placeholder

要显示表单,您需要在视图中创建表单,并将其传输到模板中。

要做到这一点,让我们更新index.html来创建表单,您还需要更新背景,以便将表单传递到模板中。

 1[label the_weather/weather/views.py]
 2...
 3from .forms import CityForm
 4
 5def index(request):
 6    ...
 7    form = CityForm()
 8
 9    weather_data = []
10    ...
11    context = {'weather_data' : weather_data, 'form' : form}

现在在index.html模板中,让我们更新表单部分以便从您的视图中使用表单和一个csrf_token,这对于Django中的POST请求是必要的。

 1[label the_weather/weather/templates/weather/index.html]
 2...
 3<form method="POST">
 4    {% csrf_token %}
 5    <div class="field has-addons">
 6        <div class="control is-expanded">
 7            {{ form.name }}
 8        </div>
 9        <div class="control">
10            <button class="button is-info">
11                Add City
12            </button>
13        </div>
14    </div>
15</form>
16...

<$>[注] 注: CSRF 代表 跨站点请求伪造

随着表单在您的HTML工作,您将需要处理表单数据,因为它进入. 为此,您将创建一个如果块检查POST请求. 您需要在开始抓取天气数据之前添加请求类型的检查,以便您立即获得您添加的城市的数据。

 1[label the_weather/weather/views.py]
 2...
 3def index(request):
 4    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'
 5
 6    cities = City.objects.all() #return all the cities in the database
 7
 8    if request.method == 'POST': # only true if form is submitted
 9        form = CityForm(request.POST) # add actual request data to form for processing
10        form.save() # will validate and save if validate
11
12    form = CityForm()
13    ...

通过通过request.POST,您将能够验证表单数据。

现在,你应该能够输入一个城市的名称,点击添加,然后看到它出现。

例如,添加迈阿密为下一个城市:

Browser window depicting weather for London, Tokyo, Las Vegas, and Miami

当您放弃如果块时,该表单将重新创建,以便您可以添加另一个城市,如果您选择。

现在您可以在您的应用程序中跟踪多个城市的天气。

结论

在本文中,你不得不与Django的不同部分合作,以便实现此功能:视图,模型,表单和模板. 你还不得不使用Python库请求来获取实际的天气数据。

Published At
Categories with 技术
comments powered by Disqus