Django 开发人员犯的 10 大错误
已发表: 2022-03-11在本教程中,我们将研究 Django 开发人员经常犯的一些常见错误以及避免这些错误的方法。 即使您是熟练的 Django 开发人员,本教程也很有用,因为错误(例如维护无法管理的大设置或静态资产中的命名冲突)不仅限于初次接触 Django 的新开发人员。
Django 是一个免费的开源 Python Web 框架,它有助于解决常见的开发挑战,并允许您构建灵活、结构良好的应用程序。 Django 有很多开箱即用的现代特性。 就我个人而言,管理、对象关系映射工具 (ORM)、路由和模板功能使 Django 成为我的首选,因为应用程序需要大量工作,虽然我像任何开发人员一样享受我的工作,但我想花在这些基本的重复性任务上尽可能少的时间。 Django 允许您在不影响灵活性的情况下完成所有这些工作。
Django 的杀手级功能是一个强大的可配置管理界面,它会自动(自动?)从您的模型的模式和管理面板模型构建,让您感觉像一个向导。 通过 Admin 界面,用户可以配置很多东西,包括访问控制列表 (ACL)、行级权限和操作、过滤器、订单、小部件、表单、额外的 URL 帮助程序以及您可以想象的任何其他内容。 我相信每个应用程序都需要一个管理面板——如果还没有,那么您的基本应用程序需要一个管理面板只是时间问题。 使用 Django admin,您可以快速灵活地创建一个。
Django 有一个强大的 ORM,它可以与所有主要的数据库一起工作。 由于它是惰性的,它仅在您需要时才访问您的数据库,这与其他 ORM 不同。 它支持您可以从 Python 源代码中使用的所有主要 SQL 指令(和函数),并且由于 Python 的特性而感觉非常舒适。
Django 的模板引擎同时非常灵活和强大。 您可以使用许多标准过滤器和标签,也可以为您的项目创建新的自定义过滤器和标签。 Django 支持其他模板引擎以及 Django 模板,它提供了一个 API,可以通过标准的模板处理快捷功能轻松集成其他模板引擎。
Django 有很多其他的大特性,比如 URL 路由器,它可以解析传入的请求并从路由器模式构建新的 URL。 总的来说,Django 框架是一种愉快的体验,当您需要帮助时,只需阅读文档即可。
错误一:对项目依赖使用全局系统 Python 环境
不要将 Python 的全局环境用于项目依赖项,因为它会产生依赖项冲突。 Python 不能同时使用多个包版本。 如果不同的项目需要同一包的不同不兼容版本,这可能会成为问题。
这个错误通常是由不了解 Python 的环境隔离特性的新 Python 和 Django 开发人员犯的。
有很多方法可以隔离您的环境,但最常见的方法是:
- virtualenv:一个 Python 包,它生成一个 Python 环境文件夹,并具有用于 [de] 激活环境和管理环境中已安装的 Python 包的脚本。 这是我最喜欢的方法,因为它是完成这项工作的最简单方法。 通常,我在项目文件夹附近创建环境。
- virtualenvwrapper:一个 Python 包,可全局安装并提供用于创建/删除/激活/等的工具集。 虚拟环境。 所有虚拟环境都存储在一个文件夹中(可以通过环境变量 WORKON_HOME 覆盖)。 我看不出使用
virtualenvwrapper
代替virtualenv
有什么好处。 - 虚拟机 (VM):没有比专用于您的应用程序的整个虚拟机更大的隔离性了。 有很多工具可供选择,包括 VirtualBox(免费)、VMware、Parallels 和 Proxmox(我个人最喜欢的,它有免费版本)。 结合像 Vagrant 这样的 VM 自动化工具,这可能是一个非常强大的解决方案。
- 容器:在过去的几年里,我几乎在每个项目中都使用了 Docker,尤其是在我从头开始的每个新项目中。 Docker 是一个了不起的工具,它提供了很多功能,并且有很多用于容器自动化的第三方工具。 它具有层缓存功能,可以非常快速地重建容器。 在容器中,我使用全局系统 Python 环境,因为每个容器都有自己的文件系统,并且项目在高层上是隔离的。 Docker 允许新团队成员更快地开始项目工作,特别是如果他们有 Docker 经验。
如果你问我,我更喜欢virtualenv
Python 包和 Docker 容器来进行项目依赖隔离和管理。
错误 2:没有在requirements.txt
文件中固定项目依赖项
每个新的 Python 项目都应该从一个 requirements.txt 文件和一个新的隔离环境开始。 通常,您通过pip/easy_install
安装所有软件包,但也不要忘记将它们添加到您的requirements.txt
文件中。 这使得更容易(可能更合适)在服务器上部署您的项目,或者让团队成员在他们自己的机器上引导项目。
此外,将依赖项的特定版本固定在requirements.txt
文件中同样重要。 通常,不同版本的包提供不同的模块、功能和功能参数; 即使是依赖项中的微小版本更改也可能会破坏您的包。 如果您的项目是实时的并且您已经定期安排部署,这是一个非常严重的问题,因为在没有版本控制的情况下,您的构建系统将始终安装最新的可用版本包。
始终固定您的包装以进行生产! 就我个人而言,我使用了一个非常好的工具,叫做 pip-tools,它可以帮助我做到这一点。 它提供了一组命令行工具来帮助管理您的依赖关系。 它会自动生成一个requirements.txt
,其中不仅包含您的依赖项,还包含整个依赖项树,其中包括您的依赖项的依赖项。
有时,您只想更新依赖项列表中的某些包(例如,只更新 Django/Flask/任何框架或实用程序),如果您使用“pip freeze”,您不知道哪些依赖项适用于哪些包,所以您无法升级依赖项。 但是,使用 pip-tools,它会根据您固定的依赖项自动固定包,因此它会自动解析需要更新的包。 作为奖励,您还可以确切地知道哪个包来自哪个依赖项,因为它如何在requirements.txt
文件中用注释标记它们。
要格外小心,备份您的依赖源文件也是一个好主意! 在您的文件系统、Git 管理的文件夹、S3 文件夹、FTP、SFTP 中保留一份副本——无论何时何地,但手头都有。 在某些情况下,未列出的相对较小的包破坏了 npm 上的大量包。 Pip 提供了将所有必需的依赖项下载为源文件的工具,通过运行pip help download
了解更多信息。
错误 3:使用旧式 Python 函数而不是基于类的视图
有时在应用程序的views.py
文件中使用一个小的 Python 函数是一个好主意,尤其是用于测试或实用程序视图,但通常,您应该在应用程序中使用基于类的视图 (CBV)。
CBV 是提供抽象类的通用视图,这些抽象类实现由专业人员构建的常见 Web 开发任务并涵盖所有常见行为。 它们有一个惊人的结构化 API,当您使用 CBV 时,您可以使用面向对象编程的所有优势。 它使您的源代码更加清晰易读。 忘记使用 Django 标准视图函数进行列表、CRUD 操作、表单处理等的痛苦。您只需为您的视图扩展合适的 CBV 并覆盖类属性或函数(通常一个函数返回一个属性,您可以在那里添加任何逻辑在使用配置视图行为的视图函数而不是 CBV 的情况下,从您的源代码制作意大利面条。
例如,您可以在您的项目中使用不同的 mix-in,它们覆盖基本的 CBV 行为以构建视图上下文、在行级别检查授权、从应用程序结构自动构建模板路径、集成智能缓存等等。
我构建了名为 Django Template Names 的包,它根据应用程序名称和视图类名称为您的视图标准化模板名称。 我每天都在使用它,它为我节省了很多时间来发明名称。 只需将 mixin 放入您的 CBV—— class Detail(TemplateNames, DetailView):
——它就会开始工作! 当然,您可以覆盖我的功能并添加移动响应模板、用户代理的不同模板或任何其他您想要的东西。
错误 4:编写胖视图和瘦模型
在视图而不是模型中编写应用程序逻辑意味着您已将属于模型的代码写入视图,使其“胖”而模型“瘦”。
你应该写胖模型,瘦视图。
将逻辑分解为模型上的小方法。 这允许您在几行代码中从多个来源(管理界面 UI、前端 UI、API 端点、多个视图)多次使用它,而不是复制粘贴大量代码。 因此,下次您向用户发送电子邮件时,请使用电子邮件功能扩展模型,而不是在控制器中编写此逻辑。
这也使您的代码更容易进行单元测试,因为您可以在一个地方测试电子邮件逻辑,而不是在发生这种情况的每个控制器中重复。
您可以在 Django Best Practices 项目中阅读有关该问题的更多信息。 解决方案很简单:编写胖模型和瘦视图,所以让我们在您的下一个项目中进行(或重构您当前的项目)。
错误 5:一个巨大的、难以管理的设置文件
甚至新的 Django 项目设置文件也有很多设置。 在实际项目中,设置文件会增长到 700 多行配置,并且将变得难以维护,尤其是当您的开发、生产和暂存环境都需要自定义配置时。
您可以手动划分配置文件并创建自定义加载器,但我想向您介绍一个很好且经过良好测试的 Python 包,Django Split Settings,它是我与人合着的。
该包提供了两个函数—— optional
和include
——它们支持路径的通配符并在相同的上下文中导入您的配置文件,从而使使用先前加载的文件中声明的配置条目构建您的配置变得简单。 它不会影响 Django 性能,您可以在任何项目中使用它。
查看最小配置示例:
from split_settings.tools import optional, include include( 'components/base.py', 'components/database.py', 'components/*.py', # the project different envs settings optional('envs/devel/*.py'), optional('envs/production/*.py'), optional('envs/staging/*.py'), # for any local settings optional('local_settings.py'), )
误区六:多合一应用、不良应用结构、不正确的资源放置
任何 Django 项目都包含多个应用程序。 在 Django 表示法中,应用程序是一个 Python 包,其中至少包含__init__.py
和models.py
文件; 在最新的 Django 版本中,不再需要models.py
。 __init__.py
就足够了。
Django 应用程序可以包含 Python 模块、Django 特定模块(视图、URL、模型、管理、表单、模板标签等)、静态文件、模板、数据库迁移、管理命令、单元测试等。 您应该使用简单的逻辑将您的单体应用程序划分为小的、可重用的应用程序。 您应该能够用一两个简短的句子来描述应用程序的整个目的。 例如:“允许用户通过电子邮件注册和激活他们的帐户。”

一个好主意是调用项目文件夹project
并将应用程序放在project/apps/
中。 然后,将所有应用程序依赖项放入它们自己的子文件夹中。
例子:
- 静态文件:
project/apps/appname/static/appname/
- 模板标签:
project/apps/appname/templatetags/appname.py
- 模板文件:
project/apps/appname/templates/appname/
始终在子文件夹中为应用程序名称添加前缀,因为所有静态文件夹都合并到一个文件夹中,如果两个或多个应用程序有一个js/core.js
文件,则settings.INSTALLED_APPLICATIONS
中的最后一个应用程序将覆盖以前的应用程序。 我曾经在我当前的项目中遇到过这个错误,并且失去了大约六个小时的调试时间,直到我意识到另一个开发人员已经覆盖了static/admin/js/core.js
,因为该团队正在实施一个自定义 SPA 管理面板并以相同的方式命名他们的文件。
这是一个包含大量资源和 Python 模块的门户应用程序的示例结构。
root@c5b96c395cfb:/test# tree project/apps/portal/ project/apps/portal/ ├── __init__.py ├── admin.py ├── apps.py ├── management │ ├── __init__.py │ └── commands │ ├── __init__.py │ └── update_portal_feeds.py ├── migrations │ └── __init__.py ├── models.py ├── static │ └── portal │ ├── css │ ├── img │ └── js ├── templates │ └── portal │ └── index.html ├── templatetags │ ├── __init__.py │ └── portal.py ├── tests.py ├── urls.py └── views.py 11 directories, 14 files
使用这样的结构,您可以随时将应用程序导出到另一个 Python 包中并再次使用它。 您甚至可以在 PyPi 中将其作为开源包发布,或将其移动到另一个文件夹。
你最终会得到一个像这样的项目结构:
root@c5b96c395cfb:/test# tree -L 3 . ├── deploy │ ├── chef │ └── docker │ ├── devel │ └── production ├── docs ├── logs ├── manage.py ├── media ├── project │ ├── __init__.py │ ├── apps │ │ ├── auth │ │ ├── blog │ │ ├── faq │ │ ├── pages │ │ ├── portal │ │ └── users │ ├── conf │ ├── settings.py │ ├── static │ ├── templates │ ├── urls.py │ └── wsgi.py └── static └── admin ├── css ├── fonts ├── img └── js 25 directories, 5 files
当然,在实际项目中,它会更复杂,但这种结构使事情变得更简单、更清晰。
错误 7: STATICFILES_DIRS
和STATIC_ROOT
迷惑新手 Django 开发人员
静态文件是不会因应用程序的使用而改变的资产,例如 JavaScript、CSS、图像、字体等。在 Django 中,它们仅在部署过程中被“收集”到公共目录中。
在开发模式下——python python manage.py runserver
——Django 使用STATICFILES_FINDERS
设置搜索静态文件。 默认情况下,它会尝试在STATICFILES_DIRS
设置中列出的文件夹中查找请求的静态文件。 如果失败,Django 会尝试使用django.contrib.staticfiles.finders.AppDirectoriesFinder
查找文件,该文件会在项目中每个已安装应用程序的static
文件夹中查找。 这允许您编写随其自己的静态文件一起提供的可重用应用程序。
在生产环境中,您使用像 Nginx 这样的独立 Web 服务器来提供静态服务。 Web 服务器对 Django 项目应用程序结构或静态文件分布在哪些文件夹中一无所知。幸运的是,Django 为您提供了收集静态管理命令python manage.py collectstatic
,它遍历STATICFILES_FINDERS
并从应用程序static
复制所有静态文件STATICFILES_DIRS
中列出的文件夹和文件夹到您在STATIC_ROOT
设置中指定的目录中。 这允许使用与 Django 开发模式服务器相同的逻辑来解析静态文件资源,并将所有静态文件放在一个位置供您的 Web 服务器使用。
不要忘记在您的生产环境中运行collectstatic
!
错误 8:默认STATICFILES_STORAGE
,生产环境中的 Django 模板加载器
STATICFILES_STORAGE
先说生产环境资产管理。 如果我们使用“资产永不过期”政策(您可以在此处了解更多信息),我们可以提供最佳用户体验。 这意味着我们所有的静态文件都应该被 Web 浏览器缓存数周、数月甚至数年。 换句话说,您的用户应该只下载一次您的资产!
这很酷,我们可以在 Nginx 配置中为我们的静态文件夹配置几行代码,但是缓存失效呢? 如果用户只下载一次我们的资源,如果您更新了徽标、字体、JavaScript 或菜单中某个项目的文本颜色,会发生什么情况? 要绕过这一点,您应该在每次部署时为我们的静态文件生成唯一的 URL 和文件名!
我们可以简单地使用 ManifestStaticFilesStorage 作为STATICFILES_STORAGE
(注意,哈希仅在DEBUG=false
模式下启用)并运行上面讨论的collectstatic
管理命令。 这将减少对您的生产网站的资产请求计数,并使您的网站渲染速度更快。
缓存的 Django 模板加载器
另一个很酷的 Django 特性是缓存模板加载器,它不会在每次模板渲染时重新加载和解析模板文件。 模板解析是一项非常昂贵的操作,并且会占用大量资源。 默认情况下,每个请求都会解析 Django 模板,但这很糟糕,尤其是在生产过程中,您可以在短时间内处理数千个请求。
查看cached.Loader
配置部分以获取一个很好的示例和有关如何执行此操作的详细信息。 不要在开发模式下使用加载器,因为它不会从文件系统重新加载已解析的模板; 您需要在每次模板更改时使用python manage.py startapp
重新启动您的项目。 这在开发过程中可能很烦人,但它非常适合生产环境。
错误 9:实用程序或脚本的纯 Python 脚本
Django 提供了一个非常好的特性,叫做管理命令。 只需使用它,而不是重新发明轮子并为您的项目实用程序编写原始 Python 脚本。
另外,请查看 Django Extensions 包,它是 Django 自定义扩展的集合。 也许有人已经执行了你的命令! 已经有很多很多常见的任务命令。
错误 10:重新发明轮子
Django 和 Python 有数以千计的现成解决方案。 在你写一些不独特的东西之前尝试谷歌搜索; 可能已经存在功能丰富的解决方案。
试着让事情变得简单。 谷歌第一! 如果您找到优质的软件包,请安装、配置、扩展和集成到您的项目中,当然,如果有机会,请为开源做出贡献。
首先,这是我自己的 Django 公共包列表:
- Django Macros URL 通过使用宏可以轻松地在 Django 应用程序中编写(和读取)URL 模式。
- Django Templates Names 是一个小型组合,可让您轻松标准化 CBV 模板名称。
- django-split-settings 允许您将 Django 设置组织到多个文件和目录中。 轻松覆盖和修改设置。 在设置文件路径中使用通配符并将设置文件标记为可选。
不要重复自己(干)!
我真的很喜欢 DRY 方法; 这就是为什么我创建 Django 骨架作为一个便利工具,它具有一些非常简洁的开箱即用功能:
- 用于开发/生产的 Docker 映像,由 docker-compose 管理,可让您轻松编排容器列表。
- 用于生产部署的简单 Fabric 脚本。
- Django Split Settings 包的配置,包含基本和本地源的设置。
- Webpack 集成到项目中 - 只有
dist
文件夹会被 Django 通过collectstatic
命令收集。 - 配置了所有基本的 Django 设置和功能,如生产中的可缓存 Django 模板、散列静态文件、集成调试工具栏、日志记录等。
它是一个现成的 Django Skeleton,可用于您的下一个从头开始的项目,并有望通过引导您的项目为您节省大量时间。 Webpack 具有最少的基本配置,但它还安装了预配置的 SASS 以处理.scss
文件。