如何编写安装的 Django 应用程序

网友投稿 744 2022-05-29

目录

先决条件

在项目中启动示例 Django 应用程序

从头开始创建 Django 项目

探索收据示例应用程序

在项目中测试 App

制作可安装的 Django 应用

将您的 Django 应用程序移出项目

在项目外引导 Django

使用可安装的 Django 应用程序运行管理命令

测试可安装的 Django 应用

使用 setup.cfg 定义可安装包

使用 tox 测试多个版本

发布到 PyPI

结论

进一步阅读

在Django框架中,项目是指特定网站的配置文件和代码的集合。Django 将业务逻辑分组到它所谓的应用程序中,它们是 Django 框架的模块。有很多关于如何构建项目和其中的应用程序的文档,但是当需要打包可安装的 Django 应用程序时,信息就更难找到了。

在本教程中,您将学习如何从Django 项目中取出应用程序并将其打包以使其可安装。打包应用程序后,您可以在PyPI上共享它,以便其他人可以通过pip install

在本教程中,您将学习:

编写独立应用程序和在项目内部编写应用程序有什么区别

如何创建用于发布 Django 应用程序的setup.cfg文件

如何在Django 项目之外引导 Django,以便您可以测试您的应用程序

如何使用 Python 和 Django 的多个版本进行测试 tox

如何使用Twine将可安装的 Django 应用程序发布到 PyPI

先决条件

本教程需要对Django、pip、PyPI、pyenv(或等效的虚拟环境工具)和tox. 要了解有关这些主题的更多信息,请查看:

Django Tutorials

What Is Pip? A Guide for New Pythonistas

How to Publish an Open-Source Python Package to PyPI

Managing Multiple Python Versions With pyenv

Getting Started With Testing in Python

在项目中启动示例 Django 应用程序

本教程包含一个工作包,可帮助指导您完成制作可安装 Django 应用程序的过程。

即使您最初打算将 Django 应用程序作为包提供,您也可能会从项目内部开始。为了演示从 Django 项目转移到可安装的 Django 应用程序的过程,我在 repo 中提供了两个分支。该项目的分支是一个Django项目的内部应用程序的起始状态。该主分支是完成安装的应用程序。

您还可以在PyPI realpython-django-receipts 包页面下载完成的应用程序。您可以通过运行安装该软件包pip install realpython-django-receipts。

示例应用程序是收据上行项目的简短表示。在项目分支中,您将找到一个名为的目录sample_project,其中包含一个正在运行的 Django 项目。该目录如下所示:

sample_project/ │ ├── receipts/ │ ├── fixtures/ │ │ └── receipts.json │ │ │ ├── migrations/ │ │ ├── 0001_initial.py │ │ └── __init__.py │ │ │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py │ ├── sample_project/ │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py │ ├── db.sqlite3 ├── manage.py ├── resetdb.sh └── runserver.sh

在编写本教程时,Django 的最新版本是 3.0.4,所有测试都是使用 Python 3.7 完成的。本教程中概述的所有步骤都不应与早期版本的 Django 不兼容——我从 Django 1.8 开始使用这些技术。但是,如果您使用的是 Python 2,则需要进行一些更改。为了使示例简单,我假设整个代码库都使用 Python 3.7。

从头开始创建 Django 项目

示例项目和收据应用程序是使用 Djangoadmin命令和一些小的编辑创建的。首先,在干净的虚拟环境中运行以下代码:

$ python -m pip install Django $ django-admin startproject sample_project $ cd sample_project $ ./manage.py startapp receipts

这将创建sample_project项目目录结构和receipts应用程序子目录,其中包含您将用于创建可安装 Django 应用程序的模板文件。

接下来,该sample_project/settings.py文件需要进行一些修改:

添加'127.0.0.1'到ALLOWED_HOSTS设置中,以便您可以在本地进行测试。

添加'receipts'到INSTALLED_APPS列表中。

您还需要receipts在sample_project/urls.py文件中注册应用程序的 URL 。为此,请添加path('receipts/', include('receipts.urls'))到url_patterns列表中。

