mirror of
				https://github.com/django/django.git
				synced 2025-10-25 06:36:07 +00:00 
			
		
		
		
	This branch migrates setuptools configuration from setup.py/setup.cfg to
pyproject.toml. In order to ensure that the generated binary files have
consistent casing (both the tarball and the wheel), setuptools version
is limited to ">=61.0.0,<69.3.0".
Configuration for flake8 was moved to a dedicated .flake8 file since
it cannot be configured via pyproject.toml.
Also, __pycache__ exclusion was removed from MANIFEST and the
extras/Makefile was replaced with a simpler build command.
Co-authored-by: Nick Pope <nick@nickpope.me.uk>
Backport of 4686541691 from main.
		
	
		
			
				
	
	
		
			511 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			511 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ============
 | |
| Coding style
 | |
| ============
 | |
| 
 | |
| Please follow these coding standards when writing code for inclusion in Django.
 | |
| 
 | |
| .. _coding-style-pre-commit:
 | |
| 
 | |
| Pre-commit checks
 | |
| =================
 | |
| 
 | |
| `pre-commit <https://pre-commit.com>`_ 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:
 | |
| 
 | |
| .. console::
 | |
| 
 | |
|     $ python -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.
 | |
| 
 | |
| .. _coding-style-python:
 | |
| 
 | |
| Python style
 | |
| ============
 | |
| 
 | |
| * 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.
 | |
| 
 | |
| * Unless otherwise specified, follow :pep:`8`.
 | |
| 
 | |
|   Use :pypi:`flake8` to check for problems in this area. Note that our
 | |
|   ``.flake8`` 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
 | |
|   :py:ref:`%-formatting <old-string-formatting>`, :py:ref:`f-strings
 | |
|   <f-strings>`, or :py:meth:`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".
 | |
| 
 | |
| * Use underscores, not camelCase, for variable, function and method names
 | |
|   (i.e. ``poll.get_unique_voters()``, not ``poll.getUniqueVoters()``).
 | |
| 
 | |
| * Use ``InitialCaps`` for class names (or for factory functions that
 | |
|   return classes).
 | |
| 
 | |
| * In docstrings, follow the style of existing docstrings and :pep:`257`.
 | |
| 
 | |
| * In tests, use
 | |
|   :meth:`~django.test.SimpleTestCase.assertRaisesMessage` and
 | |
|   :meth:`~django.test.SimpleTestCase.assertWarnsMessage`
 | |
|   instead of :meth:`~unittest.TestCase.assertRaises` and
 | |
|   :meth:`~unittest.TestCase.assertWarns` so you can check the
 | |
|   exception or warning message. Use :meth:`~unittest.TestCase.assertRaisesRegex`
 | |
|   and :meth:`~unittest.TestCase.assertWarnsRegex` only if you need regular
 | |
|   expression matching.
 | |
| 
 | |
|   Use :meth:`assertIs(…, True/False)<unittest.TestCase.assertIs>` for testing
 | |
|   boolean values, rather than :meth:`~unittest.TestCase.assertTrue` and
 | |
|   :meth:`~unittest.TestCase.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).
 | |
