编码风格

当编写Django代码时,请遵从这些编码标准。

Pre-commit checks

pre-commit is a framework for managing pre-commit hooks. These hooks help to identify simple issues before committing code for review. By checking for these issues before code review it allows the reviewer to focus on the change itself, and it can also help to reduce the number of CI runs.

To use the tool, first install pre-commit and then the git hooks:

$ python -m pip install pre-commit
$ pre-commit install
...\> py -m pip install pre-commit
...\> pre-commit install

On the first commit pre-commit will install the hooks, these are installed in their own environments and will take a short while to install on the first run. Subsequent checks will be significantly faster. If an error is found an appropriate error message will be displayed. If the error was with black or isort then the tool will go ahead and fix them for you. Review the changes and re-stage for commit if you are happy with them.

Python 编码风格

  • All files should be formatted using the black auto-formatter. This will be run by pre-commit if that is configured.

  • The project repository includes an .editorconfig file. We recommend using a text editor with EditorConfig support to avoid indentation and whitespace issues. The Python files use 4 spaces for indentation and the HTML files use 2 spaces.

  • 除非另有约定的情况下,否则遵从 PEP8 编码规范。

    Use flake8 to check for problems in this area. Note that our setup.cfg file contains some excluded files (deprecated modules we don't care about cleaning up and some third-party code that Django vendors) as well as some excluded errors that we don't consider as gross violations. Remember that PEP 8 is only a guide, so respect the style of the surrounding code as a primary goal.

    An exception to PEP 8 is our rules on line lengths. Don't limit lines of code to 79 characters if it means the code looks significantly uglier or is harder to read. We allow up to 88 characters as this is the line length used by black. This check is included when you run flake8. Documentation, comments, and docstrings should be wrapped at 79 characters, even though PEP 8 suggests 72.

  • String variable interpolation may use %-formatting, f-strings, or str.format() as appropriate, with the goal of maximizing code readability.

    Final judgments of readability are left to the Merger's discretion. As a guide, f-strings should use only plain variable and property access, with prior local variable assignment for more complex cases:

    # Allowed
    f"hello {user}"
    f"hello {user.name}"
    f"hello {self.user.name}"
    
    # Disallowed
    f"hello {get_user()}"
    f"you are {user.age * 365.25} days old"
    
    # Allowed with local variable assignment
    user = get_user()
    f"hello {user}"
    user_days_old = user.age * 365.25
    f"you are {user_days_old} days old"
    

    f-strings should not be used for any string that may require translation, including error and logging messages. In general format() is more verbose, so the other formatting methods are preferred.

    Don't waste time doing unrelated refactoring of existing code to adjust the formatting method.

  • Avoid use of "we" in comments, e.g. "Loop over" rather than "We loop over".

  • 在给变量,函数和方法命名时候使用下划线而不是小驼峰 (例如: poll.get_unique_voters(), not poll.getUniqueVoters() )。

  • 类名使用“大驼峰命名法”(或者是用在能返回类的工厂函数上面)。

  • 对于文档字符串,遵从现有文档字符串风格和 PEP257 规范。

  • In tests, use assertRaisesMessage() and assertWarnsMessage() instead of assertRaises() and assertWarns() so you can check the exception or warning message. Use assertRaisesRegex() and assertWarnsRegex() only if you need regular expression matching.

    Use assertIs(…, True/False) for testing boolean values, rather than assertTrue() and assertFalse(), so you can check the actual boolean value, not the truthiness of the expression.

  • In test docstrings, state the expected behavior that each test demonstrates. Don't include preambles such as "Tests that" or "Ensures that".

    Reserve ticket references for obscure issues where the ticket has additional details that can't be easily described in docstrings or comments. Include the ticket number at the end of a sentence like this:

    def test_foo():
        """
        A test docstring looks like this (#123456).
        """
        ...
    

导入

  • Use isort to automate import sorting using the guidelines below.

    Quick start:

    $ python -m pip install "isort >= 5.1.0"
    $ isort .
    
    ...\> py -m pip install "isort >= 5.1.0"
    ...\> isort .
    

    这将从你的当前目录递归运行 isort ,修改所有不符合指引的文件,如果你不需要排序(例如:避免循环导入)请在你的导入里面像这样加上注释:

    import module  # isort:skip
    
  • 把导入这样进行分组:future库,python标准库,第三方库, 其他的Django组件,本地Django组件,try/excepts块。按照字母顺序对每个分组进行排序,按照完整的模块名称。在每个分组里面把import module句子放在 from module importobjects句子前面。对于其他Django组件使用绝对导入,对本地Django组件使用相对导入。

  • On each line, alphabetize the items with the upper case items grouped before the lowercase items.

  • 使用括号和4个连续空格构成的缩进去打破长行,在最后一个导入之后写一个逗号,把右括号放在单独一行。

    在最后一个导入和任何代码块之间留出一个空行,在第一个函数或类前面留出两个空行。

    例如(注释仅作为解释用途):

    django/contrib/admin/example.py
    # future
    from __future__ import unicode_literals
    
    # standard library
    import json
    from itertools import chain
    
    # third-party
    import bcrypt
    
    # Django
    from django.http import Http404
    from django.http.response import (
        Http404,
        HttpResponse,
        HttpResponseNotAllowed,
        StreamingHttpResponse,
        cookie,
    )
    
    # local Django
    from .models import LogEntry
    
    # try/except
    try:
        import yaml
    except ImportError:
        yaml = None
    
    CONSTANT = "foo"
    
    
    class Example:
        ...
    
  • Use convenience imports whenever available. For example, do this

    from django.views import View
    

    替换成:

    from django.views.generic.base import View
    

模版风格

  • 在Django模板代码中,在大括号和标签内容之间放置一个(只有一个)空格。

    这样做:

    {{ foo }}
    

    不要这样做:

    {{foo}}
    

视图风格

  • 在Django的视图中,第一个参数应该总是 request

    这样做:

    def my_view(request, foo):
        ...
    

    别这样做:

    def my_view(req, foo):
        ...
    

模型风格

  • 字段名称应当全部使用小写,使用下划线替代驼峰命名。

    这样做:

    class Person(models.Model):
        first_name = models.CharField(max_length=20)
        last_name = models.CharField(max_length=40)
    

    别这样做:

    class Person(models.Model):
        FirstName = models.CharField(max_length=20)
        Last_Name = models.CharField(max_length=40)
    
  •  class Meta 类应该位于定义字段之后,用一个空行分割字段定义和类定义。

    这样做:

    class Person(models.Model):
        first_name = models.CharField(max_length=20)
        last_name = models.CharField(max_length=40)
    
        class Meta:
            verbose_name_plural = "people"
    

    别这样做:

    class Person(models.Model):
        class Meta:
            verbose_name_plural = "people"
    
        first_name = models.CharField(max_length=20)
        last_name = models.CharField(max_length=40)
    
  • 模型内的类核方法的定义应该遵循以下顺序(不是所有项都是必须的):

    • 所有数据库字段
    • 自定义管理器属性
    • Meta
    • `` __str__() 方法``
    • save() 方法
    • get_absolute_url() 方法
    • 其他自定义方法
  • If choices is defined for a given model field, define each choice as a list of tuples, with an all-uppercase name as a class attribute on the model. Example:

    class MyModel(models.Model):
        DIRECTION_UP = "U"
        DIRECTION_DOWN = "D"
        DIRECTION_CHOICES = [
            (DIRECTION_UP, "Up"),
            (DIRECTION_DOWN, "Down"),
        ]
    

django.conf.settings 配置的使用

通常,模块不应使用存储在顶层的配置 django.conf.settings 配置(即在导入模块时进行评估)。 对此的解释如下:

Manual configuration of settings (i.e. not relying on the DJANGO_SETTINGS_MODULE environment variable) is allowed and possible as follows:

from django.conf import settings

settings.configure({}, SOME_SETTING="foo")

但是,如果在配置 settings.configure 行配置之前访问了任何设置,则此操作将无效。(在内部,settings 配置是一个 LazyObject ,当尚未访问设置时,它会自动配置自己)。

因此,如果有一个包含一些代码的模块,如下所示:

from django.conf import settings
from django.urls import get_callable

default_foo_view = get_callable(settings.FOO_VIEW)

…然后导入此模块将导致设置对象被配置。 这意味着第三方在顶层导入模块的能力与手动配置设置对象的能力不兼容,或者在某些情况下使其变得非常困难。

为了替换上面的代码,必须使用惰性级别或间接级别,例如 django.utils.functional.LazyObjectdjango.utils.functional.lazy() 或者 lambda

杂项

  • 为国际化标记所有的字符串;更多细节请参见 i18n documentation
  • Remove import statements that are no longer used when you change code. flake8 will identify these imports for you. If an unused import needs to remain for backwards-compatibility, mark the end of with # NOQA to silence the flake8 warning.
  • 按部就班的删除代码结尾那些增加不必要字节的空格,增加了显示混乱还可能导致偶尔的合并冲突。一些IDE可以通过配置自动删除它们,大多数版本控制工具可以在差异比较时高亮显示它们。
  • 请不要在你贡献的代码里面放入你的名字,我们的政策是把贡献者名字保存到随Django分发的AUTHORS文件里--—而不是分散在代码库里。如果你进行的不只是一次简单修改,请随意更改在你补丁中的AUTHORS文件。

JavaScript 样式

关于 Django 所使用 JavaScript 代码样式的更多内容,请参见 JavaScript