跳转到主要内容

自动升级您的Django项目代码。

项目描述

https://img.shields.io/github/actions/workflow/status/adamchainz/django-upgrade/main.yml.svg?branch=main&style=for-the-badge https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge https://img.shields.io/pypi/v/django-upgrade.svg?style=for-the-badge https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge pre-commit

自动升级您的Django项目代码。


通过我的书 提升您的Django开发体验 提高您的代码质量,该书涵盖了使用 pre-commit、django-upgrade 以及许多其他工具。我在编写这本书时创建了 django-upgrade!


安装

使用 pip

python -m pip install django-upgrade

支持Python 3.8到3.12。

(Python 3.12+ 是正确应用 f-strings 中修复所必需的。)

pre-commit 钩子

您还可以将 django-upgrade 作为 pre-commit 钩子安装。将以下内容添加到您的 repos 部分的 .pre-commit-config.yaml 文件中(在文档中),在所有代码格式化程序(如Black)之前

-   repo: https://github.com/adamchainz/django-upgrade
    rev: ""  # replace with latest tag on GitHub
    hooks:
    -   id: django-upgrade
        args: [--target-version, "5.0"]   # Replace with Django version

然后,升级您的整个项目

pre-commit run django-upgrade --all-files

提交任何更改。在此过程中,您的其他钩子将运行,可能会重新格式化 django-upgrade 的更改以匹配您的项目代码风格。

为了升级项目中添加的所有代码,请保留钩子安装。pre-commit 的 autoupdate 命令还可以让您利用 django-upgrade 的未来功能。

用法

django-upgrade 是一个命令行工具,用于原地重写文件。将您的 Django 版本作为 <major>.<minor> 格式传递给 --target-version 标志和文件列表。django-upgrade 的修复器将重写您的代码以避免 DeprecationWarning 并使用一些新功能。

例如

django-upgrade --target-version 5.0 example/core/models.py example/settings.py

django-upgrade 专注于升级您的代码,而不是使其看起来很漂亮。在像 Black 这样的格式化程序之前运行 django-upgrade。

django-upgrade 的某些修复器会修改需要迁移的模型

  • index_together

  • null_boolean_field

添加一个 测试挂起迁移 以确保您不会错过这些。

django-upgrade 没有递归遍历目录的能力。使用 pre-commit 集成、globbing 或其他技术来应用多个文件。一些修复器依赖于包含目录的名称来激活,因此请确保您以项目根目录相对路径运行 django-upgrade。例如,使用 git ls-files | xargs

git ls-files -z -- '*.py' | xargs -0 django-upgrade --target-version 5.0

…或者 PowerShell 的 ForEach-Object

git ls-files -- '*.py' | %{django-upgrade --target-version 5.0 $_}

修复器的完整列表如下。

选项

--target-version

目标 Django 版本,格式为 <major>.<minor>。django-upgrade 启用其修复器以支持目标版本及以下的所有版本。

此选项默认为 2.2,这是项目创建时支持的最旧版本。有关可用版本的列表,请参阅 django-upgrade --help

--exit-zero-even-if-changed

即使文件已更改,也以零返回代码退出。默认情况下,如果 django-upgrade 更改了任何文件,它将使用失败返回代码 1,这可能会停止脚本或 CI 流水线。

--only <fixer_name>

仅运行命名的修复器(名称见下文)。修复器必须通过 --target-version 启用。使用多个 --only 选项选择多个修复器。

例如

django-upgrade --target-version 5.0 --only admin_allow_tags --only admin_decorators example/core/admin.py

--skip <fixer_name>

跳过命名的修复器。使用多个 --skip 选项跳过多个修复器。

例如

django-upgrade --target-version 5.0 --skip admin_register example/core/admin.py

--list-fixers

列出所有可用的修复器名称,然后退出。列出修复器时忽略所有其他选项。

例如

django-upgrade --list-fixers

历史

django-codemod 是一个现有的、更完整的 Django 自动升级工具,由 Bruno Alla 编写。不幸的是,其底层库 LibCST 特别慢,使得在每次提交和 CI 中运行 django-codemod 都很烦人。