探索收据示例应用程序

该应用程序由两个 ORM 模型类组成:Item和Receipt. 本Item类包含了描述和成本数据库字段声明。成本包含在DecimalField. 使用浮点数来表示货币是危险的——在处理货币时你应该总是使用定点数。

该Receipt班是一个收集点Item的对象。这是一个实现ForeignKey在Item该点Receipt。Receipt还包括total()获取Item包含在 中的对象的总成本Receipt:

# receipts/models.py from decimal import Decimal from django.db import models class Receipt(models.Model): created = models.DateTimeField(auto_now_add=True) def __str__(self): return f"Receipt(id={self.id})" def total(self) -> Decimal: return sum(item.cost for item in self.item_set.all()) class Item(models.Model): created = models.DateTimeField(auto_now_add=True) description = models.TextField() cost = models.DecimalField(max_digits=7, decimal_places=2) receipt = models.ForeignKey(Receipt, on_delete=models.CASCADE) def __str__(self): return f"Item(id={self.id}, description={self.description}, " \ f"cost={self.cost})"

模型对象为您提供数据库内容。一个简短的 Django 视图返回一个JSON 字典,其中Receipt包含Item数据库中的所有对象及其对象:

# receipts/views.py from django.http import JsonResponse from receipts.models import Receipt def receipt_json(request): results = { "receipts":[], } for receipt in Receipt.objects.all(): line = [str(receipt), []] for item in receipt.item_set.all(): line[1].append(str(item)) results["receipts"].append(line) return JsonResponse(results)

的receipt_json()视图遍历所有的Receipt目的,创建一对所述的Receipt对象和所述的列表Item的对象包含在。所有这些都放在字典中并通过 Django 的JsonResponse().

为了使模型在Django 管理界面中可用,您可以使用一个admin.py文件来注册模型:

# receipts/admin.py from django.contrib import admin from receipts.models import Receipt, Item @admin.register(Receipt) class ReceiptAdmin(admin.ModelAdmin): pass @admin.register(Item) class ItemAdmin(admin.ModelAdmin): pass

此代码ModelAdmin为每个Receipt和Item类创建一个 Django ,并将它们注册到 Django 管理员。

最后,urls.py文件在应用程序中针对 URL 注册单个视图:

# receipts/urls.py from django.urls import path from receipts import views urlpatterns = [ path("receipt_json/", views.receipt_json), ]

您现在可以包含receipts/urls.py在您的项目url.py文件中,以便在您的网站上提供收据视图。

一切就绪后,可以运行./manage.py makemigrations receipts,使用Django admin添加数据,然后访问/receipts/receipt_json/查看结果:

$ curl -sS http://127.0.0.1:8000/receipts/receipt_json/ | python3.8 -m json.tool { "receipts": [ [ "Receipt(id=1)", [ "Item(id=1, description=wine, cost=15.25)", "Item(id=2, description=pasta, cost=22.30)" ] ], [ "Receipt(id=2)", [ "Item(id=3, description=beer, cost=8.50)", "Item(id=4, description=pizza, cost=12.80)" ] ] ] }

在上面的块中,您使用curl访问receipt_json视图,从而产生包含Receipt对象及其对应Item对象的 JSON 响应。

在项目中测试 App

Djangounittest用自己的测试功能扩充了 Python包,使您能够将设备预加载到数据库中并运行您的测试。收据应用程序定义了一个tests.py文件和一个用于测试的装置。这个测试绝不是全面的,但它是一个足够好的概念证明:

# receipts/tests.py from decimal import Decimal from django.test import TestCase from receipts.models import Receipt class ReceiptTest(TestCase): fixtures = ["receipts.json", ] def test_receipt(self): receipt = Receipt.objects.get(id=1) total = receipt.total() expected = Decimal("37.55") self.assertEqual(expected, total)

夹具创建两个Receipt对象和四个对应的Item对象。单击下面的可折叠部分以仔细查看夹具的代码。

receives.json 测试装置显示隐藏

