进阶指南:如何编写可重用程序

这篇进阶指南从 教程 7 结尾的地方继续讲起。我们将会把我们的 Web-poll 放进一个独立的 Python 包中,以便你在新的项目中重用它或将它与他人分享。

如果你尚未完成教程 1-7,我们推荐你先浏览一遍教程,这样你的样例工程会和下面的一致。

可重用性很重要

设计,构建,测试以及维护一个 web 应用要做很多的工作。很多 Python 以及 Django 项目都有一些常见问题。如果我们能保存并利用这些重复的工作岂不是更好?

可重用性是 Python 的根本。The Python Package Index (PyPI) 有许大量的包,都可被用在你自己的 Python 项目中。同样可以在 Django Packages 中查找已发布的可重用应用,也可将其引入到你的项目中。Django 本身也是一个 Python 包,也就是说你可以将已有的 Python 包或 Django 应用并入你的项目。你只需要编写属于你的那部分即可。

假设你现在创建了一个新的项目,并且需要一个类似我们之前做的投票应用。你该如何复用这个应用呢?庆幸的是,其实你已经知道了一些。在 教程 1,我们使用过 include 从项目级别的 URLconf 分割出 polls。在本教程中,我们将进一步使这个应用易用于新的项目中,并发布给其他人安装使用。

包?应用?

一个 package 提供了一组关联的 Python 代码的简单复用方式。一个包(“模块”)包含了一个或多个 Python 代码文件。

一个包通过 import foo.barfrom foo import bar 的形式导入。一个目录(例如 polls)要成为一个包,它必须包含一个特定的文件 __init__.py,即便这个文件是空的。

Django 应用 仅仅是专用于 Django 项目的 Python 包。应用会按照 Django 规则,创建好 models, tests, urls, 以及 views 等子模块。

稍后,我们将解释术语 打包 ——为了方便其它人安装 Python 包的处理流程。我知道,这可能会使你感到一点点迷惑。

你的项目和可复用应用

通过前面的教程,我们的工程应该看起来像这样:

  1. mysite/
  2. manage.py
  3. mysite/
  4. __init__.py
  5. settings.py
  6. urls.py
  7. asgi.py
  8. wsgi.py
  9. polls/
  10. __init__.py
  11. admin.py
  12. apps.py
  13. migrations/
  14. __init__.py
  15. 0001_initial.py
  16. models.py
  17. static/
  18. polls/
  19. images/
  20. background.gif
  21. style.css
  22. templates/
  23. polls/
  24. detail.html
  25. index.html
  26. results.html
  27. tests.py
  28. urls.py
  29. views.py
  30. templates/
  31. admin/
  32. base_site.html

你在 教程 7 中创建了 mysite/templates,在 教程 3 中创建了 polls/templates。现在也许更清楚为什么我们选择为项目和应用程序设置单独的模板目录:所有属于 polls 应用程序的部分都在 polls 中。这使得应用程序自成一体,更容易放到一个新项目中。

目录 polls 现在可以被拷贝至一个新的 Django 工程,且立刻被复用。不过现在还不是发布它的时候。为了这样做,我们需要打包这个应用,便于其他人安装它。

安装必须环境

目前,打包 Python 程序需要工具,有许多工具可以完成此项工作。在此教程中,我们将使用 setuptools 来打包我们的程序。这是推荐的打包工具(与 发布 分支合并)。我们仍旧使用 pip 来安装和卸载这个工具。现在,你需要安装这两个包。如果你需要帮助,你可以参考 如何通过 pip 安装 Django,你可以通过相同的方式安装 setuptools

打包你的应用