django-upgrade 是一个实验,使用与神奇的 pyupgrade 相同的技术重新实现此类工具。该工具依赖于标准库的 asttokenize 模块,后者通过 tokenize-rt 包装器。这意味着它始终很快,并支持 Python 的最新版本。

快速基准测试:运行 django-codemod 对一个中等大小的 Django 代码库进行测试,该代码库有 153k 行 Python 代码,耗时 133 秒。pyupgrade 和 django-upgrade 都不到 0.5 秒。

修复器

所有版本

以下修复器不受目标版本的影响。

版本化块

名称: versioned_branches

从比较 django.VERSIONif 语句中删除过时的比较和块。支持以下形式的比较

if django.VERSION <comparator> (<X>, <Y>):
    ...

其中 <comparator><<=>>= 中的一个,而 <X><Y> 是整数字面量。可以存在单个 else 块,但不支持 elif

-if django.VERSION < (4, 1):
-    class RenameIndex:
-        ...

-if django.VERSION >= (4, 1):
-    constraint.validate()
-else:
-    custom_validation(constraint)
+constraint.validate()

另请参阅pyupgrade 的类似功能,该功能从对 Python 版本的检查中删除了过时代码。

Django 5.1

发行说明

CheckConstraint 条件参数

名称: check_constraint_condition

将调用旧 check 参数的 CheckConstraint 和内置子类的旧名称重写为新名称 condition

由于 ast.keyword 的更改,需要 Python 3.9+。

-CheckConstraint(check=Q(amount__gte=0))
+CheckConstraint(condition=Q(amount__gte=0))

Django 5.0

发行说明

format_html() 调用

名称: format_html

重写没有 argskwargs 但使用 str.format()format_html() 调用。这种调用很可能错误地应用了未转义的格式化,使其容易受到 HTML 注入的攻击。这就是为什么在 问题 #34609 中弃用了不带任何参数或关键字参数的 format_html() 调用的原因。

 from django.utils.html import format_html

-format_html("<marquee>{}</marquee>".format(message))
+format_html("<marquee>{}</marquee>", message)

-format_html("<marquee>{name}</marquee>".format(name=name))
+format_html("<marquee>{name}</marquee>", name=name)

Django 4.2

发行说明

STORAGES 设置

名称: settings_storages

将过时的设置 DEFAULT_FILE_STORAGESTATICFILES_STORAGE 合并到新的 STORAGES 设置中,在设置文件中。仅当所有旧设置都定义为字符串,在模块级别,并且没有定义 STORAGES 设置时才适用。

设置文件被启发式地检测为路径中包含整个单词“settings”的模块。例如 myproject/settings.pymyproject/settings/production.py

-DEFAULT_FILE_STORAGE = "example.storages.ExtendedFileSystemStorage"
-STATICFILES_STORAGE = "example.storages.ExtendedS3Storage"
+STORAGES = {
+    "default": {
+        "BACKEND": "example.storages.ExtendedFileSystemStorage",
+    },
+    "staticfiles": {
+        "BACKEND": "example.storages.ExtendedS3Storage",
+    },
+}

如果模块有一个 from ... import *,其中模块路径包含“settings”,则 django-upgrade 会据此推测从中导入了基本 STORAGES 设置。然后它使用 ** 来扩展当前模块中的任何值

 from example.settings.base import *
-DEFAULT_FILE_STORAGE = "example.storages.S3Storage"
+STORAGES = {
+    **STORAGES,
+    "default": {
+        "BACKEND": "example.storages.S3Storage",
+    },
+}

测试客户端 HTTP 标头

名称: test_http_headers

将 HTTP 标头从旧的 WSGI 关键字参数格式转换为使用新的 headers 字典,用于

  • Client 方法,如 self.client.get()

  • Client 实例化

  • RequestFactory 实例化

由于 ast.keyword 的更改,需要 Python 3.9+。

-response = self.client.get("/", HTTP_ACCEPT="text/plain")
+response = self.client.get("/", headers={"accept": "text/plain"})

 from django.test import Client
