mirror of
https://github.com/django/django.git
synced 2024-12-24 18:16:19 +00:00
313 lines
9.5 KiB
Plaintext
313 lines
9.5 KiB
Plaintext
============
|
|
Coding style
|
|
============
|
|
|
|
Please follow these coding standards when writing code for inclusion in Django.
|
|
|
|
.. _coding-style-python:
|
|
|
|
Python style
|
|
============
|
|
|
|
* Please conform to the indentation style dictated in the ``.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 `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 119 characters as this is the width of GitHub
|
|
code review; anything longer requires horizontal scrolling which makes review
|
|
more difficult. This check is included when you run ``flake8``. Documentation,
|
|
comments, and docstrings should be wrapped at 79 characters, even though
|
|
:pep:`8` suggests 72.
|
|
|
|
* Use four spaces for indentation.
|
|
|
|
* 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 :pep:`257`. For example::
|
|
|
|
def foo():
|
|
"""
|
|
Calculate something and return the result.
|
|
"""
|
|
...
|
|
|
|
* In tests, use :meth:`~django.test.SimpleTestCase.assertRaisesMessage` instead
|
|
of :meth:`~unittest.TestCase.assertRaises` so you can check the exception
|
|
message. Use :meth:`~unittest.TestCase.assertRaisesRegex`
|
|
(``six.assertRaisesRegex()`` as long as we support Python 2) only if you need
|
|
to use regular expression matching.
|
|
|
|
.. _coding-style-imports:
|
|
|
|
Imports
|
|
=======
|
|
|
|
* Use `isort <https://github.com/timothycrosley/isort#readme>`_ to automate
|
|
import sorting using the guidelines below.
|
|
|
|
Quick start:
|
|
|
|
.. code-block:: console
|
|
|
|
$ pip install isort
|
|
$ isort -rc .
|
|
|
|
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 lower case 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):
|
|
|
|
.. snippet::
|
|
:filename: 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 pytz
|
|
except ImportError:
|
|
pytz = None
|
|
|
|
CONSTANT = 'foo'
|
|
|
|
|
|
class Example(object):
|
|
# ...
|
|
|
|
* 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
|
|
==============
|
|
|
|
* In Django template code, put one (and only one) space between the curly
|
|
brackets and the tag contents.
|
|
|
|
Do this:
|
|
|
|
.. code-block:: html+django
|
|
|
|
{{ foo }}
|
|
|
|
Don't do this:
|
|
|
|
.. code-block:: html+django
|
|
|
|
{{foo}}
|
|
|
|
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):
|
|
first_name = models.CharField(max_length=20)
|
|
last_name = models.CharField(max_length=40)
|
|
class Meta:
|
|
verbose_name_plural = 'people'
|
|
|
|
Don't do this, either::
|
|
|
|
class Person(models.Model):
|
|
class Meta:
|
|
verbose_name_plural = 'people'
|
|
|
|
first_name = models.CharField(max_length=20)
|
|
last_name = models.CharField(max_length=40)
|
|
|
|
* If you define a ``__str__`` method (previously ``__unicode__`` before Python 3
|
|
was supported), decorate the model class with
|
|
:func:`~django.utils.encoding.python_2_unicode_compatible`.
|
|
|
|
* 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 tuple 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'),
|
|
)
|
|
|
|
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
|
|
``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.
|
|
`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`.
|
|
|
|
.. _editorconfig: http://editorconfig.org/
|
|
.. _flake8: https://pypi.python.org/pypi/flake8
|