Python 的 打包 将以一种特殊的格式组织你的应用,意在方便安装和使用这个应用。Django 本身就被打包成类似的形式。对于一个小应用,例如 polls,这不会太难。

  1. 首先,在你的 Django 项目目录外创建一个名为 django-polls 的文件夹,用于盛放 polls

    为你的应用选择一个名字

    当为你的包选一个名字时,避免使用像 PyPI 这样已存在的包名,否则会导致冲突。当你创建你的发布包时,可以在模块名前增加 django- 前缀,这是一个很常用也很有用的避免包名冲突的方法。同时也有助于他人在寻找 Django 应用时确认你的 app 是 Django 独有的。

    应用标签(指用点分隔的包名的最后一部分)在 INSTALLED_APPS必须 是独一无二的。避免使用任何与 Django contrib packages 文档中相同的标签名,比如 authadminmessages

  2. polls 目录移入 django-polls 目录。

  3. 创建一个名为 django-polls/README.rst 的文件,包含以下内容:

    django-polls/README.rst

    1. =====
    2. Polls
    3. =====
    4. Polls is a Django app to conduct Web-based polls. For each question,
    5. visitors can choose between a fixed number of answers.
    6. Detailed documentation is in the "docs" directory.
    7. Quick start
    8. -----------
    9. 1. Add "polls" to your INSTALLED_APPS setting like this::
    10. INSTALLED_APPS = [
    11. ...
    12. 'polls',
    13. ]
    14. 2. Include the polls URLconf in your project urls.py like this::
    15. path('polls/', include('polls.urls')),
    16. 3. Run ``python manage.py migrate`` to create the polls models.
    17. 4. Start the development server and visit http://127.0.0.1:8000/admin/
    18. to create a poll (you'll need the Admin app enabled).
    19. 5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.
  4. 创建一个 django-polls/LICENSE 文件。选择一个非本教程使用的授权协议,但是要足以说明发布代码没有授权证书是 不可能的 。Django 和很多兼容 Django 的应用是以 BSD 授权协议发布的;不过,你可以自己选择一个授权协议。只要确定你选择的协议能够限制未来会使用你的代码的人。

  5. 下一步我们将创建 setup.cfgsetup.py 文件用于说明如何构建和安装应用的细节。关于此文件的完整介绍超出了此教程的范围,但是 setuptools docs 有详细的介绍。创建文件 django-polls/setup.py 包含以下内容:

    django-polls/setup.cfg

    1. [metadata]
    2. name = django-polls
    3. version = 0.1
    4. description = A Django app to conduct Web-based polls.
    5. long_description = file: README.rst
    6. url = https://www.example.com/
    7. author = Your Name
    8. author_email = yourname@example.com
    9. license = BSD-3-Clause # Example license
    10. classifiers =
    11. Environment :: Web Environment
    12. Framework :: Django
    13. Framework :: Django :: X.Y # Replace "X.Y" as appropriate
    14. Intended Audience :: Developers
    15. License :: OSI Approved :: BSD License
    16. Operating System :: OS Independent
    17. Programming Language :: Python
    18. Programming Language :: Python :: 3
    19. Programming Language :: Python :: 3 :: Only
    20. Programming Language :: Python :: 3.6
    21. Programming Language :: Python :: 3.7
    22. Programming Language :: Python :: 3.8
    23. Topic :: Internet :: WWW/HTTP
    24. Topic :: Internet :: WWW/HTTP :: Dynamic Content
    25. [options]
    26. include_package_data = true
    27. packages = find:
    28. python_requires = >=3.6
    29. install_requires =
    30. Django >= X.Y # Replace "X.Y" as appropriate

    django-polls/setup.py

    1. from setuptools import setup
    2. setup()
  6. 默认包中只包含 Python 模块和包。为了包含额外文件,我们需要创建一个名为 MANIFEST.in 的文件。上一步中关于 setuptools 的文档详细介绍了这个文件。为了包含模板、README.rst 和我们的 LICENSE 文件,创建文件 django-polls/MANIFEST.in 包含以下内容:

    django-polls/MANIFEST.in

    1. include LICENSE
    2. include README.rst
    3. recursive-include polls/static *
    4. recursive-include polls/templates *
  7. 在应用中包含详细文档是可选的,但我们推荐你这样做。创建一个空目录 django-polls/docs 用于未来编写文档。额外添加一行至 django-polls/MANIFEST.in

    1. recursive-include docs *

    注意,现在 docs 目录不会被加入你的应用包,除非你往这个目录加几个文件。许多 Django 应用也提供他们的在线文档通过类似 readthedocs.org 这样的网站。

  8. 试着构建你自己的应用包通过 ptyhon setup.py sdist (在 django-polls``目录内)。这将创建一个名为 ``dist 的目录并构建你自己的应用包, django-polls-0.1.tar.gz

更多关于打包的信息,见 Python 的 关于打包和发布项目的教程

使用你自己的包名

由于我们把 polls 目录移出了项目,所以它无法工作了。我们现在要通过安装我们的新 django-polls 应用来修复这个问题。

作为用户库安装

以下步骤将 django-polls 以用户库的形式安装。与安装整个系统的软件包相比,用户安装具有许多优点,例如可在没有管理员访问权的系统上使用,以及防止应用包影响系统服务和其他用户。

请注意,按用户安装仍然会影响以该用户身份运行的系统工具的行为,因此使用虚拟环境是更可靠的解决方案(请参见下文)。

  1. 为了安装这个包,使用 pip (你早已 安装 pip, 对吗?):

    1. python -m pip install --user django-polls/dist/django-polls-0.1.tar.gz
  2. 幸运的话,你的 Django 项目应该再一次正确运行。启动服务器确认这一点。

  3. 通过 pip 卸载包:

    1. python -m pip uninstall django-polls

发布你的应用

现在,你已经对 django-polls 完成了打包和测试,准备好向世界分享它!如果这不是一个例子应用,你现在就可以这样做。

通过虚拟环境安装 Python 包

早些时候,我们以用户库的形式安装了投票应用。这样做有一些缺点。

  • 修改用户库会影响你系统上的其他 Python 软件。
  • 你将不能运行此包的多个版本(或者其它用有相同包名的包)。

通常,只有在维护多个 Django 项目时才会出现这些情况。当这样做时,最好的解决方法是使用 venv。使用此工具,你可以维护多个隔离的 Python 环境,每个环境都有其自己的库和包命名空间的副本。