-Client(HTTP_ACCEPT_LANGUAGE="fr-fr")
+Client(headers={"accept-language": "fr-fr"})

 from django.test import RequestFactory
-RequestFactory(HTTP_USER_AGENT="curl")
+RequestFactory(headers={"user-agent": "curl"})

index_together 弃用

名称: index_together

index_together 声明重写到模型 Meta 类中的 indexes 声明中。

 from django.db import models

 class Duck(models.Model):
     class Meta:
-       index_together = [["bill", "tail"]]
+       indexes = [models.Index(fields=["bill", "tail"])]

assertFormsetErrorassertQuerysetEqual

名称: assert_set_methods

将这些测试用例方法的调用从旧名称重写到使用大写“Set”的新名称。

-self.assertFormsetError(response.context["form"], "username", ["Too long"])
+self.assertFormSetError(response.context["form"], "username", ["Too long"])

-self.assertQuerysetEqual(authors, ["Brad Dayley"], lambda a: a.name)
+self.assertQuerySetEqual(authors, ["Brad Dayley"], lambda a: a.name)

Django 4.1

发行说明

django.utils.timezone.utc 弃用

名称: utils_timezone

django.utils.timezone.utc 的导入重写为使用 datetime.timezone.utc。需要现有 datetime 模块的导入。

 import datetime
-from django.utils.timezone import utc

-calculate_some_datetime(utc)
+calculate_some_datetime(datetime.timezone.utc)
 import datetime as dt
 from django.utils import timezone


-do_a_thing(timezone.utc)
+do_a_thing(dt.timezone.utc)

assertFormError()assertFormsetError()

名称: assert_form_error

将对这些测试用例方法的旧签名调用重写为新签名。

-self.assertFormError(response, "form", "username", ["Too long"])
+self.assertFormError(response.context["form"], "username", ["Too long"])

-self.assertFormError(response, "form", "username", None)
+self.assertFormError(response.context["form"], "username", [])

-self.assertFormsetError(response, "formset", 0, "username", ["Too long"])
+self.assertFormsetError(response.context["formset"], 0, "username", ["Too long"])

-self.assertFormsetError(response, "formset", 0, "username", None)
+self.assertFormsetError(response.context["formset"], 0, "username", [])

Django 4.0

发行说明

USE_L10N

名称: use_l10n

如果设置为默认值 True,则删除已弃用的 USE_L10N 设置。

设置文件被启发式地检测为路径中包含整个单词“settings”的模块。例如 myproject/settings.pymyproject/settings/production.py

-USE_L10N = True

lookup_needs_distinct

名称: admin_lookup_needs_distinct

将未记录的 django.contrib.admin.utils.lookup_needs_distinct 重命名为 lookup_spawns_duplicates

-from django.contrib.admin.utils import lookup_needs_distinct
+from django.contrib.admin.utils import lookup_spawns_duplicates

-if lookup_needs_distinct(self.opts, search_spec):
+if lookup_spawns_duplicates(self.opts, search_spec):
    ...

兼容性导入

重写一些兼容性导入。

  • django.utils.translation.template.TRANSLATOR_COMMENT_MARKdjango.template.base

-from django.template.base import TRANSLATOR_COMMENT_MARK
+from django.utils.translation.template import TRANSLATOR_COMMENT_MARK

Django 3.2

发行说明

@admin.action()

名称: admin_decorators

将具有管理员操作属性分配给函数的重写为使用新的 @admin.action() 装饰器。这仅适用于使用 from django.contrib import adminfrom django.contrib.gis import admin 的文件。

 from django.contrib import admin

 # Module-level actions:

+@admin.action(
+    description="Publish articles",
+)
 def make_published(modeladmin, request, queryset):
     ...

-make_published.short_description = "Publish articles"

 # …and within classes:

 @admin.register(Book)
 class BookAdmin(admin.ModelAdmin):
+    @admin.action(
+        description="Unpublish articles",
+        permissions=("unpublish",),
+    )
     def make_unpublished(self, request, queryset):
         ...

-    make_unpublished.allowed_permissions = ("unpublish",)
-    make_unpublished.short_description = "Unpublish articles"

@admin.display()

名称: admin_decorators