您可以使用 Djangomanage.py命令测试收据应用程序:

$ ./manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). . ---------------------------------------------------------------------- Ran 1 test in 0.013s OK Destroying test database for alias 'default'...

Runningmanage.py test运行 中定义的单个测试receipts/tests.py并显示结果。

制作可安装的 Django 应用

您的目标是在没有项目的情况下共享收据应用程序,并使其可被其他人重复使用。您可以压缩receipts/目录并将其分发出去,但这有些限制。相反,您希望将应用程序分成一个包,以便它可以安装。

创建可安装的 Django 应用程序的最大挑战是 Django 需要一个项目。没有项目的应用程序只是一个包含代码的目录。没有项目,Django 不知道如何处理您的代码,包括运行测试。

将您的 Django 应用程序移出项目

保留一个示例项目是个好主意,这样您就可以运行 Django 开发服务器并使用您的应用程序的实时版本。您不会将此示例项目包含在应用程序包中,但它仍然可以存在于您的存储库中。按照这个想法,您可以通过将可安装的 Django 应用程序上移到一个目录来开始打包它:

$ mv receipts ..

目录结构现在看起来像这样:

django-receipts/ │ ├── receipts/ │ ├── fixtures/ │ │ └── receipts.json │ │ │ ├── migrations/ │ │ ├── 0001_initial.py │ │ └── __init__.py │ │ │ ├── __init__.py │ ├── models.py │ ├── tests.py │ ├── urls.py │ ├── views.py │ ├── admin.py │ └── apps.py │ ├── sample_project/ │ ├── sample_project/ │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ │ │ ├── db.sqlite3 │ ├── manage.py │ ├── resetdb.sh │ └── runserver.sh │ ├── LICENSE └── README.rst

要打包您的应用程序,您需要将其从项目中拉出。移动它是第一步。我通常会保留原始项目以进行测试,但我不会将其包含在生成的包中。

在项目外引导 Django

现在您的应用程序在 Django 项目之外,您需要告诉 Django 如何找到它。如果您想测试您的应用程序,请运行一个可以找到您的应用程序或运行您的迁移的 Django shell 。您需要配置 Django 并使其可用。

Django's settings.configure()anddjango.setup()是在项目之外与您的应用程序交互的关键。有关这些调用的更多信息,请参阅 Django 文档。

您可能在多个地方需要 Django 的这种配置,因此在函数中定义它是有意义的。创建一个名为的文件,boot_django.py其中包含以下代码:

1# boot_django.py 2# 3# This file sets up and configures Django. It's used by scripts that need to 4# execute as if running in a Django server. 5import os 6import django 7from django.conf import settings 8 9BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "receipts")) 10 11def boot_django(): 12 settings.configure( 13 BASE_DIR=BASE_DIR, 14 DEBUG=True, 15 DATABASES={ 16 "default":{ 17 "ENGINE":"django.db.backends.sqlite3", 18 "NAME": os.path.join(BASE_DIR, "db.sqlite3"), 19 } 20 }, 21 INSTALLED_APPS=( 22 "receipts", 23 ), 24 TIME_ZONE="UTC", 25 USE_TZ=True, 26 ) 27 django.setup()

第 12 和 27 行设置了 Django 环境。该settings.configure()调用采用与settings.py文件中定义的变量等效的参数列表。settings.py使您的应用程序运行所需的任何内容都会传递到settings.configure().

上面的代码是一个相当精简的配置。收据应用程序不会对会话或模板执行任何操作,因此INSTALLED_APPS只需要"receipts",并且您可以跳过任何中间件定义。该USE_TZ=True值是必需的,因为Receipt模型包含created时间戳。否则,您将在加载测试夹具时遇到问题。

使用可安装的 Django 应用程序运行管理命令

现在您有了boot_django.py,您可以使用一个非常短的脚本运行任何 Django 管理命令:

#!/usr/bin/env python # makemigrations.py from django.core.management import call_command from boot_django import boot_django boot_django() call_command("makemigrations", "receipts")