|         """
 | |
|         ...
 | |
| 
 | |
| .. _coding-style-imports:
 | |
| 
 | |
| Imports
 | |
| =======
 | |
| 
 | |
| * Use :pypi:`isort` to automate import sorting using the guidelines below.
 | |
| 
 | |
|   Quick start:
 | |
| 
 | |
|   .. console::
 | |
| 
 | |
|       $ python -m pip install "isort >= 5.1.0"
 | |
|       $ isort .
 | |
| 
 | |
|   This runs ``isort`` recursively from your current directory, modifying any
 | |
|   files that don't conform to the guidelines. If you need to have imports out
 | |
|   of order (to avoid a circular import, for example) use a comment like this::
 | |
| 
 | |
|       import module  # isort:skip
 | |
| 
 | |
| * Put imports in these groups: future, standard library, third-party libraries,
 | |
|   other Django components, local Django component, try/excepts. Sort lines in
 | |
|   each group alphabetically by the full module name. Place all ``import module``
 | |
|   statements before ``from module import objects`` in each section. Use absolute
 | |
|   imports for other Django components and relative imports for local components.
 | |
| 
 | |
| * On each line, alphabetize the items with the upper case items grouped before
 | |
|   the lowercase items.
 | |
| 
 | |
| * Break long lines using parentheses and indent continuation lines by 4 spaces.
 | |
|   Include a trailing comma after the last import and put the closing
 | |
|   parenthesis on its own line.
 | |
| 
 | |
|   Use a single blank line between the last import and any module level code,
 | |
|   and use two blank lines above the first function or class.
 | |
| 
 | |
|   For example (comments are for explanatory purposes only):
 | |
| 
 | |
|   .. code-block:: python
 | |
|       :caption: ``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
 | |
| 
 | |
|   instead of::
 | |
| 
 | |
|       from django.views.generic.base import View
 | |
| 
 | |
| Template style
 | |
| ==============
 | |
| 
 | |
| Follow the below rules in Django template code.
 | |
| 
 | |
| * ``{% extends %}`` should be the first non-comment line.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% extends "base.html" %}
 | |
| 
 | |
|       {% block content %}
 | |
|         <h1 class="font-semibold text-xl">
 | |
|           {{ pages.title }}
 | |
|         </h1>
 | |
|       {% endblock content %}
 | |
| 
 | |
|   Or this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {# This is a comment #}
 | |
|       {% extends "base.html" %}
 | |
| 
 | |
|       {% block content %}
 | |
|         <h1 class="font-semibold text-xl">
 | |
|           {{ pages.title }}
 | |
|         </h1>
 | |
|       {% endblock content %}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% load i18n %}
 | |
|       {% extends "base.html" %}
 | |
| 
 | |
|       {% block content %}
 | |
|         <h1 class="font-semibold text-xl">
 | |
|           {{ pages.title }}
 | |
|         </h1>
 | |
|       {% endblock content %}
 | |
| 
 | |
| * Put exactly one space between ``{{``, variable contents, and ``}}``.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {{ user }}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {{user}}
 | |
| 
 | |
| * In ``{% load ... %}``, list libraries in alphabetical order.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% load i18n l10 tz %}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% load l10 i18n tz %}
 | |
| 
 | |
| * Put exactly one space between ``{%``, tag contents, and ``%}``.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% load humanize %}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {%load humanize%}
 | |
| 
 | |
| * Put the ``{% block %}`` tag name in the ``{% endblock %}`` tag if it is not
 | |
|   on the same line.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% block header %}
 | |
| 
 | |
|         Code goes here
 | |
| 
 | |
|       {% endblock header %}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% block header %}
 | |
| 
 | |
|         Code goes here
 | |
| 
 | |
|       {% endblock %}
 | |
| 
 | |
| * Inside curly braces, separate tokens by single spaces, except for around the
 | |
|   ``.`` for attribute access and the ``|`` for a filter.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|     {% if user.name|lower == "admin" %}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|     {% if user . name | lower  ==  "admin" %}
 | |
| 
 | |
|     {{ user.name | upper }}
 | |
| 
 | |
| * Within a template using ``{% extends %}``, avoid indenting top-level
 | |
|   ``{% block %}`` tags.
 | |
| 
 | |
|   Do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% extends "base.html" %}
 | |
| 
 | |
|       {% block content %}
 | |
| 
 | |
|   Don't do this:
 | |
| 
 | |
|   .. code-block:: html+django
 | |
| 
 | |
|       {% extends "base.html" %}
 | |
| 
 | |
|         {% block content %}
 | |
|         ...
 | |
| 
 | |
| View style
 | |
| ==========
 | |
| 
 | |
| * In Django views, the first parameter in a view function should be called
 | |
|   ``request``.
 | |
| 
 | |
|   Do this::
 | |
| 
 | |
|       def my_view(request, foo): ...
 | |
| 
 | |
|   Don't do this::
 | |
| 
 | |
|       def my_view(req, foo): ...
 | |
| 
 | |
| Model style
 | |
| ===========
 | |
| 
 | |
| * Field names should be all lowercase, using underscores instead of
 | |
|   camelCase.
 | |
| 
 | |
|   Do this::
 | |
| 
 | |
|       class Person(models.Model):
 | |
|           first_name = models.CharField(max_length=20)
 | |
|           last_name = models.CharField(max_length=40)
 | |
| 
 | |
|   Don't do this::
 | |
| 
 | |
|       class Person(models.Model):
 | |
|           FirstName = models.CharField(max_length=20)
 | |
|           Last_Name = models.CharField(max_length=40)
 | |
| 
 | |
| * The ``class Meta`` should appear *after* the fields are defined, with
 | |
|   a single blank line separating the fields and the class definition.
 | |
| 
 | |
|   Do this::
 | |
| 
 | |
|       class Person(models.Model):
 | |
|           first_name = models.CharField(max_length=20)
 | |
|           last_name = models.CharField(max_length=40)
 | |
| 
 | |
|           class Meta:
 | |
|               verbose_name_plural = "people"
 | |
| 
 | |
|   Don't do this::
 | |
| 
 | |
|       class Person(models.Model):
 | |
|           class Meta:
 | |
|               verbose_name_plural = "people"
 | |
| 
 | |
|           first_name = models.CharField(max_length=20)
 | |
|           last_name = models.CharField(max_length=40)
 | |
| 
 | |
| * The order of model inner classes and standard methods should be as
 | |
|   follows (noting that these are not all required):
 | |
| 
 | |
|   * All database fields
 | |
|   * Custom manager attributes
 | |
|   * ``class Meta``
 | |
|   * ``def __str__()``
 | |
|   * ``def save()``
 | |
|   * ``def get_absolute_url()``
 | |
|   * Any custom methods
 | |
| 
 | |
| * If ``choices`` is defined for a given model field, define each choice as a
 | |
|   mapping, 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",
 | |
|         }
 | |
| 
 | |
|   Alternatively, consider using :ref:`field-choices-enum-types`::
 | |
| 
 | |
|     class MyModel(models.Model):
 | |
|         class Direction(models.TextChoices):
 | |
|             UP = "U", "Up"
 | |
|             DOWN = "D", "Down"
 | |
| 
 | |
| Use of ``django.conf.settings``
 | |
| ===============================
 | |
| 
 | |
| Modules should not in general use settings stored in ``django.conf.settings``
 | |
| at the top level (i.e. evaluated when the module is imported). The explanation
 | |
| for this is as follows:
 | |
| 
 | |
| Manual configuration of settings (i.e. not relying on the
 | |
| :envvar:`DJANGO_SETTINGS_MODULE` environment variable) is allowed and possible
 | |
| as follows::
 | |
| 
 | |
|     from django.conf import settings
 | |
| 
 | |
|     settings.configure({}, SOME_SETTING="foo")
 | |
| 
 | |
| However, if any setting is accessed before the ``settings.configure`` line,
 | |
| this will not work. (Internally, ``settings`` is a ``LazyObject`` which
 | |
| configures itself automatically when the settings are accessed if it has not
 | |
| already been configured).
 | |
| 
 | |
| So, if there is a module containing some code as follows::
 | |
| 
 | |
|     from django.conf import settings
 | |
|     from django.urls import get_callable
 | |
| 
 | |
|     default_foo_view = get_callable(settings.FOO_VIEW)
 | |
| 
 | |
| ...then importing this module will cause the settings object to be configured.
 | |
| That means that the ability for third parties to import the module at the top
 | |
| level is incompatible with the ability to configure the settings object
 | |
| manually, or makes it very difficult in some circumstances.
 | |
| 
 | |
| Instead of the above code, a level of laziness or indirection must be used,
 | |
| such as ``django.utils.functional.LazyObject``,
 | |
| ``django.utils.functional.lazy()`` or ``lambda``.
 | |
| 
 | |
| Miscellaneous
 | |
| =============
 | |
| 
 | |
| * Mark all strings for internationalization; see the :doc:`i18n
 | |
|   documentation </topics/i18n/index>` for details.
 | |
| 
 | |
| * Remove ``import`` statements that are no longer used when you change code.
 | |
|   :pypi:`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.
 | |
| 
 | |
| * Systematically remove all trailing whitespaces from your code as those
 | |
|   add unnecessary bytes, add visual clutter to the patches and can also
 | |
|   occasionally cause unnecessary merge conflicts. Some IDE's can be
 | |
|   configured to automatically remove them and most VCS tools can be set to
 | |
|   highlight them in diff outputs.
 | |
| 
 | |
| * Please don't put your name in the code you contribute. Our policy is to
 | |
|   keep contributors' names in the ``AUTHORS`` file distributed with Django
 | |
|   -- not scattered throughout the codebase itself. Feel free to include a
 | |
|   change to the ``AUTHORS`` file in your patch if you make more than a
 | |
|   single trivial change.
 | |
| 
 | |
| JavaScript style
 | |
| ================
 | |
| 
 | |
| For details about the JavaScript code style used by Django, see
 | |
| :doc:`javascript`.
 | |
| 
 | |
| .. _black: https://black.readthedocs.io/en/stable/
 | |
| .. _editorconfig: https://editorconfig.org/
 |