将具有管理员显示属性分配给函数的重写为使用新的 @admin.display() 装饰器。这仅适用于使用 from django.contrib import adminfrom django.contrib.gis import admin 的文件。

 from django.contrib import admin

 # Module-level display functions:

+@admin.display(
+    description="NAME",
+)
 def upper_case_name(obj):
     ...

-upper_case_name.short_description = "NAME"

 # …and within classes:

 @admin.register(Book)
 class BookAdmin(admin.ModelAdmin):
+    @admin.display(
+        description='Is Published?',
+        boolean=True,
+        ordering='-publish_date',
+    )
     def is_published(self, obj):
         ...

-    is_published.boolean = True
-    is_published.admin_order_field = '-publish_date'
-    is_published.short_description = 'Is Published?'

BaseCommand.requires_system_checks

名称: management_commands

将管理命令类中的 requires_system_checks 属性从布尔值重写为 "__all__"[],根据需要。这仅适用于命令文件,这些文件被启发式地检测为路径中包含 management/commands 的文件。

 from django.core.management.base import BaseCommand

 class Command(BaseCommand):
-    requires_system_checks = True
+    requires_system_checks = "__all__"

 class SecondCommand(BaseCommand):
-    requires_system_checks = False
+    requires_system_checks = []

EmailValidator

名称: email_validator

将关键字参数 whitelist 重命名为新名称 allowlist

 from django.core.validators import EmailValidator

-EmailValidator(whitelist=["example.com"])
+EmailValidator(allowlist=["example.com"])

default_app_config

名称: default_app_config

__init__.py 文件中删除模块级 default_app_config 赋值。

-default_app_config = 'my_app.apps.AppConfig'

Django 3.1

发行说明

JSONField

名称: compatibility_imports

JSONField 和相关转换类的导入从 django.contrib.postgres 重写为新数据库版本。忽略迁移文件中的使用,因为 Django 保留旧类以支持旧迁移。在修复更改模型后,您将需要创建迁移。

-from django.contrib.postgres.fields import JSONField
+from django.db.models import JSONField

PASSWORD_RESET_TIMEOUT_DAYS

名称: password_reset_timeout_days

将设置 PASSWORD_RESET_TIMEOUT_DAYS 重写为 PASSWORD_RESET_TIMEOUT,并添加乘以一天中的秒数。

设置文件被启发式地检测为路径中包含整个单词“settings”的模块。例如 myproject/settings.pymyproject/settings/production.py

-PASSWORD_RESET_TIMEOUT_DAYS = 4
+PASSWORD_RESET_TIMEOUT = 60 * 60 * 24 * 4

Signal

名称: signal_providing_args

删除仅文档化的已弃用 providing_args 参数。

 from django.dispatch import Signal
-my_cool_signal = Signal(providing_args=["documented", "arg"])
+my_cool_signal = Signal()

get_random_string

名称: crypto_get_random_string

注入现在必需的 length 参数,其之前的默认值为 12

 from django.utils.crypto import get_random_string
-key = get_random_string(allowed_chars="01234567899abcdef")
+key = get_random_string(length=12, allowed_chars="01234567899abcdef")

NullBooleanField

名称: null_boolean_field

将模型字段 NullBooleanField() 转换为 BooleanField(null=True)。仅在模型文件中应用,不在迁移文件中应用,因为 Django 保留旧类以支持旧迁移。在修复更改模型后,您将需要创建迁移。

-from django.db.models import Model, NullBooleanField
+from django.db.models import Model, BooleanField

 class Book(Model):
-    valuable = NullBooleanField("Valuable")
+    valuable = BooleanField("Valuable", null=True)

ModelMultipleChoiceField

名称: forms_model_multiple_choice_field

在表单 ModelMultipleChoiceField 中,将 list 错误信息键替换为 list_invalid

-forms.ModelMultipleChoiceField(error_messages={"list": "Enter multiple values."})
+forms.ModelMultipleChoiceField(error_messages={"invalid_list": "Enter multiple values."})

Django 3.0

发行说明

django.utils.encoding 别名

名称: utils_encoding