Django 允许您通过call_command(). 您现在可以通过导入和调用boot_django()后跟call_command().

你的应用程序现在在项目之外,允许你对它做各种各样的 Django-y 事情。我经常定义四个实用程序脚本:

load_tests.py 测试您的应用

makemigrations.py 创建迁移文件

migrate.py 执行表迁移

djangoshell.py 生成一个知道你的应用程序的 Django shell

测试可安装的 Django 应用

该load_test.py文件可以像makemigrations.py脚本一样简单,但它只能一次运行所有测试。通过添加几行,您可以将命令行参数传递给测试运行器,从而允许您运行选择性测试:

1#!/usr/bin/env python 2# load_tests.py 3import sys 4from unittest import TestSuite 5from boot_django import boot_django 6 7boot_django() 8 9default_labels = ["receipts.tests", ] 10 11def get_suite(labels=default_labels): 12 from django.test.runner import DiscoverRunner 13 runner = DiscoverRunner(verbosity=1) 14 failures = runner.run_tests(labels) 15 if failures: 16 sys.exit(failures) 17 18 # In case this is called from setuptools, return a test suite 19 return TestSuite() 20 21if __name__ == "__main__": 22 labels = default_labels 23 if len(sys.argv[1:]) > 0: 24 labels = sys.argv[1:] 25 26 get_suite(labels)

DjangoDiscoverRunner是一个测试发现类,与 Python 的unittest. 它负责设置测试环境、构建测试套件、设置数据库、运行测试,然后将其全部拆除。从第 11 行开始,get_suite()获取测试标签列表并直接调用DiscoverRunner它们。

该脚本类似于 Django 管理命令的test作用。该__main__块将任何命令行参数传递给get_suite(),如果没有,则它将传递到应用程序的测试套件中receipts.tests。您现在可以load_tests.py使用测试标签参数调用并运行单个测试。

第 19 行是一个特殊情况,可在使用tox. 您将tox在后面的部分中了解更多信息。您还可以DiscoverRunner在下面的可折叠部分中查看潜在的替代品。

DiscoverRunner 替代方案显示隐藏

定义您的可安装包 setup.cfg

要将可安装的 Django 应用程序放在 PyPI 上,您需要先将其放入一个包中。PyPI中期望一个egg,wheel或源分布。这些是使用setuptools. 为此,您需要在与目录相同的目录级别创建一个setup.cfg文件和一个setup.py文件receipts。

但是,在深入研究之前,您需要确保您有一些文档。您可以在 中包含项目描述setup.cfg,它会自动显示在 PyPI 项目页面上。确保写一个README.rst或类似的关于你的包的信息。

PyPI默认支持reStructuredText格式,但它也可以处理带有额外参数的Markdown:

1# setup.cfg 2[metadata] 3name = realpython-django-receipts 4version = 1.0.3 5description = Sample installable django app 6long_description = file:README.rst 7url = https://github.com/realpython/django-receipts 8license = MIT 9classifiers = 10 Development Status :: 4 - Beta 11 Environment :: Web Environment 12 Intended Audience :: Developers 13 License :: OSI Approved :: MIT License 14 Operating System :: OS Independent 15 Programming Language :: Python :: 3 :: Only 16 Programming Language :: Python :: 3.7 17 Programming Language :: Python :: Implementation :: CPython 18 Topic :: Software Development :: Libraries :: Application Frameworks 19 Topic :: Software Development :: Libraries :: Python Modules 20 21[options] 22include_package_data = true 23python_requires = >=3.6 24setup_requires = 25 setuptools >= 38.3.0 26install_requires = 27 Django>=2.2

该setup.cfg文件描述了您将构建的包。第 6 行使用file:指令读取您的README.rst文件。这使您不必在两个地方编写长描述。

第26 行的install_requires条目告诉任何安装程序,例如,您的应用程序具有的依赖项。您将始终希望将可安装的 Django 应用程序与其支持的最低 Django 版本联系起来。pip install

如果您的代码具有任何仅运行测试所需的依赖tests_require =项,则您可以添加一个条目。例如,在mock成为标准 Python 库的一部分之前,tests_require = mock>=2.0.0在setup.cfg.

