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

来自菜鸟教程
跳转至:导航、​搜索

介绍

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

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

您将使用数据库并创建一个表单。 您在本教程中学到的内容以后可以应用于更复杂的项目。

先决条件

  • 这个项目需要安装Python,你应该可以参考这个教程系列获取更多信息。

本文中的代码是使用 Python 3 和 Django 3.0 编写的,因此要学习本教程,您应该对两者都有一定的了解。

第 1 步 — 设置项目

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

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

任何一种方法都有效,但在本文中,您将使用 pipenv

注意: Django的替代安装方法,你应该可以参考这个教程系列获取更多信息。


官方文档提供了使用Homebrew或Linuxbrew安装pipenv的说明。 也可以将 pipenvpip 一起安装。

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

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

这将产生一个新的 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

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

如果您看到“恭喜”页面,您就知道您已经正确设置了 Django。

第 3 步 — 登录管理仪表板

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

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

接下来,在终端中运行 migrate 命令:

python manage.py migrate

通过运行该命令,Django 为您创建了一个 SQLite 数据库,这是设置中的默认数据库,并且它已向该数据库添加了几个表。 如果您在项目目录中看到一个新的 db.sqlite3 文件,您将知道数据库是否已创建。

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

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

python manage.py createsuperuser

按照说明为您的管理员用户提供用户名、电子邮件地址和密码。 完成后,您需要在终端中再次启动服务器:

python manage.py runserver

在您的网络浏览器中,通过转到 127.0.0.1:8000/admin 访问管理仪表板。

之所以能到这个页面,是因为在你的urls.py中设置了admin

如果您使用刚刚创建的用户名和密码登录,您将看到 Django Admin Dashboard:

GroupsUsers 代表 Django 允许您访问的两个模型。 Models 只是数据库中表的代码表示。 即使 Django 创建了更多表,也无需直接访问其余表,因此没有创建模型。

如果单击“用户”,您应该会看到有关用户表的更多详细信息,并且应该会看到您创建的用户。 通过单击仪表板中的不同链接花点时间进行探索,以查看可用的内容。 请注意不要删除您的用户,否则您将不得不再次运行 createsuperuser

让我们暂时离开管理仪表板并处理代码。 您需要在项目中为天气应用程序创建一个应用程序。

第 4 步 - 创建应用程序

在 Django 中,您可以使用 apps 来分离项目中的功能。 对于 Django,app 指的是项目中的特定功能。

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

第一个安装的应用程序 - django.contrib.admin - 是您刚刚使用的。 它处理所有管理功能,仅此而已。 默认情况下,您项目中的另一个应用程序是 django.contrib.auth,它允许您登录管理仪表板。

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

首先,您需要停止服务器。

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

python manage.py startapp weather

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

生成最新文件后,让我们在 weather 应用目录中创建一个名为 urls.py 的新文件:

the_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 文件。 为此,您在管理仪表板的现有路径下添加一行。 您还需要导入 include 以便您可以指向您的应用程序 urls.py 文件。

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>

注意: 在幕后,我们使用 Bulma 来处理样式和布局。 要深入了解 Bulma 和 CSS 框架,请考虑阅读 了解 Bulma:我目前最喜欢的 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。 要渲染模板,您返回 request,这是 render 函数所必需的,以及要渲染的模板文件的名称,在本例中为 weather/index.html .

让我们添加将向该视图发送请求的 URL。 在应用程序的 urls.py 文件中,更新 urlpatterns 列表。

the_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

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

您将观察到 index.html 文件呈现的 HTML。 有一个添加城市的输入。 并且显示了拉斯维加斯的硬编码天气。 但是,此时的表格不起作用,天气只是一个占位符。 让我们继续努力。

第 6 步 — 使用天气 API

您现在要做的是注册 Open Weather Map API。 这将允许您获取您添加到应用程序中的任何城市的实时天气。

转到该站点,创建一个帐户,然后转到其仪表板上的 API 密钥。 您可以使用他们提供的默认密钥,也可以创建新的 API 密钥。 此密钥将允许您使用 API 获取天气。

注意: API 密钥保密很重要,以防止它们被其他方使用。 您将希望避免将 API 密钥提交到 GitHub 等远程存储库。


您将使用的一个端点如下所示,因此您可以通过使用 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 requestsurlcitycity_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 浏览器中重新加载页面,您将看到数据打印到您的控制台。

在验证为 true 后,您可以从代码中删除 print 语句。

第 7 步 — 在模板中显示数据

接下来,您需要将数据传递给模板,以便将其显示给用户。

让我们创建一个字典来保存您需要的所有数据。 在返回的数据中,您将需要 tempdescriptionicon

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 中,您将添加 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/天气/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

然后,用 cities 更新 request

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
    ...

由于您有城市列表,您将希望遍历它们并获取每个城市的天气并将其添加到最终将传递给模板的列表中。

这只是您在前面步骤中所做的事情的一种变体。 不同之处在于您正在循环并将每个字典附加到列表中。

首先,您将创建一个 weather_data 列表来保存每个 cityweather

然后,替换原来的 city 变量并在 cities 上循环。

接下来,每个 cityweather 响应应附加到 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。 为此,您可以在 HTML 周围放置一个 for 循环,为 city 生成一个“框”<div>

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/天气/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 模板中,让我们更新表单部分以使用您视图中的表单和 csrf_token,这是 Django 中的 POST 请求所必需的。

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代表跨站请求伪造。 这是一种安全措施,可确保表单数据是从预期的可信来源提交的。


随着 HTML 中的表单工作,您将需要处理传入的表单数据。 为此,您将创建一个 if 块来检查 POST 请求。 您需要在开始获取天气数据之前添加对请求类型的检查,以便立即获取您添加的城市的数据。

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 来获取实际的天气数据。 因此,即使应用程序很简单,您也会在更复杂的应用程序中使用许多相同的概念。