smart_text() 重写为 smart_str(),将 force_text() 重写为 force_str()

-from django.utils.encoding import force_text, smart_text
+from django.utils.encoding import force_str, smart_str


-force_text("yada")
-smart_text("yada")
+force_str("yada")
+smart_str("yada")

django.utils.http 弃用

名称: utils_http

urlquote()urlquote_plus()urlunquote()urlunquote_plus() 函数重写为 urllib.parse 版本。还将内部函数 is_safe_url() 重写为 url_has_allowed_host_and_scheme()

-from django.utils.http import urlquote
+from urllib.parse import quote

-escaped_query_string = urlquote(query_string)
+escaped_query_string = quote(query_string)

django.utils.text 弃用

名称: utils_text

使用标准库 html.escape() 重写了 unescape_entities()

-from django.utils.text import unescape_entities
+import html

-unescape_entities("some input string")
+html.escape("some input string")

django.utils.translation 弃用

名称: utils_translation

ugettext()ugettext_lazy()ugettext_noop()ungettext()ungettext_lazy() 函数重写为它们的非-u-前缀版本。

-from django.utils.translation import ugettext as _, ungettext
+from django.utils.translation import gettext as _, ngettext

-ungettext("octopus", "octopodes", n)
+ngettext("octopus", "octopodes", n)

Django 2.2

发行说明

HttpRequest.headers

名称: request_headers

将使用 request.META 读取 HTTP 头部信息重写为使用 request.headers。根据 HTTP/2 规范,头部查找将转换为小写。

-request.META['HTTP_ACCEPT_ENCODING']
+request.headers['accept-encoding']

-self.request.META.get('HTTP_SERVER', '')
+self.request.headers.get('server', '')

-request.META.get('CONTENT_LENGTH')
+request.headers.get('content-length')

-"HTTP_SERVER" in request.META
+"server" in request.headers

QuerySetPaginator

名称: queryset_paginator

将弃用的别名 django.core.paginator.QuerySetPaginator 重写为 Paginator

-from django.core.paginator import QuerySetPaginator
+from django.core.paginator import Paginator

-QuerySetPaginator(...)
+Paginator(...)

FixedOffset

名称: timezone_fixedoffset

将弃用的类 FixedOffset(x, y)) 重写为 timezone(timedelta(minutes=x), y)

已知限制:如果使用仅含 *args**kwargsFixedOffset 调用,则此修正器将使代码损坏并产生 ImportError

-from django.utils.timezone import FixedOffset
-FixedOffset(120, "Super time")
+from datetime import timedelta, timezone
+timezone(timedelta(minutes=120), "Super time")

FloatRangeField

名称: postgres_float_range_field

将使用 FloatRangeField 的模型和表单字段重写为 DecimalRangeField,从相关的 django.contrib.postgres 模块。

 from django.db.models import Model
-from django.contrib.postgres.fields import FloatRangeField
+from django.contrib.postgres.fields import DecimalRangeField

 class MyModel(Model):
-    my_field = FloatRangeField("My range of numbers")
+    my_field = DecimalRangeField("My range of numbers")

TestCase 类数据库声明

名称: testcase_databases

将 Django 的 TestCase 类的 allow_database_queriesmulti_db 属性重写为新的 databases 属性。这仅适用于测试文件,这些文件通过其路径中的“test”或“tests”位置进行启发式检测。

注意,这将仅重写为 databases = []databases = "__all__"。在多个数据库的情况下,可以通过限制测试案例到它们所需的数据库来节省一些测试时间(这就是为什么 Django 进行了更改)。

 from django.test import SimpleTestCase

 class MyTests(SimpleTestCase):
-    allow_database_queries = True
+    databases = "__all__"

     def test_something(self):
         self.assertEqual(2 * 2, 4)

Django 2.1

发行说明

目前还没有修正器。

Django 2.0

发行说明

URL

名称: django_urls

include()url() 的导入从 django.conf.urls 重写为 django.urls。使用兼容正则表达式的 url() 调用被重写为新的 path() 语法,否则转换为调用 re_path()