pyproject.toml在你的包中包含一个文件被认为是最佳实践。布雷特·坎农 (Brett Cannon)关于该主题的出色文章可以让您了解详细信息。pyproject.toml示例代码中还包含一个文件。

您几乎已准备好为可安装的 Django 应用程序构建包。测试它的最简单方法是使用您的示例项目——另一个保留示例项目的好理由。该pip install命令支持本地定义的包。这可用于确保您的应用程序仍然适用于项目。但是,一个警告是setup.cfg在这种情况下它不会单独工作。您还必须创建一个 shim 版本setup.py:

#!/usr/bin/env python if __name__ == "__main__": import setuptools setuptools.setup()

此脚本将自动使用您的setup.cfg文件。您现在可以安装包的本地可编辑版本以从sample_project. 可以肯定的是,最好从一个全新的虚拟环境开始。requirements.txt在sample_project目录中添加以下文件:

# requirements.txt -e ../../django-receipts

该-e告诉pip,这是一个本地可编辑的安装。您现在已准备好安装:

$ pip install -r requirements.txt Obtaining django-receipts (from -r requirements.txt (line 1)) Collecting Django>=3.0 Using cached Django-3.0.4-py3-none-any.whl (7.5 MB) Collecting asgiref~=3.2 Using cached asgiref-3.2.7-py2.py3-none-any.whl (19 kB) Collecting pytz Using cached pytz-2019.3-py2.py3-none-any.whl (509 kB) Collecting sqlparse>=0.2.2 Using cached sqlparse-0.3.1-py2.py3-none-any.whl (40 kB) Installing collected packages: asgiref, pytz, sqlparse, Django, realpython-django-receipts Running setup.py develop for realpython-django-receipts Successfully installed Django-3.0.4 asgiref-3.2.7 pytz-2019.3 realpython-django-receipts sqlparse-0.3.1

该install_requires列表setup.cfg告诉pip install它需要Django的。Django 需要asgiref, pytz, 和sqlparse. 所有依赖项都已处理完毕,您现在应该可以运行sample_projectDjango 开发服务器了。恭喜 - 您的应用程序现在已从示例项目中打包和引用!

测试多个版本 tox

Django 和 Python 都在不断进步。如果您要与全世界共享您的可安装 Django 应用程序,那么您可能需要在多种环境中进行测试。该tox工具需要一点帮助才能测试您的 Django 应用程序。继续并在内部进行以下更改setup.cfg:

1# setup.cfg 2[metadata] 3name = realpython-django-receipts 4version = 1.0.3 5description = Sample installable django app 6long_description = file:README.rst 7url = https://github.com/realpython/django-receipts 8license = MIT 9classifiers = 10 Development Status :: 4 - Beta 11 Environment :: Web Environment 12 Intended Audience :: Developers 13 License :: OSI Approved :: MIT License 14 Operating System :: OS Independent 15 Programming Language :: Python :: 3 :: Only 16 Programming Language :: Python :: 3.7 17 Programming Language :: Python :: Implementation :: CPython 18 Topic :: Software Development :: Libraries :: Application Frameworks 19 Topic :: Software Development :: Libraries :: Python Modules 20 21[options] 22include_package_data = true 23python_requires = >=3.6 24setup_requires = 25 setuptools >= 38.3.0 26install_requires = 27 Django>=2.2 28test_suite = load_tests.get_suite

第 28 行告诉包管理器使用load_tests.py脚本来获取它的测试套件。该tox实用程序使用它来运行其测试。记得get_suite()在load_tests.py:

1# Defined inside load_tests.py 2def get_suite(labels=default_labels): 3 from django.test.runner import DiscoverRunner 4 runner = DiscoverRunner(verbosity=1) 5 failures = runner.run_tests(labels) 6 if failures: 7 sys.exit(failures) 8 9 # If this is called from setuptools, then return a test suite 10 return TestSuite()

