Django 是 Python API 开发中最流行的框架之一，在这个教程中，我们来学习如何使用它。Django 所有 Web 框架中最全面的，也是最受欢迎的一个。自 2005 年以来，其流行度大幅上升。Django 是由 Django 软件基金会维护，并且获得了社区的大力支持，在全球拥有超过 11,600 名成员。在 Stack Overflow 上，约有 191,000 个带 Django 标签的问题。Spotify、YouTube 和 Instagram 等都使用 Django 来构建应用程序和数据管理。本文演示了一个简单的 API，通过它可以使用 HTTP 协议的 GET 方法来从服务器获取数据。构建一个项目首先，为你的 Django 应用程序创建一个目录结构，你可以在系统的任何位置创建：$ mkdir myproject $ cd myproject然后，在项目目录中创建一个虚拟环境来隔离本地包依赖关系：$ python3 -m venv env $ source env/bin/activate在 Windows 上，使用命令 env\Scripts\activate 来激活虚拟环境。安装 Django 和 Django REST framework然后，安装 Django 和 Django REST 模块：$ pip3 install django $ pip3 install djangorestframework实例化一个新的 Django 项目现在你的应用程序已经有了一个工作环境，你必须实例化一个新的 Django 项目。与 Flask 这样微框架不同的是，Django 有专门的命令来创建（注意第一条命令后的 . 字符）。$ django-admin startproject tutorial . $ cd tutorial $ django-admin startapp quickstartDjango 使用数据库来管理后端，所以你应该在开始开发之前同步数据库，数据库可以通过 manage.py 脚本管理，它是在你运行 django-admin 命令时创建的。因为你现在在 tutorial 目录，所以使用 ../ 符号来运行脚本，它位于上一层目录：$ python3 ../manage.py makemigrations No changes detected $ python3 ../manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying sessions.0001_initial... OK在 Django 中创建用户创建一个名为 admin，示例密码为 password123 的初始用户：$ python3 ../manage.py createsuperuser \ --email admin@example.com \ --username admin在提示时创建密码。在 Django 中实现序列化和视图为了使 Django 能够将信息传递给 HTTP GET 请求，必须将信息对象转化为有效的响应数据。Django 为此实现了“序列化类” serializers。在你的项目中，创建一个名为 quickstart/serializers.py 的新模块，使用它来定义一些序列化器，模块将用于数据展示：from django.contrib.auth.models import User, Group from rest_framework import serializers class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User fields = ['url', 'username', 'email', 'groups'] class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group fields = ['url', 'name']Django 中的视图是一个接受 Web 请求并返回 Web 响应的函数。响应可以是 HTML、HTTP 重定向、HTTP 错误、JSON 或 XML 文档、图像或 TAR 文件，或者可以是从 Internet 获得的任何其他内容。要创建视图，打开 quickstart/views.py 并输入以下代码。该文件已经存在，并且其中包含一些示例文本，保留这些文本并将以下代码添加到文件中：from django.contrib.auth.models import User, Group from rest_framework import viewsets from tutorial.quickstart.serializers import UserSerializer, GroupSerializer class UserViewSet(viewsets.ModelViewSet): """ API 允许查看或编辑用户 """ queryset = User.objects.all().order_by('-date_joined') serializer_class = UserSerializer class GroupViewSet(viewsets.ModelViewSet): """ API 允许查看或编辑组 """ queryset = Group.objects.all() serializer_class = GroupSerializer使用 Django 生成 URL现在，你可以生成 URL 以便人们可以访问你刚起步的 API。在文本编辑器中打开 urls.py 并将默认示例代码替换为以下代码：from django.urls import include, path from rest_framework import routers from tutorial.quickstart import views router = routers.DefaultRouter() router.register(r'users', views.UserViewSet) router.register(r'groups', views.GroupViewSet) # 使用自动路由 URL # 还有登录 URL urlpatterns = [ path('', include(router.urls)), path('api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]调整你的 Django 项目设置这个示例项目的设置模块存储在 tutorial/settings.py 中，因此在文本编辑器中将其打开，然后在 INSTALLED_APPS 列表的末尾添加 rest_framework：INSTALLED_APPS = [ ... 'rest_framework', ]测试 Django API现在，你可以测试构建的 API。首先，从命令行启动内置服务器：$ python3 manage.py runserver你可以通过使用 curl 导航至 URL http://localhost:8000/users 来访问 API：$ curl --get http://localhost:8000/users/?format=json [{"url":"http://localhost:8000/users/1/?format=json","username":"admin","email":"admin@example.com","groups":[]}]使用 Firefox 或你选择的开源浏览器：有关使用 Django 和 Python 的 RESTful API 的更多深入知识，参考出色的 Django 文档。为什么要使用 Djago？Django 的主要优点：Django 社区的规模正在不断扩大，因此即使你做一个复杂项目，也会有大量的指导资源。默认包含模板、路由、表单、身份验证和管理工具等功能，你不必寻找外部工具，也不必担心第三方工具会引入兼容性问题。用户、循环和条件的简单结构使你可以专注于编写代码。这是一个成熟且经过优化的框架，它非常快速且可靠。Django 的主要缺点：Django 很复杂！从开发人员视角的角度来看，它可能比简单的框架更难学。Django 有一个很大的生态系统。一旦你熟悉它，这会很棒，但是当你深入学习时，它可能会令人感到无所适从。对你的应用程序或 API 来说，Django 是绝佳选择。下载并熟悉它，开始开发一个迷人的项目！via: https://opensource.com/article/19/11/python-web-api-django作者：Rachel Waston 选题：lujun9972 译者：MjSeven 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
在这个比较 Python 框架的最后一篇中，让我们看看 Django。在本系列（由四部分组成）的前三篇文章中，我们讨论了 Pyramid、Flask 和 Tornado 这 3 个 Web 框架。我们已经构建了三次相同的应用程序，最终我们遇到了 Django。总的来说，Django 是目前 Python 开发人员使用的主要 Web 框架，并且原因显而易见。它擅长隐藏大量的配置逻辑，让你专注于能够快速构建大型应用程序。也就是说，当涉及到小型项目时，比如我们的待办事项列表应用程序，Django 可能有点像用消防水管来进行水枪大战。让我们来看看它们是如何结合在一起的。关于 DjangoDjango 将自己定位为“一个鼓励快速开发和整洁、实用的设计的高级 Python Web 框架。它由经验丰富的开发人员构建，解决了 Web 开发的很多麻烦，因此你可以专注于编写应用程序而无需重新发明轮子”。而且它确实做到了！这个庞大的 Web 框架附带了非常多的工具，以至于在开发过程中，如何将所有内容组合在一起协同工作可能是个谜。除了框架本身很大，Django 社区也是非常庞大的。事实上，它非常庞大和活跃，以至于有一个网站专门用于为人们收集第三方包，这些第三方包可集成进 Django 来做一大堆事情。包括从身份验证和授权到完全基于 Django 的内容管理系统，电子商务附加组件以及与 Stripe（LCTT 译注：美版“支付宝”）集成的所有内容。至于不要重新发明轮子：如果你想用 Django 完成一些事情，有人可能已经做过了，你只需将它集成进你的项目就行。为此，我们希望使用 Django 构建 REST API，因此我们将使用流行的 Django REST 框架。它的工作是将 Django 框架（Django 使用自己的模板引擎构建 HTML 页面）转换为专门用于有效地处理 REST 交互的系统。让我们开始吧。Django 启动和配置$ mkdir django_todo $ cd django_todo $ pipenv install --python 3.6 $ pipenv shell (django-someHash) $ pipenv install django djangorestframework作为参考，我们使用的是 django-2.0.7 和 djangorestframework-3.8.2。与 Flask, Tornado 和 Pyramid 不同，我们不需要自己编写 setup.py 文件，我们并不是在做一个可安装的 Python 发布版。像很多事情一样，Django 以自己的方式处理这个问题。我们仍然需要一个 requirements.txt 文件来跟踪我们在其它地方部署的所有必要安装。但是，就 Django 项目中的目标模块而言，Django 会让我们列出我们想要访问的子目录，然后允许我们从这些目录中导入，就像它们是已安装的包一样。首先，我们必须创建一个 Django 项目。当我们安装了 Django 后，我们还安装了命令行脚本 django-admin。它的工作是管理所有与 Django 相关的命令，这些命令有助于我们将项目整合在一起，并在我们继续开发的过程中对其进行维护。django-admin 并不是让我们从头开始构建整个 Django 生态系统，而是让我们从标准 Django 项目所需的所有必要文件（以及更多）的基础上开始。调用 django-admin 的 start-project 命令的语法是 django-admin startproject <项目名称> <存放目录>。我们希望文件存于当前的工作目录中，所以：(django-someHash) $ django-admin startproject django_todo .输入 ls 将显示一个新文件和一个新目录。(django-someHash) $ ls manage.py django_todomanage.py 是一个可执行命令行 Python 文件，它最终成为 django-admin 的封装。因此，它的工作与 django-admin 是一样的：帮助我们管理项目。因此得名 manage.py。它在 django_todo 目录里创建了一个新目录 django_todo，其代表了我们项目的配置根目录。现在让我们深入研究一下。配置 Django可以将 django_todo 目录称为“配置根目录”，我们的意思是这个目录包含了通常配置 Django 项目所需的文件。几乎所有这个目录之外的内容都只关注与项目模型、视图、路由等相关的“业务逻辑”。所有连接项目的点都将在这里出现。在 django_todo 目录中调用 ls 会显示以下四个文件：(django-someHash) $ cd django_todo (django-someHash) $ ls __init__.py settings.py urls.py wsgi.py__init__.py 文件为空，之所以存在是为了将此目录转换为可导入的 Python 包。settings.py 是设置大多数配置项的地方。例如项目是否处于 DEBUG 模式，正在使用哪些数据库，Django 应该定位文件的位置等等。它是配置根目录的“主要配置”部分，我们将在一会深入研究。urls.py 顾名思义就是设置 URL 的地方。虽然我们不必在此文件中显式写入项目的每个 URL，但我们需要让此文件知道在其他任何地方已声明的 URL。如果此文件未指向其它 URL，则那些 URL 就不存在。wsgi.py 用于在生产环境中提供应用程序。就像 Pyramid、 Tornado 和 Flask 暴露了一些 “app” 对象一样，它们用来提供配置好的应用程序，Django 也必须暴露一个，就是在这里完成的。它可以和 Gunicorn、Waitress 或者 uWSGI 一起配合来提供服务。设置 settings看一看 settings.py，它里面有大量的配置项，那些只是默认值！这甚至不包括数据库、静态文件、媒体文件、任何集成的钩子，或者可以配置 Django 项目的任何其它几种方式。让我们从上到下看看有什么：BASE_DIR 设置目录的绝对路径，或者是 manage.py 所在的目录。这对于定位文件非常有用。SECRET_KEY 是用于 Django 项目中加密签名的密钥。在实际中，它用于会话、cookie、CSRF 保护和身份验证令牌等。最好在第一次提交之前，尽快应该更改 SECRET_KEY 的值并将其放置到环境变量中。DEBUG 告诉 Django 是以开发模式还是生产模式运行项目。这是一个非常关键的区别。在开发模式下，当弹出一个错误时，Django 将显示导致错误的完整堆栈跟踪，以及运行项目所涉及的所有设置和配置。如果在生产环境中将 DEBUG 设置为 True，这可能成为一个巨大的安全问题。在生产模式下，当出现问题时，Django 会显示一个简单的错误页面，即除错误代码外不提供任何信息。保护我们项目的一个简单方法是将 DEBUG 设置为环境变量，如 bool(os.environ.get('DEBUG', ''))。ALLOWED_HOSTS 是应用程序提供服务的主机名的列表。在开发模式中，这可能是空的；但是在生产环境中，如果为项目提供服务的主机不在 ALLOWED_HOSTS 列表中，Django 项目将无法运行。这是设置为环境变量的另一种情况。INSTALLED_APPS 是我们的 Django 项目可以访问的 Django “apps” 列表（将它们视为子目录，稍后会详细介绍）。默认情况下，它将提供：内置的 Django 管理网站Django 的内置认证系统Django 的数据模型通用管理器会话管理Cookie 和基于会话的消息传递站点固有的静态文件的用法，比如 css 文件、js 文件、任何属于我们网站设计的图片等。MIDDLEWARE 顾名思义：帮助 Django 项目运行的中间件。其中很大一部分用于处理各种类型的安全，尽管我们可以根据需要添加其它中间件。ROOT_URLCONF 设置基本 URL 配置文件的导入路径。还记得我们之前见过的那个 urls.py 吗？默认情况下，Django 指向该文件以此来收集所有的 URL。如果我们想让 Django 在其它地方寻找，我们将在这里设置 URL 位置的导入路径。TEMPLATES 是 Django 用于我们网站前端的模板引擎列表，假如我们依靠 Django 来构建我们的 HTML。我们在这里不需要，那就无关紧要了。WSGI_APPLICATION 设置我们的 WSGI 应用程序的导入路径 —— 在生产环境下使用的东西。默认情况下，它指向 wsgi.py 中的 application 对象。这很少（如果有的话）需要修改。DATABASES 设置 Django 项目将访问那些数据库。必须设置 default 数据库。我们可以通过名称设置别的数据库，只要我们提供 HOST、USER、PASSWORD、PORT、数据库名称 NAME 和合适的 ENGINE。可以想象，这些都是敏感的信息，因此最好将它们隐藏在环境变量中。查看 Django 文档了解更多详情。注意：如果不是提供数据库的每个单个部分，而是提供完整的数据库 URL，请查看 djdatabaseurl。AUTH_PASSWORD_VALIDATORS 实际上是运行以检查输入密码的函数列表。默认情况下我们有一些，但是如果我们有其它更复杂的验证需求：不仅仅是检查密码是否与用户的属性匹配，是否超过最小长度，是否是 1000 个最常用的密码之一，或者密码完全是数字，我们可以在这里列出它们。LANGUAGE_CODE 设置网站的语言。默认情况下它是美国英语，但我们可以将其切换为其它语言。TIME_ZONE 是我们 Django 项目后中自动生成的时间戳的时区。我强调坚持使用 UTC 并在其它地方执行任何特定于时区的处理，而不是尝试重新配置此设置。正如这篇文章 所述，UTC 是所有时区的共同点，因为不需要担心偏移。如果偏移很重要，我们可以根据需要使用与 UTC 的适当偏移来计算它们。USE_I18N 将让 Django 使用自己的翻译服务来为前端翻译字符串。I18N = 国际化（internationalization，“i” 和 “n” 之间共 18 个字符）。USE_L10N L10N = 本地化（localization，在 l 和 n 之间共 10 个字符) 。如果设置为 True，那么将使用数据的公共本地格式。一个很好的例子是日期：在美国它是 MM-DD-YYYY。在欧洲，日期往往写成 DD-MM-YYYY。STATIC_URL 是用于提供静态文件的主体部分。我们将构建一个 REST API，因此我们不需要考虑静态文件。通常，这会为每个静态文件的域名设置根路径。所以，如果我们有一个 Logo 图像，那就是 http://<domainname>/<STATIC_URL>/logo.gif。默认情况下，这些设置已准备就绪。我们必须改变的一个选项是 DATABASES 设置。首先，我们创建将要使用的数据库：(django-someHash) $ createdb django_todo我们想要像使用 Flask、Pyramid 和 Tornado 一样使用 PostgreSQL 数据库，这意味着我们必须更改 DATABASES 设置以允许我们的服务器访问 PostgreSQL 数据库。首先是引擎。默认情况下，数据库引擎是 django.db.backends.sqlite3，我们把它改成 django.db.backends.postgresql。有关 Django 可用引擎的更多信息，请查看文档。请注意，尽管技术上可以将 NoSQL 解决方案整合到 Django 项目中，但为了开箱即用，Django 强烈偏向于 SQL 解决方案。接下来，我们必须为连接参数的不同部分指定键值对。NAME 是我们刚刚创建的数据库的名称。USER 是 Postgres 数据库用户名。PASSWORD 是访问数据库所需的密码。HOST 是数据库的主机。当我们在本地开发时，localhost 或 127.0.0.1 都将起作用。PORT 是我们为 Postgres 开放的端口，它通常是 5432。settings.py 希望我们为每个键提供字符串值。但是，这是高度敏感的信息。任何负责任的开发人员都不应该这样做。有几种方法可以解决这个问题，一种是我们需要设置环境变量。DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME', ''), 'USER': os.environ.get('DB_USER', ''), 'PASSWORD': os.environ.get('DB_PASS', ''), 'HOST': os.environ.get('DB_HOST', ''), 'PORT': os.environ.get('DB_PORT', ''), } }在继续之前，请确保设置环境变量，否则 Django 将无法工作。此外，我们需要在此环境中安装 psycopg2，以便我们可以与数据库通信。Django 路由和视图让我们在这个项目中实现一些函数。我们将使用 Django REST 框架来构建 REST API，所以我们必须确保在 settings.py 中将 rest_framework 添加到 INSTALLED_APPS 的末尾。INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework' ]虽然 Django REST 框架并不专门需要基于类的视图（如 Tornado）来处理传入的请求，但类是编写视图的首选方法。让我们来定义一个类视图。让我们在 django_todo 创建一个名为 views.py 的文件。在 views.py 中，我们将创建 “Hello, world!” 视图。# in django_todo/views.py from rest_framework.response import JsonResponse from rest_framework.views import APIView class HelloWorld(APIView): def get(self, request, format=None): """Print 'Hello, world!' as the response body.""" return JsonResponse("Hello, world!")每个 Django REST 框架基于类的视图都直接或间接地继承自 APIView。APIView 处理大量的东西，但针对我们的用途，它做了以下特定的事情：* 根据 HTTP 方法（例如 GET、POST、PUT、DELETE）来设置引导对应请求所需的方法 * 用我们需要的所有数据和属性来填充 `request` 对象，以便解析和处理传入的请求 * 采用 `Response` 或 `JsonResponse`，每个调度方法（即名为 `get`、`post`、`put`、`delete` 的方法）返回并构造格式正确的 HTTP 响应。终于，我们有一个视图了！它本身没有任何作用，我们需要将它连接到路由。如果我们跳转到 django_todo/urls.py，我们会到达默认的 URL 配置文件。如前所述：如果 Django 项目中的路由不包含在此处，则它不存在。我们在给定的 urlpatterns 列表中添加所需的 URL。默认情况下，我们有一整套 URL 用于 Django 的内置管理后端系统。我们会完全删除它。我们还得到一些非常有用的文档字符串，它告诉我们如何向 Django 项目添加路由。我们需要调用 path()，伴随三个参数：所需的路由，作为字符串（没有前导斜线）处理该路由的视图函数（只能有一个函数！）在 Django 项目中路由的名称让我们导入 HelloWorld 视图并将其附加到主路径 / 。我们可以从 urlpatterns 中删除 admin 的路径，因为我们不会使用它。# django_todo/urls.py, after the big doc string from django.urls import path from django_todo.views import HelloWorld urlpatterns = [ path('', HelloWorld.as_view(), name="hello"), ]好吧，这里有一点不同。我们指定的路由只是一个空白字符串，为什么它会工作？Django 假设我们声明的每个路由都以一个前导斜杠开头，我们只是在初始域名后指定资源路由。如果一条路由没有去往一个特定的资源，而只是一个主页，那么该路由是 ''，实际上是“没有资源”。HelloWorld 视图是从我们刚刚创建的 views.py 文件导入的。为了执行此导入，我们需要更新 settings.py 中的 INSTALLED_APPS 列表使其包含 django_todo。是的，这有点奇怪。以下是一种理解方式。INSTALLED_APPS 指的是 Django 认为可导入的目录或包的列表。它是 Django 处理项目的各个组件的方式，比如安装了一个包，而不需要经过 setup.py 的方式。我们希望将 django_todo 目录视为可导入的包，因此我们将该目录包含在 INSTALLED_APPS 中。现在，在该目录中的任何模块也是可导入的。所以我们得到了我们的视图。path 函数只将视图函数作为第二个参数，而不仅仅是基于类的视图。幸运的是，所有有效的基于 Django 类的视图都包含 .as_view() 方法。它的工作是将基于类的视图的所有优点汇总到一个视图函数中并返回该视图函数。所以，我们永远不必担心转换的工作。相反，我们只需要考虑业务逻辑，让 Django 和 Django REST 框架处理剩下的事情。让我们在浏览器中打开它！Django 提供了自己的本地开发服务器，可通过 manage.py 访问。让我们切换到包含 manage.py 的目录并输入：(django-someHash) $ ./manage.py runserver Performing system checks... System check identified no issues (0 silenced). August 01, 2018 - 16:47:24 Django version 2.0.7, using settings 'django_todo.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.当 runserver 执行时，Django 会检查以确保项目（或多或少）正确连接在一起。这不是万无一失的，但确实会发现一些明显的问题。如果我们的数据库与代码不同步，它会通知我们。毫无疑问，因为我们没有将任何应用程序的东西提交到我们的数据库，但现在这样做还是可以的。让我们访问 http://127.0.0.1:8000 来查看 HelloWorld 视图的输出。咦？这不是我们在 Pyramid、Flask 和 Tornado 中看到的明文数据。当使用 Django REST 框架时，HTTP 响应（在浏览器中查看时）是这样呈现的 HTML，以红色显示我们的实际 JSON 响应。但不要担心！如果我们在命令行中使用 curl 快速访问 http://127.0.0.1:8000，我们就不会得到任何花哨的 HTML，只有内容。# 注意：在不同的终端口窗口中执行此操作，在虚拟环境之外 $ curl http://127.0.0.1:8000 "Hello, world!"棒极了！Django REST 框架希望我们在使用浏览器浏览时拥有一个人性化的界面。这是有道理的，如果在浏览器中查看 JSON，通常是因为人们想要检查它是否正确，或者在设计一些消费者 API 时想要了解 JSON 响应。这很像你从 Postman 中获得的东西。无论哪种方式，我们都知道我们的视图工作了！酷！让我们概括一下我们做过的事情：使用 django-admin startproject <项目名称> 开始一个项目使用环境变量来更新 django_todo/settings.py 中的 DEBUG、SECRET_KEY，还有 DATABASES 字典安装 Django REST 框架，并将它添加到 INSTALLED_APPS创建 django_todo/views.py 来包含我们的第一个类视图，它返回响应 “Hello, world!”更新 django_todo/urls.py，其中包含我们的根路由在 django_todo/settings.py 中更新 INSTALLED_APPS 以包含 django_todo 包创建模型现在让我们来创建数据模型吧。Django 项目的整个基础架构都是围绕数据模型构建的，它是这样编写的，每个数据模型够可以拥有自己的小天地，拥有自己的视图，自己与其资源相关的 URL 集合，甚至是自己的测试（如果我们想要的话）。如果我们想构建一个简单的 Django 项目，我们可以通过在 django_todo 目录中编写我们自己的 models.py 文件并将其导入我们的视图来避免这种情况。但是，我们想以“正确”的方式编写 Django 项目，因此我们应该尽可能地将模型拆分成符合 Django Way™（Django 风格）的包。Django Way 涉及创建所谓的 Django “应用程序”，它本身并不是单独的应用程序，它们没有自己的设置和诸如此类的东西（虽然它们也可以）。但是，它们可以拥有一个人们可能认为属于独立应用程序的东西：一组自建的 URL一组自建的 HTML 模板（如果我们想要提供 HTML）一个或多个数据模型一套自建的视图一套自建的测试它们是独立的，因此可以像独立应用程序一样轻松共享。实际上，Django REST 框架是 Django 应用程序的一个例子。它包含自己的视图和 HTML 模板，用于提供我们的 JSON。我们只是利用这个 Django 应用程序将我们的项目变成一个全面的 RESTful API 而不用那么麻烦。要为我们的待办事项列表项创建 Django 应用程序，我们将要使用 manage.py 的 startapp 命令。(django-someHash) $ ./manage.py startapp todostartapp 命令成功执行后没有输出。我们可以通过使用 ls 来检查它是否完成它应该做的事情。(django-someHash) $ ls Pipfile Pipfile.lock django_todo manage.py todo看看：我们有一个全新的 todo 目录。让我们看看里面!(django-someHash) $ ls todo __init__.py admin.py apps.py migrations models.py tests.py views.py以下是 manage.py startapp 创建的文件：__init__.py 是空文件。它之所以存在是因为此目录可看作是模型、视图等的有效导入路径。admin.py 不是空文件。它用于在 Django admin 中规范化这个应用程序的模型，我们在本文中没有涉及到它。apps.py 这里基本不起作用。它有助于规范化 Django admin 的模型。migrations 是一个包含我们数据模型快照的目录。它用于更新数据库。这是少数几个内置了数据库管理的框架之一，其中一部分允许我们更新数据库，而不必拆除它并重建它以更改 Schema。models.py 是数据模型所在。tests.py 是测试所在的地方，如果我们需要写测试。views.py 用于我们编写的与此应用程序中的模型相关的视图。它们不是一定得写在这里。例如，我们可以在 django_todo/views.py 中写下我们所有的视图。但是，它在这个应用程序中更容易将我们的概念理清。在覆盖了许多概念的扩展应用程序的关系之间会变得更加密切。它并没有为这个应用程序创建 urls.py 文件，但我们可以自己创建。(django-someHash) $ touch todo/urls.py在继续之前，我们应该帮自己一个忙，将这个新 Django 应用程序添加到 django_todo/settings.py 中的 INSTALLED_APPS 列表中。# settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_todo', 'todo' # <--- 添加了这行 ]检查 todo/models.py 发现 manage.py 已经为我们编写了一些代码。不同于在 Flask、Tornado 和 Pyramid 实现中创建模型的方式，Django 不利用第三方来管理数据库会话或构建其对象实例。它全部归入 Django 的 django.db.models 子模块。然而，建立模型的方式或多或少是相同的。要在 Django 中创建模型，我们需要构建一个继承自 models.Model 的 class，将应用于该模型实例的所有字段都应视为类属性。我们不像过去那样从 SQLAlchemy 导入列和字段类型，而是直接从 django.db.models 导入。# todo/models.py from django.db import models class Task(models.Model): """Tasks for the To Do list.""" name = models.CharField(max_length=256) note = models.TextField(blank=True, null=True) creation_date = models.DateTimeField(auto_now_add=True) due_date = models.DateTimeField(blank=True, null=True) completed = models.BooleanField(default=False)虽然 Django 的需求和基于 SQLAlchemy 的系统之间存在一些明显的差异，但总体内容和结构或多或少相同。让我们来指出这些差异。我们不再需要为对象实例声明自动递增 ID 的单独字段。除非我们指定一个不同的字段作为主键，否则 Django 会为我们构建一个。我们只是直接引用数据类型作为列本身，而不是实例化传递数据类型对象的 Column 对象。Unicode 字段变为 models.CharField 或 models.TextField。CharField 用于特定最大长度的小文本字段，而 TextField 用于任何数量的文本。TextField 应该是空白的，我们以两种方式指定它。blank = True 表示当构建此模型的实例，并且正在验证附加到该字段的数据时，该数据是可以为空的。这与 null = True 不同，后者表示当构造此模型类的表时，对应于 note 的列将允许空白或为 NULL。因此，总而言之，blank = True 控制如何将数据添加到模型实例，而 null = True 控制如何构建保存该数据的数据库表。DateTime 字段增加了一些属性，并且能够为我们做一些工作，使得我们不必修改类的 __init__ 方法。对于 creation_date 字段，我们指定 auto_now_add = True。在实际意义上意味着，当创建一个新模型实例时，Django 将自动记录现在的日期和时间作为该字段的值。这非常方便！当 auto_now_add 及其类似属性 auto_now 都没被设置为 True 时，DateTimeField 会像其它字段一样需要预期的数据。它需要提供一个适当的 datetime 对象才能生效。due_date 列的 blank 和 null 属性都设置为 True，这样待办事项列表中的项目就可以成为将来某个时间点完成，没有确定的日期或时间。BooleanField 最终可以取两个值：True 或 False。这里，默认值设置为 False。管理数据库如前所述，Django 有自己的数据库管理方式。我们可以利用 Django 提供的 manage.py 脚本，而不必编写任何关于数据库的代码。它不仅可以管理我们数据库的表的构建，还可以管理我们希望对这些表进行的任何更新，而不必将整个事情搞砸！因为我们构建了一个新模型，所以我们需要让数据库知道它。首先，我们需要将与此模型对应的模式放入代码中。manage.py 的 makemigrations 命令对我们构建的模型类及其所有字段进行快照。它将获取该信息并将其打包成一个 Python 脚本，该脚本将存在于特定 Django 应用程序的 migrations 目录中。永远没有理由直接运行这个迁移脚本。它的存在只是为了让 Django 可以使用它作为更新数据库表的基础，或者在我们更新模型类时继承信息。(django-someHash) $ ./manage.py makemigrations Migrations for 'todo': todo/migrations/0001_initial.py - Create model Task这将查找 INSTALLED_APPS 中列出的每个应用程序，并检查这些应用程序中存在的模型。然后，它将检查相应的 migrations 目录中的迁移文件，并将它们与每个 INSTALLED_APPS 中的模型进行比较。如果模型已经升级超出最新迁移所应存在的范围，则将创建一个继承自最新迁移文件的新迁移文件，它将自动命名，并且还会显示一条消息，说明自上次迁移以来发生了哪些更改。如果你上次处理 Django 项目已经有一段时间了，并且不记得模型是否与迁移同步，那么你无需担心。makemigrations 是一个幂等操作。无论你运行 makemigrations 一次还是 20 次，migrations 目录只有一个与当前模型配置的副本。更棒的是，当我们运行 ./manage.py runserver 时，Django 检测到我们的模型与迁移不同步，它会用彩色文本告诉我们以便我们可以做出适当的选择。下一个要点是至少让每个人访问一次：创建一个迁移文件不会立即影响我们的数据库。当我们运行 makemigrations 时，我们布置我们的 Django 项目定义了给定的表应该如何创建和最终查找。我们仍要将这些更改应用于数据库。这就是 migrate 命令的用途。(django-someHash) $ ./manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, todo Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK Applying todo.0001_initial... OK当我们应用这些迁移时，Django 首先检查其他 INSTALLED_APPS 是否有需要应用的迁移，它大致按照列出的顺序检查它们。我们希望我们的应用程序最后列出，因为我们希望确保，如果我们的模型依赖于任何 Django 的内置模型，我们所做的数据库更新不会受到依赖性问题的影响。我们还有另一个要构建的模型：User 模型。但是，因为我们正在使用 Django，事情有一些变化。许多应用程序需要某种类型的用户模型，Django 的 django.contrib.auth 包构建了自己的用户模型供我们使用。如果无需用户所需要的身份验证令牌，我们可以继续使用它而不是重新发明轮子。但是，我们需要那个令牌。我们可以通过两种方式来处理这个问题。继承 Django 的 User 对象，我们自己的对象通过添加 token 字段来扩展它创建一个与 Django 的 User 对象一对一关系的新对象，其唯一目的是持有一个令牌我习惯于建立对象关系，所以让我们选择第二种选择。我们称之为 Owner，因为它基本上具有与 User 类似的内涵，这就是我们想要的。出于纯粹的懒惰，我们可以在 todo/models.py 中包含这个新的 Owner 对象，但是不要这样做。Owner 没有明确地与任务列表上的项目的创建或维护有关。从概念上讲，Owner 只是任务的所有者。甚至有时候我们想要扩展这个 Owner 以包含与任务完全无关的其他数据。为了安全起见，让我们创建一个 owner 应用程序，其工作是容纳和处理这个 Owner 对象。(django-someHash) $ ./manage.py startapp owner不要忘记在 settings.py 文件中的 INSTALLED_APPS 中添加它。 `INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'django_todo', 'todo', 'owner']`如果我们查看 Django 项目的根目录，我们现在有两个 Django 应用程序：(django-someHash) $ ls Pipfile Pipfile.lock django_todo manage.py owner todo在 owner/models.py 中，让我们构建这个 Owner 模型。如前所述，它与 Django 的内置 User 对象有一对一的关系。我们可以用 Django 的 models.OneToOneField 强制实现这种关系。# owner/models.py from django.db import models from django.contrib.auth.models import User import secrets class Owner(models.Model): """The object that owns tasks.""" user = models.OneToOneField(User, on_delete=models.CASCADE) token = models.CharField(max_length=256) def __init__(self, *args, **kwargs): """On construction, set token.""" self.token = secrets.token_urlsafe(64) super().__init__(*args, **kwargs)这表示 Owner 对象对应到 User 对象，每个 user 实例有一个 owner 实例。on_delete = models.CASCADE 表示如果相应的 User 被删除，它所对应的 Owner 实例也将被删除。让我们运行 makemigrations 和 migrate 来将这个新模型放入到我们的数据库中。(django-someHash) $ ./manage.py makemigrations Migrations for 'owner': owner/migrations/0001_initial.py - Create model Owner (django-someHash) $ ./manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, owner, sessions, todo Running migrations: Applying owner.0001_initial... OK现在我们的 Owner 需要拥有一些 Task 对象。它与上面看到的 OneToOneField 非常相似，只不过我们会在 Task 对象上贴一个 ForeignKey 字段指向 Owner。# todo/models.py from django.db import models from owner.models import Owner class Task(models.Model): """Tasks for the To Do list.""" name = models.CharField(max_length=256) note = models.TextField(blank=True, null=True) creation_date = models.DateTimeField(auto_now_add=True) due_date = models.DateTimeField(blank=True, null=True) completed = models.BooleanField(default=False) owner = models.ForeignKey(Owner, on_delete=models.CASCADE)每个待办事项列表任务只有一个可以拥有多个任务的所有者。删除该所有者后，他们拥有的任务都会随之删除。现在让我们运行 makemigrations 来获取我们的数据模型设置的新快照，然后运行 migrate 将这些更改应用到我们的数据库。(django-someHash) django $ ./manage.py makemigrations You are trying to add a non-nullable field 'owner' to task without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py不好了！出现了问题！发生了什么？其实，当我们创建 Owner 对象并将其作为 ForeignKey 添加到 Task 时，要求每个 Task 都需要一个 Owner。但是，我们为 Task 对象进行的第一次迁移不包括该要求。因此，即使我们的数据库表中没有数据，Django 也会对我们的迁移进行预先检查，以确保它们兼容，而我们提议的这种新迁移不是。有几种方法可以解决这类问题：退出当前迁移并构建一个包含当前模型配置的新迁移将一个默认值添加到 Task 对象的 owner 字段允许任务为 owner 字段设置 NULL 值方案 2 在这里没有多大意义。我们建议，默认情况下，任何创建的 Task 都会对应到某个默认所有者，尽管默认所有者不一定存在。 方案 1 要求我们销毁和重建我们的迁移，而我们应该把它们留下。让我们考虑选项 3。在这种情况下，如果我们允许 Task 表为所有者提供空值，它不会很糟糕。从这一点开始创建的任何任务都必然拥有一个所有者。如果你的数据库表并非不能接受重新架构，请删除该迁移、删除表并重建迁移。# todo/models.py from django.db import models from owner.models import Owner class Task(models.Model): """Tasks for the To Do list.""" name = models.CharField(max_length=256) note = models.TextField(blank=True, null=True) creation_date = models.DateTimeField(auto_now_add=True) due_date = models.DateTimeField(blank=True, null=True) completed = models.BooleanField(default=False) owner = models.ForeignKey(Owner, on_delete=models.CASCADE, null=True) (django-someHash) $ ./manage.py makemigrations Migrations for 'todo': todo/migrations/0002_task_owner.py - Add field owner to task (django-someHash) $ ./manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, owner, sessions, todo Running migrations: Applying todo.0002_task_owner... OK酷！我们有模型了！欢迎使用 Django 声明对象的方式。出于更好的权衡，让我们确保无论何时制作 User，它都会自动与新的 Owner 对象对应。我们可以使用 Django 的 signals 系统来做到这一点。基本上，我们确切地表达了意图：“当我们得到一个新的 User 被构造的信号时，构造一个新的 Owner 并将新的 User 设置为 Owner 的 user 字段。”在实践中看起来像这样：# owner/models.py from django.contrib.auth.models import User from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver import secrets class Owner(models.Model): """The object that owns tasks.""" user = models.OneToOneField(User, on_delete=models.CASCADE) token = models.CharField(max_length=256) def __init__(self, *args, **kwargs): """On construction, set token.""" self.token = secrets.token_urlsafe(64) super().__init__(*args, **kwargs) @receiver(post_save, sender=User) def link_user_to_owner(sender, **kwargs): """If a new User is saved, create a corresponding Owner.""" if kwargs['created']: owner = Owner(user=kwargs['instance']) owner.save()我们设置了一个函数，用于监听从 Django 中内置的 User 对象发送的信号。它正在等待 User 对象被保存之后的情况。这可以来自新的 User 或对现有 User 的更新。我们在监听功能中辨别出两种情况。如果发送信号的东西是新创建的实例，kwargs ['created'] 将具有值 True。如果是 True 的话，我们想做点事情。如果它是一个新实例，我们创建一个新的 Owner，将其 user 字段设置为创建的新 User 实例。之后，我们 save() 新的 Owner。如果一切正常，这将提交更改到数据库。如果数据没通过我们声明的字段的验证，它将失败。现在让我们谈谈我们将如何访问数据。访问模型数据在 Flask、Pyramid 和 Tornado 框架中，我们通过对某些数据库会话运行查询来访问模型数据。也许它被附加到 request 对象，也许它是一个独立的 session 对象。无论如何，我们必须建立与数据库的实时连接并在该连接上进行查询。这不是 Django 的工作方式。默认情况下，Django 不利用任何第三方对象关系映射（ORM）与数据库进行通信。相反，Django 允许模型类维护自己与数据库的对话。从 django.db.models.Model 继承的每个模型类都会附加一个 objects 对象。这将取代我们熟悉的 session 或 dbsession。让我们打开 Django 给我们的特殊 shell，并研究这个 objects 对象是如何工作的。(django-someHash) $ ./manage.py shell Python 3.7.0 (default, Jun 29 2018, 20:13:13) [Clang 9.1.0 (clang-902.0.39.2)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>Django shell 与普通的 Python shell 不同，因为它知道我们正在构建的 Django 项目，可以轻松导入我们的模型、视图、设置等，而不必担心安装包。我们可以通过简单的 import 访问我们的模型。>>> from owner.models import Owner >>> Owner <class 'owner.models.Owner'>目前，我们没有 Owner 实例。我们可以通过 Owner.objects.all() 查询它们。>>> Owner.objects.all() <QuerySet []>无论何时我们在 <Model> .objects 对象上运行查询方法，我们都会得到 QuerySet。为了我们的目的，它实际上是一个列表，这个列表向我们显示它是空的。让我们通过创建一个 User 来创建一个 Owner。>>> from django.contrib.auth.models import User >>> new_user = User(username='kenyattamurphy', email='kenyatta.murphy@gmail.com') >>> new_user.set_password('wakandaforever') >>> new_user.save()如果我们现在查询所有的 Owner，我们应该会找到 Kenyatta。>>> Owner.objects.all() <QuerySet [<Owner: Owner object (1)>]>棒极了！我们得到了数据！序列化模型我们将在 “Hello World” 之外来回传递数据。因此，我们希望看到某种类似于 JSON 类型的输出，它可以很好地表示数据。获取该对象的数据并将其转换为 JSON 对象以通过 HTTP 提交是数据序列化的一种方式。在序列化数据时，我们正在获取我们目前拥有的数据并重新格式化以适应一些标准的、更易于理解的形式。如果我用 Flask、Pyramid 和 Tornado 这样做，我会在每个模型上创建一个新方法，让用户可以直接调用 to_json()。to_json() 的唯一工作是返回一个 JSON 可序列化的（即数字、字符串、列表、字典）字典，其中包含我想要为所讨论的对象显示的任何字段。对于 Task 对象，它可能看起来像这样：class Task(Base): ...all the fields... def to_json(self): """Convert task attributes to a JSON-serializable dict.""" return { 'id': self.id, 'name': self.name, 'note': self.note, 'creation_date': self.creation_date.strftime('%m/%d/%Y %H:%M:%S'), 'due_date': self.due_date.strftime('%m/%d/%Y %H:%M:%S'), 'completed': self.completed, 'user': self.user_id }这不花哨，但它确实起到了作用。然而，Django REST 框架为我们提供了一个对象，它不仅可以为我们这样做，还可以在我们想要创建新对象实例或更新现有实例时验证输入，它被称为 ModelSerializer。Django REST 框架的 ModelSerializer 是我们模型的有效文档。如果没有附加模型，它们就没有自己的生命（因为那里有 Serializer 类）。它们的主要工作是准确地表示我们的模型，并在我们的模型数据需要序列化并通过线路发送时，将其转换为 JSON。Django REST 框架的 ModelSerializer 最适合简单对象。举个例子，假设我们在 Task 对象上没有 ForeignKey。我们可以为 Task 创建一个序列化器，它将根据需要将其字段值转换为 JSON，声明如下：# todo/serializers.py from rest_framework import serializers from todo.models import Task class TaskSerializer(serializers.ModelSerializer): """Serializer for the Task model.""" class Meta: model = Task fields = ('id', 'name', 'note', 'creation_date', 'due_date', 'completed')在我们新的 TaskSerializer 中，我们创建了一个 Meta 类。Meta 的工作就是保存关于我们试图序列化的东西的信息（或元数据）。然后，我们会注意到要显示的特定字段。如果我们想要显示所有字段，我们可以简化过程并使用 __all __。或者，我们可以使用 exclude 关键字而不是 fields 来告诉 Django REST 框架我们想要除了少数几个字段以外的每个字段。我们可以拥有尽可能多的序列化器，所以也许我们想要一个用于一小部分字段，而另一个用于所有字段？在这里都可以。在我们的例子中，每个 Task 和它的所有者 Owner 之间都有一个关系，必须在这里反映出来。因此，我们需要借用 serializers.PrimaryKeyRelatedField 对象来指定每个 Task 都有一个 Owner，并且该关系是一对一的。它的所有者将从已有的全部所有者的集合中找到。我们通过对这些所有者进行查询并返回我们想要与此序列化程序关联的结果来获得该集合：Owner.objects.all()。我们还需要在字段列表中包含 owner，因为我们总是需要一个与 Task 相关联的 Owner。# todo/serializers.py from rest_framework import serializers from todo.models import Task from owner.models import Owner class TaskSerializer(serializers.ModelSerializer): """Serializer for the Task model.""" owner = serializers.PrimaryKeyRelatedField(queryset=Owner.objects.all()) class Meta: model = Task fields = ('id', 'name', 'note', 'creation_date', 'due_date', 'completed', 'owner')现在构建了这个序列化器，我们可以将它用于我们想要为我们的对象做的所有 CRUD 操作：如果我们想要 GET 一个特定的 Task 的 JSON 类型版本，我们可以做 TaskSerializer((some_task).data如果我们想接受带有适当数据的 POST 来创建一个新的 Task，我们可以使用 TaskSerializer(data = new_data).save()如果我们想用 PUT 更新一些现有数据，我们可以用 TaskSerializer(existing_task, data = data).save()我们没有包括 delete，因为我们不需要对 delete 操作做任何事情。如果你可以删除一个对象，只需使用 object_instance.delete()。以下是一些序列化数据的示例：>>> from todo.models import Task >>> from todo.serializers import TaskSerializer >>> from owner.models import Owner >>> from django.contrib.auth.models import User >>> new_user = User(username='kenyatta', email='kenyatta@gmail.com') >>> new_user.save_password('wakandaforever') >>> new_user.save() # creating the User that builds the Owner >>> kenyatta = Owner.objects.first() # 找到 kenyatta 的所有者 >>> new_task = Task(name="Buy roast beef for the Sunday potluck", owner=kenyatta) >>> new_task.save() >>> TaskSerializer(new_task).data {'id': 1, 'name': 'Go to the supermarket', 'note': None, 'creation_date': '2018-07-31T06:00:25.165013Z', 'due_date': None, 'completed': False, 'owner': 1}使用 ModelSerializer 对象可以做更多的事情，我建议查看文档以获得更强大的功能。否则，这就是我们所需要的。现在是时候深入视图了。查看视图我们已经构建了模型和序列化器，现在我们需要为我们的应用程序设置视图和 URL。毕竟，对于没有视图的应用程序，我们无法做任何事情。我们已经看到了上面的 HelloWorld 视图的示例。然而，这总是一个人为的、概念验证的例子，并没有真正展示 Django REST 框架的视图可以做些什么。让我们清除 HelloWorld 视图和 URL，这样我们就可以从我们的视图重新开始。我们要构建的第一个视图是 InfoView。与之前的框架一样，我们只想打包并发送一个我们用到的路由的字典。视图本身可以存在于 django_todo.views 中，因为它与特定模型无关（因此在概念上不属于特定应用程序）。# django_todo/views.py from rest_framework.response import JsonResponse from rest_framework.views import APIView class InfoView(APIView): """List of routes for this API.""" def get(self, request): output = { 'info': 'GET /api/v1', 'register': 'POST /api/v1/accounts', 'single profile detail': 'GET /api/v1/accounts/<username>', 'edit profile': 'PUT /api/v1/accounts/<username>', 'delete profile': 'DELETE /api/v1/accounts/<username>', 'login': 'POST /api/v1/accounts/login', 'logout': 'GET /api/v1/accounts/logout', "user's tasks": 'GET /api/v1/accounts/<username>/tasks', "create task": 'POST /api/v1/accounts/<username>/tasks', "task detail": 'GET /api/v1/accounts/<username>/tasks/<id>', "task update": 'PUT /api/v1/accounts/<username>/tasks/<id>', "delete task": 'DELETE /api/v1/accounts/<username>/tasks/<id>' } return JsonResponse(output)这与我们在 Tornado 中所拥有的完全相同。让我们将它放置到合适的路由并继续。为了更好的测试，我们还将删除 admin/ 路由，因为我们不会在这里使用 Django 管理后端。# in django_todo/urls.py from django_todo.views import InfoView from django.urls import path urlpatterns = [ path('api/v1', InfoView.as_view(), name="info"), ]连接模型与视图让我们弄清楚下一个 URL，它将是创建新的 Task 或列出用户现有任务的入口。这应该存在于 todo 应用程序的 urls.py 中，因为它必须专门处理 Task对象而不是整个项目的一部分。# in todo/urls.py from django.urls import path from todo.views import TaskListView urlpatterns = [ path('', TaskListView.as_view(), name="list_tasks") ]这个路由处理的是什么？我们根本没有指定特定用户或路径。由于会有一些路由需要基本路径 /api/v1/accounts/<username>/tasks，为什么我们只需写一次就能一次又一次地写它？Django 允许我们用一整套 URL 并将它们导入 django_todo/urls.py 文件。然后，我们可以为这些导入的 URL 中的每一个提供相同的基本路径，只关心可变部分，你知道它们是不同的。# in django_todo/urls.py from django.urls import include, path from django_todo.views import InfoView urlpatterns = [ path('api/v1', InfoView.as_view(), name="info"), path('api/v1/accounts/<str:username>/tasks', include('todo.urls')) ]现在，来自 todo/urls.py 的每个 URL 都将以路径 api/v1/accounts/<str:username>/tasks 为前缀。让我们在 todo/views.py 中构建视图。# todo/views.py from django.shortcuts import get_object_or_404 from rest_framework.response import JsonResponse from rest_framework.views import APIView from owner.models import Owner from todo.models import Task from todo.serializers import TaskSerializer class TaskListView(APIView): def get(self, request, username, format=None): """Get all of the tasks for a given user.""" owner = get_object_or_404(Owner, user__username=username) tasks = Task.objects.filter(owner=owner).all() serialized = TaskSerializer(tasks, many=True) return JsonResponse({ 'username': username, 'tasks': serialized.data })这里一点代码里面有许多要说明的，让我们来看看吧。我们从与我们一直使用的 APIView 的继承开始，为我们的视图奠定基础。我们覆盖了之前覆盖的相同 get 方法，添加了一个参数，允许我们的视图从传入的请求中接收 username。然后我们的 get 方法将使用 username 来获取与该用户关联的 Owner。这个 get_object_or_404 函数允许我们这样做，添加一些特殊的东西以方便使用。如果无法找到指定的用户，那么查找任务是没有意义的。实际上，我们想要返回 404 错误。get_object_or_404 根据我们传入的任何条件获取单个对象，并返回该对象或引发 Http 404 异常。我们可以根据对象的属性设置该条件。Owner 对象都通过 user 属性附加到 User。但是，我们没有要搜索的 User 对象，我们只有一个 username。所以，当你寻找一个 Owner 时，我们对 get_object_or_404 说：通过指定 user__username（这是两个下划线）来检查附加到它的 User 是否具有我想要的 username。通过 QuerySet 过滤时，这两个下划线表示 “此嵌套对象的属性”。这些属性可以根据需要进行深度嵌套。我们现在拥有与给定用户名相对应的 Owner。我们使用 Owner 来过滤所有任务，只用 Task.objects.filter 检索它拥有的任务。我们可以使用与 get_object_or_404 相同的嵌套属性模式来钻入连接到 Tasks 的 Owner 的 User（tasks = Task.objects.filter(owner__user__username = username)).all()），但是没有必要那么宽松。Task.objects.filter(owner = owner).all() 将为我们提供与我们的查询匹配的所有 Task 对象的QuerySet。很棒。然后，TaskSerializer 将获取 QuerySet 及其所有数据以及 many = True 标志，以通知其为项目集合而不是仅仅一个项目，并返回一系列序列化结果。实际上是一个词典列表。最后，我们使用 JSON 序列化数据和用于查询的用户名提供传出响应。处理 POST 请求post 方法看起来与我们之前看到的有些不同。# still in todo/views.py # ...other imports... from rest_framework.parsers import JSONParser from datetime import datetime class TaskListView(APIView): def get(self, request, username, format=None): ... def post(self, request, username, format=None): """Create a new Task.""" owner = get_object_or_404(Owner, user__username=username) data = JSONParser().parse(request) data['owner'] = owner.id if data['due_date']: data['due_date'] = datetime.strptime(data['due_date'], '%d/%m/%Y %H:%M:%S') new_task = TaskSerializer(data=data) if new_task.is_valid(): new_task.save() return JsonResponse({'msg': 'posted'}, status=201) return JsonResponse(new_task.errors, status=400)当我们从客户端接收数据时，我们使用 JSONParser().parse(request) 将其解析为字典。我们将所有者添加到数据中并格式化任务的 due_date（如果存在）。我们的 TaskSerializer 完成了繁重的任务。它首先接收传入的数据并将其转换为我们在模型上指定的字段。然后验证该数据以确保它适合指定的字段。如果附加到新 Task 的数据有效，它将使用该数据构造一个新的 Task 对象并将其提交给数据库。然后我们发回适当的“耶！我们做了一件新东西！”响应。如果没有，我们收集 TaskSerializer 生成的错误，并将这些错误发送回客户端，并返回 400 Bad Request 状态代码。如果我们要构建 put 视图来更新 Task，它看起来会非常相似。主要区别在于，当我们实例化 TaskSerializer 时，我们将传递旧对象和该对象的新数据，如 TaskSerializer(existing_task，data = data)。我们仍然会进行有效性检查并发回我们想要发回的响应。总结Django 作为一个框架是高度可定制的，每个人都有自己打造 Django 项目的方式。我在这里写出来的方式不一定是 Django 建立项目的确切方式。它只是 a) 我熟悉的方式，以及 b) 利用 Django 的管理系统。当你将概念切分到不同的小块时，Django 项目的复杂性会增加。这样做是为了让多个人更容易为整个项目做出贡献，而不会麻烦彼此。然而，作为 Django 项目的大量文件映射并不能使其更高效或自然地偏向于微服务架构。相反，它很容易成为一个令人困惑的独石应用，这可能对你的项目仍然有用，它也可能使你的项目难以管理，尤其是随着项目的增长。仔细考虑你的需求并使用合适的工具来完成正确的工作。对于像这样的简单项目，Django 可能不是合适的工具。Django 旨在处理多种模型，这些模型涵盖了不同的项目领域，但它们可能有一些共同点。这个项目是一个小型的双模型项目，有一些路由。即便我们把它构建更复杂，也只有七条路由，而仍然只是相同的两个模型。这还不足以证明一个完整的 Django 项目。如果我们期望这个项目能够拓展，那么将会是一个很好的选择。这不是那种项目。这就是使用火焰喷射器来点燃蜡烛，绝对是大材小用了。尽管如此，Web 框架就是一个 Web 框架，无论你使用哪个框架。它都可以接收请求并做出任何响应，因此你可以按照自己的意愿进行操作。只需要注意你选择的框架所带来的开销。就是这样！我们已经到了这个系列的最后！我希望这是一次启发性的冒险。当你在考虑如何构建你的下一个项目时，它将帮助你做出的不仅仅是最熟悉的选择。请务必阅读每个框架的文档，以扩展本系列中涉及的任何内容（因为它没有那么全面）。每个人都有一个广阔的世界。愉快地写代码吧！via: https://opensource.com/article/18/8/django-framework作者：Nicholas Hunt-Walker 选题：lujun9972 译者：MjSeven 校对：Bestony, wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
这个月的 Python 专栏将介绍一些 Django 包，它们有益于你的工作，以及你的个人或业余项目。Django 开发者们，在这个月的 Python 专栏中，我们会介绍一些能帮助你们的软件包。这些软件包是我们最喜欢的 Django 库，能够节省开发时间，减少样板代码，通常来说，这会让我们的生活更加轻松。我们为 Django 应用准备了六个包，为 Django 的 REST 框架准备了两个包。几乎所有我们的项目里，都用到了这些包，真的，不是说笑。不过在继续阅读之前，请先看看我们关于让 Django 管理后台更安全的几个提示，以及这篇关于 5 个最受欢迎的开源 Django 包 的文章。有用又省时的工具集合：django-extensionsdjango-extensions 这个 Django 包非常受欢迎，全是有用的工具，比如下面这些管理命令：shell_plus 打开 Django 的管理 shell，这个 shell 已经自动导入了所有的数据库模型。在测试复杂的数据关系时，就不需要再从几个不同的应用里做导入操作了。clean_pyc 删除项目目录下所有位置的 .pyc 文件create_template_tags 在指定的应用下，创建模板标签的目录结构。describe_form 输出模型的表单定义，可以粘贴到 forms.py 文件中。（需要注意的是，这种方法创建的是普通 Django 表单，而不是模型表单。）notes 输出你项目里所有带 TODO、FIXME 等标记的注释。Django-extensions 还包括几个有用的抽象基类，在定义模型时，它们能满足常见的模式。当你需要以下模型时，可以继承这些基类：TimeStampedModel：这个模型的基类包含了 created 字段和 modified 字段，还有一个 save() 方法，在适当的场景下，该方法自动更新 created 和 modified 字段的值。ActivatorModel：如果你的模型需要像 status、activate_date 和 deactivate_date 这样的字段，可以使用这个基类。它还自带了一个启用 .active() 和 .inactive() 查询集的 manager。TitleDescriptionModel 和 TitleSlugDescriptionModel：这两个模型包括了 title 和 description 字段，其中 description 字段还包括 slug，它根据 title 字段自动产生。django-extensions 还有其他更多的功能，也许对你的项目有帮助，所以，去浏览一下它的文档吧！12 因子应用的配置：django-environ在 Django 项目的配置方面，django-environ 提供了符合 12 因子应用 方法论的管理方法。它是另外一些库的集合，包括 envparse 和 honcho 等。安装了 django-environ 之后，在项目的根目录创建一个 .env 文件，用这个文件去定义那些随环境不同而不同的变量，或者需要保密的变量。（比如 API 密钥，是否启用调试，数据库的 URL 等）然后，在项目的 settings.py 中引入 environ，并参考官方文档的例子设置好 environ.PATH() 和 environ.Env()。就可以通过 env('VARIABLE_NAME') 来获取 .env 文件中定义的变量值了。创建出色的管理命令：django-clickdjango-click 是基于 Click 的，（我们之前推荐过… 两次 Click)，它对编写 Django 管理命令很有帮助。这个库没有很多文档，但是代码仓库中有个存放测试命令的目录，非常有参考价值。 django-click 基本的 Hello World 命令是这样写的：# app_name.management.commands.hello.py import djclick as click @click.command() @click.argument('name') def command(name): click.secho(f'Hello, {name}')在命令行下调用它，这样执行即可：>> ./manage.py hello Lacey Hello, Lacey处理有限状态机：django-fsmdjango-fsm 给 Django 的模型添加了有限状态机的支持。如果你管理一个新闻网站，想用类似于“写作中”、“编辑中”、“已发布”来流转文章的状态，django-fsm 能帮你定义这些状态，还能管理状态变化的规则与限制。Django-fsm 为模型提供了 FSMField 字段，用来定义模型实例的状态。用 django-fsm 的 @transition 修饰符，可以定义状态变化的方法，并处理状态变化的任何副作用。虽然 django-fsm 文档很轻量，不过 Django 中的工作流（状态） 这篇 GitHub Gist 对有限状态机和 django-fsm 做了非常好的介绍。联系人表单：#django-contact-form联系人表单可以说是网站的标配。但是不要自己去写全部的样板代码，用 django-contact-form 在几分钟内就可以搞定。它带有一个可选的能过滤垃圾邮件的表单类（也有不过滤的普通表单类）和一个 ContactFormView 基类，基类的方法可以覆盖或自定义修改。而且它还能引导你完成模板的创建，好让表单正常工作。用户注册和认证：django-allauthdjango-allauth 是一个 Django 应用，它为用户注册、登录/注销、密码重置，还有第三方用户认证（比如 GitHub 或 Twitter）提供了视图、表单和 URL，支持邮件地址作为用户名的认证方式，而且有大量的文档记录。第一次用的时候，它的配置可能会让人有点晕头转向；请仔细阅读安装说明，在自定义你的配置时要专注，确保启用某个功能的所有配置都用对了。处理 Django REST 框架的用户认证：django-rest-auth如果 Django 开发中涉及到对外提供 API，你很可能用到了 Django REST Framework（DRF）。如果你在用 DRF，那么你应该试试 django-rest-auth，它提供了用户注册、登录/注销，密码重置和社交媒体认证的端点（是通过添加 django-allauth 的支持来实现的，这两个包协作得很好）。Django REST 框架的 API 可视化：django-rest-swaggerDjango REST Swagger 提供了一个功能丰富的用户界面，用来和 Django REST 框架的 API 交互。你只需要安装 Django REST Swagger，把它添加到 Django 项目的已安装应用中，然后在 urls.py 中添加 Swagger 的视图和 URL 模式就可以了，剩下的事情交给 API 的 docstring 处理。API 的用户界面按照 app 的维度展示了所有端点和可用方法，并列出了这些端点的可用操作，而且它提供了和 API 交互的功能（比如添加/删除/获取记录）。django-rest-swagger 从 API 视图中的 docstrings 生成每个端点的文档，通过这种方法，为你的项目创建了一份 API 文档，这对你，对前端开发人员和用户都很有用。via: https://opensource.com/article/18/9/django-packages作者：Jeff Triplett 选题：lujun9972 译者：belitex 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
Docker 是一个开源项目，为开发人员和系统管理员提供了一个开放平台，可以将应用程序构建、打包为一个轻量级容器，并在任何地方运行。Docker 会在软件容器中自动部署应用程序。Django 是一个用 Python 编写的 Web 应用程序框架，遵循 MVC（模型-视图-控制器）架构。它是免费的，并在开源许可下发布。它速度很快，旨在帮助开发人员尽快将他们的应用程序上线。在本教程中，我将逐步向你展示在 Ubuntu 16.04 中如何为现有的 Django 应用程序创建 docker 镜像。我们将学习如何 docker 化一个 Python Django 应用程序，然后使用一个 docker-compose 脚本将应用程序作为容器部署到 docker 环境。为了部署我们的 Python Django 应用程序，我们需要其它 docker 镜像：一个用于 Web 服务器的 nginx docker 镜像和用于数据库的 PostgreSQL 镜像。我们要做什么？安装 Docker-ce安装 Docker-compose配置项目环境构建并运行测试步骤 1 - 安装 Docker-ce在本教程中，我们将从 docker 仓库安装 docker-ce 社区版。我们将安装 docker-ce 社区版和 docker-compose（其支持 compose 文件版本 3）。在安装 docker-ce 之前，先使用 apt 命令安装所需的 docker 依赖项。sudo apt install -y \ apt-transport-https \ ca-certificates \ curl \ software-properties-common现在通过运行以下命令添加 docker 密钥和仓库。curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable"更新仓库并安装 docker-ce。sudo apt update sudo apt install -y docker-ce安装完成后，启动 docker 服务并使其能够在每次系统引导时启动。systemctl start docker systemctl enable docker接着，我们将添加一个名为 omar 的新用户并将其添加到 docker 组。useradd -m -s /bin/bash omar usermod -a -G docker omar以 omar 用户身份登录并运行 docker 命令，如下所示。su - omar docker run hello-world确保你能从 Docker 获得 hello-world 消息。Docker-ce 安装已经完成。步骤 2 - 安装 Docker-compose在本教程中，我们将使用支持 compose 文件版本 3 的最新 docker-compose。我们将手动安装 docker-compose。使用 curl 命令将最新版本的 docker-compose 下载到 /usr/local/bin 目录，并使用 chmod 命令使其有执行权限。运行以下命令：sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose现在检查 docker-compose 版本。docker-compose version确保你安装的是最新版本的 docker-compose 1.21。已安装支持 compose 文件版本 3 的 docker-compose 最新版本。步骤 3 - 配置项目环境在这一步中，我们将配置 Python Django 项目环境。我们将创建新目录 guide01，并使其成为我们项目文件的主目录，例如包括 Dockerfile、Django 项目、nginx 配置文件等。登录到 omar 用户。su - omar创建一个新目录 guide01，并进入目录。mkdir -p guide01 cd guide01/现在在 guide01 目录下，创建两个新目录 project 和 config。mkdir project/ config/注意：project 目录：我们所有的 python Django 项目文件都将放在该目录中。config 目录：项目配置文件的目录，包括 nginx 配置文件、python pip 的requirements.txt 文件等。创建一个新的 requirements.txt 文件接下来，使用 vim 命令在 config 目录中创建一个新的 requirements.txt 文件。vim config/requirements.txt粘贴下面的配置：Django==2.0.4 gunicorn==19.7.0 psycopg2==2.7.4保存并退出。创建 Nginx 虚拟主机文件 django.conf在 config 目录下创建 nginx 配置目录并添加虚拟主机配置文件 django.conf。mkdir -p config/nginx/ vim config/nginx/django.conf粘贴下面的配置：upstream web { ip_hash; server web:8000; } # portal server { location / { proxy_pass http://web/; } listen 8000; server_name localhost; location /static { autoindex on; alias /src/static/; } }保存并退出。创建 Dockerfile在 guide01 目录下创建新文件 Dockerfile。运行以下命令：vim Dockerfile现在粘贴下面的 Dockerfile 脚本：FROM python:3.5-alpine ENV PYTHONUNBUFFERED 1 RUN apk update && \ apk add --virtual build-deps gcc python-dev musl-dev && \ apk add postgresql-dev bash RUN mkdir /config ADD /config/requirements.txt /config/ RUN pip install -r /config/requirements.txt RUN mkdir /src WORKDIR /src保存并退出。注意：我们想要为我们的 Django 项目构建基于 Alpine Linux 的 Docker 镜像，Alpine 是最小的 Linux 版本。我们的 Django 项目将运行在带有 Python 3.5 的 Alpine Linux 上，并添加 postgresql-dev 包以支持 PostgreSQL 数据库。然后，我们将使用 python pip 命令安装在 requirements.txt 上列出的所有 Python 包，并为我们的项目创建新目录 /src。创建 Docker-compose 脚本使用 vim 命令在 guide01 目录下创建 docker-compose.yml 文件。vim docker-compose.yml粘贴以下配置内容：version: '3' services: db: image: postgres:10.3-alpine container_name: postgres01 nginx: image: nginx:1.13-alpine container_name: nginx01 ports: - "8000:8000" volumes: - ./project:/src - ./config/nginx:/etc/nginx/conf.d depends_on: - web web: build: . container_name: django01 command: bash -c "python manage.py makemigrations && python manage.py migrate && python manage.py collectstatic --noinput && gunicorn hello_django.wsgi -b 0.0.0.0:8000" depends_on: - db volumes: - ./project:/src expose: - "8000" restart: always保存并退出。注意：使用这个 docker-compose 文件脚本，我们将创建三个服务。使用 alpine Linux 版的 PostgreSQL 创建名为 db 的数据库服务，再次使用 alpine Linux 版的 Nginx 创建 nginx 服务，并使用从 Dockerfile 生成的自定义 docker 镜像创建我们的 python Django 容器。配置 Django 项目将 Django 项目文件复制到 project 目录。cd ~/django cp -r * ~/guide01/project/进入 project 目录并编辑应用程序设置 settings.py。cd ~/guide01/project/ vim hello_django/settings.py注意：我们将部署名为 “hello\_django” 的简单 Django 应用程序。在 ALLOW_HOSTS 行中，添加服务名称 web。ALLOW_HOSTS = ['web']现在更改数据库设置，我们将使用 PostgreSQL 数据库来运行名为 db 的服务，使用默认用户和密码。DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'postgres', 'USER': 'postgres', 'HOST': 'db', 'PORT': 5432, } }至于 STATIC_ROOT 配置目录，将此行添加到文件行的末尾。STATIC_ROOT = os.path.join(BASE_DIR, 'static/')保存并退出。现在我们准备在 docker 容器下构建和运行 Django 项目。步骤 4 - 构建并运行 Docker 镜像在这一步中，我们想要使用 guide01 目录中的配置为我们的 Django 项目构建一个 Docker 镜像。进入 guide01 目录。cd ~/guide01/现在使用 docker-compose 命令构建 docker 镜像。docker-compose build启动 docker-compose 脚本中的所有服务。docker-compose up -d等待几分钟让 Docker 构建我们的 Python 镜像并下载 nginx 和 postgresql docker 镜像。完成后，使用以下命令检查运行容器并在系统上列出 docker 镜像。docker-compose ps docker-compose images现在，你将在系统上运行三个容器，列出 Docker 镜像，如下所示。我们的 Python Django 应用程序现在在 docker 容器内运行，并且已经创建了为我们服务的 docker 镜像。步骤 5 - 测试打开 Web 浏览器并使用端口 8000 键入服务器地址，我的是：http://ovh01:8000/。现在你将看到默认的 Django 主页。接下来，通过在 URL 上添加 /admin 路径来测试管理页面。http://ovh01:8000/admin/然后你将会看到 Django 管理登录页面。Docker 化 Python Django 应用程序已成功完成。参考https://docs.docker.com/via: https://www.howtoforge.com/tutorial/docker-guide-dockerizing-python-django-application/作者：Muhammad Arul 译者：MjSeven 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
学习怎么去使用 Python 的 web 框架中的对象关系映射与你的数据库交互，就像你使用 SQL 一样。你可能听说过 Django，它是一个被称为“完美主义者的最后期限” 的 Python web 框架。它是一匹 可爱的小矮马。Django 的一个强大的功能是它的 对象关系映射 Object-Relational Mapper （ORM），它允许你就像使用 SQL 一样去和你的数据库交互。事实上，Django 的 ORM 就是创建 SQL 去查询和操作数据库的一个 Python 式方式，并且获得 Python 风格的结果。 我说的是一种方式，但实际上，它是一种非常聪明的工程方法，它利用了 Python 中一些很复杂的部分，而使得开发者更加轻松。在我们开始去了解 ORM 是怎么工作之前，我们需要一个可以操作的数据库。和任何一个关系型数据库一样，我们需要去定义一堆表和它们的关系（即，它们相互之间联系起来的方式)。让我们使用我们熟悉的东西。比如说，我们需要去建模一个有博客文章和作者的博客。每个作者有一个名字。一位作者可以有很多的博客文章。一篇博客文章可以有很多的作者、标题、内容和发布日期。在 Django 村里，这个文章和作者的概念可以被称为博客应用。在这个语境中，一个应用是一个自包含一系列描述我们的博客行为和功能的模型和视图的集合。用正确的方式打包，以便于其它的 Django 项目可以使用我们的博客应用。在我们的项目中，博客正是其中的一个应用。比如，我们也可以有一个论坛应用。但是，我们仍然坚持我们的博客应用的原有范围。这是为这个教程事先准备的 models.py：from django.db import models class Author(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Post(models.Model): title = models.CharField(max_length=100) content = models.TextField() published_date = models.DateTimeField(blank=True, null=True) author = models.ManyToManyField(Author, related_name="posts") def __str__(self): return self.title 现在，看上去似乎有点令人恐惧，因此，我们把它分解来看。我们有两个模型：作者（Author）和文章（Post）。它们都有名字（name）或者标题（title）。文章有个放内容的大的文本字段，以及用于发布时间和日期的 DateTimeField。文章也有一个 ManyToManyField，它同时链接到文章和作者。大多数的教程都是从头开始的，但是，在实践中并不会发生这种情况。实际上，你会得到一堆已存在的代码，就像上面的 model.py 一样，而你必须去搞清楚它们是做什么的。因此，现在你的任务是去进入到应用程序中去了解它。做到这一点有几种方法，你可以登入到 Django admin，这是一个 Web 后端，它会列出全部的应用和操作它们的方法。我们先退出它，现在我们感兴趣的东西是 ORM。我们可以在 Django 项目的主目录中运行 python manage.py shell 去访问 ORM。/srv/web/django/ $ python manage.py shell Python 3.6.3 (default, Nov 9 2017, 15:58:30) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.38)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> 这将带我们进入到交互式控制台。shell 命令 为我们做了很多设置，包括导入我们的设置和配置 Django 环境。虽然我们启动了 shell，但是，在我们导入它之前，我们并不能访问我们的博客模型。>>> from blog.models import * 它导入了全部的博客模型，因此，我们可以玩我们的博客了。首先，我们列出所有的作者：>>> Author.objects.all() 我们将从这个命令取得结果，它是一个 QuerySet，它列出了我们所有的作者对象。它不会充满我们的整个控制台，因为，如果有很多查询结果，Django 将自动截断输出结果。>>> Author.objects.all() <QuerySet [<Author: VM (Vicky) Brasseur>, <Author: Rikki Endsley>, <Author: Jen Wike Huger>, '...(remaining elements truncated)...'] 我们可以使用 get 代替 all 去检索单个作者。但是，我们需要一些更多的信息才能 get 一个单个记录。在关系型数据库中，表有一个主键，它唯一标识了表中的每个记录，但是，作者名并不唯一。许多人都 重名，因此，它不是唯一约束的好选择。解决这个问题的一个方法是使用一个序列（1、2、3 ……）或者一个通用唯一标识符（UUID）作为主键。但是，因为它对人类并不好用，我们可以通过使用 name 来操作我们的作者对象。>>> Author.objects.get(name="VM (Vicky) Brasseur") <Author: VM (Vicky) Brasseur> 到现在为止，我们已经有了一个我们可以交互的对象，而不是一个 QuerySet 列表。我们现在可以与这个 Python 对象进行交互了，使用任意一个表列做为属性去查看对象。>>> vmb = Author.objects.get(name="VM (Vicky) Brasseur") >>> vmb.name u'VM (Vicky) Brasseur' 然后，很酷的事件发生了。通常在关系型数据库中，如果我们希望去展示其它表的信息，我们需要去写一个 LEFT JOIN，或者其它的表耦合函数，并确保它们之间有匹配的外键。而 Django 可以为我们做到这些。在我们的模型中，由于作者写了很多的文章，因此，我们的作者对象可以检索他自己的文章。>>> vmb.posts.all() QuerySet[<Post: "7 tips for nailing your job interview">, <Post: "5 tips for getting the biggest bang for your cover letter buck">, <Post: "Quit making these 10 common resume mistakes">, '...(remaining elements truncated)...'] 我们可以使用正常的 Python 式的列表操作方式来操作 QuerySets。>>> for post in vmb.posts.all(): ... print(post.title) ... 7 tips for nailing your job interview 5 tips for getting the biggest bang for your cover letter buck Quit making these 10 common resume mistakes 要实现更复杂的查询，我们可以使用过滤得到我们想要的内容。这有点复杂。在 SQL 中，你可以有一些选项，比如，like、contains 和其它的过滤对象。在 ORM 中这些事情也可以做到。但是，是通过 特别的 方式实现的：是通过使用一个隐式（而不是显式）定义的函数实现的。如果在我的 Python 脚本中调用了一个函数 do_thing()，我会期望在某个地方有一个匹配的 def do_thing。这是一个显式的函数定义。然而，在 ORM 中，你可以调用一个 不显式定义的 函数。之前，我们使用 name 去匹配一个名字。但是，如果我们想做一个子串搜索，我们可以使用 name__contains。>>> Author.objects.filter(name__contains="Vic") QuerySet[<Author: VM (Vicky) Brasseur>, <Author: Victor Hugo">] 现在，关于双下划线（__）我有一个小小的提示。这些是 Python 特有的。在 Python 的世界里，你可以看到如 __main__ 或者 __repr__。这些有时被称为 dunder methods，是 “ 双下划线 double underscore ” 的缩写。仅有几个非字母数字的字符可以被用于 Python 中的对象名字；下划线是其中的一个。这些在 ORM 中被用于显式分隔 过滤关键字 filter key name 的各个部分。在底层，字符串用这些下划线分割开，然后这些标记分开处理。name__contains 被替换成 attribute: name, filter: contains。在其它编程语言中，你可以使用箭头代替，比如，在 PHP 中是 name->contains。不要被双下划线吓着你，正好相反，它们是 Python 的好帮手（并且如果你斜着看，你就会发现它看起来像一条小蛇，想去帮你写代码的小蟒蛇）。ORM 是非常强大并且是 Python 特有的。不过，还记得我在上面提到过的 Django 的管理网站吗？Django 的其中一个非常精彩的用户可访问特性是它的管理界面，如果你定义你的模型，你将看到一个非常好用的基于 web 的编辑门户，而且它是免费的。ORM，有多强大？好吧！给你一些代码去创建最初的模型，Django 就变成了一个基于 web 的门户，它是非常强大的，它可以使用我们前面用过的同样的原生函数。默认情况下，这个管理门户只有基本的东西，但这只是在你的模型中添加一些定义去改变外观的问题。例如，在早期的这些 __str__ 方法中，我们使用这些去定义作者对象应该有什么？（在这种情况中，比如，作者的名字），做了一些工作后，你可以创建一个界面，让它看起来像一个内容管理系统，以允许你的用户去编辑他们的内容。（例如，为一个标记为 “已发布” 的文章，增加一些输入框和过滤）。如果你想去了解更多内容，Django 美女的教程 中关于 the ORM 的节有详细的介绍。在 Django project website 上也有丰富的文档。（题图 Christian Holmér，Opensource.com 修改. CC BY-SA 4.0）作者简介：Katie McLaughlin - Katie 在过去的这几年有许多不同的头衔，她以前是使用多种语言的一位软件开发人员，多种操作系统的系统管理员，和多个不同话题的演讲者。当她不改变 “世界” 的时候，她也去享受烹饪、挂毯艺术，和去研究各种应用程序栈怎么去处理 emoji。via: https://opensource.com/article/17/11/django-orm作者：Katie McLaughlin 译者：qhwdw 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
在 Linux 上使用 Nginx 和 Gunicorn 托管 Django 应用
介绍托管 Django Web 应用程序相当简单，虽然它比标准的 PHP 应用程序更复杂一些。 让 Web 服务器对接 Django 的方法有很多。 Gunicorn 就是其中最简单的一个。Gunicorn（Green Unicorn 的缩写）在你的 Web 服务器 Django 之间作为中间服务器使用，在这里，Web 服务器就是 Nginx。 Gunicorn 服务于应用程序，而 Nginx 处理静态内容。Gunicorn安装使用 Pip 安装 Gunicorn 是超级简单的。 如果你已经使用 virtualenv 搭建好了你的 Django 项目，那么你就有了 Pip，并且应该熟悉 Pip 的工作方式。 所以，在你的 virtualenv 中安装 Gunicorn。$ pip install gunicorn 配置Gunicorn 最有吸引力的一个地方就是它的配置非常简单。处理配置最好的方法就是在 Django 项目的根目录下创建一个名叫 Gunicorn 的文件夹。然后在该文件夹内，创建一个配置文件。在本篇教程中，配置文件名称是 gunicorn-conf.py。在该文件中，创建类似于下面的配置：import multiprocessing bind = 'unix:///tmp/gunicorn1.sock' workers = multiprocessing.cpu_count() * 2 + 1 reload = True daemon = True 在上述配置的情况下，Gunicorn 会在 /tmp/ 目录下创建一个名为 gunicorn1.sock 的 Unix 套接字。 还会启动一些工作进程，进程数量相当于 CPU 内核数量的 2 倍。 它还会自动重新加载并作为守护进程运行。运行Gunicorn 的运行命令有点长，指定了一些附加的配置项。 最重要的部分是将 Gunicorn 指向你项目的 .wsgi 文件。gunicorn -c gunicorn/gunicorn-conf.py -D --error-logfile gunicorn/error.log yourproject.wsgi 上面的命令应该从项目的根目录运行。 -c 选项告诉 Gunicorn 使用你创建的配置文件。 -D 再次指定 gunicorn 为守护进程。 最后一部分指定 Gunicorn 的错误日志文件在你创建 Gunicorn 文件夹中的位置。 命令结束部分就是为 Gunicorn 指定 .wsgi 文件的位置。Nginx现在 Gunicorn 配置好了并且已经开始运行了，你可以设置 Nginx 连接它，为你的静态文件提供服务。 本指南假定你已经配置好了 Nginx，而且你通过它托管的站点使用了单独的 server 块。 它还将包括一些 SSL 信息。如果你想知道如何让你的网站获得免费的 SSL 证书，请查看我们的 Let'sEncrypt 指南。# 连接到 Gunicorn upstream yourproject-gunicorn { server unix:/tmp/gunicorn1.sock fail_timeout=0; } # 将未加密的流量重定向到加密的网站 server { listen 80; server_name yourwebsite.com; return 301 https://yourwebsite.com$request_uri; } # 主服务块 server { # 设置监听的端口，指定监听的域名 listen 443 default ssl; client_max_body_size 4G; server_name yourwebsite.com; # 指定日志位置 access_log /var/log/nginx/yourwebsite.access_log main; error_log /var/log/nginx/yourwebsite.error_log info; # 告诉 nginx 你的 ssl 证书 ssl on; ssl_certificate /etc/letsencrypt/live/yourwebsite.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/yourwebsite.com/privkey.pem; # 设置根目录 root /var/www/yourvirtualenv/yourproject; # 为 Nginx 指定静态文件路径 location /static/ { # Autoindex the files to make them browsable if you want autoindex on; # The location of your files alias /var/www/yourvirtualenv/yourproject/static/; # Set up caching for your static files expires 1M; access_log off; add_header Cache-Control "public"; proxy_ignore_headers "Set-Cookie"; } # 为 Nginx 指定你上传文件的路径 location /media/ { Autoindex if you want autoindex on; # The location of your uploaded files alias /var/www/yourvirtualenv/yourproject/media/; # Set up aching for your uploaded files expires 1M; access_log off; add_header Cache-Control "public"; proxy_ignore_headers "Set-Cookie"; } location / { # Try your static files first, then redirect to Gunicorn try_files $uri @proxy_to_app; } # 将请求传递给 Gunicorn location @proxy_to_app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://njc-gunicorn; } # 缓存 HTML、XML 和 JSON location ~* \.(html?|xml|json)$ { expires 1h; } # 缓存所有其他的静态资源 location ~* \.(jpg|jpeg|png|gif|ico|css|js|ttf|woff2)$ { expires 1M; access_log off; add_header Cache-Control "public"; proxy_ignore_headers "Set-Cookie"; } } 配置文件有点长，但是还可以更长一些。其中重点是指向 Gunicorn 的 upstream 块以及将流量传递给 Gunicorn 的 location 块。大多数其他的配置项都是可选，但是你应该按照一定的形式来配置。配置中的注释应该可以帮助你了解具体细节。保存文件之后，你可以重启 Nginx，让修改的配置生效。# systemctl restart nginx 一旦 Nginx 在线生效，你的站点就可以通过域名访问了。结语如果你想深入研究，Nginx 可以做很多事情。但是，上面提供的配置是一个很好的开始，并且你可以用于实践中。 如果你见惯了 Apache 和臃肿的 PHP 应用程序，像这样的服务器配置的速度应该是一个惊喜。via: https://linuxconfig.org/hosting-django-with-nginx-and-gunicorn-on-linux作者：Nick Congleton 译者：Flowsnow 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
Instagram 基于 Python 语言的 Web Service 效率提升之道
Instagram 目前部署了世界上最大规模的 Django Web 框架（该框架完全使用 Python 编写）。我们最初选用 Python 是因为它久负盛名的简洁性与实用性，这非常符合我们的哲学思想——“先做简单的事情”。但简洁性也会带来效率方面的折衷。Instagram 的规模在过去两年中已经翻番，并且最近已突破 5 亿用户，所以急需最大程度地提升 web 服务效率以便我们的平台能够继续顺利地扩大。在过去的一年，我们已经将 效率计划 （ efficiency program ） 提上日程，并在过去的六个月，我们已经能够做到无需向我们的 Django 层 （ Django tiers ） 添加新的容量来维持我们的用户增长。我们将在本文分享一些由我们构建的工具以及如何使用它们来优化我们的日常部署流程。为何需要提升效率？Instagram，正如所有的软件，受限于像服务器和数据中心能源这样的物理限制。鉴于这些限制，在我们的效率计划中有两个我们希望实现的主要目标：Instagram 应当能够利用持续代码发布正常地提供通信服务，防止因为自然灾害、区域性网络问题等造成某一个数据中心区丢失。Instagram 应当能够自由地滚动发布新产品和新功能，不必因容量而受阻。想要实现这些目标，我们意识到我们需要持续不断地监控我们的系统并与 回归 （ regressions ） 进行战斗。定义效率Web services 的瓶颈通常在于每台服务器上可用的 CPU 时间。在这种环境下，效率就意味着利用相同的 CPU 资源完成更多的任务，也就是说， 每秒处理更多的用户请求 （ requests per second，RPS ） 。当我们寻找优化方法时，我们面临的第一个最大的挑战就是尝试量化我们当前的效率。到目前为止，我们一直在使用“每次请求的平均 CPU 时间”来评估效率，但使用这种指标也有其固有限制：设备多样性。使用 CPU 时间来测量 CPU 资源并非理想方案，因为它同时受到 CPU 型号与 CPU 负载的影响。请求影响数据。测量每次请求的 CPU 资源并非理想方案，因为在使用 每次请求测量 （ per-request measurement ） 方案时，添加或移除轻量级或重量级的请求也会影响到效率指标。相对于 CPU 时间来说，CPU 指令是一种更好的指标，因为对于相同的请求，它会报告相同的数字，不管 CPU 型号和 CPU 负载情况如何。我们选择使用了一种叫做” 每个活动用户 （ per active user ） “的指标，而不是将我们所有的数据关联到每个用户请求上。我们最终采用“ 每个活动用户在高峰期间的 CPU 指令 （ CPU instruction per active user during peak minute ） ”来测量效率。我们建立好新的度量标准后，下一步就是通过对 Django 的分析来更多的了解一下我们的回归。Django web services 分析通过分析我们的 Django web services，我们希望回答两个主要问题：CPU 回归会发生吗？是什么导致了 CPU 回归发生以及我们该怎样修复它？想要回答第一个问题，我们需要追踪“ 每个活动用户的 CPU 指令 （ CPU-instruction-per-active-user ） ”指标。如果该指标增加，我们就知道已经发生了一次 CPU 回归。我们为此构建的工具叫做 Dynostats。Dynostats 利用 Django 中间件以一定的速率采样用户请求，记录关键的效率以及性能指标，例如 CPU 总指令数、端到端请求时延、花费在访问内存缓存（memcache）和数据库服务的时间等。另一方面，每个请求都有很多可用于聚合的 元数据 （ metadata ） ，例如端点名称、HTTP 请求返回码、服务该请求的服务器名称以及请求中最新提交的 哈希值 （ hash ） 。对于单个请求记录来说，有两个方面非常强大，因为我们可以在不同的维度上进行切割，那将帮助我们减少任何导致 CPU 回归的原因。例如，我们可以根据它们的端点名称聚合所有请求，正如下面的时间序列图所示，从图中可以清晰地看出在特定端点上是否发生了回归。CPU 指令对测量效率很重要——当然，它们也很难获得。Python 并没有支持直接访问 CPU 硬件计数器（CPU 硬件计数器是指可编程 CPU 寄存器，用于测量性能指标，例如 CPU 指令）的公共库。另一方面，Linux 内核提供了 perf_event_open 系统调用。通过 Python ctypes 桥接技术能够让我们调用标准 C 库的系统调用函数 syscall，它也为我们提供了兼容 C 的数据类型，从而可以编程硬件计数器并从它们读取数据。使用 Dynostats，我们已经可以找出 CPU 回归，并探究 CPU 回归发生的原因，例如哪个端点受到的影响最多，谁提交了真正会导致 CPU 回归的变更等。然而，当开发者收到他们的变更已经导致一次 CPU 回归发生的通知时，他们通常难以找出问题所在。如果问题很明显，那么回归可能就不会一开始就被提交！这就是为何我们需要一个 Python 分析器，（一旦 Dynostats 发现了它）从而使开发者能够使用它找出回归发生的根本原因。不同于白手起家，我们决定对一个现成的 Python 分析器 cProfile 做适当的修改。cProfile 模块通常会提供一个统计集合来描述程序不同的部分执行时间和执行频率。我们将 cProfile 的 定时器 （ timer ） 替换成了一个从硬件计数器读取的 CPU 指令计数器，以此取代对时间的测量。我们在采样请求后产生数据并把数据发送到数据流水线。我们也会发送一些我们在 Dynostats 所拥有的类似元数据，例如服务器名称、集群、区域、端点名称等。在数据流水线的另一边，我们创建了一个消费数据的 尾随者 （ tailer ） 。尾随者的主要功能是解析 cProfile 的统计数据并创建能够表示 Python 函数级别的 CPU 指令的实体。如此，我们能够通过 Python 函数来聚合 CPU 指令，从而更加方便地找出是什么函数导致了 CPU 回归。监控与警报机制在 Instagram，我们每天部署 30-50 次后端服务。这些部署中的任何一个都能发生 CPU 回归的问题。因为每次发生通常都包含至少一个 差异 （ diff ） ，所以找出任何回归是很容易的。我们的效率监控机制包括在每次发布前后都会在 Dynostats 中扫描 CPU 指令，并且当变更超出某个阈值时发出警告。对于长期会发生 CPU 回归的情况，我们也有一个探测器为负载最繁重的端点提供日常和每周的变更扫描。部署新的变更并非触发一次 CPU 回归的唯一情况。在许多情况下，新的功能和新的代码路径都由 全局环境变量 （ global environment variables，GEV ） 控制。 在一个计划好的时间表上，给一部分用户发布新功能是很常见事情。我们在 Dynostats 和 cProfile 统计数据中为每个请求添加了这个信息作为额外的元数据字段。按这些字段将请求分组可以找出由全局环境变量（GEV）改变导致的可能的 CPU 回归问题。这让我们能够在它们对性能造成影响前就捕获到 CPU 回归。接下来是什么？Dynostats 和我们定制的 cProfile，以及我们建立的支持它们的监控和警报机制能够有效地找出大多数导致 CPU 回归的元凶。这些进展已经帮助我们恢复了超过 50% 的不必要的 CPU 回归，否则我们就根本不会知道。我们仍然还有一些可以提升的方面，并很容易将它们地加入到 Instagram 的日常部署流程中：CPU 指令指标应该要比其它指标如 CPU 时间更加稳定，但我们仍然观察了让我们头疼的差异。保持“ 信噪比 （ signal:noise ratio ） ”合理地低是非常重要的，这样开发者们就可以集中于真实的回归上。这可以通过引入 置信区间 （ confidence intervals ） 的概念来提升，并在信噪比过高时发出警报。针对不同的端点，变化的阈值也可以设置为不同值。通过更改 GEV 来探测 CPU 回归的一个限制就是我们要在 Dynostats 中手动启用这些比较的日志输出。当 GEV 的数量逐渐增加，开发了越来越多的功能，这就不便于扩展了。相反，我们能够利用一个自动化框架来调度这些比较的日志输出，并对所有的 GEV 进行遍历，然后当检查到回归时就发出警告。cProfile 需要一些增强以便更好地处理封装函数以及它们的子函数。鉴于我们在为 Instagram 的 web service 构建效率框架中所投入的工作，所以我们对于将来使用 Python 继续扩展我们的服务很有信心。我们也开始向 Python 语言本身投入更多，并且开始探索从 Python 2 转移 Python 3 之道。我们将会继续探索并做更多的实验以继续提升基础设施与开发者效率，我们期待着很快能够分享更多的经验。本文作者 Min Ni 是 Instagram 的软件工程师。（题图来自：nostarch.com）via: https://engineering.instagram.com/web-service-efficiency-at-instagram-with-python-4976d078e366#.tiakuoi4p作者：Min Ni 译者：ChrisLeeGit 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出
Django 围绕“可重用应用”的思想建立：自包含的包提供了可重复使用的特性。你可以将这些可重用应用组装起来，在加上适用于你的网站的特定代码，来搭建你自己的网站。Django 具有一个丰富多样的、由可供你使用的可重用应用组建起来的生态系统——PyPI 列出了超过 8000个 Django 应用——可你该如何知道哪些是最好的呢？为了节省你的时间，我们总结了五个最受喜爱的 Django 应用。它们是：Cookiecutter: 建立 Django 网站的最佳方式。Whitenoise: 最棒的静态资源服务器。Django Rest Framework: 使用 Django 开发 REST API 的最佳方式。Wagtail: 基于 Django 的最佳内容管理系统（CMS）。django-allauth: 提供社交账户登录的最佳应用（如 Twitter, Facebook, GitHub 等）。我们同样推荐你看看 Django Packages，这是一个可重用 Django 应用的目录。Django Packages 将 Django 应用组织成“表格”，你可以在功能相似的不同应用之间进行比较并做出选择。你可以查看每个包中提供的特性和使用统计情况。（比如：这是 REST 工具的表格，也许可以帮助你理解我们为何推荐 Django REST Framework。为什么你应该相信我们？我们使用 Django 的时间几乎比任何人都长。在 Django 发布之前，我们当中的两个人（Frank 和 Jacob）就在 Lawrence Journal-World （Django 的发源地）工作（事实上，是他们两人推动了 Django 开源发布的进程）。我们在过去的八年当中运行着一个咨询公司，来建议公司怎样最好地应用 Django。所以，我们见证了 Django 项目和社群的完整历史，我们见证了那些流行的软件包的兴起和没落。在我们三个之中，我们个人可能试用了 8000 个应用中至少一半以上，或者我们知道谁试用过这些。我们对如何使应用变得坚实可靠有着深刻的理解，并且我们对给予这些应用持久力量的来源也有着深入的了解。建立 Django 网站的最佳方式：Cookiecutter建立一个新项目或应用总是有些痛苦。你可以用 Django 内建的 startproject。不过，如果你像我们一样，对如何做事比较挑剔。Cookiecutter 为你提供了一个快捷简单的方式来构建项目或易于重用的应用模板，从而解决了这个问题。一个简单的例子：键入 pip install cookiecutter，然后在命令行中运行以下命令：$ cookiecutter https://github.com/marcofucci/cookiecutter-simple-django 接下来你需要回答几个简单的问题，比如你的项目名称、 目录 （ repo ） 、作者名字、E-Mail 和其他几个关于配置的小问题。这些能够帮你补充项目相关的细节。我们使用最最原始的 “foo” 作为我们的目录名称。所以 cokkiecutter 在子目录 “foo” 下建立了一个简单的 Django 项目。如果你在 “foo” 项目中闲逛，你会看见你刚刚选择的其它设置已通过模板，连同所需的子目录一同嵌入到文件当中。这个“模板”在我们刚刚在执行 cookiecutter 命令时输入的唯一一个参数 Github 仓库 URL 中定义。这个样例工程使用了一个 Github 远程仓库作为模板；不过你也可以使用本地的模板，这在建立非重用项目时非常有用。我们认为 cookiecutter 是一个极棒的 Django 包，但是，事实上其实它在面对纯 Python 甚至非 Python 相关需求时也极为有用。你能够将所有文件以一种可重复的方式精确地摆放在任何位置上，使得 cookiecutter 成为了一个简化（DRY）工作流程的极佳工具。最棒的静态资源服务器：Whitenoise多年来，托管网站的静态资源——图片、Javascript、CSS——都是一件很痛苦的事情。Django 内建的 django.views.static.serve 视图，就像 Django 文章所述的那样，“在生产环境中不可靠，所以只应为开发环境的提供辅助功能。”但使用一个“真正的” Web 服务器，如 NGINX 或者借助 CDN 来托管媒体资源，配置起来会比较困难。Whitenoice 很简洁地解决了这个问题。它可以像在开发环境那样轻易地在生产环境中设置静态服务器，并且针对生产环境进行了加固和优化。它的设置方法极为简单：确保你在使用 Django 的 contrib.staticfiles 应用，并确认你在配置文件中正确设置了 STATIC_ROOT 变量。在 wsgi.py 文件中启用 Whitenoise:from django.core.wsgi import get_wsgi_application from whitenoise.django import DjangoWhiteNoise application = get_wsgi_application() application = DjangoWhiteNoise(application) 配置它真的就这么简单！对于大型应用，你可能想要使用一个专用的媒体服务器和/或一个 CDN，但对于大多数小型或中型 Django 网站，Whitenoise 已经足够强大。如需查看更多关于 Whitenoise 的信息，请查看文档。开发 REST API 的最佳工具：Django REST FrameworkREST API 正在迅速成为现代 Web 应用的标准功能。 API 就是简单的使用 JSON 对话而不是 HTML，当然你可以只用 Django 做到这些。你可以制作自己的视图，设置合适的 Content-Type，然后返回 JSON 而不是渲染后的 HTML 响应。这是在像 Django Rest Framework（下称 DRF）这样的 API 框架发布之前，大多数人所做的。如果你对 Django 的视图类很熟悉，你会觉得使用 DRF 构建 REST API 与使用它们很相似，不过 DRF 只针对特定 API 使用场景而设计。一般的 API 设置只需要一点代码，所以我们没有提供一份让你兴奋的示例代码，而是强调了一些可以让你生活的更舒适的 DRF 特性：可自动预览的 API 可以使你的开发和人工测试轻而易举。你可以查看 DRF 的示例代码。你可以查看 API 响应，并且不需要你做任何事就可以支持 POST/PUT/DELETE 类型的操作。便于集成各种认证方式，如 OAuth, Basic Auth, 或API Tokens。内建请求速率限制。当与 django-rest-swagger 组合使用时，API 文档几乎可以自动生成。广泛的第三方库生态。当然，你可以不依赖 DRF 来构建 API，但我们无法想象你不去使用 DRF 的原因。就算你不使用 DRF 的全部特性，使用一个成熟的视图库来构建你自己的 API 也会使你的 API 更加一致、完全，更能提高你的开发速度。如果你还没有开始使用 DRF, 你应该找点时间去体验一下。基于 Django 的最佳 CMS：WagtailWagtail 是当下 Django CMS（内容管理系统）世界中最受人青睐的应用，并且它的热门有足够的理由。就像大多数的 CMS 一样，它具有极佳的灵活性，可以通过简单的 Django 模型来定义不同类型的页面及其内容。使用它，你可以从零开始在几个小时而不是几天之内来和建造一个基本可以运行的内容管理系统。举一个小例子，为你公司的员工定义一个员工页面类型可以像下面一样简单：from wagtail.wagtailcore.models import Page from wagtail.wagtailcore.fields import RichTextField from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel from wagtail.wagtailimages.edit_handlers import ImageChooserPanel class StaffPage(Page): name = models.CharField(max_length=100) hire_date = models.DateField() bio = models.RichTextField() email = models.EmailField() headshot = models.ForeignKey('wagtailimages.Image', null=True, blank=True) content_panels = Page.content_panels + [ FieldPanel('name'), FieldPanel('hire_date'), FieldPanel('email'), FieldPanel('bio',classname="full"), ImageChoosePanel('headshot'), ] 然而，Wagtail 真正出彩的地方在于它的灵活性及其易于使用的现代化管理页面。你可以控制不同类型的页面在哪网站的哪些区域可以访问，为页面添加复杂的附加逻辑，还天生就支持标准的适应/审批工作流。在大多数 CMS 系统中，你会在开发时在某些点上遇到困难。而使用 Wagtail 时，我们经过不懈努力找到了一个突破口，使得让我们轻易地开发出一套简洁稳定的系统，使得程序完全依照我们的想法运行。如果你对此感兴趣，我们写了一篇[深入理解 Wagtail][17。提供社交账户登录的最佳工具：django-allauthdjango-allauth 是一个能够解决你的注册和认证需求的、可重用的 Django 应用。无论你需要构建本地注册系统还是社交账户注册系统，django-allauth 都能够帮你做到。这个应用支持多种认证体系，比如用户名或电子邮件。一旦用户注册成功，它还可以提供从无需认证到电子邮件认证的多种账户验证的策略。同时，它也支持多种社交账户和电子邮件账户。它还支持插拔式注册表单，可让用户在注册时回答一些附加问题。django-allauth 支持多于 20 种认证提供者，包括 Facebook、Github、Google 和 Twitter。如果你发现了一个它不支持的社交网站，很有可能通过第三方插件提供该网站的接入支持。这个项目还支持自定义后端，可以支持自定义的认证方式，对每个有定制认证需求的人来说这都很棒。django-allauth 易于配置，且有完善的文档。该项目通过了很多测试，所以你可以相信它的所有部件都会正常运作。你有最喜爱的 Django 包吗？请在评论中告诉我们。关于作者Jeff Triplett 劳伦斯，堪萨斯州 http://www.jefftriplett.com/我在 2007 年搬到了堪萨斯州的劳伦斯，在 Django 的发源地—— Lawrence Journal-World 工作。我现在在劳伦斯市的 Revolution Systems (Revsys) 工作，做一位开发者兼顾问。我是北美 Django 运动基金会(DEFNA)的联合创始人，2015 和 2016 年 DjangoCon US 的会议主席，而且我在 Django 的发源地劳伦斯参与组织了 Django Birthday 来庆祝 Django 的 10 岁生日。我是当地越野跑小组的成员，我喜欢篮球，我还喜欢梦见自己随着一道气流游遍美国。Jacob Kaplan-Moss 和 Frank Wiles 也参与了本文的写作。via: https://opensource.com/business/15/12/5-favorite-open-source-django-packages作者：Jeff Triplett 译者：StdioA 校对：wxy本文由 LCTT 原创编译，Linux中国 荣誉推出