-from django.conf.urls import include, url
+from django.urls import include, path, re_path

 urlpatterns = [
-    url(r'^$', views.index, name='index'),
+    path('', views.index, name='index'),
-    url(r'^about/$', views.about, name='about'),
+    path('about/', views.about, name='about'),
-    url(r'^post/(?P<slug>[-a-zA-Z0-9_]+)/$', views.post, name='post'),
+    path('post/<slug:slug>/', views.post, name='post'),
-    url(r'^weblog', include('blog.urls')),
+    re_path(r'^weblog', include('blog.urls')),
 ]

现有的 re_path() 调用在符合条件时也会重写为 path() 语法。

-from django.urls import include, re_path
+from django.urls import include, path, re_path

 urlpatterns = [
-    re_path(r'^about/$', views.about, name='about'),
+    path('about/', views.about, name='about'),
     re_path(r'^post/(?P<slug>[\w-]+)/$', views.post, name='post'),
 ]

以下是将转换为使用 path converters 的兼容正则表达式:

  • [^/]+str

  • [0-9]+int

  • [-a-zA-Z0-9_]+slug

  • [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}uuid

  • .+path

这些来自路径转换类。

在某些情况下,此更改会改变传递给视图的参数类型,从 str 更改为转换类型(例如 int)。这不能保证向后兼容:有可能会出现视图期望字符串而不是转换类型的情况。但是,从实用主义的角度来看,似乎有 99.9% 的视图不需要字符串,而是使用字符串或转换类型。因此,你应该在修复器进行任何更改后测试受影响的路径。

注意,[\w-] 有时用于 slug,但它不会被转换,因为它可能不兼容。该模式匹配所有 Unicode 单词字符,如“α”,而 Django 的 slug 转换器仅匹配拉丁字符。

lru_cache

名称: compatibility_imports

lru_cache 的导入从 django.utils.functional 重写为使用 functools

-from django.utils.functional import lru_cache
+from functools import lru_cache

ContextDecorator

ContextDecorator 的导入从 django.utils.decorators 重写为使用 contextlib

-from django.utils.decorators import ContextDecorator
+from contextlib import ContextDecorator

<func>.allow_tags = True

名称: admin_allow_tags

移除了将 allow_tags 属性赋值为 True 的操作。这是一个管理员功能,允许显示函数返回 HTML 而不将其标记为不安全,在 Django 1.9 中已弃用。实际上,大多数返回 HTML 的显示函数已经使用 format_html() 或类似的功能,因此该属性不是必需的。这仅适用于使用 from django.contrib import adminfrom django.contrib.gis import admin 的文件。

 from django.contrib import admin

 def upper_case_name(obj):
     ...

-upper_case_name.allow_tags = True

Django 1.11

发行说明

兼容性导入

名称: compatibility_imports

重写一些兼容性导入。

  • django.core.exceptions.EmptyResultSetdjango.db.models.querydjango.db.models.sqldjango.db.models.sql.datastructures

  • django.core.exceptions.FieldDoesNotExistdjango.db.models.fields

尽管在 Django 3.1 发布说明 中提到,但这些功能自 Django 1.11 以来一直是可能的。

-from django.db.models.query import EmptyResultSet
+from django.core.exceptions import EmptyResultSet

-from django.db.models.fields import FieldDoesNotExist
+from django.core.exceptions import FieldDoesNotExist

Django 1.10

发行说明

request.user 布尔属性

名称: request_user_attributes

按照 弃用说明,重写了 request.user.is_authenticated()request.user.is_anonymous() 的调用,去除了括号。

-request.user.is_authenticated()
+request.user.is_authenticated

-self.request.user.is_anonymous()
+self.request.user.is_anonymous

兼容性导入

重写一些兼容性导入。

  • django.templatetags.static.staticdjango.contrib.staticfiles.templatetags.staticfiles

    (虽然在Django 2.1版本说明中提到过,但这一功能自Django 1.10以来就已经可能实现。)

  • django.urls.*django.core.urlresolvers.*

-from django.contrib.staticfiles.templatetags.staticfiles import static
+from django.templatetags.static import static

-from django.core.urlresolvers import reverse
+from django.urls import reverse