诚然,这里发生的事情有点奇怪。通常,test_suite字段 insetup.cfg指向返回一组测试的方法。当tox调用时setup.py,它读取test_suite参数并运行load_tests.get_suite()。

如果这个调用没有返回一个TestSuite对象,那么tox就会抱怨。奇怪的是,您实际上并不想tox获得一套测试,因为tox您不知道 Django 测试环境。相反,在第 10 行get_suite()创建 aDiscoverRunner并返回一个空TestSuite对象。

您不能简单地DiscoverRunner返回一组测试,因为您必须调用DiscoverRunner.run_tests()Django 测试环境的设置和拆卸才能正确执行。仅仅通过正确的测试是tox行不通的,因为不会创建数据库。get_suite()运行所有测试,但作为函数调用的副作用而不是作为返回测试套件tox执行的正常情况。

该tox工具允许您测试多个组合。甲tox.ini文件确定测试环境的哪个组合。下面是一个例子:

[tox] envlist = py{36,37}-django220, py{36,37}-django300 [testenv] deps = django220: Django>=2.2,<3 django300: Django>=3 commands= python setup.py test

该文件指出,应针对 Python 3.6 和 3.7 以及 Django 2.2 和 3.0 运行测试。总共有四个测试环境。该commands=部分是您告诉tox通过 调用测试的地方setup.py。这就是您test_suite = load_tests.get_suite在setup.cfg.

注意:的test子命令setup.py已被弃用。目前,Python 中的打包正在迅速变化。虽然python setup.py test通常不推荐调用,但在这种特定情况下它是有效的。

发布到 PyPI

最后,是时候在 PyPI 上分享您的可安装 Django 应用程序了。有多种工具可用于上传包,但在本教程中您将重点介绍Twine。以下代码构建包并调用 Twine:

$ python -m pip install -U wheel twine setuptools $ python setup.py sdist $ python setup.py bdist_wheel $ twine upload dist/*

前两个命令构建包的源代码和二进制分发版。调用twine上传到 PyPI。如果您.pypirc的主目录中有一个文件,那么您可以预设您的用户名,这样系统只会提示您输入密码:

[disutils] index-servers = pypi [pypi] username:

我经常使用一个小的shell脚本来grep从代码中获取版本号。然后我调用git tag用版本号标记repo,删除旧的build/和dist/目录,并调用上述三个命令。

有关使用 Twine 的更多详细信息,请参阅如何将开源 Python 包发布到 PyPI。Twine 的两个流行替代品是Poetry和Flit。Python 中的包管理正在迅速变化。PEP 517和PEP 518正在重新定义如何描述 Python 包和依赖项。

结论

Django 应用程序依赖于 Django 项目结构,因此单独打包它们需要额外的步骤。您已经了解了如何通过从项目中提取、打包并在 PyPI 上共享来制作可安装的 Django 应用程序。请务必从以下链接下载示例代码:

在本教程中,您学习了如何:

在项目之外使用Django 框架

在独立于项目的应用程序上调用 Django管理命令

编写一个调用Django 测试的脚本,可选择使用单个测试标签

构建一个setup.py文件来定义你的包

修改setup.py脚本以适应tox

使用Twine上传可安装的 Django 应用

您已准备好与全世界分享您的下一个应用程序。快乐编码!

进一步阅读

Django、打包、测试都是非常深奥的话题。那里有很多信息。要深入挖掘,请查看以下资源:

Django 文档

Django 入门:构建投资组合应用程序

Django 教程

使用 pyenv 管理多个 Python 版本

什么是 pip?Python 新手指南

如何将开源 Python 包发布到 PyPi

Python 测试入门

Poetry

Flit

PyPI有大量可安装的 Django 应用程序,值得一试。以下是一些最受欢迎的:

Django-CSP

Django reCAPTCHA

django-allauth

如何编写可安装的 Django 应用程序

Haystack

响应式 Django 管理员

Django 调试工具栏

Django Python 数据库

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:认证杯网络挑战赛C题破局共享汽车
下一篇:Linux_NFS/Samba服务器
相关文章