-from django.core.urlresolvers import resolve
+from django.urls import resolve

Django 1.9

发行说明

on_delete 参数

名称: on_delete

on_delete=models.CASCADE 添加到 ForeignKeyOneToOneField

 from django.db import models

-models.ForeignKey("auth.User")
+models.ForeignKey("auth.User", on_delete=models.CASCADE)

-models.OneToOneField("auth.User")
+models.OneToOneField("auth.User", on_delete=models.CASCADE)

此修复程序还支持从导入

-from django.db.models import ForeignKey
+from django.db.models import CASCADE, ForeignKey

-ForeignKey("auth.User")
+ForeignKey("auth.User", on_delete=CASCADE)

DATABASES

名称: settings_database_postgresql

更新 DATABASES 设置的后端路径 django.db.backends.postgresql_psycopg2,以使用重命名的版本 django.db.backends.postgresql

设置文件被启发式地检测为路径中包含整个单词“settings”的模块。例如 myproject/settings.pymyproject/settings/production.py

 DATABASES = {
     "default": {
-        "ENGINE": "django.db.backends.postgresql_psycopg2",
+        "ENGINE": "django.db.backends.postgresql",
         "NAME": "mydatabase",
         "USER": "mydatabaseuser",
         "PASSWORD": "mypassword",
         "HOST": "127.0.0.1",
         "PORT": "5432",
     }
 }

兼容性导入

名称: compatibility_imports

重写一些兼容性导入。

  • django.forms.utils.pretty_namedjango.forms.forms

  • django.forms.boundfield.BoundFielddjango.forms.forms

  • django.forms.widgets.SelectDateWidgetdjango.forms.extras

虽然在Django 3.1版本说明中提到过,但这些功能自Django 1.9以来就已经可能实现。

-from django.forms.forms import pretty_name
+from django.forms.utils import pretty_name

Django 1.8

发行说明

目前还没有修正器。

Django 1.7

发行说明

管理模型注册

名称: admin_register

当符合条件时,将 admin.site.register() 调用重写为新的 @admin.register() 装饰器语法。这仅适用于使用 from django.contrib import adminfrom django.contrib.gis import admin 的文件。

 from django.contrib import admin

+@admin.register(MyModel1, MyModel2)
 class MyCustomAdmin(admin.ModelAdmin):
     ...

-admin.site.register(MyModel1, MyCustomAdmin)
-admin.site.register(MyModel2, MyCustomAdmin)

这也适用于自定义管理站点。此类调用基于以下三个标准进行启发式检测

  1. 被调用 register() 方法的对象名称以 site 结尾。

  2. 注册的类名称以 Admin 结尾。

  3. 文件名在其路径中包含单词 admin

from myapp.admin import custom_site
from django.contrib import admin

+@admin.register(MyModel)
+@admin.register(MyModel, site=custom_site)
class MyModelAdmin(admin.ModelAdmin):
    pass

-custom_site.register(MyModel, MyModelAdmin)
-admin.site.register(MyModel, MyModelAdmin)

如果 register() 调用之前有一个包含相同模型的 unregister() 调用,则忽略该调用。

from django.contrib import admin


class MyCustomAdmin(admin.ModelAdmin):
    ...


admin.site.unregister(MyModel1)
admin.site.register(MyModel1, MyCustomAdmin)

兼容性导入

重写一些兼容性导入。

  • django.contrib.admin.helpers.ACTION_CHECKBOX_NAMEdjango.contrib.admin

  • django.template.context.BaseContextdjango.template.context.Contextdjango.template.context.ContextPopExceptiondjango.template.context.RequestContextdjango.template.base

-from django.contrib.admin import ACTION_CHECKBOX_NAME
+from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME

-from django.template.base import Context
+from django.template.context import Context

项目详情


下载文件

下载适用于您的平台文件。如果您不确定选择哪个,请了解更多关于安装软件包的信息。

源分发

django_upgrade-1.21.0.tar.gz (66.7 kB 查看哈希值)

上传于

构建分发

django_upgrade-1.21.0-py3-none-any.whl (67.8 kB 查看哈希值)

上传于 Python 3

支持者