mirror of
https://github.com/django/django.git
synced 2025-09-03 02:45:09 +03:00
Compare commits
58 Commits
3b8145edcc
...
b8edb1816c
Author | SHA1 | Date | |
---|---|---|---|
|
b8edb1816c | ||
|
a627194567 | ||
|
bb7a7701b1 | ||
|
2d453a2a68 | ||
|
183fcebf88 | ||
|
eaaf01c96a | ||
|
0be1c4575b | ||
|
550822bcee | ||
|
292b9e6fe8 | ||
|
dc4ee99152 | ||
|
41ff30f6f9 | ||
|
c93dddf659 | ||
|
56955636e6 | ||
|
ae03f81ffa | ||
|
a9fe98d5bd | ||
|
05bac8c420 | ||
|
3c0c54351b | ||
|
1285de557b | ||
|
d8426f64a7 | ||
|
4f07767106 | ||
|
1b0c4d5ea5 | ||
|
4c71e33440 | ||
|
d0e4dd5cdd | ||
|
c594574175 | ||
|
d454aefbd1 | ||
|
66082a7dac | ||
|
07f44c9e9a | ||
|
3e7aedfb2e | ||
|
9efce80ca7 | ||
|
43e4d0a142 | ||
|
f81e6e3a53 | ||
|
4286a23df6 | ||
|
01a460f23e | ||
|
724e5ec6f2 | ||
|
6f8e23d1c1 | ||
|
ef2f16bc48 | ||
|
0246f47888 | ||
|
3ba24c18e7 | ||
|
165ad74c57 | ||
|
836894f27a | ||
|
b3166e1e15 | ||
|
0b2493a0da | ||
|
dd15f7dabb | ||
|
d6a8e5f5e1 | ||
|
aae7836cc0 | ||
|
f2a6c0477f | ||
|
ed7c1a5640 | ||
|
d3cf24e9b4 | ||
|
a2ce4900a6 | ||
|
fb0d463b1f | ||
|
7063d31cc3 | ||
|
cd7554e551 | ||
|
bcddf641ae | ||
|
f5c944b314 | ||
|
f02b49d2f3 | ||
|
4187da258f | ||
|
ad4a9e0f3b | ||
|
73b904c5e0 |
|
@ -11,3 +11,4 @@ ba755ca13123d2691a0926ddb64e5d0a2906a880
|
|||
1ecf6889cabc9f3f60d3fdd651468cddd8f4da6e
|
||||
69a93a88edb56ba47f624dac7a21aacc47ea474f
|
||||
78298b51629e14c0e472898b635bc819d47b7f27
|
||||
f81e6e3a53ee36e3f730a71aa55a5744982dd016
|
||||
|
|
16
.github/workflows/docs.yml
vendored
16
.github/workflows/docs.yml
vendored
|
@ -58,3 +58,19 @@ jobs:
|
|||
echo "💥 📢 Code blocks in documentation must be reformatted with blacken-docs 📢 💥"
|
||||
fi;
|
||||
exit $RESULT
|
||||
|
||||
lint-docs:
|
||||
runs-on: ubuntu-latest
|
||||
name: lint-docs
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
- run: python -m pip install sphinx-lint
|
||||
- name: Build docs
|
||||
run: |
|
||||
cd docs
|
||||
make lint
|
||||
|
|
14
.github/workflows/postgis.yml
vendored
14
.github/workflows/postgis.yml
vendored
|
@ -14,13 +14,17 @@ permissions:
|
|||
contents: read
|
||||
|
||||
jobs:
|
||||
postgis-latest:
|
||||
postgis:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'geodjango')
|
||||
runs-on: ubuntu-latest
|
||||
name: Latest PostGIS
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
postgis-version: [latest, "17-3.5-alpine", "17-master"]
|
||||
name: PostGIS ${{ matrix.postgis-version }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgis/postgis:latest
|
||||
image: postgis/postgis:${{ matrix.postgis-version }}
|
||||
env:
|
||||
POSTGRES_DB: geodjango
|
||||
POSTGRES_USER: user
|
||||
|
@ -41,8 +45,10 @@ jobs:
|
|||
python-version: '3.13'
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'tests/requirements/py3.txt'
|
||||
- name: Update apt repo
|
||||
run: sudo apt update
|
||||
- name: Install libmemcached-dev for pylibmc
|
||||
run: sudo apt install libmemcached-dev
|
||||
run: sudo apt install -y libmemcached-dev
|
||||
- name: Install geospatial dependencies
|
||||
run: sudo apt install -y binutils libproj-dev gdal-bin
|
||||
- name: Print PostGIS versions
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
from django.urls import include
|
||||
from django.views import defaults
|
||||
|
||||
__all__ = ["handler400", "handler403", "handler404", "handler500", "include"]
|
||||
|
||||
handler400 = defaults.bad_request
|
||||
handler403 = defaults.permission_denied
|
||||
handler404 = defaults.page_not_found
|
||||
handler500 = defaults.server_error
|
||||
__all__ = ["include"]
|
||||
|
|
|
@ -173,6 +173,7 @@ class AdminField:
|
|||
self.is_first = is_first # Whether this field is first on the line
|
||||
self.is_checkbox = isinstance(self.field.field.widget, forms.CheckboxInput)
|
||||
self.is_readonly = False
|
||||
self.is_fieldset = self.field.field.widget.use_fieldset
|
||||
|
||||
def label_tag(self):
|
||||
classes = []
|
||||
|
@ -185,12 +186,14 @@ class AdminField:
|
|||
if not self.is_first:
|
||||
classes.append("inline")
|
||||
attrs = {"class": " ".join(classes)} if classes else {}
|
||||
tag = "legend" if self.is_fieldset else None
|
||||
# checkboxes should not have a label suffix as the checkbox appears
|
||||
# to the left of the label.
|
||||
return self.field.label_tag(
|
||||
contents=mark_safe(contents),
|
||||
attrs=attrs,
|
||||
label_suffix="" if self.is_checkbox else None,
|
||||
tag=tag,
|
||||
)
|
||||
|
||||
def errors(self):
|
||||
|
|
|
@ -1161,7 +1161,6 @@ a.deletelink:focus, a.deletelink:hover {
|
|||
line-height: 22px;
|
||||
margin: 0;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
@ -1176,25 +1175,17 @@ a.deletelink:focus, a.deletelink:hover {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.paginator a:link, .paginator a:visited {
|
||||
.paginator a {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.paginator a:not(.showall) {
|
||||
background: var(--button-bg);
|
||||
text-decoration: none;
|
||||
color: var(--button-fg);
|
||||
}
|
||||
|
||||
.paginator a.showall {
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--link-fg);
|
||||
}
|
||||
|
||||
.paginator a.showall:focus, .paginator a.showall:hover {
|
||||
background: none;
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
.paginator a[aria-current="page"] {
|
||||
color: var(--body-quiet-color);
|
||||
background: transparent;
|
||||
|
@ -1202,8 +1193,8 @@ a.deletelink:focus, a.deletelink:hover {
|
|||
cursor: default;
|
||||
}
|
||||
|
||||
.paginator a:not([aria-current="page"]):focus,
|
||||
.paginator a:not([aria-current="page"]):hover {
|
||||
.paginator a:not([aria-current="page"], .showall):focus,
|
||||
.paginator a:not([aria-current="page"], .showall):hover {
|
||||
color: white;
|
||||
background: var(--link-hover-color);
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#changelist .changelist-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
border-top: 1px solid var(--hairline-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
|
@ -64,18 +65,17 @@
|
|||
background: var(--body-bg);
|
||||
border: none;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#changelist .paginator {
|
||||
color: var(--body-quiet-color);
|
||||
border-bottom: 1px solid var(--hairline-color);
|
||||
background: var(--body-bg);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#changelist .paginator ul {
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* CHANGELIST TABLES */
|
||||
|
|
|
@ -36,12 +36,13 @@ form .form-row p {
|
|||
|
||||
/* FORM LABELS */
|
||||
|
||||
label {
|
||||
legend, label {
|
||||
font-weight: normal;
|
||||
color: var(--body-quiet-color);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.required legend, legend.required,
|
||||
.required label, label.required {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -91,6 +92,20 @@ fieldset .inline-heading,
|
|||
|
||||
/* ALIGNED FIELDSETS */
|
||||
|
||||
.aligned fieldset {
|
||||
width: 100%;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.aligned fieldset > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aligned legend {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.aligned legend,
|
||||
.aligned label {
|
||||
display: block;
|
||||
padding: 4px 10px 0 0;
|
||||
|
@ -138,6 +153,10 @@ form .aligned div.radiolist {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
form .aligned fieldset div.help {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
form .aligned p.help,
|
||||
form .aligned div.help {
|
||||
margin-top: 0;
|
||||
|
@ -413,9 +432,12 @@ body.popup .submit-row {
|
|||
border: none;
|
||||
}
|
||||
|
||||
.inline-related.tabular div.wrapper {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.inline-related.tabular fieldset.module table {
|
||||
width: 100%;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.last-related fieldset {
|
||||
|
|
|
@ -170,6 +170,7 @@ input[type="submit"], button {
|
|||
|
||||
/* Forms */
|
||||
|
||||
legend,
|
||||
label {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
@ -484,6 +485,7 @@ input[type="submit"], button {
|
|||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.aligned legend,
|
||||
.aligned label {
|
||||
width: 100%;
|
||||
min-width: auto;
|
||||
|
|
|
@ -301,6 +301,10 @@ p.datetime {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
p.datetime label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.datetime span {
|
||||
white-space: nowrap;
|
||||
font-weight: normal;
|
||||
|
|
|
@ -15,7 +15,8 @@ Requires core.js and SelectBox.js.
|
|||
const from_box = document.getElementById(field_id);
|
||||
from_box.id += '_from'; // change its ID
|
||||
from_box.className = 'filtered';
|
||||
from_box.setAttribute('aria-labelledby', field_id + '_from_title');
|
||||
from_box.setAttribute('aria-labelledby', field_id + '_from_label');
|
||||
from_box.setAttribute('aria-describedby', `${field_id}_helptext ${field_id}_choose_helptext`);
|
||||
|
||||
for (const p of from_box.parentNode.getElementsByTagName('p')) {
|
||||
if (p.classList.contains("info")) {
|
||||
|
@ -42,12 +43,20 @@ Requires core.js and SelectBox.js.
|
|||
const selector_available_title = quickElement('div', selector_available);
|
||||
selector_available_title.id = field_id + '_from_title';
|
||||
selector_available_title.className = 'selector-available-title';
|
||||
quickElement('label', selector_available_title, interpolate(gettext('Available %s') + ' ', [field_name]), 'for', field_id + '_from');
|
||||
quickElement(
|
||||
'label',
|
||||
selector_available_title,
|
||||
interpolate(gettext('Available %s') + ' ', [field_name]),
|
||||
'id',
|
||||
field_id + '_from_label',
|
||||
'for',
|
||||
field_id + '_from'
|
||||
);
|
||||
quickElement(
|
||||
'p',
|
||||
selector_available_title,
|
||||
interpolate(gettext('Choose %s by selecting them and then select the "Choose" arrow button.'), [field_name]),
|
||||
'class', 'helptext'
|
||||
'id', `${field_id}_choose_helptext`, 'class', 'helptext'
|
||||
);
|
||||
|
||||
const filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
|
||||
|
@ -102,12 +111,20 @@ Requires core.js and SelectBox.js.
|
|||
const selector_chosen_title = quickElement('div', selector_chosen);
|
||||
selector_chosen_title.className = 'selector-chosen-title';
|
||||
selector_chosen_title.id = field_id + '_to_title';
|
||||
quickElement('label', selector_chosen_title, interpolate(gettext('Chosen %s') + ' ', [field_name]), 'for', field_id + '_to');
|
||||
quickElement(
|
||||
'label',
|
||||
selector_chosen_title,
|
||||
interpolate(gettext('Chosen %s') + ' ', [field_name]),
|
||||
'id',
|
||||
field_id + '_to_label',
|
||||
'for',
|
||||
field_id + '_to'
|
||||
);
|
||||
quickElement(
|
||||
'p',
|
||||
selector_chosen_title,
|
||||
interpolate(gettext('Remove %s by selecting them and then select the "Remove" arrow button.'), [field_name]),
|
||||
'class', 'helptext'
|
||||
'id', `${field_id}_remove_helptext`, 'class', 'helptext'
|
||||
);
|
||||
|
||||
const filter_selected_p = quickElement('p', selector_chosen, '', 'id', field_id + '_filter_selected');
|
||||
|
@ -134,7 +151,8 @@ Requires core.js and SelectBox.js.
|
|||
'multiple', '',
|
||||
'size', from_box.size,
|
||||
'name', from_box.name,
|
||||
'aria-labelledby', field_id + '_to_title',
|
||||
'aria-labelledby', field_id + '_to_label',
|
||||
'aria-describedby', `${field_id}_helptext ${field_id}_remove_helptext`,
|
||||
'class', 'filtered'
|
||||
);
|
||||
const warning_footer = quickElement('div', selector_chosen, '', 'class', 'list-footer-display');
|
||||
|
|
|
@ -91,7 +91,10 @@
|
|||
message = interpolate(message, [timezoneOffset]);
|
||||
|
||||
const warning = document.createElement('div');
|
||||
const id = inp.id;
|
||||
const field_id = inp.closest('p.datetime') ? id.slice(0, id.lastIndexOf("_")) : id;
|
||||
warning.classList.add('help', warningClass);
|
||||
warning.id = `${field_id}_timezone_warning_helptext`;
|
||||
warning.textContent = message;
|
||||
inp.parentNode.appendChild(warning);
|
||||
},
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
|
||||
<div class="form-row">
|
||||
{{ form.usable_password.errors }}
|
||||
<div class="flex-container">{{ form.usable_password.label_tag }} {{ form.usable_password }}</div>
|
||||
<fieldset class="flex-container">{{ form.usable_password.legend_tag }} {{ form.usable_password }}</fieldset>
|
||||
{% if form.usable_password.help_text %}
|
||||
<div class="help"{% if form.usable_password.id_for_label %} id="{{ form.usable_password.id_for_label }}_helptext"{% endif %}>
|
||||
<p>{{ form.usable_password.help_text|safe }}</p>
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
</h2>
|
||||
{% if inline_admin_formset.is_collapsible %}</summary>{% endif %}
|
||||
{{ inline_admin_formset.formset.non_form_errors }}
|
||||
<table>
|
||||
<div class="wrapper">
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th class="original"></th>
|
||||
{% for field in inline_admin_formset.fields %}
|
||||
|
@ -62,7 +63,8 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
{% if inline_admin_formset.is_collapsible %}</details>{% endif %}
|
||||
</fieldset>
|
||||
</div>
|
||||
|
|
|
@ -11,13 +11,14 @@
|
|||
<div class="form-row{% if line.fields|length == 1 and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
|
||||
{% if line.fields|length == 1 %}{{ line.errors }}{% else %}<div class="flex-container form-multiline">{% endif %}
|
||||
{% for field in line %}
|
||||
{% if field.is_fieldset %}<fieldset class="flex-container"{% if field.field.help_text %} aria-describedby="{{ field.field.id_for_label }}_helptext"{% endif %}>{{ field.label_tag }}{% endif %}
|
||||
<div>
|
||||
{% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}
|
||||
<div class="flex-container{% if not line.fields|length == 1 %} fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}{% endif %}{% if field.is_checkbox %} checkbox-row{% endif %}">
|
||||
{% if field.is_checkbox %}
|
||||
{{ field.field }}{{ field.label_tag }}
|
||||
{{ field.field }}{% if not field.is_fieldset %}{{ field.label_tag }}{% endif %}
|
||||
{% else %}
|
||||
{{ field.label_tag }}
|
||||
{% if not field.is_fieldset %}{{ field.label_tag }}{% endif %}
|
||||
{% if field.is_readonly %}
|
||||
<div class="readonly">{{ field.contents }}</div>
|
||||
{% else %}
|
||||
|
@ -31,6 +32,7 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if field.is_fieldset %}</fieldset>{% endif %}
|
||||
{% endfor %}
|
||||
{% if not line.fields|length == 1 %}</div>{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<p class="datetime">
|
||||
{{ date_label }} {% with widget=widget.subwidgets.0 %}{% include widget.template_name %}{% endwith %}<br>
|
||||
{{ time_label }} {% with widget=widget.subwidgets.1 %}{% include widget.template_name %}{% endwith %}
|
||||
<label {% if widget.attrs.id %}for="{{ widget.subwidgets.0.attrs.id }}"{% endif %}>{{ date_label }}</label> {% with widget=widget.subwidgets.0 %}{% include widget.template_name %}{% endwith %}<br>
|
||||
<label {% if widget.attrs.id %}for="{{ widget.subwidgets.1.attrs.id }}"{% endif %}>{{ time_label }}</label> {% with widget=widget.subwidgets.1 %}{% include widget.template_name %}{% endwith %}
|
||||
</p>
|
||||
|
|
|
@ -10,6 +10,7 @@ from django.conf import settings
|
|||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator
|
||||
from django.db.models import CASCADE, UUIDField
|
||||
from django.forms.widgets import Select
|
||||
from django.urls import reverse
|
||||
from django.urls.exceptions import NoReverseMatch
|
||||
from django.utils.html import smart_urlquote
|
||||
|
@ -49,7 +50,16 @@ class FilteredSelectMultiple(forms.SelectMultiple):
|
|||
return context
|
||||
|
||||
|
||||
class BaseAdminDateWidget(forms.DateInput):
|
||||
class DateTimeWidgetContextMixin:
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
context["widget"]["attrs"][
|
||||
"aria-describedby"
|
||||
] = f"id_{name}_timezone_warning_helptext"
|
||||
return context
|
||||
|
||||
|
||||
class BaseAdminDateWidget(DateTimeWidgetContextMixin, forms.DateInput):
|
||||
class Media:
|
||||
js = [
|
||||
"admin/js/calendar.js",
|
||||
|
@ -65,7 +75,7 @@ class AdminDateWidget(BaseAdminDateWidget):
|
|||
template_name = "admin/widgets/date.html"
|
||||
|
||||
|
||||
class BaseAdminTimeWidget(forms.TimeInput):
|
||||
class BaseAdminTimeWidget(DateTimeWidgetContextMixin, forms.TimeInput):
|
||||
class Media:
|
||||
js = [
|
||||
"admin/js/calendar.js",
|
||||
|
@ -98,8 +108,13 @@ class AdminSplitDateTime(forms.SplitDateTimeWidget):
|
|||
context = super().get_context(name, value, attrs)
|
||||
context["date_label"] = _("Date:")
|
||||
context["time_label"] = _("Time:")
|
||||
for widget in context["widget"]["subwidgets"]:
|
||||
widget["attrs"]["aria-describedby"] = f"id_{name}_timezone_warning_helptext"
|
||||
return context
|
||||
|
||||
def id_for_label(self, id_):
|
||||
return id_
|
||||
|
||||
|
||||
class AdminRadioSelect(forms.RadioSelect):
|
||||
template_name = "admin/widgets/radio.html"
|
||||
|
@ -270,18 +285,21 @@ class RelatedFieldWidgetWrapper(forms.Widget):
|
|||
if can_add_related is None:
|
||||
can_add_related = admin_site.is_registered(rel.model)
|
||||
self.can_add_related = can_add_related
|
||||
# XXX: The UX does not support multiple selected values.
|
||||
multiple = getattr(widget, "allow_multiple_selected", False)
|
||||
if not isinstance(widget, AutocompleteMixin):
|
||||
self.attrs["data-context"] = "available-source"
|
||||
self.can_change_related = not multiple and can_change_related
|
||||
# Only single-select Select widgets are supported.
|
||||
supported = not getattr(
|
||||
widget, "allow_multiple_selected", False
|
||||
) and isinstance(widget, Select)
|
||||
self.can_change_related = supported and can_change_related
|
||||
# XXX: The deletion UX can be confusing when dealing with cascading
|
||||
# deletion.
|
||||
cascade = getattr(rel, "on_delete", None) is CASCADE
|
||||
self.can_delete_related = not multiple and not cascade and can_delete_related
|
||||
self.can_view_related = not multiple and can_view_related
|
||||
self.can_delete_related = supported and not cascade and can_delete_related
|
||||
self.can_view_related = supported and can_view_related
|
||||
# To check if the related object is registered with this AdminSite.
|
||||
self.admin_site = admin_site
|
||||
self.use_fieldset = True
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
obj = copy.copy(self)
|
||||
|
|
|
@ -205,7 +205,7 @@ async def alogin(request, user, backend=None):
|
|||
# RemovedInDjango61Warning.
|
||||
if user is None:
|
||||
warnings.warn(
|
||||
"Fallback to request.user when user is None will be removed.",
|
||||
"Fallback to request.auser() when user is None will be removed.",
|
||||
RemovedInDjango61Warning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
@ -269,8 +269,8 @@ async def alogout(request):
|
|||
user = getattr(request, "auser", None)
|
||||
if user is not None:
|
||||
user = await user()
|
||||
if not getattr(user, "is_authenticated", True):
|
||||
user = None
|
||||
if not getattr(user, "is_authenticated", True):
|
||||
user = None
|
||||
await user_logged_out.asend(sender=user.__class__, request=request, user=user)
|
||||
await request.session.aflush()
|
||||
if hasattr(request, "auser"):
|
||||
|
@ -364,8 +364,8 @@ async def aget_user(request):
|
|||
session_hash_verified = False
|
||||
else:
|
||||
session_auth_hash = user.get_session_auth_hash()
|
||||
session_hash_verified = session_hash and constant_time_compare(
|
||||
session_hash, user.get_session_auth_hash()
|
||||
session_hash_verified = constant_time_compare(
|
||||
session_hash, session_auth_hash
|
||||
)
|
||||
if not session_hash_verified:
|
||||
# If the current secret does not verify the session, try
|
||||
|
@ -408,5 +408,5 @@ def update_session_auth_hash(request, user):
|
|||
async def aupdate_session_auth_hash(request, user):
|
||||
"""See update_session_auth_hash()."""
|
||||
await request.session.acycle_key()
|
||||
if hasattr(user, "get_session_auth_hash") and request.user == user:
|
||||
if hasattr(user, "get_session_auth_hash") and await request.auser() == user:
|
||||
await request.session.aset(HASH_SESSION_KEY, user.get_session_auth_hash())
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
from django.apps import AppConfig
|
||||
from django.core import checks
|
||||
from django.db.models.query_utils import DeferredAttribute
|
||||
from django.db.models.signals import post_migrate
|
||||
from django.db.models.signals import post_migrate, pre_migrate
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from . import get_user_model
|
||||
from .checks import check_middleware, check_models_permissions, check_user_model
|
||||
from .management import create_permissions
|
||||
from .management import create_permissions, rename_permissions
|
||||
from .signals import user_logged_in
|
||||
|
||||
|
||||
|
@ -20,6 +20,11 @@ class AuthConfig(AppConfig):
|
|||
create_permissions,
|
||||
dispatch_uid="django.contrib.auth.management.create_permissions",
|
||||
)
|
||||
pre_migrate.connect(
|
||||
rename_permissions,
|
||||
dispatch_uid="django.contrib.auth.management.rename_permissions",
|
||||
)
|
||||
|
||||
last_login_field = getattr(get_user_model(), "last_login", None)
|
||||
# Register the handler only if UserModel.last_login is a field.
|
||||
if isinstance(last_login_field, DeferredAttribute):
|
||||
|
|
|
@ -9,7 +9,9 @@ from django.apps import apps as global_apps
|
|||
from django.contrib.auth import get_permission_codename
|
||||
from django.contrib.contenttypes.management import create_contenttypes
|
||||
from django.core import exceptions
|
||||
from django.db import DEFAULT_DB_ALIAS, router
|
||||
from django.db import DEFAULT_DB_ALIAS, migrations, router, transaction
|
||||
from django.db.utils import IntegrityError
|
||||
from django.utils.text import camel_case_to_spaces
|
||||
|
||||
|
||||
def _get_all_permissions(opts):
|
||||
|
@ -108,6 +110,84 @@ def create_permissions(
|
|||
print("Adding permission '%s'" % perm)
|
||||
|
||||
|
||||
class RenamePermission(migrations.RunPython):
|
||||
def __init__(self, app_label, old_model, new_model):
|
||||
self.app_label = app_label
|
||||
self.old_model = old_model
|
||||
self.new_model = new_model
|
||||
super(RenamePermission, self).__init__(
|
||||
self.rename_forward, self.rename_backward
|
||||
)
|
||||
|
||||
def _rename(self, apps, schema_editor, old_model, new_model):
|
||||
ContentType = apps.get_model("contenttypes", "ContentType")
|
||||
# Use the live Permission model instead of the frozen one, since frozen
|
||||
# models do not retain foreign key constraints.
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
db = schema_editor.connection.alias
|
||||
ctypes = ContentType.objects.filter(
|
||||
app_label=self.app_label, model__icontains=old_model.lower()
|
||||
)
|
||||
for permission in Permission.objects.filter(
|
||||
content_type_id__in=ctypes.values("id")
|
||||
):
|
||||
prefix = permission.codename.split("_")[0]
|
||||
default_verbose_name = camel_case_to_spaces(new_model)
|
||||
|
||||
new_codename = f"{prefix}_{new_model.lower()}"
|
||||
new_name = f"Can {prefix} {default_verbose_name}"
|
||||
|
||||
if permission.codename != new_codename or permission.name != new_name:
|
||||
permission.codename = new_codename
|
||||
permission.name = new_name
|
||||
try:
|
||||
with transaction.atomic(using=db):
|
||||
permission.save(update_fields={"name", "codename"})
|
||||
except IntegrityError:
|
||||
pass
|
||||
|
||||
def rename_forward(self, apps, schema_editor):
|
||||
self._rename(apps, schema_editor, self.old_model, self.new_model)
|
||||
|
||||
def rename_backward(self, apps, schema_editor):
|
||||
self._rename(apps, schema_editor, self.new_model, self.old_model)
|
||||
|
||||
|
||||
def rename_permissions(
|
||||
plan,
|
||||
verbosity=2,
|
||||
interactive=True,
|
||||
using=DEFAULT_DB_ALIAS,
|
||||
apps=global_apps,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Insert a `RenamePermissionType` operation after every planned `RenameModel`
|
||||
operation.
|
||||
"""
|
||||
try:
|
||||
Permission = apps.get_model("auth", "Permission")
|
||||
except LookupError:
|
||||
return
|
||||
else:
|
||||
if not router.allow_migrate_model(using, Permission):
|
||||
return
|
||||
|
||||
for migration, backward in plan:
|
||||
inserts = []
|
||||
for index, operation in enumerate(migration.operations):
|
||||
if isinstance(operation, migrations.RenameModel):
|
||||
operation = RenamePermission(
|
||||
migration.app_label,
|
||||
operation.old_name,
|
||||
operation.new_name,
|
||||
)
|
||||
inserts.append((index + 1, operation))
|
||||
for inserted, (index, operation) in enumerate(inserts):
|
||||
migration.operations.insert(inserted + index, operation)
|
||||
|
||||
|
||||
def get_system_username():
|
||||
"""
|
||||
Return the current system user's username, or an empty string if the
|
||||
|
|
|
@ -4,8 +4,7 @@ an interface for reading vector geometry data from many different file
|
|||
formats (including ESRI shapefiles).
|
||||
|
||||
When instantiating a DataSource object, use the filename of a
|
||||
GDAL-supported data source. For example, an SHP file or a
|
||||
TIGER/Line file from the government.
|
||||
GDAL-supported data source. For example, an SHP file.
|
||||
|
||||
The ds_driver keyword is used internally when a ctypes pointer
|
||||
is passed in directly.
|
||||
|
|
|
@ -2,6 +2,7 @@ from ctypes import c_void_p
|
|||
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.gdal.libgdal import GDAL_VERSION
|
||||
from django.contrib.gis.gdal.prototypes import ds as capi
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
|
||||
|
@ -23,8 +24,6 @@ class Driver(GDALBase):
|
|||
"esri": "ESRI Shapefile",
|
||||
"shp": "ESRI Shapefile",
|
||||
"shape": "ESRI Shapefile",
|
||||
"tiger": "TIGER",
|
||||
"tiger/line": "TIGER",
|
||||
# raster
|
||||
"tiff": "GTiff",
|
||||
"tif": "GTiff",
|
||||
|
@ -32,6 +31,14 @@ class Driver(GDALBase):
|
|||
"jpg": "JPEG",
|
||||
}
|
||||
|
||||
if GDAL_VERSION[:2] <= (3, 10):
|
||||
_alias.update(
|
||||
{
|
||||
"tiger": "TIGER",
|
||||
"tiger/line": "TIGER",
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(self, dr_input):
|
||||
"""
|
||||
Initialize an GDAL/OGR driver on either a string or integer input.
|
||||
|
|
|
@ -22,6 +22,7 @@ if lib_path:
|
|||
elif os.name == "nt":
|
||||
# Windows NT shared libraries
|
||||
lib_names = [
|
||||
"gdal311",
|
||||
"gdal310",
|
||||
"gdal309",
|
||||
"gdal308",
|
||||
|
@ -38,6 +39,7 @@ elif os.name == "posix":
|
|||
lib_names = [
|
||||
"gdal",
|
||||
"GDAL",
|
||||
"gdal3.11.0",
|
||||
"gdal3.10.0",
|
||||
"gdal3.9.0",
|
||||
"gdal3.8.0",
|
||||
|
|
|
@ -2,24 +2,16 @@
|
|||
This module contains useful utilities for GeoDjango.
|
||||
"""
|
||||
|
||||
from django.contrib.gis.utils.layermapping import LayerMapError, LayerMapping
|
||||
from django.contrib.gis.utils.ogrinfo import ogrinfo
|
||||
from django.contrib.gis.utils.ogrinspect import mapping, ogrinspect
|
||||
from django.contrib.gis.utils.srs import add_srs_entry
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
__all__ = [
|
||||
"add_srs_entry",
|
||||
"mapping",
|
||||
"ogrinfo",
|
||||
"ogrinspect",
|
||||
"LayerMapError",
|
||||
"LayerMapping",
|
||||
]
|
||||
|
||||
try:
|
||||
# LayerMapping requires DJANGO_SETTINGS_MODULE to be set,
|
||||
# and ImproperlyConfigured is raised if that's not the case.
|
||||
from django.contrib.gis.utils.layermapping import LayerMapError, LayerMapping
|
||||
|
||||
__all__ += ["LayerMapError", "LayerMapping"]
|
||||
|
||||
except ImproperlyConfigured:
|
||||
pass
|
||||
|
|
|
@ -7,4 +7,3 @@ class RedirectAdmin(admin.ModelAdmin):
|
|||
list_display = ("old_path", "new_path")
|
||||
list_filter = ("site",)
|
||||
search_fields = ("old_path", "new_path")
|
||||
radio_fields = {"site": admin.VERTICAL}
|
||||
|
|
|
@ -131,7 +131,7 @@ def check_custom_error_handlers(app_configs, **kwargs):
|
|||
errors = []
|
||||
# All handlers take (request, exception) arguments except handler500
|
||||
# which takes (request).
|
||||
for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 1)]:
|
||||
for status_code, num_parameters in [(400, 2), (403, 2), (404, 2), (500, 2)]:
|
||||
try:
|
||||
handler = resolver.resolve_error_handler(status_code)
|
||||
except (ImportError, ViewDoesNotExist) as e:
|
||||
|
|
|
@ -94,7 +94,11 @@ class ASGIRequest(HttpRequest):
|
|||
# HTTP/2 say only ASCII chars are allowed in headers, but decode
|
||||
# latin1 just in case.
|
||||
value = value.decode("latin1")
|
||||
if corrected_name in self.META:
|
||||
if corrected_name == "HTTP_COOKIE":
|
||||
value = value.rstrip("; ")
|
||||
if "HTTP_COOKIE" in self.META:
|
||||
value = self.META[corrected_name] + "; " + value
|
||||
elif corrected_name in self.META:
|
||||
value = self.META[corrected_name] + "," + value
|
||||
self.META[corrected_name] = value
|
||||
# Pull out request encoding, if provided.
|
||||
|
|
|
@ -208,13 +208,6 @@ class BaseDatabaseOperations:
|
|||
else:
|
||||
return ["DISTINCT"], []
|
||||
|
||||
def fetch_returned_insert_columns(self, cursor, returning_params):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the newly created data.
|
||||
"""
|
||||
return cursor.fetchone()
|
||||
|
||||
def force_group_by(self):
|
||||
"""
|
||||
Return a GROUP BY clause to use with a HAVING clause when no grouping
|
||||
|
@ -358,13 +351,31 @@ class BaseDatabaseOperations:
|
|||
"""
|
||||
return value
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
def returning_columns(self, fields):
|
||||
"""
|
||||
For backends that support returning columns as part of an insert query,
|
||||
return the SQL and params to append to the INSERT query. The returned
|
||||
fragment should contain a format string to hold the appropriate column.
|
||||
For backends that support returning columns as part of an insert or
|
||||
update query, return the SQL and params to append to the query.
|
||||
The returned fragment should contain a format string to hold the
|
||||
appropriate column.
|
||||
"""
|
||||
pass
|
||||
if not fields:
|
||||
return "", ()
|
||||
columns = [
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
for field in fields
|
||||
]
|
||||
return "RETURNING %s" % ", ".join(columns), ()
|
||||
|
||||
def fetch_returned_rows(self, cursor, returning_params):
|
||||
"""
|
||||
Given a cursor object for a DML query with a RETURNING statement,
|
||||
return the selected returning rows of tuples.
|
||||
"""
|
||||
return cursor.fetchall()
|
||||
|
||||
def compiler(self, compiler_name):
|
||||
"""
|
||||
|
|
|
@ -148,13 +148,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
else:
|
||||
return f"TIME({sql})", params
|
||||
|
||||
def fetch_returned_insert_rows(self, cursor):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the tuple of returned data.
|
||||
"""
|
||||
return cursor.fetchall()
|
||||
|
||||
def format_for_duration_arithmetic(self, sql):
|
||||
return "INTERVAL %s MICROSECOND" % sql
|
||||
|
||||
|
@ -182,20 +175,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return name # Quoting once is enough.
|
||||
return "`%s`" % name
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
# MySQL doesn't support an INSERT...RETURNING statement.
|
||||
if not fields:
|
||||
return "", ()
|
||||
columns = [
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
for field in fields
|
||||
]
|
||||
return "RETURNING %s" % ", ".join(columns), ()
|
||||
|
||||
def sql_flush(self, style, tables, *, reset_sequences=False, allow_cascade=False):
|
||||
if not tables:
|
||||
return []
|
||||
|
|
|
@ -22,7 +22,7 @@ from django.utils.functional import cached_property
|
|||
from django.utils.regex_helper import _lazy_re_compile
|
||||
|
||||
from .base import Database
|
||||
from .utils import BulkInsertMapper, InsertVar, Oracle_datetime
|
||||
from .utils import BoundVar, BulkInsertMapper, Oracle_datetime
|
||||
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
|
@ -298,12 +298,27 @@ END;
|
|||
def deferrable_sql(self):
|
||||
return " DEFERRABLE INITIALLY DEFERRED"
|
||||
|
||||
def fetch_returned_insert_columns(self, cursor, returning_params):
|
||||
columns = []
|
||||
for param in returning_params:
|
||||
value = param.get_value()
|
||||
columns.append(value[0])
|
||||
return tuple(columns)
|
||||
def returning_columns(self, fields):
|
||||
if not fields:
|
||||
return "", ()
|
||||
field_names = []
|
||||
params = []
|
||||
for field in fields:
|
||||
field_names.append(
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
)
|
||||
params.append(BoundVar(field))
|
||||
return "RETURNING %s INTO %s" % (
|
||||
", ".join(field_names),
|
||||
", ".join(["%s"] * len(params)),
|
||||
), tuple(params)
|
||||
|
||||
def fetch_returned_rows(self, cursor, returning_params):
|
||||
return list(zip(*(param.get_value() for param in returning_params)))
|
||||
|
||||
def no_limit_value(self):
|
||||
return None
|
||||
|
@ -391,25 +406,6 @@ END;
|
|||
match_option = "'i'"
|
||||
return "REGEXP_LIKE(%%s, %%s, %s)" % match_option
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
if not fields:
|
||||
return "", ()
|
||||
field_names = []
|
||||
params = []
|
||||
for field in fields:
|
||||
field_names.append(
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
)
|
||||
params.append(InsertVar(field))
|
||||
return "RETURNING %s INTO %s" % (
|
||||
", ".join(field_names),
|
||||
", ".join(["%s"] * len(params)),
|
||||
), tuple(params)
|
||||
|
||||
def __foreign_key_constraints(self, table_name, recursive):
|
||||
with self.connection.cursor() as cursor:
|
||||
if recursive:
|
||||
|
|
|
@ -4,7 +4,7 @@ import decimal
|
|||
from .base import Database
|
||||
|
||||
|
||||
class InsertVar:
|
||||
class BoundVar:
|
||||
"""
|
||||
A late-binding cursor variable that can be passed to Cursor.execute
|
||||
as a parameter, in order to receive the id of the row created by an
|
||||
|
|
|
@ -155,13 +155,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return f"SELECT * FROM {placeholder_rows}"
|
||||
return super().bulk_insert_sql(fields, placeholder_rows)
|
||||
|
||||
def fetch_returned_insert_rows(self, cursor):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the tuple of returned data.
|
||||
"""
|
||||
return cursor.fetchall()
|
||||
|
||||
def lookup_cast(self, lookup_type, internal_type=None):
|
||||
lookup = "%s"
|
||||
# Cast text lookups to text to allow things like filter(x__contains=4)
|
||||
|
@ -324,19 +317,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return cursor.query.decode()
|
||||
return None
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
if not fields:
|
||||
return "", ()
|
||||
columns = [
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
for field in fields
|
||||
]
|
||||
return "RETURNING %s" % ", ".join(columns), ()
|
||||
|
||||
if is_psycopg3:
|
||||
|
||||
def adapt_integerfield_value(self, value, internal_type):
|
||||
|
|
|
@ -32,9 +32,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
"""
|
||||
SQLite has a variable limit defined by SQLITE_LIMIT_VARIABLE_NUMBER
|
||||
(reflected in max_query_params).
|
||||
|
||||
If there's only a single field to insert, the limit is 500
|
||||
(SQLITE_MAX_COMPOUND_SELECT).
|
||||
"""
|
||||
fields = list(
|
||||
chain.from_iterable(
|
||||
|
@ -46,9 +43,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
for field in fields
|
||||
)
|
||||
)
|
||||
if len(fields) == 1:
|
||||
return 500
|
||||
elif len(fields) > 1:
|
||||
if fields:
|
||||
return self.connection.features.max_query_params // len(fields)
|
||||
else:
|
||||
return len(objs)
|
||||
|
@ -89,13 +84,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
"""
|
||||
return f"django_date_extract(%s, {sql})", (lookup_type.lower(), *params)
|
||||
|
||||
def fetch_returned_insert_rows(self, cursor):
|
||||
"""
|
||||
Given a cursor object that has just performed an INSERT...RETURNING
|
||||
statement into a table, return the list of returned data.
|
||||
"""
|
||||
return cursor.fetchall()
|
||||
|
||||
def format_for_duration_arithmetic(self, sql):
|
||||
"""Do nothing since formatting is handled in the custom function."""
|
||||
return sql
|
||||
|
@ -404,20 +392,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
return "INSERT OR IGNORE INTO"
|
||||
return super().insert_statement(on_conflict=on_conflict)
|
||||
|
||||
def return_insert_columns(self, fields):
|
||||
# SQLite < 3.35 doesn't support an INSERT...RETURNING statement.
|
||||
if not fields:
|
||||
return "", ()
|
||||
columns = [
|
||||
"%s.%s"
|
||||
% (
|
||||
self.quote_name(field.model._meta.db_table),
|
||||
self.quote_name(field.column),
|
||||
)
|
||||
for field in fields
|
||||
]
|
||||
return "RETURNING %s" % ", ".join(columns), ()
|
||||
|
||||
def on_conflict_suffix_sql(self, fields, on_conflict, update_fields, unique_fields):
|
||||
if (
|
||||
on_conflict == OnConflict.UPDATE
|
||||
|
|
|
@ -932,9 +932,7 @@ class Field(RegisterLookupMixin):
|
|||
@property
|
||||
def db_returning(self):
|
||||
"""Private API intended only to be used by Django itself."""
|
||||
return (
|
||||
self.has_db_default() and connection.features.can_return_columns_from_insert
|
||||
)
|
||||
return self.has_db_default()
|
||||
|
||||
def set_attributes_from_name(self, name):
|
||||
self.name = self.name or name
|
||||
|
|
|
@ -35,6 +35,7 @@ from django.db.models.utils import (
|
|||
resolve_callables,
|
||||
)
|
||||
from django.utils import timezone
|
||||
from django.utils.deprecation import RemovedInDjango70Warning
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
# The maximum number of results to fetch in a get() query.
|
||||
|
@ -1187,10 +1188,8 @@ class QuerySet(AltersData):
|
|||
if not id_list:
|
||||
return {}
|
||||
filter_key = "{}__in".format(field_name)
|
||||
max_params = connections[self.db].features.max_query_params or 0
|
||||
num_fields = len(opts.pk_fields) if field_name == "pk" else 1
|
||||
batch_size = max_params // num_fields
|
||||
id_list = tuple(id_list)
|
||||
batch_size = connections[self.db].ops.bulk_batch_size([opts.pk], id_list)
|
||||
# If the database has a limit on the number of query parameters
|
||||
# (e.g. SQLite), retrieve objects in batches if necessary.
|
||||
if batch_size and batch_size < len(id_list):
|
||||
|
@ -1396,7 +1395,12 @@ class QuerySet(AltersData):
|
|||
def _values(self, *fields, **expressions):
|
||||
clone = self._chain()
|
||||
if expressions:
|
||||
clone = clone.annotate(**expressions)
|
||||
# RemovedInDjango70Warning: When the deprecation ends, deindent as:
|
||||
# clone = clone.annotate(**expressions)
|
||||
with warnings.catch_warnings(
|
||||
action="ignore", category=RemovedInDjango70Warning
|
||||
):
|
||||
clone = clone.annotate(**expressions)
|
||||
clone._fields = fields
|
||||
clone.query.set_values(fields)
|
||||
return clone
|
||||
|
|
|
@ -1890,7 +1890,7 @@ class SQLInsertCompiler(SQLCompiler):
|
|||
result.append(on_conflict_suffix_sql)
|
||||
# Skip empty r_sql to allow subclasses to customize behavior for
|
||||
# 3rd party backends. Refs #19096.
|
||||
r_sql, self.returning_params = self.connection.ops.return_insert_columns(
|
||||
r_sql, self.returning_params = self.connection.ops.returning_columns(
|
||||
self.returning_fields
|
||||
)
|
||||
if r_sql:
|
||||
|
@ -1925,20 +1925,16 @@ class SQLInsertCompiler(SQLCompiler):
|
|||
cursor.execute(sql, params)
|
||||
if not self.returning_fields:
|
||||
return []
|
||||
obj_len = len(self.query.objs)
|
||||
if (
|
||||
self.connection.features.can_return_rows_from_bulk_insert
|
||||
and len(self.query.objs) > 1
|
||||
and obj_len > 1
|
||||
) or (
|
||||
self.connection.features.can_return_columns_from_insert and obj_len == 1
|
||||
):
|
||||
rows = self.connection.ops.fetch_returned_insert_rows(cursor)
|
||||
cols = [field.get_col(opts.db_table) for field in self.returning_fields]
|
||||
elif self.connection.features.can_return_columns_from_insert:
|
||||
assert len(self.query.objs) == 1
|
||||
rows = [
|
||||
self.connection.ops.fetch_returned_insert_columns(
|
||||
cursor,
|
||||
self.returning_params,
|
||||
)
|
||||
]
|
||||
rows = self.connection.ops.fetch_returned_rows(
|
||||
cursor, self.returning_params
|
||||
)
|
||||
cols = [field.get_col(opts.db_table) for field in self.returning_fields]
|
||||
elif returning_fields and isinstance(
|
||||
returning_field := returning_fields[0], AutoField
|
||||
|
|
|
@ -1219,7 +1219,7 @@ class Query(BaseExpression):
|
|||
if "aggregate" in {frame.function for frame in inspect.stack()}:
|
||||
stacklevel = 5
|
||||
else:
|
||||
# annotate() and alias().
|
||||
# annotate(), alias(), and values().
|
||||
stacklevel = 6
|
||||
warnings.warn(
|
||||
"Using percent signs in a column alias is deprecated.",
|
||||
|
@ -2269,8 +2269,21 @@ class Query(BaseExpression):
|
|||
join_info.joins,
|
||||
join_info.path,
|
||||
)
|
||||
for target in targets:
|
||||
cols.append(join_info.transform_function(target, final_alias))
|
||||
if len(targets) > 1:
|
||||
transformed_targets = [
|
||||
join_info.transform_function(target, final_alias)
|
||||
for target in targets
|
||||
]
|
||||
cols.append(
|
||||
ColPairs(
|
||||
final_alias if self.alias_cols else None,
|
||||
[col.target for col in transformed_targets],
|
||||
[col.output_field for col in transformed_targets],
|
||||
join_info.final_field,
|
||||
)
|
||||
)
|
||||
else:
|
||||
cols.append(join_info.transform_function(targets[0], final_alias))
|
||||
if cols:
|
||||
self.set_select(cols)
|
||||
except MultiJoin:
|
||||
|
|
|
@ -1134,11 +1134,10 @@ class BaseInlineFormSet(BaseModelFormSet):
|
|||
self.unique_fields = {self.fk.name}
|
||||
super().__init__(data, files, prefix=prefix, queryset=qs, **kwargs)
|
||||
|
||||
# Add the generated field to form._meta.fields if it's defined to make
|
||||
# sure validation isn't skipped on that field.
|
||||
# Add the inline foreign key field to form._meta.fields if it's defined
|
||||
# to make sure validation isn't skipped on that field.
|
||||
if self.form._meta.fields and self.fk.name not in self.form._meta.fields:
|
||||
if isinstance(self.form._meta.fields, tuple):
|
||||
self.form._meta.fields = list(self.form._meta.fields)
|
||||
self.form._meta.fields = list(self.form._meta.fields)
|
||||
self.form._meta.fields.append(self.fk.name)
|
||||
|
||||
def initial_form_count(self):
|
||||
|
|
|
@ -530,6 +530,7 @@ class ClearableFileInput(FileInput):
|
|||
input_text = _("Change")
|
||||
template_name = "django/forms/widgets/clearable_file_input.html"
|
||||
checked = False
|
||||
use_fieldset = True
|
||||
|
||||
def clear_checkbox_name(self, name):
|
||||
"""
|
||||
|
|
|
@ -721,11 +721,10 @@ def parse_boundary_stream(stream, max_header_size):
|
|||
|
||||
# Eliminate blank lines
|
||||
for line in header.split(b"\r\n"):
|
||||
# This terminology ("main value" and "dictionary of
|
||||
# parameters") is from the Python docs.
|
||||
try:
|
||||
main_value_pair, params = parse_header_parameters(line.decode())
|
||||
name, value = main_value_pair.split(":", 1)
|
||||
header_name, value_and_params = line.decode().split(":", 1)
|
||||
name = header_name.lower().rstrip(" ")
|
||||
value, params = parse_header_parameters(value_and_params.lstrip(" "))
|
||||
params = {k: v.encode() for k, v in params.items()}
|
||||
except ValueError: # Invalid header.
|
||||
continue
|
||||
|
|
|
@ -211,6 +211,23 @@ class HttpResponseBase:
|
|||
def get(self, header, alternate=None):
|
||||
return self.headers.get(header, alternate)
|
||||
|
||||
@classmethod
|
||||
def response_class_by_status_code(cls, status_code):
|
||||
return {
|
||||
200: HttpResponse,
|
||||
301: HttpResponsePermanentRedirect,
|
||||
302: HttpResponseRedirect,
|
||||
304: HttpResponseNotModified,
|
||||
307: HttpResponseRedirect,
|
||||
308: HttpResponsePermanentRedirect,
|
||||
400: HttpResponseBadRequest,
|
||||
403: HttpResponseForbidden,
|
||||
404: HttpResponseNotFound,
|
||||
405: HttpResponseNotAllowed,
|
||||
410: HttpResponseGone,
|
||||
500: HttpResponseServerError,
|
||||
}.get(status_code, cls)
|
||||
|
||||
def set_cookie(
|
||||
self,
|
||||
key,
|
||||
|
|
|
@ -100,8 +100,17 @@ class UpdateCacheMiddleware(MiddlewareMixin):
|
|||
):
|
||||
return response
|
||||
|
||||
# Don't cache a response with 'Cache-Control: private'
|
||||
if "private" in response.get("Cache-Control", ()):
|
||||
# Don't cache responses when the Cache-Control header is set to
|
||||
# private, no-cache, or no-store.
|
||||
cache_control = response.get("Cache-Control", ())
|
||||
if any(
|
||||
directive in cache_control
|
||||
for directive in (
|
||||
"private",
|
||||
"no-cache",
|
||||
"no-store",
|
||||
)
|
||||
):
|
||||
return response
|
||||
|
||||
# Page timeout takes precedence over the "max-age" and the default
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from http import HTTPStatus
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.csp import CSP, LazyNonce, build_policy
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
@ -14,22 +12,21 @@ class ContentSecurityPolicyMiddleware(MiddlewareMixin):
|
|||
request._csp_nonce = LazyNonce()
|
||||
|
||||
def process_response(self, request, response):
|
||||
# In DEBUG mode, exclude CSP headers for specific status codes that
|
||||
# trigger the debug view.
|
||||
exempted_status_codes = {
|
||||
HTTPStatus.NOT_FOUND,
|
||||
HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
if settings.DEBUG and response.status_code in exempted_status_codes:
|
||||
return response
|
||||
|
||||
nonce = get_nonce(request)
|
||||
|
||||
sentinel = object()
|
||||
if (csp_config := getattr(response, "_csp_config", sentinel)) is sentinel:
|
||||
csp_config = settings.SECURE_CSP
|
||||
if (csp_ro_config := getattr(response, "_csp_ro_config", sentinel)) is sentinel:
|
||||
csp_ro_config = settings.SECURE_CSP_REPORT_ONLY
|
||||
|
||||
for header, config in [
|
||||
(CSP.HEADER_ENFORCE, settings.SECURE_CSP),
|
||||
(CSP.HEADER_REPORT_ONLY, settings.SECURE_CSP_REPORT_ONLY),
|
||||
(CSP.HEADER_ENFORCE, csp_config),
|
||||
(CSP.HEADER_REPORT_ONLY, csp_ro_config),
|
||||
]:
|
||||
# If headers are already set on the response, don't overwrite them.
|
||||
# This allows for views to set their own CSP headers as needed.
|
||||
# An empty config means CSP headers are not added to the response.
|
||||
if config and header not in response:
|
||||
response.headers[str(header)] = build_policy(config, nonce)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import functools
|
|||
import inspect
|
||||
import re
|
||||
import string
|
||||
import warnings
|
||||
from importlib import import_module
|
||||
from pickle import PicklingError
|
||||
from urllib.parse import quote
|
||||
|
@ -21,6 +22,7 @@ from django.core.checks import Error, Warning
|
|||
from django.core.checks.urls import check_resolver
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.deprecation import RemovedInDjango61Warning
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import RFC3986_SUBDELIMS, escape_leading_slashes
|
||||
from django.utils.regex_helper import _lazy_re_compile, normalize
|
||||
|
@ -739,15 +741,26 @@ class URLResolver:
|
|||
raise ImproperlyConfigured(msg.format(name=self.urlconf_name)) from e
|
||||
return patterns
|
||||
|
||||
def resolve_error_handler(self, view_type):
|
||||
callback = getattr(self.urlconf_module, "handler%s" % view_type, None)
|
||||
if not callback:
|
||||
def resolve_error_handler(self, status_code):
|
||||
# RemovedInDjango61Warning.
|
||||
callback = getattr(self.urlconf_module, f"handler{status_code}", None)
|
||||
if callback:
|
||||
warnings.warn(
|
||||
"handler<status> custom error handlers are deprecated, please "
|
||||
"replace them by a generic `error_handler` view function.",
|
||||
RemovedInDjango61Warning,
|
||||
)
|
||||
return get_callable(callback)
|
||||
error_view = getattr(self.urlconf_module, "error_handler", None)
|
||||
if error_view:
|
||||
error_view.status_code = status_code
|
||||
else:
|
||||
# No handler specified in file; use lazy import, since
|
||||
# django.conf.urls imports this file.
|
||||
from django.conf import urls
|
||||
# django.views.defaults imports this file.
|
||||
from django.views.defaults import DefaultErrorView
|
||||
|
||||
callback = getattr(urls, "handler%s" % view_type)
|
||||
return get_callable(callback)
|
||||
error_view = DefaultErrorView.as_view(status_code=status_code)
|
||||
return error_view
|
||||
|
||||
def reverse(self, lookup_view, *args, **kwargs):
|
||||
return self._reverse_with_prefix(lookup_view, "", *args, **kwargs)
|
||||
|
|
|
@ -10,7 +10,7 @@ from urllib.parse import parse_qsl, quote, unquote, urlencode, urlsplit, urlunsp
|
|||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import SuspiciousOperation, ValidationError
|
||||
from django.core.validators import EmailValidator
|
||||
from django.core.validators import DomainNameValidator, EmailValidator
|
||||
from django.utils.deprecation import RemovedInDjango70Warning
|
||||
from django.utils.functional import Promise, cached_property, keep_lazy, keep_lazy_text
|
||||
from django.utils.http import MAX_URL_LENGTH, RFC3986_GENDELIMS, RFC3986_SUBDELIMS
|
||||
|
@ -296,7 +296,9 @@ class Urlizer:
|
|||
|
||||
simple_url_re = _lazy_re_compile(r"^https?://\[?\w", re.IGNORECASE)
|
||||
simple_url_2_re = _lazy_re_compile(
|
||||
r"^www\.|^(?!http)\w[^@]+\.(com|edu|gov|int|mil|net|org)($|/.*)$", re.IGNORECASE
|
||||
rf"^www\.|^(?!http)(?:{DomainNameValidator.hostname_re})"
|
||||
r"\.(com|edu|gov|int|mil|net|org)($|/.*)$",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
word_split_re = _lazy_re_compile(r"""([\s<>"']+)""")
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ from django.utils.encoding import force_str
|
|||
from django.utils.module_loading import import_string
|
||||
from django.utils.regex_helper import _lazy_re_compile
|
||||
from django.utils.version import get_docs_version
|
||||
from django.views.decorators.csp import csp_override, csp_report_only_override
|
||||
from django.views.decorators.debug import coroutine_functions_to_sensitive_variables
|
||||
|
||||
# Minimal Django templates engine to render the error templates
|
||||
|
@ -59,6 +60,8 @@ class CallableSettingWrapper:
|
|||
return repr(self._wrapped)
|
||||
|
||||
|
||||
@csp_override({})
|
||||
@csp_report_only_override({})
|
||||
def technical_500_response(request, exc_type, exc_value, tb, status_code=500):
|
||||
"""
|
||||
Create a technical server error response. The last three arguments are
|
||||
|
@ -606,6 +609,8 @@ class ExceptionReporter:
|
|||
tb = tb.tb_next
|
||||
|
||||
|
||||
@csp_override({})
|
||||
@csp_report_only_override({})
|
||||
def technical_404_response(request, exception):
|
||||
"""Create a technical 404 error response. `exception` is the Http404."""
|
||||
try:
|
||||
|
|
39
django/views/decorators/csp.py
Normal file
39
django/views/decorators/csp.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
from functools import wraps
|
||||
|
||||
from asgiref.sync import iscoroutinefunction
|
||||
|
||||
|
||||
def _make_csp_decorator(config_attr_name, config_attr_value):
|
||||
"""General CSP override decorator factory."""
|
||||
|
||||
if not isinstance(config_attr_value, dict):
|
||||
raise TypeError("CSP config should be a mapping.")
|
||||
|
||||
def decorator(view_func):
|
||||
@wraps(view_func)
|
||||
async def _wrapped_async_view(request, *args, **kwargs):
|
||||
response = await view_func(request, *args, **kwargs)
|
||||
setattr(response, config_attr_name, config_attr_value)
|
||||
return response
|
||||
|
||||
@wraps(view_func)
|
||||
def _wrapped_sync_view(request, *args, **kwargs):
|
||||
response = view_func(request, *args, **kwargs)
|
||||
setattr(response, config_attr_name, config_attr_value)
|
||||
return response
|
||||
|
||||
if iscoroutinefunction(view_func):
|
||||
return _wrapped_async_view
|
||||
return _wrapped_sync_view
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def csp_override(config):
|
||||
"""Override the Content-Security-Policy header for a view."""
|
||||
return _make_csp_decorator("_csp_config", config)
|
||||
|
||||
|
||||
def csp_report_only_override(config):
|
||||
"""Override the Content-Security-Policy-Report-Only header for a view."""
|
||||
return _make_csp_decorator("_csp_ro_config", config)
|
|
@ -1,13 +1,17 @@
|
|||
from urllib.parse import quote
|
||||
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseBadRequest,
|
||||
HttpResponseForbidden,
|
||||
HttpResponseNotFound,
|
||||
HttpResponseServerError,
|
||||
)
|
||||
from django.template import Context, Engine, TemplateDoesNotExist, loader
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.debug import DEBUG_ENGINE
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
from django.views.generic.base import ContextMixin, View
|
||||
|
||||
ERROR_404_TEMPLATE_NAME = "404.html"
|
||||
ERROR_403_TEMPLATE_NAME = "403.html"
|
||||
|
@ -148,3 +152,63 @@ def permission_denied(request, exception, template_name=ERROR_403_TEMPLATE_NAME)
|
|||
return HttpResponseForbidden(
|
||||
template.render(request=request, context={"exception": str(exception)})
|
||||
)
|
||||
|
||||
|
||||
@method_decorator(requires_csrf_token, name="dispatch")
|
||||
class DefaultErrorView(ContextMixin, View):
|
||||
status_code = None
|
||||
context_by_status = {
|
||||
400: {"title": "Bad Request (400)", "details": ""},
|
||||
403: {"title": "403 Forbidden", "details": ""},
|
||||
404: {
|
||||
"title": "Not Found",
|
||||
"details": "The requested resource was not found on this server.",
|
||||
},
|
||||
500: {"title": "Server Error (500)", "details": ""},
|
||||
}
|
||||
|
||||
def setup(self, request, exception=None, **kwargs):
|
||||
self.exception = exception
|
||||
return super().setup(request, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
response_class = HttpResponse.response_class_by_status_code(self.status_code)
|
||||
context = self.get_context_data(**kwargs)
|
||||
try:
|
||||
template = loader.get_template(self.get_template_name())
|
||||
content = template.render(context, request)
|
||||
except TemplateDoesNotExist:
|
||||
template = DEBUG_ENGINE.from_string(ERROR_PAGE_TEMPLATE % context)
|
||||
content = template.render(context=Context(context))
|
||||
return response_class(content, status=self.status_code)
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
return self.get(*args, **kwargs)
|
||||
|
||||
def get_template_name(self):
|
||||
return f"{self.status_code}.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context |= self.context_by_status.get(
|
||||
self.status_code, {"title": f"Error ({self.status_code})", "details": ""}
|
||||
)
|
||||
context |= {
|
||||
"request_path": quote(self.request.path),
|
||||
"exception": self.exception_as_string(),
|
||||
}
|
||||
return context
|
||||
|
||||
def exception_as_string(self):
|
||||
if self.status_code == 404:
|
||||
# Try to get an "interesting" exception message, if any (and not the
|
||||
# ugly Resolver404 dictionary)
|
||||
try:
|
||||
message = self.exception.args[0]
|
||||
except (AttributeError, IndexError):
|
||||
pass
|
||||
else:
|
||||
if isinstance(message, str):
|
||||
return message
|
||||
return self.exception.__class__.__name__
|
||||
return str(self.exception)
|
||||
|
|
|
@ -51,6 +51,7 @@ help:
|
|||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " spelling to check for typos in documentation"
|
||||
@echo " black to apply the black formatting to code blocks in documentation"
|
||||
@echo " lint to check for linting errors in documentation"
|
||||
|
||||
|
||||
clean:
|
||||
|
@ -175,6 +176,11 @@ black:
|
|||
@echo
|
||||
@echo "Code blocks reformatted"
|
||||
|
||||
check: spelling black
|
||||
lint:
|
||||
$(PYTHON) lint.py
|
||||
@echo
|
||||
@echo "Documentation lint complete."
|
||||
|
||||
check: spelling black lint
|
||||
@echo
|
||||
@echo "Style and spelling checks completed."
|
||||
|
|
|
@ -25,4 +25,4 @@ Indices, glossary and tables
|
|||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :doc:`glossary`
|
||||
* :doc:`/glossary`
|
||||
|
|
|
@ -22,8 +22,8 @@ only allows access to users with those two fields both set to True.
|
|||
How do I automatically set a field's value to the user who last edited the object in the admin?
|
||||
===============================================================================================
|
||||
|
||||
The :class:`~django.contrib.admin.ModelAdmin` class provides customization hooks
|
||||
that allow you to transform an object as it saved, using details from the
|
||||
The :class:`~django.contrib.admin.ModelAdmin` class provides customization
|
||||
hooks that allow you to transform an object as it saved, using details from the
|
||||
request. By extracting the current user from the request, and customizing the
|
||||
:meth:`~django.contrib.admin.ModelAdmin.save_model` hook, you can update an
|
||||
object to reflect the user that edited it. See :ref:`the documentation on
|
||||
|
@ -33,8 +33,8 @@ How do I limit admin access so that objects can only be edited by the users who
|
|||
=============================================================================================
|
||||
|
||||
The :class:`~django.contrib.admin.ModelAdmin` class also provides customization
|
||||
hooks that allow you to control the visibility and editability of objects in the
|
||||
admin. Using the same trick of extracting the user from the request, the
|
||||
hooks that allow you to control the visibility and editability of objects in
|
||||
the admin. Using the same trick of extracting the user from the request, the
|
||||
:meth:`~django.contrib.admin.ModelAdmin.get_queryset` and
|
||||
:meth:`~django.contrib.admin.ModelAdmin.has_change_permission` can be used to
|
||||
control the visibility and editability of objects in the admin.
|
||||
|
|
|
@ -32,8 +32,9 @@ thrilled to be able to give something back to the open-source community.
|
|||
What does "Django" mean, and how do you pronounce it?
|
||||
=====================================================
|
||||
|
||||
Django is named after `Django Reinhardt`_, a jazz manouche guitarist from the 1930s
|
||||
to early 1950s. To this day, he's considered one of the best guitarists of all time.
|
||||
Django is named after `Django Reinhardt`_, a jazz manouche guitarist from the
|
||||
1930s to early 1950s. To this day, he's considered one of the best guitarists
|
||||
of all time.
|
||||
|
||||
Listen to his music. You'll like it.
|
||||
|
||||
|
@ -185,9 +186,10 @@ corresponds to a web page on the official Django site.
|
|||
Because the documentation is :source:`stored in revision control <docs>`, you
|
||||
can browse documentation changes just like you can browse code changes.
|
||||
|
||||
Technically, the docs on Django's site are generated from the latest development
|
||||
versions of those reST documents, so the docs on the Django site may offer more
|
||||
information than the docs that come with the latest Django release.
|
||||
Technically, the docs on Django's site are generated from the latest
|
||||
development versions of those reST documents, so the docs on the Django site
|
||||
may offer more information than the docs that come with the latest Django
|
||||
release.
|
||||
|
||||
How do I cite Django?
|
||||
=====================
|
||||
|
|
|
@ -8,8 +8,8 @@ How do I get started?
|
|||
#. `Download the code`_.
|
||||
#. Install Django (read the :doc:`installation guide </intro/install>`).
|
||||
#. Walk through the :doc:`tutorial </intro/tutorial01>`.
|
||||
#. Check out the rest of the :doc:`documentation </index>`, and `ask questions`_ if you
|
||||
run into trouble.
|
||||
#. Check out the rest of the :doc:`documentation </index>`, and
|
||||
`ask questions`_ if you run into trouble.
|
||||
|
||||
.. _`Download the code`: https://www.djangoproject.com/download/
|
||||
.. _ask questions: https://www.djangoproject.com/community/
|
||||
|
|
|
@ -27,8 +27,8 @@ the following:
|
|||
``connection.queries`` includes all SQL statements -- INSERTs, UPDATES,
|
||||
SELECTs, etc. Each time your app hits the database, the query will be recorded.
|
||||
|
||||
If you are using :doc:`multiple databases</topics/db/multi-db>`, you can use the
|
||||
same interface on each member of the ``connections`` dictionary:
|
||||
If you are using :doc:`multiple databases</topics/db/multi-db>`, you can use
|
||||
the same interface on each member of the ``connections`` dictionary:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
|
|
|
@ -51,7 +51,8 @@ Glossary
|
|||
See :class:`property`.
|
||||
|
||||
queryset
|
||||
An object representing some set of rows to be fetched from the database.
|
||||
An object representing some set of rows to be fetched from the
|
||||
database.
|
||||
|
||||
See :doc:`/topics/db/queries`.
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ How to authenticate using ``REMOTE_USER``
|
|||
|
||||
This document describes how to make use of external authentication sources
|
||||
(where the web server sets the ``REMOTE_USER`` environment variable) in your
|
||||
Django applications. This type of authentication solution is typically seen on
|
||||
Django applications. This type of authentication solution is typically seen on
|
||||
intranet sites, with single sign-on solutions such as IIS and Integrated
|
||||
Windows Authentication or Apache and `mod_authnz_ldap`_, `CAS`_, `WebAuth`_,
|
||||
`mod_auth_sspi`_, etc.
|
||||
|
@ -15,9 +15,9 @@ Windows Authentication or Apache and `mod_authnz_ldap`_, `CAS`_, `WebAuth`_,
|
|||
.. _mod_auth_sspi: https://sourceforge.net/projects/mod-auth-sspi
|
||||
|
||||
When the web server takes care of authentication it typically sets the
|
||||
``REMOTE_USER`` environment variable for use in the underlying application. In
|
||||
``REMOTE_USER`` environment variable for use in the underlying application. In
|
||||
Django, ``REMOTE_USER`` is made available in the :attr:`request.META
|
||||
<django.http.HttpRequest.META>` attribute. Django can be configured to make
|
||||
<django.http.HttpRequest.META>` attribute. Django can be configured to make
|
||||
use of the ``REMOTE_USER`` value using the ``RemoteUserMiddleware``
|
||||
or ``PersistentRemoteUserMiddleware``, and
|
||||
:class:`~django.contrib.auth.backends.RemoteUserBackend` classes found in
|
||||
|
@ -76,7 +76,7 @@ regardless of ``AUTHENTICATION_BACKENDS``.
|
|||
|
||||
If your authentication mechanism uses a custom HTTP header and not
|
||||
``REMOTE_USER``, you can subclass ``RemoteUserMiddleware`` and set the
|
||||
``header`` attribute to the desired ``request.META`` key. For example:
|
||||
``header`` attribute to the desired ``request.META`` key. For example:
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``mysite/middleware.py``
|
||||
|
|
|
@ -37,9 +37,9 @@ Using CSRF protection with AJAX
|
|||
===============================
|
||||
|
||||
While the above method can be used for AJAX POST requests, it has some
|
||||
inconveniences: you have to remember to pass the CSRF token in as POST data with
|
||||
every POST request. For this reason, there is an alternative method: on each
|
||||
XMLHttpRequest, set a custom ``X-CSRFToken`` header (as specified by the
|
||||
inconveniences: you have to remember to pass the CSRF token in as POST data
|
||||
with every POST request. For this reason, there is an alternative method: on
|
||||
each XMLHttpRequest, set a custom ``X-CSRFToken`` header (as specified by the
|
||||
:setting:`CSRF_HEADER_NAME` setting) to the value of the CSRF token. This is
|
||||
often easier because many JavaScript frameworks provide hooks that allow
|
||||
headers to be set on every request.
|
||||
|
@ -217,11 +217,11 @@ Testing and CSRF protection
|
|||
===========================
|
||||
|
||||
The ``CsrfViewMiddleware`` will usually be a big hindrance to testing view
|
||||
functions, due to the need for the CSRF token which must be sent with every POST
|
||||
request. For this reason, Django's HTTP client for tests has been modified to
|
||||
set a flag on requests which relaxes the middleware and the ``csrf_protect``
|
||||
decorator so that they no longer rejects requests. In every other respect
|
||||
(e.g. sending cookies etc.), they behave the same.
|
||||
functions, due to the need for the CSRF token which must be sent with every
|
||||
POST request. For this reason, Django's HTTP client for tests has been modified
|
||||
to set a flag on requests which relaxes the middleware and the ``csrf_protect``
|
||||
decorator so that they no longer rejects requests. In every other respect (e.g.
|
||||
sending cookies etc.), they behave the same.
|
||||
|
||||
If, for some reason, you *want* the test client to perform CSRF
|
||||
checks, you can create an instance of the test client that enforces
|
||||
|
@ -237,8 +237,8 @@ Edge cases
|
|||
|
||||
Certain views can have unusual requirements that mean they don't fit the normal
|
||||
pattern envisaged here. A number of utilities can be useful in these
|
||||
situations. The scenarios they might be needed in are described in the following
|
||||
section.
|
||||
situations. The scenarios they might be needed in are described in the
|
||||
following section.
|
||||
|
||||
Disabling CSRF protection for just a few views
|
||||
----------------------------------------------
|
||||
|
@ -265,8 +265,8 @@ There may be some views that are unprotected and have been exempted by
|
|||
``csrf_exempt``, but still need to include the CSRF token.
|
||||
|
||||
Solution: use :func:`~django.views.decorators.csrf.csrf_exempt` followed by
|
||||
:func:`~django.views.decorators.csrf.requires_csrf_token`. (i.e. ``requires_csrf_token``
|
||||
should be the innermost decorator).
|
||||
:func:`~django.views.decorators.csrf.requires_csrf_token`. (i.e.
|
||||
``requires_csrf_token`` should be the innermost decorator).
|
||||
|
||||
Protecting a view for only one path
|
||||
-----------------------------------
|
||||
|
@ -304,8 +304,8 @@ view that sends the page.
|
|||
CSRF protection in reusable applications
|
||||
========================================
|
||||
|
||||
Because it is possible for the developer to turn off the ``CsrfViewMiddleware``,
|
||||
all relevant views in contrib apps use the ``csrf_protect`` decorator to ensure
|
||||
the security of these applications against CSRF. It is recommended that the
|
||||
developers of other reusable apps that want the same guarantees also use the
|
||||
``csrf_protect`` decorator on their views.
|
||||
Because it is possible for the developer to turn off the
|
||||
``CsrfViewMiddleware``, all relevant views in contrib apps use the
|
||||
``csrf_protect`` decorator to ensure the security of these applications against
|
||||
CSRF. It is recommended that the developers of other reusable apps that want
|
||||
the same guarantees also use the ``csrf_protect`` decorator on their views.
|
||||
|
|
|
@ -16,8 +16,9 @@ You'll need to follow these steps:
|
|||
|
||||
class MyStorage(Storage): ...
|
||||
|
||||
#. Django must be able to instantiate your storage system without any arguments.
|
||||
This means that any settings should be taken from ``django.conf.settings``::
|
||||
#. Django must be able to instantiate your storage system without any
|
||||
arguments. This means that any settings should be taken from
|
||||
``django.conf.settings``::
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import Storage
|
||||
|
@ -29,17 +30,17 @@ You'll need to follow these steps:
|
|||
option = settings.CUSTOM_STORAGE_OPTIONS
|
||||
...
|
||||
|
||||
#. Your storage class must implement the :meth:`_open()` and :meth:`_save()`
|
||||
#. Your storage class must implement the :meth:`_open` and :meth:`_save`
|
||||
methods, along with any other methods appropriate to your storage class. See
|
||||
below for more on these methods.
|
||||
|
||||
In addition, if your class provides local file storage, it must override
|
||||
the ``path()`` method.
|
||||
|
||||
#. Your storage class must be :ref:`deconstructible <custom-deconstruct-method>`
|
||||
so it can be serialized when it's used on a field in a migration. As long
|
||||
as your field has arguments that are themselves
|
||||
:ref:`serializable <migration-serializing>`, you can use the
|
||||
#. Your storage class must be :ref:`deconstructible
|
||||
<custom-deconstruct-method>` so it can be serialized when it's used on a
|
||||
field in a migration. As long as your field has arguments that are
|
||||
themselves :ref:`serializable <migration-serializing>`, you can use the
|
||||
``django.utils.deconstruct.deconstructible`` class decorator for this
|
||||
(that's what Django uses on FileSystemStorage).
|
||||
|
||||
|
@ -73,16 +74,16 @@ objects. These are:
|
|||
**Required**.
|
||||
|
||||
Called by ``Storage.open()``, this is the actual mechanism the storage class
|
||||
uses to open the file. This must return a ``File`` object, though in most cases,
|
||||
you'll want to return some subclass here that implements logic specific to the
|
||||
backend storage system. The :exc:`FileNotFoundError` exception should be raised
|
||||
when a file doesn't exist.
|
||||
uses to open the file. This must return a ``File`` object, though in most
|
||||
cases, you'll want to return some subclass here that implements logic specific
|
||||
to the backend storage system. The :exc:`FileNotFoundError` exception should be
|
||||
raised when a file doesn't exist.
|
||||
|
||||
.. method:: _save(name, content)
|
||||
|
||||
Called by ``Storage.save()``. The ``name`` will already have gone through
|
||||
``get_valid_name()`` and ``get_available_name()``, and the ``content`` will be a
|
||||
``File`` object itself.
|
||||
``get_valid_name()`` and ``get_available_name()``, and the ``content`` will be
|
||||
a ``File`` object itself.
|
||||
|
||||
Should return the actual name of the file saved (usually the ``name`` passed
|
||||
in, but if the storage needs to change the file name return the new name
|
||||
|
|
|
@ -39,9 +39,9 @@ lookup, then we need to tell Django about it::
|
|||
return "%s <> %s" % (lhs, rhs), params
|
||||
|
||||
To register the ``NotEqual`` lookup we will need to call ``register_lookup`` on
|
||||
the field class we want the lookup to be available for. In this case, the lookup
|
||||
makes sense on all ``Field`` subclasses, so we register it with ``Field``
|
||||
directly::
|
||||
the field class we want the lookup to be available for. In this case, the
|
||||
lookup makes sense on all ``Field`` subclasses, so we register it with
|
||||
``Field`` directly::
|
||||
|
||||
from django.db.models import Field
|
||||
|
||||
|
@ -61,10 +61,10 @@ could place the implementation in a ``models.py`` file, or register the lookup
|
|||
in the ``ready()`` method of an ``AppConfig``.
|
||||
|
||||
Taking a closer look at the implementation, the first required attribute is
|
||||
``lookup_name``. This allows the ORM to understand how to interpret ``name__ne``
|
||||
and use ``NotEqual`` to generate the SQL. By convention, these names are always
|
||||
lowercase strings containing only letters, but the only hard requirement is
|
||||
that it must not contain the string ``__``.
|
||||
``lookup_name``. This allows the ORM to understand how to interpret
|
||||
``name__ne`` and use ``NotEqual`` to generate the SQL. By convention, these
|
||||
names are always lowercase strings containing only letters, but the only hard
|
||||
requirement is that it must not contain the string ``__``.
|
||||
|
||||
We then need to define the ``as_sql`` method. This takes a ``SQLCompiler``
|
||||
object, called ``compiler``, and the active database connection.
|
||||
|
@ -112,8 +112,8 @@ or where it did not exceed a certain amount
|
|||
functionality which is possible in a database backend independent manner,
|
||||
and without duplicating functionality already in Django.
|
||||
|
||||
We will start by writing an ``AbsoluteValue`` transformer. This will use the SQL
|
||||
function ``ABS()`` to transform the value before comparison::
|
||||
We will start by writing an ``AbsoluteValue`` transformer. This will use the
|
||||
SQL function ``ABS()`` to transform the value before comparison::
|
||||
|
||||
from django.db.models import Transform
|
||||
|
||||
|
@ -227,16 +227,16 @@ Notice also that as both sides are used multiple times in the query the params
|
|||
need to contain ``lhs_params`` and ``rhs_params`` multiple times.
|
||||
|
||||
The final query does the inversion (``27`` to ``-27``) directly in the
|
||||
database. The reason for doing this is that if the ``self.rhs`` is something else
|
||||
than a plain integer value (for example an ``F()`` reference) we can't do the
|
||||
transformations in Python.
|
||||
database. The reason for doing this is that if the ``self.rhs`` is something
|
||||
else than a plain integer value (for example an ``F()`` reference) we can't do
|
||||
the transformations in Python.
|
||||
|
||||
.. note::
|
||||
In fact, most lookups with ``__abs`` could be implemented as range queries
|
||||
like this, and on most database backends it is likely to be more sensible to
|
||||
do so as you can make use of the indexes. However with PostgreSQL you may
|
||||
want to add an index on ``abs(change)`` which would allow these queries to
|
||||
be very efficient.
|
||||
like this, and on most database backends it is likely to be more sensible
|
||||
to do so as you can make use of the indexes. However with PostgreSQL you
|
||||
may want to add an index on ``abs(change)`` which would allow these queries
|
||||
to be very efficient.
|
||||
|
||||
A bilateral transformer example
|
||||
===============================
|
||||
|
@ -252,10 +252,10 @@ very useful in practice as Django already comes with a bunch of built-in
|
|||
case-insensitive lookups, but it will be a nice demonstration of bilateral
|
||||
transformations in a database-agnostic way.
|
||||
|
||||
We define an ``UpperCase`` transformer which uses the SQL function ``UPPER()`` to
|
||||
transform the values before comparison. We define
|
||||
:attr:`bilateral = True <django.db.models.Transform.bilateral>` to indicate that
|
||||
this transformation should apply to both ``lhs`` and ``rhs``::
|
||||
We define an ``UpperCase`` transformer which uses the SQL function ``UPPER()``
|
||||
to transform the values before comparison. We define :attr:`bilateral = True
|
||||
<django.db.models.Transform.bilateral>` to indicate that this transformation
|
||||
should apply to both ``lhs`` and ``rhs``::
|
||||
|
||||
from django.db.models import Transform
|
||||
|
||||
|
@ -272,8 +272,8 @@ Next, let's register it::
|
|||
CharField.register_lookup(UpperCase)
|
||||
TextField.register_lookup(UpperCase)
|
||||
|
||||
Now, the queryset ``Author.objects.filter(name__upper="doe")`` will generate a case
|
||||
insensitive query like this:
|
||||
Now, the queryset ``Author.objects.filter(name__upper="doe")`` will generate a
|
||||
case insensitive query like this:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ whose name doesn't begin with an underscore. For example:
|
|||
tests.py
|
||||
views.py
|
||||
|
||||
In this example, the ``closepoll`` command will be made available to any project
|
||||
that includes the ``polls`` application in :setting:`INSTALLED_APPS`.
|
||||
In this example, the ``closepoll`` command will be made available to any
|
||||
project that includes the ``polls`` application in :setting:`INSTALLED_APPS`.
|
||||
|
||||
The ``_private.py`` module will not be available as a management command.
|
||||
|
||||
|
@ -73,13 +73,12 @@ look like this::
|
|||
.. _management-commands-output:
|
||||
|
||||
.. note::
|
||||
When you are using management commands and wish to provide console
|
||||
output, you should write to ``self.stdout`` and ``self.stderr``,
|
||||
instead of printing to ``stdout`` and ``stderr`` directly. By
|
||||
using these proxies, it becomes much easier to test your custom
|
||||
command. Note also that you don't need to end messages with a newline
|
||||
character, it will be added automatically, unless you specify the ``ending``
|
||||
parameter::
|
||||
When you are using management commands and wish to provide console output,
|
||||
you should write to ``self.stdout`` and ``self.stderr``, instead of
|
||||
printing to ``stdout`` and ``stderr`` directly. By using these proxies, it
|
||||
becomes much easier to test your custom command. Note also that you don't
|
||||
need to end messages with a newline character, it will be added
|
||||
automatically, unless you specify the ``ending`` parameter::
|
||||
|
||||
self.stdout.write("Unterminated line", ending="")
|
||||
|
||||
|
@ -99,7 +98,8 @@ Accepting optional arguments
|
|||
|
||||
The same ``closepoll`` could be easily modified to delete a given poll instead
|
||||
of closing it by accepting additional command line options. These custom
|
||||
options can be added in the :meth:`~BaseCommand.add_arguments` method like this::
|
||||
options can be added in the :meth:`~BaseCommand.add_arguments` method like
|
||||
this::
|
||||
|
||||
class Command(BaseCommand):
|
||||
def add_arguments(self, parser):
|
||||
|
@ -120,7 +120,7 @@ options can be added in the :meth:`~BaseCommand.add_arguments` method like this:
|
|||
# ...
|
||||
|
||||
The option (``delete`` in our example) is available in the options dict
|
||||
parameter of the handle method. See the :py:mod:`argparse` Python documentation
|
||||
parameter of the handle method. See the :mod:`argparse` Python documentation
|
||||
for more about ``add_argument`` usage.
|
||||
|
||||
In addition to being able to add custom command line options, all
|
||||
|
@ -208,7 +208,7 @@ All attributes can be set in your derived class and can be used in
|
|||
|
||||
If your command defines mandatory positional arguments, you can customize
|
||||
the message error returned in the case of missing arguments. The default is
|
||||
output by :py:mod:`argparse` ("too few arguments").
|
||||
output by :mod:`argparse` ("too few arguments").
|
||||
|
||||
.. attribute:: BaseCommand.output_transaction
|
||||
|
||||
|
@ -273,7 +273,8 @@ the :meth:`~BaseCommand.handle` method must be implemented.
|
|||
Django.
|
||||
|
||||
You can customize the instance by overriding this method and calling
|
||||
``super()`` with ``kwargs`` of :class:`~argparse.ArgumentParser` parameters.
|
||||
``super()`` with ``kwargs`` of :class:`~argparse.ArgumentParser`
|
||||
parameters.
|
||||
|
||||
.. method:: BaseCommand.add_arguments(parser)
|
||||
|
||||
|
|
|
@ -7,17 +7,18 @@ How to create custom model fields
|
|||
Introduction
|
||||
============
|
||||
|
||||
The :doc:`model reference </topics/db/models>` documentation explains how to use
|
||||
Django's standard field classes -- :class:`~django.db.models.CharField`,
|
||||
The :doc:`model reference </topics/db/models>` documentation explains how to
|
||||
use Django's standard field classes -- :class:`~django.db.models.CharField`,
|
||||
:class:`~django.db.models.DateField`, etc. For many purposes, those classes are
|
||||
all you'll need. Sometimes, though, the Django version won't meet your precise
|
||||
requirements, or you'll want to use a field that is entirely different from
|
||||
those shipped with Django.
|
||||
|
||||
Django's built-in field types don't cover every possible database column type --
|
||||
only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure
|
||||
Django's built-in field types don't cover every possible database column type
|
||||
-- only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure
|
||||
column types, such as geographic polygons or even user-created types such as
|
||||
`PostgreSQL custom types`_, you can define your own Django ``Field`` subclasses.
|
||||
`PostgreSQL custom types`_, you can define your own Django ``Field``
|
||||
subclasses.
|
||||
|
||||
.. _PostgreSQL custom types: https://www.postgresql.org/docs/current/sql-createtype.html
|
||||
|
||||
|
@ -33,7 +34,7 @@ easier to follow, we'll use a consistent example throughout this document:
|
|||
wrapping a Python object representing the deal of cards in a hand of Bridge_.
|
||||
Don't worry, you don't have to know how to play Bridge to follow this example.
|
||||
You only need to know that 52 cards are dealt out equally to four players, who
|
||||
are traditionally called *north*, *east*, *south* and *west*. Our class looks
|
||||
are traditionally called *north*, *east*, *south* and *west*. Our class looks
|
||||
something like this::
|
||||
|
||||
class Hand:
|
||||
|
@ -106,13 +107,13 @@ What does a field class do?
|
|||
---------------------------
|
||||
|
||||
All of Django's fields (and when we say *fields* in this document, we always
|
||||
mean model fields and not :doc:`form fields </ref/forms/fields>`) are subclasses
|
||||
of :class:`django.db.models.Field`. Most of the information that Django records
|
||||
about a field is common to all fields -- name, help text, uniqueness and so
|
||||
forth. Storing all that information is handled by ``Field``. We'll get into the
|
||||
precise details of what ``Field`` can do later on; for now, suffice it to say
|
||||
that everything descends from ``Field`` and then customizes key pieces of the
|
||||
class behavior.
|
||||
mean model fields and not :doc:`form fields </ref/forms/fields>`) are
|
||||
subclasses of :class:`django.db.models.Field`. Most of the information that
|
||||
Django records about a field is common to all fields -- name, help text,
|
||||
uniqueness and so forth. Storing all that information is handled by ``Field``.
|
||||
We'll get into the precise details of what ``Field`` can do later on; for now,
|
||||
suffice it to say that everything descends from ``Field`` and then customizes
|
||||
key pieces of the class behavior.
|
||||
|
||||
It's important to realize that a Django field class is not what is stored in
|
||||
your model attributes. The model attributes contain normal Python objects. The
|
||||
|
@ -149,9 +150,9 @@ is most similar to. Can you subclass an existing Django field and save yourself
|
|||
some work? If not, you should subclass the :class:`~django.db.models.Field`
|
||||
class, from which everything is descended.
|
||||
|
||||
Initializing your new field is a matter of separating out any arguments that are
|
||||
specific to your case from the common arguments and passing the latter to the
|
||||
``__init__()`` method of :class:`~django.db.models.Field` (or your parent
|
||||
Initializing your new field is a matter of separating out any arguments that
|
||||
are specific to your case from the common arguments and passing the latter to
|
||||
the ``__init__()`` method of :class:`~django.db.models.Field` (or your parent
|
||||
class).
|
||||
|
||||
In our example, we'll call our field ``HandField``. (It's a good idea to call
|
||||
|
@ -214,9 +215,9 @@ The ``Field.__init__()`` method takes the following parameters:
|
|||
* :attr:`~django.db.models.Field.choices`
|
||||
* :attr:`~django.db.models.Field.help_text`
|
||||
* :attr:`~django.db.models.Field.db_column`
|
||||
* :attr:`~django.db.models.Field.db_tablespace`: Only for index creation, if the
|
||||
backend supports :doc:`tablespaces </topics/db/tablespaces>`. You can usually
|
||||
ignore this option.
|
||||
* :attr:`~django.db.models.Field.db_tablespace`: Only for index creation, if
|
||||
the backend supports :doc:`tablespaces </topics/db/tablespaces>`. You can
|
||||
usually ignore this option.
|
||||
* :attr:`~django.db.models.Field.auto_created`: ``True`` if the field was
|
||||
automatically created, as for the :class:`~django.db.models.OneToOneField`
|
||||
used by model inheritance. For advanced use only.
|
||||
|
@ -253,9 +254,9 @@ name and import path. You do, however, have to care about the positional
|
|||
and keyword arguments, as these are likely the things you are changing.
|
||||
|
||||
For example, in our ``HandField`` class we're always forcibly setting
|
||||
max_length in ``__init__()``. The ``deconstruct()`` method on the base ``Field``
|
||||
class will see this and try to return it in the keyword arguments; thus,
|
||||
we can drop it from the keyword arguments for readability::
|
||||
max_length in ``__init__()``. The ``deconstruct()`` method on the base
|
||||
``Field`` class will see this and try to return it in the keyword arguments;
|
||||
thus, we can drop it from the keyword arguments for readability::
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
@ -471,10 +472,11 @@ over this field. You are then responsible for creating the column in the right
|
|||
table in some other way, but this gives you a way to tell Django to get out of
|
||||
the way.
|
||||
|
||||
The :meth:`~Field.rel_db_type` method is called by fields such as ``ForeignKey``
|
||||
and ``OneToOneField`` that point to another field to determine their database
|
||||
column data types. For example, if you have an ``UnsignedAutoField``, you also
|
||||
need the foreign keys that point to that field to use the same data type::
|
||||
The :meth:`~Field.rel_db_type` method is called by fields such as
|
||||
``ForeignKey`` and ``OneToOneField`` that point to another field to determine
|
||||
their database column data types. For example, if you have an
|
||||
``UnsignedAutoField``, you also need the foreign keys that point to that field
|
||||
to use the same data type::
|
||||
|
||||
# MySQL unsigned integer (range 0 to 4294967295).
|
||||
class UnsignedAutoField(models.AutoField):
|
||||
|
@ -648,8 +650,8 @@ a custom form field (and even a form widget). See the :doc:`forms documentation
|
|||
If you wish to exclude the field from the :class:`~django.forms.ModelForm`, you
|
||||
can override the :meth:`~Field.formfield` method to return ``None``.
|
||||
|
||||
Continuing our ongoing example, we can write the :meth:`~Field.formfield` method
|
||||
as::
|
||||
Continuing our ongoing example, we can write the :meth:`~Field.formfield`
|
||||
method as::
|
||||
|
||||
class HandField(models.Field):
|
||||
# ...
|
||||
|
|
|
@ -39,8 +39,8 @@ For example:
|
|||
|
||||
The customization above adds :func:`~django.urls.resolve` and
|
||||
:func:`~django.urls.reverse` to the default namespace, which already includes
|
||||
all models from the apps listed in :setting:`INSTALLED_APPS` plus what is
|
||||
imported by default. These objects will be available in the ``shell`` without
|
||||
all models from the apps listed in :setting:`INSTALLED_APPS` plus what is
|
||||
imported by default. These objects will be available in the ``shell`` without
|
||||
requiring a manual import.
|
||||
|
||||
Running this customized ``shell`` command with ``verbosity=2`` would show:
|
||||
|
@ -48,7 +48,7 @@ Running this customized ``shell`` command with ``verbosity=2`` would show:
|
|||
.. console::
|
||||
|
||||
13 objects imported automatically:
|
||||
|
||||
|
||||
from django.db import connection, reset_queries, models
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.models import LogEntry
|
||||
|
|
|
@ -264,7 +264,7 @@ Template filter code falls into one of two situations:
|
|||
reviewing your code.
|
||||
|
||||
Marking a filter ``is_safe`` will coerce the filter's return value to
|
||||
a string. If your filter should return a boolean or other non-string
|
||||
a string. If your filter should return a boolean or other non-string
|
||||
value, marking it ``is_safe`` will probably have unintended
|
||||
consequences (such as converting a boolean False to the string
|
||||
'False').
|
||||
|
@ -285,13 +285,13 @@ Template filter code falls into one of two situations:
|
|||
order to make things easier for your template authors.
|
||||
|
||||
In order for your filter to know the current auto-escaping state, set the
|
||||
``needs_autoescape`` flag to ``True`` when you register your filter function.
|
||||
(If you don't specify this flag, it defaults to ``False``). This flag tells
|
||||
Django that your filter function wants to be passed an extra keyword
|
||||
argument, called ``autoescape``, that is ``True`` if auto-escaping is in
|
||||
effect and ``False`` otherwise. It is recommended to set the default of the
|
||||
``autoescape`` parameter to ``True``, so that if you call the function
|
||||
from Python code it will have escaping enabled by default.
|
||||
``needs_autoescape`` flag to ``True`` when you register your filter
|
||||
function. (If you don't specify this flag, it defaults to ``False``). This
|
||||
flag tells Django that your filter function wants to be passed an extra
|
||||
keyword argument, called ``autoescape``, that is ``True`` if auto-escaping
|
||||
is in effect and ``False`` otherwise. It is recommended to set the default
|
||||
of the ``autoescape`` parameter to ``True``, so that if you call the
|
||||
function from Python code it will have escaping enabled by default.
|
||||
|
||||
For example, let's write a filter that emphasizes the first character of
|
||||
a string::
|
||||
|
@ -509,7 +509,7 @@ Simple block tags
|
|||
|
||||
When a section of rendered template needs to be passed into a custom tag,
|
||||
Django provides the ``simple_block_tag`` helper function to accomplish this.
|
||||
Similar to :meth:`~django.template.Library.simple_tag()`, this function accepts
|
||||
Similar to :meth:`~django.template.Library.simple_tag`, this function accepts
|
||||
a custom tag function, but with the additional ``content`` argument, which
|
||||
contains the rendered content as defined inside the tag. This allows dynamic
|
||||
template sections to be easily incorporated into custom tags.
|
||||
|
@ -827,8 +827,8 @@ Advanced custom template tags
|
|||
-----------------------------
|
||||
|
||||
Sometimes the basic features for custom template tag creation aren't enough.
|
||||
Don't worry, Django gives you complete access to the internals required to build
|
||||
a template tag from the ground up.
|
||||
Don't worry, Django gives you complete access to the internals required to
|
||||
build a template tag from the ground up.
|
||||
|
||||
A quick overview
|
||||
----------------
|
||||
|
@ -841,7 +841,7 @@ When Django compiles a template, it splits the raw template text into *nodes*.
|
|||
Each node is an instance of ``django.template.Node`` and has a ``render()``
|
||||
method. A compiled template is a list of ``Node`` objects. When you call
|
||||
``render()`` on a compiled template object, the template calls ``render()`` on
|
||||
each ``Node`` in its node list, with the given context. The results are all
|
||||
each ``Node`` in its node list, with the given context. The results are all
|
||||
concatenated together to form the output of the template.
|
||||
|
||||
Thus, to define a custom template tag, you specify how the raw template tag is
|
||||
|
@ -856,10 +856,10 @@ function with the tag contents and the parser object itself. This function is
|
|||
responsible for returning a ``Node`` instance based on the contents of the tag.
|
||||
|
||||
For example, let's write a full implementation of our template tag,
|
||||
``{% current_time %}``, that displays the current date/time, formatted according
|
||||
to a parameter given in the tag, in :func:`~time.strftime` syntax. It's a good
|
||||
idea to decide the tag syntax before anything else. In our case, let's say the
|
||||
tag should be used like this:
|
||||
``{% current_time %}``, that displays the current date/time, formatted
|
||||
according to a parameter given in the tag, in :func:`~time.strftime` syntax.
|
||||
It's a good idea to decide the tag syntax before anything else. In our case,
|
||||
let's say the tag should be used like this:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
|
@ -1159,7 +1159,7 @@ Now your tag should begin to look like this::
|
|||
return FormatTimeNode(date_to_be_formatted, format_string[1:-1])
|
||||
|
||||
You also have to change the renderer to retrieve the actual contents of the
|
||||
``date_updated`` property of the ``blog_entry`` object. This can be
|
||||
``date_updated`` property of the ``blog_entry`` object. This can be
|
||||
accomplished by using the ``Variable()`` class in ``django.template``.
|
||||
|
||||
To use the ``Variable`` class, instantiate it with the name of the variable to
|
||||
|
@ -1300,9 +1300,10 @@ Here's how a simplified ``{% comment %}`` tag might be implemented::
|
|||
The actual implementation of :ttag:`{% comment %}<comment>` is slightly
|
||||
different in that it allows broken template tags to appear between
|
||||
``{% comment %}`` and ``{% endcomment %}``. It does so by calling
|
||||
``parser.skip_past('endcomment')`` instead of ``parser.parse(('endcomment',))``
|
||||
followed by ``parser.delete_first_token()``, thus avoiding the generation of a
|
||||
node list.
|
||||
``parser.skip_past('endcomment')`` instead of
|
||||
``parser.parse(('endcomment',))`` followed by
|
||||
``parser.delete_first_token()``, thus avoiding the generation of a node
|
||||
list.
|
||||
|
||||
``parser.parse()`` takes a tuple of names of block tags *to parse until*. It
|
||||
returns an instance of ``django.template.NodeList``, which is a list of
|
||||
|
|
|
@ -40,8 +40,9 @@ For more advanced usage, please read the `Uvicorn documentation <Uvicorn_>`_.
|
|||
Deploying Django using Uvicorn and Gunicorn
|
||||
===========================================
|
||||
|
||||
Gunicorn_ is a robust web server that implements process monitoring and automatic
|
||||
restarts. This can be useful when running Uvicorn in a production environment.
|
||||
Gunicorn_ is a robust web server that implements process monitoring and
|
||||
automatic restarts. This can be useful when running Uvicorn in a production
|
||||
environment.
|
||||
|
||||
To install Uvicorn and Gunicorn, use the following:
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ You should also consider how you will handle :doc:`static files
|
|||
:doc:`error reporting</howto/error-reporting>`.
|
||||
|
||||
Finally, before you deploy your application to production, you should run
|
||||
through our :doc:`deployment checklist<checklist>` to ensure that your
|
||||
configurations are suitable.
|
||||
through our :doc:`deployment checklist </howto/deployment/checklist>` to ensure
|
||||
that your configurations are suitable.
|
||||
|
||||
.. _WSGI: https://wsgi.readthedocs.io/en/latest/
|
||||
.. _ASGI: https://asgi.readthedocs.io/en/latest/
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
How to authenticate against Django's user database from Apache
|
||||
==============================================================
|
||||
|
||||
Since keeping multiple authentication databases in sync is a common problem when
|
||||
dealing with Apache, you can configure Apache to authenticate against Django's
|
||||
:doc:`authentication system </topics/auth/index>` directly. This requires Apache
|
||||
version >= 2.2 and mod_wsgi >= 2.0. For example, you could:
|
||||
Since keeping multiple authentication databases in sync is a common problem
|
||||
when dealing with Apache, you can configure Apache to authenticate against
|
||||
Django's :doc:`authentication system </topics/auth/index>` directly. This
|
||||
requires Apache version >= 2.2 and mod_wsgi >= 2.0. For example, you could:
|
||||
|
||||
* Serve static/media files directly from Apache only to authenticated users.
|
||||
|
||||
|
|
|
@ -37,9 +37,9 @@ deployments.
|
|||
|
||||
WSGI servers obtain the path to the ``application`` callable from their
|
||||
configuration. Django's built-in server, namely the :djadmin:`runserver`
|
||||
command, reads it from the :setting:`WSGI_APPLICATION` setting. By default, it's
|
||||
set to ``<project_name>.wsgi.application``, which points to the ``application``
|
||||
callable in :file:`<project_name>/wsgi.py`.
|
||||
command, reads it from the :setting:`WSGI_APPLICATION` setting. By default,
|
||||
it's set to ``<project_name>.wsgi.application``, which points to the
|
||||
``application`` callable in :file:`<project_name>/wsgi.py`.
|
||||
|
||||
Configuring the settings module
|
||||
===============================
|
||||
|
|
|
@ -124,8 +124,8 @@ use ``WSGIPythonPath``; instead you should use the ``python-path`` option to
|
|||
WSGIProcessGroup example.com
|
||||
|
||||
If you want to serve your project in a subdirectory
|
||||
(``https://example.com/mysite`` in this example), you can add ``WSGIScriptAlias``
|
||||
to the configuration above:
|
||||
(``https://example.com/mysite`` in this example), you can add
|
||||
``WSGIScriptAlias`` to the configuration above:
|
||||
|
||||
.. code-block:: apache
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ How to manage error reporting
|
|||
|
||||
When you're running a public site you should always turn off the
|
||||
:setting:`DEBUG` setting. That will make your server run much faster, and will
|
||||
also prevent malicious users from seeing details of your application that can be
|
||||
revealed by the error pages.
|
||||
also prevent malicious users from seeing details of your application that can
|
||||
be revealed by the error pages.
|
||||
|
||||
However, running with :setting:`DEBUG` set to ``False`` means you'll never see
|
||||
errors generated by your site -- everyone will instead see your public error
|
||||
|
@ -87,11 +87,11 @@ regular expression objects. For example::
|
|||
re.compile(r"^/phpmyadmin/"),
|
||||
]
|
||||
|
||||
In this example, a 404 to any URL ending with ``.php`` or ``.cgi`` will *not* be
|
||||
reported. Neither will any URL starting with ``/phpmyadmin/``.
|
||||
In this example, a 404 to any URL ending with ``.php`` or ``.cgi`` will *not*
|
||||
be reported. Neither will any URL starting with ``/phpmyadmin/``.
|
||||
|
||||
The following example shows how to exclude some conventional URLs that browsers and
|
||||
crawlers often request::
|
||||
The following example shows how to exclude some conventional URLs that browsers
|
||||
and crawlers often request::
|
||||
|
||||
import re
|
||||
|
||||
|
@ -220,7 +220,8 @@ filtered out of error reports in a production environment (that is, where
|
|||
disclosed.
|
||||
|
||||
To systematically hide all POST parameters of a request in error reports,
|
||||
do not provide any argument to the ``sensitive_post_parameters`` decorator::
|
||||
do not provide any argument to the ``sensitive_post_parameters``
|
||||
decorator::
|
||||
|
||||
@sensitive_post_parameters()
|
||||
def my_view(request): ...
|
||||
|
@ -379,5 +380,5 @@ within any given view by setting the ``HttpRequest``’s
|
|||
|
||||
You can also set up custom error reporting by writing a custom piece of
|
||||
:ref:`exception middleware <exception-middleware>`. If you do write custom
|
||||
error handling, it's a good idea to emulate Django's built-in error handling
|
||||
and only report/log errors if :setting:`DEBUG` is ``False``.
|
||||
error handling, it's a good idea to emulate Django's built-in error
|
||||
handling and only report/log errors if :setting:`DEBUG` is ``False``.
|
||||
|
|
|
@ -26,8 +26,8 @@ however, this data isn't loaded automatically, except if you use
|
|||
A fixture is a collection of data that Django knows how to import into a
|
||||
database. The most straightforward way of creating a fixture if you've already
|
||||
got some data is to use the :djadmin:`manage.py dumpdata <dumpdata>` command.
|
||||
Or, you can write fixtures by hand; fixtures can be written as JSON, XML or YAML
|
||||
(with PyYAML_ installed) documents. The :doc:`serialization documentation
|
||||
Or, you can write fixtures by hand; fixtures can be written as JSON, XML or
|
||||
YAML (with PyYAML_ installed) documents. The :doc:`serialization documentation
|
||||
</topics/serialization>` has more details about each of these supported
|
||||
:ref:`serialization formats <serialization-formats>`.
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ Save this as a file by using standard Unix output redirection:
|
|||
|
||||
$ python manage.py inspectdb > models.py
|
||||
|
||||
This feature is meant as a shortcut, not as definitive model generation. See the
|
||||
:djadmin:`documentation of inspectdb <inspectdb>` for more information.
|
||||
This feature is meant as a shortcut, not as definitive model generation. See
|
||||
the :djadmin:`documentation of inspectdb <inspectdb>` for more information.
|
||||
|
||||
Once you've cleaned up your models, name the file ``models.py`` and put it in
|
||||
the Python package that holds your app. Then add the app to your
|
||||
|
|
|
@ -25,7 +25,7 @@ To send a log message from within your code, you place a logging call into it.
|
|||
logging, use a view function as suggested in the example below.
|
||||
|
||||
First, import the Python logging library, and then obtain a logger instance
|
||||
with :py:func:`logging.getLogger`. Provide the ``getLogger()`` method with a
|
||||
with :func:`logging.getLogger`. Provide the ``getLogger()`` method with a
|
||||
name to identify it and the records it emits. A good option is to use
|
||||
``__name__`` (see :ref:`naming-loggers` below for more on this) which will
|
||||
provide the name of the current Python module as a dotted path::
|
||||
|
@ -43,7 +43,7 @@ And then in a function, for example in a view, send a record to the logger::
|
|||
if some_risky_state:
|
||||
logger.warning("Platform is running at risk")
|
||||
|
||||
When this code is executed, a :py:class:`~logging.LogRecord` containing that
|
||||
When this code is executed, a :class:`~logging.LogRecord` containing that
|
||||
message will be sent to the logger. If you're using Django's default logging
|
||||
configuration, the message will appear in the console.
|
||||
|
||||
|
@ -129,7 +129,7 @@ file ``general.log`` (at the project root):
|
|||
Different handler classes take different configuration options. For more
|
||||
information on available handler classes, see the
|
||||
:class:`~django.utils.log.AdminEmailHandler` provided by Django and the various
|
||||
:py:mod:`handler classes <logging.handlers>` provided by Python.
|
||||
:mod:`handler classes <logging.handlers>` provided by Python.
|
||||
|
||||
Logging levels can also be set on the handlers (by default, they accept log
|
||||
messages of all levels). Using the example above, adding:
|
||||
|
@ -238,7 +238,7 @@ application. A named logging configuration will capture logs only from loggers
|
|||
with matching names.
|
||||
|
||||
The namespace of a logger instance is defined using
|
||||
:py:func:`~logging.getLogger`. For example in ``views.py`` of ``my_app``::
|
||||
:func:`~logging.getLogger`. For example in ``views.py`` of ``my_app``::
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ How to create CSV output
|
|||
========================
|
||||
|
||||
This document explains how to output CSV (Comma Separated Values) dynamically
|
||||
using Django views. To do this, you can either use the Python CSV library or the
|
||||
Django template system.
|
||||
using Django views. To do this, you can either use the Python CSV library or
|
||||
the Django template system.
|
||||
|
||||
Using the Python CSV library
|
||||
============================
|
||||
|
@ -101,9 +101,10 @@ the assembly and transmission of a large CSV file::
|
|||
Using the template system
|
||||
=========================
|
||||
|
||||
Alternatively, you can use the :doc:`Django template system </topics/templates>`
|
||||
to generate CSV. This is lower-level than using the convenient Python :mod:`csv`
|
||||
module, but the solution is presented here for completeness.
|
||||
Alternatively, you can use the :doc:`Django template system
|
||||
</topics/templates>` to generate CSV. This is lower-level than using the
|
||||
convenient Python :mod:`csv` module, but the solution is presented here for
|
||||
completeness.
|
||||
|
||||
The idea here is to pass a list of items to your template, and have the
|
||||
template output the commas in a :ttag:`for` loop.
|
||||
|
|
|
@ -87,8 +87,8 @@ mention:
|
|||
browsers will handle the PDF using whatever program/plugin they've been
|
||||
configured to use for PDFs.
|
||||
|
||||
* You can provide an arbitrary ``filename`` parameter. It'll be used by browsers
|
||||
in the "Save as..." dialog.
|
||||
* You can provide an arbitrary ``filename`` parameter. It'll be used by
|
||||
browsers in the "Save as..." dialog.
|
||||
|
||||
* You can hook into the ReportLab API: The same buffer passed as the first
|
||||
argument to ``canvas.Canvas`` can be fed to the
|
||||
|
@ -112,8 +112,8 @@ Other formats
|
|||
Notice that there isn't a lot in these examples that's PDF-specific -- just the
|
||||
bits using ``reportlab``. You can use a similar technique to generate any
|
||||
arbitrary format that you can find a Python library for. Also see
|
||||
:doc:`/howto/outputting-csv` for another example and some techniques you can use
|
||||
when generated text-based formats.
|
||||
:doc:`/howto/outputting-csv` for another example and some techniques you can
|
||||
use when generated text-based formats.
|
||||
|
||||
.. seealso::
|
||||
|
||||
|
|
|
@ -63,7 +63,8 @@ in the ``templates`` directory, and add the template files to that folder:
|
|||
|
||||
The template loader first looks for templates in the ``DIRS`` directory. When
|
||||
the views in the ``blog`` app ask for the ``blog/post.html`` and
|
||||
``blog/list.html`` templates, the loader will return the files you just created.
|
||||
``blog/list.html`` templates, the loader will return the files you just
|
||||
created.
|
||||
|
||||
Overriding from an app's template directory
|
||||
===========================================
|
||||
|
|
|
@ -3,7 +3,7 @@ How to manage static files (e.g. images, JavaScript, CSS)
|
|||
=========================================================
|
||||
|
||||
Websites generally need to serve additional files such as images, JavaScript,
|
||||
or CSS. In Django, we refer to these files as "static files". Django provides
|
||||
or CSS. In Django, we refer to these files as "static files". Django provides
|
||||
:mod:`django.contrib.staticfiles` to help you manage them.
|
||||
|
||||
This page describes how you can serve these static files.
|
||||
|
@ -155,10 +155,10 @@ file-serving functionality: It doesn't know about the finders feature of the
|
|||
collected under :setting:`STATIC_ROOT`.
|
||||
|
||||
Because of this, ``staticfiles`` ships its own
|
||||
:class:`django.contrib.staticfiles.testing.StaticLiveServerTestCase`, a subclass
|
||||
of the built-in one that has the ability to transparently serve all the assets
|
||||
during execution of these tests in a way very similar to what we get at
|
||||
development time with ``DEBUG = True``, i.e. without having to collect them
|
||||
:class:`django.contrib.staticfiles.testing.StaticLiveServerTestCase`, a
|
||||
subclass of the built-in one that has the ability to transparently serve all
|
||||
the assets during execution of these tests in a way very similar to what we get
|
||||
at development time with ``DEBUG = True``, i.e. without having to collect them
|
||||
using :djadmin:`collectstatic` first.
|
||||
|
||||
Deployment
|
||||
|
|
|
@ -91,7 +91,8 @@ Once you're ready, it is time to :doc:`install the new Django version
|
|||
is a major upgrade, you might want to set up a new environment with all the
|
||||
dependencies first.
|
||||
|
||||
If you installed Django with pip_, you can use the ``--upgrade`` or ``-U`` flag:
|
||||
If you installed Django with pip_, you can use the ``--upgrade`` or ``-U``
|
||||
flag:
|
||||
|
||||
.. console::
|
||||
|
||||
|
@ -127,6 +128,6 @@ If you are using caching provided by Django, you should consider clearing your
|
|||
cache after upgrading. Otherwise you may run into problems, for example, if you
|
||||
are caching pickled objects as these objects are not guaranteed to be
|
||||
pickle-compatible across Django versions. A past instance of incompatibility
|
||||
was caching pickled :class:`~django.http.HttpResponse` objects, either
|
||||
directly or indirectly via the :func:`~django.views.decorators.cache.cache_page`
|
||||
was caching pickled :class:`~django.http.HttpResponse` objects, either directly
|
||||
or indirectly via the :func:`~django.views.decorators.cache.cache_page`
|
||||
decorator.
|
||||
|
|
|
@ -37,7 +37,7 @@ attribute::
|
|||
migrations.RunPython(forwards),
|
||||
]
|
||||
|
||||
You can also provide hints that will be passed to the :meth:`allow_migrate()`
|
||||
You can also provide hints that will be passed to the :meth:`allow_migrate`
|
||||
method of database routers as ``**hints``:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -173,7 +173,8 @@ the respective field according to your needs.
|
|||
migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
|
||||
* Now you can apply the migrations as usual with the :djadmin:`migrate` command.
|
||||
* Now you can apply the migrations as usual with the :djadmin:`migrate`
|
||||
command.
|
||||
|
||||
Note there is a race condition if you allow objects to be created while this
|
||||
migration is running. Objects created after the ``AddField`` and before
|
||||
|
@ -197,7 +198,7 @@ a transaction by setting the ``atomic`` attribute to ``False``::
|
|||
|
||||
Within such a migration, all operations are run without a transaction. It's
|
||||
possible to execute parts of the migration inside a transaction using
|
||||
:func:`~django.db.transaction.atomic()` or by passing ``atomic=True`` to
|
||||
:func:`~django.db.transaction.atomic` or by passing ``atomic=True`` to
|
||||
``RunPython``.
|
||||
|
||||
Here's an example of a non-atomic data migration that updates a large table in
|
||||
|
@ -277,12 +278,13 @@ Migrating data between third-party apps
|
|||
You can use a data migration to move data from one third-party application to
|
||||
another.
|
||||
|
||||
If you plan to remove the old app later, you'll need to set the ``dependencies``
|
||||
property based on whether or not the old app is installed. Otherwise, you'll
|
||||
have missing dependencies once you uninstall the old app. Similarly, you'll
|
||||
need to catch :exc:`LookupError` in the ``apps.get_model()`` call that
|
||||
retrieves models from the old app. This approach allows you to deploy your
|
||||
project anywhere without first installing and then uninstalling the old app.
|
||||
If you plan to remove the old app later, you'll need to set the
|
||||
``dependencies`` property based on whether or not the old app is installed.
|
||||
Otherwise, you'll have missing dependencies once you uninstall the old app.
|
||||
Similarly, you'll need to catch :exc:`LookupError` in the ``apps.get_model()``
|
||||
call that retrieves models from the old app. This approach allows you to deploy
|
||||
your project anywhere without first installing and then uninstalling the old
|
||||
app.
|
||||
|
||||
Here's a sample migration:
|
||||
|
||||
|
|
302
docs/index.txt
302
docs/index.txt
|
@ -12,34 +12,34 @@ First steps
|
|||
Are you new to Django or to programming? This is the place to start!
|
||||
|
||||
* **From scratch:**
|
||||
:doc:`Overview <intro/overview>` |
|
||||
:doc:`Installation <intro/install>`
|
||||
:doc:`Overview </intro/overview>` |
|
||||
:doc:`Installation </intro/install>`
|
||||
|
||||
* **Tutorial:**
|
||||
:doc:`Part 1: Requests and responses <intro/tutorial01>` |
|
||||
:doc:`Part 2: Models and the admin site <intro/tutorial02>` |
|
||||
:doc:`Part 3: Views and templates <intro/tutorial03>` |
|
||||
:doc:`Part 4: Forms and generic views <intro/tutorial04>` |
|
||||
:doc:`Part 5: Testing <intro/tutorial05>` |
|
||||
:doc:`Part 6: Static files <intro/tutorial06>` |
|
||||
:doc:`Part 7: Customizing the admin site <intro/tutorial07>` |
|
||||
:doc:`Part 8: Adding third-party packages <intro/tutorial08>`
|
||||
:doc:`Part 1: Requests and responses </intro/tutorial01>` |
|
||||
:doc:`Part 2: Models and the admin site </intro/tutorial02>` |
|
||||
:doc:`Part 3: Views and templates </intro/tutorial03>` |
|
||||
:doc:`Part 4: Forms and generic views </intro/tutorial04>` |
|
||||
:doc:`Part 5: Testing </intro/tutorial05>` |
|
||||
:doc:`Part 6: Static files </intro/tutorial06>` |
|
||||
:doc:`Part 7: Customizing the admin site </intro/tutorial07>` |
|
||||
:doc:`Part 8: Adding third-party packages </intro/tutorial08>`
|
||||
|
||||
* **Advanced Tutorials:**
|
||||
:doc:`How to write reusable apps <intro/reusable-apps>` |
|
||||
:doc:`Writing your first contribution to Django <intro/contributing>`
|
||||
:doc:`How to write reusable apps </intro/reusable-apps>` |
|
||||
:doc:`Writing your first contribution to Django </intro/contributing>`
|
||||
|
||||
Getting help
|
||||
============
|
||||
|
||||
Having trouble? We'd like to help!
|
||||
|
||||
* Try the :doc:`FAQ <faq/index>` -- it's got answers to many common questions.
|
||||
* Try the :doc:`FAQ </faq/index>` -- it's got answers to many common questions.
|
||||
|
||||
* Looking for specific information? Try the :ref:`genindex`, :ref:`modindex` or
|
||||
the :doc:`detailed table of contents <contents>`.
|
||||
the :doc:`detailed table of contents </contents>`.
|
||||
|
||||
* Not found anything? See :doc:`faq/help` for information on getting support
|
||||
* Not found anything? See :doc:`/faq/help` for information on getting support
|
||||
and asking questions to the community.
|
||||
|
||||
* Report bugs with Django in our `ticket tracker`_.
|
||||
|
@ -74,46 +74,46 @@ Django provides an abstraction layer (the "models") for structuring and
|
|||
manipulating the data of your web application. Learn more about it below:
|
||||
|
||||
* **Models:**
|
||||
:doc:`Introduction to models <topics/db/models>` |
|
||||
:doc:`Field types <ref/models/fields>` |
|
||||
:doc:`Indexes <ref/models/indexes>` |
|
||||
:doc:`Meta options <ref/models/options>` |
|
||||
:doc:`Model class <ref/models/class>`
|
||||
:doc:`Introduction to models </topics/db/models>` |
|
||||
:doc:`Field types </ref/models/fields>` |
|
||||
:doc:`Indexes </ref/models/indexes>` |
|
||||
:doc:`Meta options </ref/models/options>` |
|
||||
:doc:`Model class </ref/models/class>`
|
||||
|
||||
* **QuerySets:**
|
||||
:doc:`Making queries <topics/db/queries>` |
|
||||
:doc:`QuerySet method reference <ref/models/querysets>` |
|
||||
:doc:`Lookup expressions <ref/models/lookups>`
|
||||
:doc:`Making queries </topics/db/queries>` |
|
||||
:doc:`QuerySet method reference </ref/models/querysets>` |
|
||||
:doc:`Lookup expressions </ref/models/lookups>`
|
||||
|
||||
* **Model instances:**
|
||||
:doc:`Instance methods <ref/models/instances>` |
|
||||
:doc:`Accessing related objects <ref/models/relations>`
|
||||
:doc:`Instance methods </ref/models/instances>` |
|
||||
:doc:`Accessing related objects </ref/models/relations>`
|
||||
|
||||
* **Migrations:**
|
||||
:doc:`Introduction to Migrations<topics/migrations>` |
|
||||
:doc:`Operations reference <ref/migration-operations>` |
|
||||
:doc:`SchemaEditor <ref/schema-editor>` |
|
||||
:doc:`Writing migrations <howto/writing-migrations>`
|
||||
:doc:`Introduction to Migrations</topics/migrations>` |
|
||||
:doc:`Operations reference </ref/migration-operations>` |
|
||||
:doc:`SchemaEditor </ref/schema-editor>` |
|
||||
:doc:`Writing migrations </howto/writing-migrations>`
|
||||
|
||||
* **Advanced:**
|
||||
:doc:`Managers <topics/db/managers>` |
|
||||
:doc:`Raw SQL <topics/db/sql>` |
|
||||
:doc:`Transactions <topics/db/transactions>` |
|
||||
:doc:`Aggregation <topics/db/aggregation>` |
|
||||
:doc:`Search <topics/db/search>` |
|
||||
:doc:`Custom fields <howto/custom-model-fields>` |
|
||||
:doc:`Multiple databases <topics/db/multi-db>` |
|
||||
:doc:`Custom lookups <howto/custom-lookups>` |
|
||||
:doc:`Query Expressions <ref/models/expressions>` |
|
||||
:doc:`Conditional Expressions <ref/models/conditional-expressions>` |
|
||||
:doc:`Database Functions <ref/models/database-functions>`
|
||||
:doc:`Managers </topics/db/managers>` |
|
||||
:doc:`Raw SQL </topics/db/sql>` |
|
||||
:doc:`Transactions </topics/db/transactions>` |
|
||||
:doc:`Aggregation </topics/db/aggregation>` |
|
||||
:doc:`Search </topics/db/search>` |
|
||||
:doc:`Custom fields </howto/custom-model-fields>` |
|
||||
:doc:`Multiple databases </topics/db/multi-db>` |
|
||||
:doc:`Custom lookups </howto/custom-lookups>` |
|
||||
:doc:`Query Expressions </ref/models/expressions>` |
|
||||
:doc:`Conditional Expressions </ref/models/conditional-expressions>` |
|
||||
:doc:`Database Functions </ref/models/database-functions>`
|
||||
|
||||
* **Other:**
|
||||
:doc:`Supported databases <ref/databases>` |
|
||||
:doc:`Legacy databases <howto/legacy-databases>` |
|
||||
:doc:`Providing initial data <howto/initial-data>` |
|
||||
:doc:`Optimize database access <topics/db/optimization>` |
|
||||
:doc:`PostgreSQL specific features <ref/contrib/postgres/index>`
|
||||
:doc:`Supported databases </ref/databases>` |
|
||||
:doc:`Legacy databases </howto/legacy-databases>` |
|
||||
:doc:`Providing initial data </howto/initial-data>` |
|
||||
:doc:`Optimize database access </topics/db/optimization>` |
|
||||
:doc:`PostgreSQL specific features </ref/contrib/postgres/index>`
|
||||
|
||||
The view layer
|
||||
==============
|
||||
|
@ -123,39 +123,39 @@ processing a user's request and for returning the response. Find all you need
|
|||
to know about views via the links below:
|
||||
|
||||
* **The basics:**
|
||||
:doc:`URLconfs <topics/http/urls>` |
|
||||
:doc:`View functions <topics/http/views>` |
|
||||
:doc:`Shortcuts <topics/http/shortcuts>` |
|
||||
:doc:`Decorators <topics/http/decorators>` |
|
||||
:doc:`Asynchronous Support <topics/async>`
|
||||
:doc:`URLconfs </topics/http/urls>` |
|
||||
:doc:`View functions </topics/http/views>` |
|
||||
:doc:`Shortcuts </topics/http/shortcuts>` |
|
||||
:doc:`Decorators </topics/http/decorators>` |
|
||||
:doc:`Asynchronous Support </topics/async>`
|
||||
|
||||
* **Reference:**
|
||||
:doc:`Built-in Views <ref/views>` |
|
||||
:doc:`Request/response objects <ref/request-response>` |
|
||||
:doc:`TemplateResponse objects <ref/template-response>`
|
||||
:doc:`Built-in Views </ref/views>` |
|
||||
:doc:`Request/response objects </ref/request-response>` |
|
||||
:doc:`TemplateResponse objects </ref/template-response>`
|
||||
|
||||
* **File uploads:**
|
||||
:doc:`Overview <topics/http/file-uploads>` |
|
||||
:doc:`File objects <ref/files/file>` |
|
||||
:doc:`Storage API <ref/files/storage>` |
|
||||
:doc:`Managing files <topics/files>` |
|
||||
:doc:`Custom storage <howto/custom-file-storage>`
|
||||
:doc:`Overview </topics/http/file-uploads>` |
|
||||
:doc:`File objects </ref/files/file>` |
|
||||
:doc:`Storage API </ref/files/storage>` |
|
||||
:doc:`Managing files </topics/files>` |
|
||||
:doc:`Custom storage </howto/custom-file-storage>`
|
||||
|
||||
* **Class-based views:**
|
||||
:doc:`Overview <topics/class-based-views/index>` |
|
||||
:doc:`Built-in display views <topics/class-based-views/generic-display>` |
|
||||
:doc:`Built-in editing views <topics/class-based-views/generic-editing>` |
|
||||
:doc:`Using mixins <topics/class-based-views/mixins>` |
|
||||
:doc:`API reference <ref/class-based-views/index>` |
|
||||
:doc:`Flattened index<ref/class-based-views/flattened-index>`
|
||||
:doc:`Overview </topics/class-based-views/index>` |
|
||||
:doc:`Built-in display views </topics/class-based-views/generic-display>` |
|
||||
:doc:`Built-in editing views </topics/class-based-views/generic-editing>` |
|
||||
:doc:`Using mixins </topics/class-based-views/mixins>` |
|
||||
:doc:`API reference </ref/class-based-views/index>` |
|
||||
:doc:`Flattened index </ref/class-based-views/flattened-index>`
|
||||
|
||||
* **Advanced:**
|
||||
:doc:`Generating CSV <howto/outputting-csv>` |
|
||||
:doc:`Generating PDF <howto/outputting-pdf>`
|
||||
:doc:`Generating CSV </howto/outputting-csv>` |
|
||||
:doc:`Generating PDF </howto/outputting-pdf>`
|
||||
|
||||
* **Middleware:**
|
||||
:doc:`Overview <topics/http/middleware>` |
|
||||
:doc:`Built-in middleware classes <ref/middleware>`
|
||||
:doc:`Overview </topics/http/middleware>` |
|
||||
:doc:`Built-in middleware classes </ref/middleware>`
|
||||
|
||||
The template layer
|
||||
==================
|
||||
|
@ -165,17 +165,17 @@ information to be presented to the user. Learn how this syntax can be used by
|
|||
designers and how it can be extended by programmers:
|
||||
|
||||
* **The basics:**
|
||||
:doc:`Overview <topics/templates>`
|
||||
:doc:`Overview </topics/templates>`
|
||||
|
||||
* **For designers:**
|
||||
:doc:`Language overview <ref/templates/language>` |
|
||||
:doc:`Built-in tags and filters <ref/templates/builtins>` |
|
||||
:doc:`Humanization <ref/contrib/humanize>`
|
||||
:doc:`Language overview </ref/templates/language>` |
|
||||
:doc:`Built-in tags and filters </ref/templates/builtins>` |
|
||||
:doc:`Humanization </ref/contrib/humanize>`
|
||||
|
||||
* **For programmers:**
|
||||
:doc:`Template API <ref/templates/api>` |
|
||||
:doc:`Custom tags and filters <howto/custom-template-tags>` |
|
||||
:doc:`Custom template backend <howto/custom-template-backend>`
|
||||
:doc:`Template API </ref/templates/api>` |
|
||||
:doc:`Custom tags and filters </howto/custom-template-tags>` |
|
||||
:doc:`Custom template backend </howto/custom-template-backend>`
|
||||
|
||||
Forms
|
||||
=====
|
||||
|
@ -184,16 +184,16 @@ Django provides a rich framework to facilitate the creation of forms and the
|
|||
manipulation of form data.
|
||||
|
||||
* **The basics:**
|
||||
:doc:`Overview <topics/forms/index>` |
|
||||
:doc:`Form API <ref/forms/api>` |
|
||||
:doc:`Built-in fields <ref/forms/fields>` |
|
||||
:doc:`Built-in widgets <ref/forms/widgets>`
|
||||
:doc:`Overview </topics/forms/index>` |
|
||||
:doc:`Form API </ref/forms/api>` |
|
||||
:doc:`Built-in fields </ref/forms/fields>` |
|
||||
:doc:`Built-in widgets </ref/forms/widgets>`
|
||||
|
||||
* **Advanced:**
|
||||
:doc:`Forms for models <topics/forms/modelforms>` |
|
||||
:doc:`Integrating media <topics/forms/media>` |
|
||||
:doc:`Formsets <topics/forms/formsets>` |
|
||||
:doc:`Customizing validation <ref/forms/validation>`
|
||||
:doc:`Forms for models </topics/forms/modelforms>` |
|
||||
:doc:`Integrating media </topics/forms/media>` |
|
||||
:doc:`Formsets </topics/forms/formsets>` |
|
||||
:doc:`Customizing validation </ref/forms/validation>`
|
||||
|
||||
The development process
|
||||
=======================
|
||||
|
@ -202,32 +202,32 @@ Learn about the various components and tools to help you in the development and
|
|||
testing of Django applications:
|
||||
|
||||
* **Settings:**
|
||||
:doc:`Overview <topics/settings>` |
|
||||
:doc:`Full list of settings <ref/settings>`
|
||||
:doc:`Overview </topics/settings>` |
|
||||
:doc:`Full list of settings </ref/settings>`
|
||||
|
||||
* **Applications:**
|
||||
:doc:`Overview <ref/applications>`
|
||||
:doc:`Overview </ref/applications>`
|
||||
|
||||
* **Exceptions:**
|
||||
:doc:`Overview <ref/exceptions>`
|
||||
:doc:`Overview </ref/exceptions>`
|
||||
|
||||
* **django-admin and manage.py:**
|
||||
:doc:`Overview <ref/django-admin>` |
|
||||
:doc:`Adding custom commands <howto/custom-management-commands>`
|
||||
:doc:`Overview </ref/django-admin>` |
|
||||
:doc:`Adding custom commands </howto/custom-management-commands>`
|
||||
|
||||
* **Testing:**
|
||||
:doc:`Introduction <topics/testing/index>` |
|
||||
:doc:`Writing and running tests <topics/testing/overview>` |
|
||||
:doc:`Included testing tools <topics/testing/tools>` |
|
||||
:doc:`Advanced topics <topics/testing/advanced>`
|
||||
:doc:`Introduction </topics/testing/index>` |
|
||||
:doc:`Writing and running tests </topics/testing/overview>` |
|
||||
:doc:`Included testing tools </topics/testing/tools>` |
|
||||
:doc:`Advanced topics </topics/testing/advanced>`
|
||||
|
||||
* **Deployment:**
|
||||
:doc:`Overview <howto/deployment/index>` |
|
||||
:doc:`WSGI servers <howto/deployment/wsgi/index>` |
|
||||
:doc:`ASGI servers <howto/deployment/asgi/index>` |
|
||||
:doc:`Deploying static files <howto/static-files/deployment>` |
|
||||
:doc:`Tracking code errors by email <howto/error-reporting>` |
|
||||
:doc:`Deployment checklist <howto/deployment/checklist>`
|
||||
:doc:`Overview </howto/deployment/index>` |
|
||||
:doc:`WSGI servers </howto/deployment/wsgi/index>` |
|
||||
:doc:`ASGI servers </howto/deployment/asgi/index>` |
|
||||
:doc:`Deploying static files </howto/static-files/deployment>` |
|
||||
:doc:`Tracking code errors by email </howto/error-reporting>` |
|
||||
:doc:`Deployment checklist </howto/deployment/checklist>`
|
||||
|
||||
The admin
|
||||
=========
|
||||
|
@ -235,9 +235,9 @@ The admin
|
|||
Find all you need to know about the automated admin interface, one of Django's
|
||||
most popular features:
|
||||
|
||||
* :doc:`Admin site <ref/contrib/admin/index>`
|
||||
* :doc:`Admin actions <ref/contrib/admin/actions>`
|
||||
* :doc:`Admin documentation generator<ref/contrib/admin/admindocs>`
|
||||
* :doc:`Admin site </ref/contrib/admin/index>`
|
||||
* :doc:`Admin actions </ref/contrib/admin/actions>`
|
||||
* :doc:`Admin documentation generator </ref/contrib/admin/admindocs>`
|
||||
|
||||
Security
|
||||
========
|
||||
|
@ -245,13 +245,13 @@ Security
|
|||
Security is a topic of paramount importance in the development of web
|
||||
applications and Django provides multiple protection tools and mechanisms:
|
||||
|
||||
* :doc:`Security overview <topics/security>`
|
||||
* :doc:`Disclosed security issues in Django <releases/security>`
|
||||
* :doc:`Clickjacking protection <ref/clickjacking>`
|
||||
* :doc:`Cross Site Request Forgery protection <ref/csrf>`
|
||||
* :doc:`Cryptographic signing <topics/signing>`
|
||||
* :doc:`Security overview </topics/security>`
|
||||
* :doc:`Disclosed security issues in Django </releases/security>`
|
||||
* :doc:`Clickjacking protection </ref/clickjacking>`
|
||||
* :doc:`Cross Site Request Forgery protection </ref/csrf>`
|
||||
* :doc:`Cryptographic signing </topics/signing>`
|
||||
* :ref:`Security Middleware <security-middleware>`
|
||||
* :doc:`Content Security Policy <ref/csp>`
|
||||
* :doc:`Content Security Policy </ref/csp>`
|
||||
|
||||
Internationalization and localization
|
||||
=====================================
|
||||
|
@ -260,10 +260,10 @@ Django offers a robust internationalization and localization framework to
|
|||
assist you in the development of applications for multiple languages and world
|
||||
regions:
|
||||
|
||||
* :doc:`Overview <topics/i18n/index>` |
|
||||
:doc:`Internationalization <topics/i18n/translation>` |
|
||||
* :doc:`Overview </topics/i18n/index>` |
|
||||
:doc:`Internationalization </topics/i18n/translation>` |
|
||||
:ref:`Localization <how-to-create-language-files>` |
|
||||
:doc:`Localized web UI formatting and form input <topics/i18n/formatting>`
|
||||
:doc:`Localized web UI formatting and form input </topics/i18n/formatting>`
|
||||
* :doc:`Time zones </topics/i18n/timezones>`
|
||||
|
||||
Performance and optimization
|
||||
|
@ -272,14 +272,14 @@ Performance and optimization
|
|||
There are a variety of techniques and tools that can help get your code running
|
||||
more efficiently - faster, and using fewer system resources.
|
||||
|
||||
* :doc:`Performance and optimization overview <topics/performance>`
|
||||
* :doc:`Performance and optimization overview </topics/performance>`
|
||||
|
||||
Geographic framework
|
||||
====================
|
||||
|
||||
:doc:`GeoDjango <ref/contrib/gis/index>` intends to be a world-class geographic
|
||||
web framework. Its goal is to make it as easy as possible to build GIS web
|
||||
applications and harness the power of spatially enabled data.
|
||||
:doc:`GeoDjango </ref/contrib/gis/index>` intends to be a world-class
|
||||
geographic web framework. Its goal is to make it as easy as possible to build
|
||||
GIS web applications and harness the power of spatially enabled data.
|
||||
|
||||
Common web application tools
|
||||
============================
|
||||
|
@ -288,36 +288,36 @@ Django offers multiple tools commonly needed in the development of web
|
|||
applications:
|
||||
|
||||
* **Authentication:**
|
||||
:doc:`Overview <topics/auth/index>` |
|
||||
:doc:`Using the authentication system <topics/auth/default>` |
|
||||
:doc:`Password management <topics/auth/passwords>` |
|
||||
:doc:`Customizing authentication <topics/auth/customizing>` |
|
||||
:doc:`API Reference <ref/contrib/auth>`
|
||||
* :doc:`Caching <topics/cache>`
|
||||
* :doc:`Logging <topics/logging>`
|
||||
* :doc:`Sending emails <topics/email>`
|
||||
* :doc:`Syndication feeds (RSS/Atom) <ref/contrib/syndication>`
|
||||
* :doc:`Pagination <topics/pagination>`
|
||||
* :doc:`Messages framework <ref/contrib/messages>`
|
||||
* :doc:`Serialization <topics/serialization>`
|
||||
* :doc:`Sessions <topics/http/sessions>`
|
||||
* :doc:`Sitemaps <ref/contrib/sitemaps>`
|
||||
* :doc:`Static files management <ref/contrib/staticfiles>`
|
||||
* :doc:`Data validation <ref/validators>`
|
||||
:doc:`Overview </topics/auth/index>` |
|
||||
:doc:`Using the authentication system </topics/auth/default>` |
|
||||
:doc:`Password management </topics/auth/passwords>` |
|
||||
:doc:`Customizing authentication </topics/auth/customizing>` |
|
||||
:doc:`API Reference </ref/contrib/auth>`
|
||||
* :doc:`Caching </topics/cache>`
|
||||
* :doc:`Logging </topics/logging>`
|
||||
* :doc:`Sending emails </topics/email>`
|
||||
* :doc:`Syndication feeds (RSS/Atom) </ref/contrib/syndication>`
|
||||
* :doc:`Pagination </topics/pagination>`
|
||||
* :doc:`Messages framework </ref/contrib/messages>`
|
||||
* :doc:`Serialization </topics/serialization>`
|
||||
* :doc:`Sessions </topics/http/sessions>`
|
||||
* :doc:`Sitemaps </ref/contrib/sitemaps>`
|
||||
* :doc:`Static files management </ref/contrib/staticfiles>`
|
||||
* :doc:`Data validation </ref/validators>`
|
||||
|
||||
Other core functionalities
|
||||
==========================
|
||||
|
||||
Learn about some other core functionalities of the Django framework:
|
||||
|
||||
* :doc:`Conditional content processing <topics/conditional-view-processing>`
|
||||
* :doc:`Content types and generic relations <ref/contrib/contenttypes>`
|
||||
* :doc:`Flatpages <ref/contrib/flatpages>`
|
||||
* :doc:`Redirects <ref/contrib/redirects>`
|
||||
* :doc:`Signals <topics/signals>`
|
||||
* :doc:`System check framework <topics/checks>`
|
||||
* :doc:`The sites framework <ref/contrib/sites>`
|
||||
* :doc:`Unicode in Django <ref/unicode>`
|
||||
* :doc:`Conditional content processing </topics/conditional-view-processing>`
|
||||
* :doc:`Content types and generic relations </ref/contrib/contenttypes>`
|
||||
* :doc:`Flatpages </ref/contrib/flatpages>`
|
||||
* :doc:`Redirects </ref/contrib/redirects>`
|
||||
* :doc:`Signals </topics/signals>`
|
||||
* :doc:`System check framework </topics/checks>`
|
||||
* :doc:`The sites framework </ref/contrib/sites>`
|
||||
* :doc:`Unicode in Django </ref/unicode>`
|
||||
|
||||
The Django open-source project
|
||||
==============================
|
||||
|
@ -326,23 +326,23 @@ Learn about the development process for the Django project itself and about how
|
|||
you can contribute:
|
||||
|
||||
* **Community:**
|
||||
:doc:`Contributing to Django <internals/contributing/index>` |
|
||||
:doc:`The release process <internals/release-process>` |
|
||||
:doc:`Team organization <internals/organization>` |
|
||||
:doc:`The Django source code repository <internals/git>` |
|
||||
:doc:`Security policies <internals/security>` |
|
||||
:doc:`Mailing lists and Forum<internals/mailing-lists>`
|
||||
:doc:`Contributing to Django </internals/contributing/index>` |
|
||||
:doc:`The release process </internals/release-process>` |
|
||||
:doc:`Team organization </internals/organization>` |
|
||||
:doc:`The Django source code repository </internals/git>` |
|
||||
:doc:`Security policies </internals/security>` |
|
||||
:doc:`Mailing lists and Forum </internals/mailing-lists>`
|
||||
|
||||
* **Design philosophies:**
|
||||
:doc:`Overview <misc/design-philosophies>`
|
||||
:doc:`Overview </misc/design-philosophies>`
|
||||
|
||||
* **Documentation:**
|
||||
:doc:`About this documentation <internals/contributing/writing-documentation>`
|
||||
:doc:`About this documentation </internals/contributing/writing-documentation>`
|
||||
|
||||
* **Third-party distributions:**
|
||||
:doc:`Overview <misc/distributions>`
|
||||
:doc:`Overview </misc/distributions>`
|
||||
|
||||
* **Django over time:**
|
||||
:doc:`API stability <misc/api-stability>` |
|
||||
:doc:`Release notes and upgrading instructions <releases/index>` |
|
||||
:doc:`Deprecation Timeline <internals/deprecation>`
|
||||
:doc:`API stability </misc/api-stability>` |
|
||||
:doc:`Release notes and upgrading instructions </releases/index>` |
|
||||
:doc:`Deprecation Timeline </internals/deprecation>`
|
||||
|
|
|
@ -5,7 +5,7 @@ Reporting bugs and requesting features
|
|||
.. Important::
|
||||
|
||||
Please report security issues **only** to
|
||||
security@djangoproject.com. This is a private list only open to
|
||||
security@djangoproject.com. This is a private list only open to
|
||||
long-time, highly trusted Django developers, and its archives are
|
||||
not public. For further details, please see :doc:`our security
|
||||
policies </internals/security>`.
|
||||
|
@ -55,7 +55,7 @@ particular:
|
|||
as they are filed.
|
||||
|
||||
To understand the lifecycle of your ticket once you have created it, refer to
|
||||
:doc:`triaging-tickets`.
|
||||
:ref:`triage-workflow`.
|
||||
|
||||
Reporting user interface bugs
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -4,7 +4,8 @@ Committing code
|
|||
|
||||
This section is addressed to the mergers and to anyone interested in knowing
|
||||
how code gets committed into Django. If you're a community member who wants to
|
||||
contribute code to Django, look at :doc:`writing-code/working-with-git` instead.
|
||||
contribute code to Django, look at
|
||||
:doc:`/internals/contributing/writing-code/working-with-git` instead.
|
||||
|
||||
.. _handling-pull-requests:
|
||||
|
||||
|
|
|
@ -61,8 +61,8 @@ the date, time and numbers formatting particularities of your locale. See
|
|||
The format files aren't managed by the use of Transifex. To change them, you
|
||||
must:
|
||||
|
||||
* :doc:`Create a pull request<writing-code/submitting-patches>` against the
|
||||
Django Git ``main`` branch, as for any code change.
|
||||
* :ref:`Create a pull request <patch-review-checklist>` against the Django Git
|
||||
``main`` branch, as for any code change.
|
||||
|
||||
* Open a ticket in Django's ticket system, set its ``Component`` field to
|
||||
``Translations``, set the "has patch" flag, and include the link to the pull
|
||||
|
|
|
@ -47,14 +47,15 @@ Keep old patches up-to-date
|
|||
Oftentimes the codebase will change between a patch being submitted and the
|
||||
time it gets reviewed. Make sure it still applies cleanly and functions as
|
||||
expected. Updating a patch is both useful and important! See more on
|
||||
:doc:`writing-code/submitting-patches`.
|
||||
:ref:`patch-review-checklist`.
|
||||
|
||||
Write some documentation
|
||||
------------------------
|
||||
|
||||
Django's documentation is great but it can always be improved. Did you find a
|
||||
typo? Do you think that something should be clarified? Go ahead and suggest a
|
||||
documentation patch! See also the guide on :doc:`writing-documentation`.
|
||||
documentation patch! See also the guide on
|
||||
:doc:`/internals/contributing/writing-documentation`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -29,11 +29,13 @@ confusion or disagreement.
|
|||
Django is a community project, and every contribution helps. We can't do this
|
||||
without **you**!
|
||||
|
||||
.. _triage-workflow:
|
||||
|
||||
Triage workflow
|
||||
===============
|
||||
|
||||
Unfortunately, not all reports in the ticket tracker provide all the
|
||||
:doc:`required details<bugs-and-features>`. A number of tickets have proposed
|
||||
:ref:`required details <reporting-bugs>`. A number of tickets have proposed
|
||||
solutions, but those don't necessarily meet all the requirements :ref:`adhering
|
||||
to the guidelines for contributing <patch-style>`.
|
||||
|
||||
|
@ -168,8 +170,8 @@ Has patch
|
|||
---------
|
||||
|
||||
This means the ticket has an associated solution. These will be reviewed to
|
||||
ensure they adhere to the :doc:`documented guidelines
|
||||
<writing-code/submitting-patches>`.
|
||||
ensure they adhere to the :ref:`documented guidelines
|
||||
<patch-review-checklist>`.
|
||||
|
||||
The following three fields (Needs documentation, Needs tests,
|
||||
Patch needs improvement) apply only if a patch has been supplied.
|
||||
|
@ -234,8 +236,11 @@ majority of tickets have a severity of "Normal".
|
|||
Version
|
||||
-------
|
||||
|
||||
It is possible to use the *version* attribute to indicate in which
|
||||
version the reported bug was identified.
|
||||
The *version* attribute indicates the earliest version in which the bug was
|
||||
reproduced. During triage, this field can be updated, but there is no need to
|
||||
make further updates when that version goes out of support. The field should
|
||||
not be reset to "dev" to show the issue still exists: instead, the tested
|
||||
commit hash can be noted in a comment.
|
||||
|
||||
UI/UX
|
||||
-----
|
||||
|
@ -350,8 +355,7 @@ Then, you can help out by:
|
|||
"wontfix".
|
||||
|
||||
* Closing "Unreviewed" tickets as "needsinfo" when the description is too
|
||||
sparse to be actionable, or when they're feature requests requiring a
|
||||
discussion on the `Django Forum`_.
|
||||
sparse to be actionable.
|
||||
|
||||
* Correcting the "Needs tests", "Needs documentation", or "Has patch"
|
||||
flags for tickets where they are incorrectly set.
|
||||
|
@ -383,7 +387,7 @@ Then, you can help out by:
|
|||
several that are useful for triaging tickets and reviewing proposals as
|
||||
suggested above.
|
||||
|
||||
You can also find more :doc:`new-contributors`.
|
||||
You can also find more :doc:`/internals/contributing/new-contributors`.
|
||||
|
||||
.. _Reports page: https://code.djangoproject.com/wiki/Reports
|
||||
|
||||
|
@ -438,10 +442,10 @@ Next, we mark the current point in history as being "bad" since the test fails:
|
|||
|
||||
Now, we need to find a point in git history before the regression was
|
||||
introduced (i.e. a point where the test passes). Use something like
|
||||
``git checkout HEAD~100`` to check out an earlier revision (100 commits earlier,
|
||||
in this case). Check if the test fails. If so, mark that point as "bad"
|
||||
(``git bisect bad``), then check out an earlier revision and recheck. Once you
|
||||
find a revision where your test passes, mark it as "good":
|
||||
``git checkout HEAD~100`` to check out an earlier revision (100 commits
|
||||
earlier, in this case). Check if the test fails. If so, mark that point as
|
||||
"bad" (``git bisect bad``), then check out an earlier revision and recheck.
|
||||
Once you find a revision where your test passes, mark it as "good":
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
|
|
|
@ -46,11 +46,9 @@ Python style
|
|||
* 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.
|
||||
``.flake8`` file excludes some 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. We allow up to 88
|
||||
characters in code, as this is the line length used by ``black``.
|
||||
|
@ -58,8 +56,8 @@ Python style
|
|||
These limits are checked when ``flake8`` is run.
|
||||
|
||||
* 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
|
||||
:ref:`%-formatting <old-string-formatting>`, :ref:`f-strings
|
||||
<f-strings>`, or :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
|
||||
|
@ -98,13 +96,12 @@ Python style
|
|||
|
||||
* 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
|
||||
* 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
|
||||
|
@ -151,9 +148,10 @@ Imports
|
|||
|
||||
* 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.
|
||||
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.
|
||||
|
@ -506,6 +504,6 @@ JavaScript style
|
|||
================
|
||||
|
||||
For details about the JavaScript code style used by Django, see
|
||||
:doc:`javascript`.
|
||||
:doc:`/internals/contributing/writing-code/javascript`.
|
||||
|
||||
.. _editorconfig: https://editorconfig.org/
|
||||
|
|
|
@ -17,8 +17,8 @@ Code style
|
|||
for indentation, but there are some exceptions.
|
||||
|
||||
* When naming variables, use ``camelCase`` instead of ``underscore_case``.
|
||||
Different JavaScript files sometimes use a different code style. Please try to
|
||||
conform to the code style of each file.
|
||||
Different JavaScript files sometimes use a different code style. Please try
|
||||
to conform to the code style of each file.
|
||||
|
||||
* Use the `ESLint`_ code linter to check your code for bugs and style errors.
|
||||
ESLint will be run when you run the JavaScript tests. We also recommended
|
||||
|
@ -89,8 +89,8 @@ The JavaScript tests may be run from a web browser or from the command line.
|
|||
Testing from a web browser
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To run the tests from a web browser, open up :source:`js_tests/tests.html` in your
|
||||
browser.
|
||||
To run the tests from a web browser, open up :source:`js_tests/tests.html` in
|
||||
your browser.
|
||||
|
||||
To measure code coverage when running the tests, you need to view that file
|
||||
over HTTP. To view code coverage:
|
||||
|
|
|
@ -15,7 +15,8 @@ If you are fixing a really trivial issue, for example changing a word in the
|
|||
documentation, the preferred way to provide the patch is using GitHub pull
|
||||
requests without a Trac ticket.
|
||||
|
||||
See the :doc:`working-with-git` for more details on how to use pull requests.
|
||||
See the :doc:`/internals/contributing/writing-code/working-with-git` for more
|
||||
details on how to use pull requests.
|
||||
|
||||
"Claiming" tickets
|
||||
==================
|
||||
|
@ -109,19 +110,20 @@ requirements:
|
|||
|
||||
* The code required to fix a problem or add a feature is an essential part
|
||||
of a solution, but it is not the only part. A good fix should also include a
|
||||
:doc:`regression test <unit-tests>` to validate the behavior that has been
|
||||
fixed and to prevent the problem from arising again. Also, if some tickets
|
||||
are relevant to the code that you've written, mention the ticket numbers in
|
||||
some comments in the test so that one can easily trace back the relevant
|
||||
discussions after your patch gets committed, and the tickets get closed.
|
||||
:doc:`regression test </internals/contributing/writing-code/unit-tests>` to
|
||||
validate the behavior that has been fixed and to prevent the problem from
|
||||
arising again. Also, if some tickets are relevant to the code that you've
|
||||
written, mention the ticket numbers in some comments in the test so that one
|
||||
can easily trace back the relevant discussions after your patch gets
|
||||
committed, and the tickets get closed.
|
||||
|
||||
* If the code adds a new feature, or modifies the behavior of an existing
|
||||
feature, the change should also contain documentation.
|
||||
|
||||
When you think your work is ready to be reviewed, send :doc:`a GitHub pull
|
||||
request <working-with-git>`.
|
||||
If you can't send a pull request for some reason, you can also use patches in
|
||||
Trac. When using this style, follow these guidelines.
|
||||
request </internals/contributing/writing-code/working-with-git>`. If you can't
|
||||
send a pull request for some reason, you can also use patches in Trac. When
|
||||
using this style, follow these guidelines.
|
||||
|
||||
* Submit patches in the format returned by the ``git diff`` command.
|
||||
|
||||
|
@ -204,9 +206,12 @@ whether to accept it.
|
|||
|
||||
Some examples of DEPs that have been approved and fully implemented:
|
||||
|
||||
* `DEP 181: ORM Expressions <https://github.com/django/deps/blob/main/final/0181-orm-expressions.rst>`_
|
||||
* `DEP 182: Multiple Template Engines <https://github.com/django/deps/blob/main/final/0182-multiple-template-engines.rst>`_
|
||||
* `DEP 201: Simplified routing syntax <https://github.com/django/deps/blob/main/final/0201-simplified-routing-syntax.rst>`_
|
||||
* `DEP 181: ORM Expressions
|
||||
<https://github.com/django/deps/blob/main/final/0181-orm-expressions.rst>`_
|
||||
* `DEP 182: Multiple Template Engines
|
||||
<https://github.com/django/deps/blob/main/final/0182-multiple-template-engines.rst>`_
|
||||
* `DEP 201: Simplified routing syntax
|
||||
<https://github.com/django/deps/blob/main/final/0201-simplified-routing-syntax.rst>`_
|
||||
|
||||
.. _Django Forum: https://forum.djangoproject.com/
|
||||
.. _Django Enhancement Proposals: https://github.com/django/deps
|
||||
|
@ -226,19 +231,19 @@ There are a couple of reasons that code in Django might be deprecated:
|
|||
no longer needs to support the older version of Python that doesn't include
|
||||
the library, the library will be deprecated in Django.
|
||||
|
||||
As the :ref:`deprecation policy<internal-release-deprecation-policy>` describes,
|
||||
the first release of Django that deprecates a feature (``A.B``) should raise a
|
||||
``RemovedInDjangoXXWarning`` (where XX is the Django version where the feature
|
||||
will be removed) when the deprecated feature is invoked. Assuming we have good
|
||||
test coverage, these warnings are converted to errors when :ref:`running the
|
||||
test suite <running-unit-tests>` with warnings enabled:
|
||||
As the :ref:`deprecation policy<internal-release-deprecation-policy>`
|
||||
describes, the first release of Django that deprecates a feature (``A.B``)
|
||||
should raise a ``RemovedInDjangoXXWarning`` (where XX is the Django version
|
||||
where the feature will be removed) when the deprecated feature is invoked.
|
||||
Assuming we have good test coverage, these warnings are converted to errors
|
||||
when :ref:`running the test suite <running-unit-tests>` with warnings enabled:
|
||||
``python -Wa runtests.py``. Thus, when adding a ``RemovedInDjangoXXWarning``
|
||||
you need to eliminate or silence any warnings generated when running the tests.
|
||||
|
||||
The first step is to remove any use of the deprecated behavior by Django itself.
|
||||
Next you can silence warnings in tests that actually test the deprecated
|
||||
behavior by using the ``ignore_warnings`` decorator, either at the test or class
|
||||
level:
|
||||
The first step is to remove any use of the deprecated behavior by Django
|
||||
itself. Next you can silence warnings in tests that actually test the
|
||||
deprecated behavior by using the ``ignore_warnings`` decorator, either at the
|
||||
test or class level:
|
||||
|
||||
#) In a particular test::
|
||||
|
||||
|
@ -305,8 +310,9 @@ Finally, there are a couple of updates to Django's documentation to make:
|
|||
applicable, to the current release notes (``docs/releases/A.B.txt``) under
|
||||
the "Features deprecated in A.B" heading.
|
||||
|
||||
#) Add an entry in the deprecation timeline (``docs/internals/deprecation.txt``)
|
||||
under the appropriate version describing what code will be removed.
|
||||
#) Add an entry in the deprecation timeline
|
||||
(``docs/internals/deprecation.txt``) under the appropriate version
|
||||
describing what code will be removed.
|
||||
|
||||
Once you have completed these steps, you are finished with the deprecation.
|
||||
In each :term:`feature release <Feature release>`, all
|
||||
|
@ -402,10 +408,10 @@ Bugs
|
|||
|
||||
* Is there a proper regression test (the test should fail before the fix
|
||||
is applied)?
|
||||
* If it's a bug that :ref:`qualifies for a backport <supported-versions-policy>`
|
||||
to the stable version of Django, is there a release note in
|
||||
``docs/releases/A.B.C.txt``? Bug fixes that will be applied only to the main
|
||||
branch don't need a release note.
|
||||
* If it's a bug that :ref:`qualifies for a backport
|
||||
<supported-versions-policy>` to the stable version of Django, is there a
|
||||
release note in ``docs/releases/A.B.C.txt``? Bug fixes that will be applied
|
||||
only to the main branch don't need a release note.
|
||||
|
||||
New Features
|
||||
------------
|
||||
|
|
|
@ -69,11 +69,11 @@ command from any place in the Django source tree:
|
|||
$ tox
|
||||
|
||||
By default, ``tox`` runs the test suite with the bundled test settings file for
|
||||
SQLite, ``black``, ``blacken-docs``, ``flake8``, ``isort``, and the
|
||||
documentation spelling checker. In addition to the system dependencies noted
|
||||
elsewhere in this documentation, the command ``python3`` must be on your path
|
||||
and linked to the appropriate version of Python. A list of default environments
|
||||
can be seen as follows:
|
||||
SQLite, ``black``, ``blacken-docs``, ``flake8``, ``isort``, ``lint-docs`` and
|
||||
the documentation spelling checker. In addition to the system dependencies
|
||||
noted elsewhere in this documentation, the command ``python3`` must be on your
|
||||
path and linked to the appropriate version of Python. A list of default
|
||||
environments can be seen as follows:
|
||||
|
||||
.. console::
|
||||
|
||||
|
@ -84,6 +84,7 @@ can be seen as follows:
|
|||
flake8>=3.7.0
|
||||
docs
|
||||
isort>=5.1.0
|
||||
lint-docs
|
||||
|
||||
Testing other Python versions and database backends
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -398,9 +399,9 @@ and also excludes several directories not relevant to the results
|
|||
Contrib apps
|
||||
============
|
||||
|
||||
Tests for contrib apps can be found in the :source:`tests/` directory, typically
|
||||
under ``<app_name>_tests``. For example, tests for ``contrib.auth`` are located
|
||||
in :source:`tests/auth_tests`.
|
||||
Tests for contrib apps can be found in the :source:`tests/` directory,
|
||||
typically under ``<app_name>_tests``. For example, tests for ``contrib.auth``
|
||||
are located in :source:`tests/auth_tests`.
|
||||
|
||||
.. _troubleshooting-unit-tests:
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ Working with Git and GitHub
|
|||
|
||||
This section explains how the community can contribute code to Django via pull
|
||||
requests. If you're interested in how :ref:`mergers <mergers-team>` handle
|
||||
them, see :doc:`../committing-code`.
|
||||
them, see :ref:`handling-pull-requests`.
|
||||
|
||||
Below, we are going to show how to create a GitHub pull request containing the
|
||||
changes for Trac ticket #xxxxx. By creating a fully-ready pull request, you
|
||||
|
@ -142,7 +142,7 @@ When you think your work is ready to be pulled into Django, you should create
|
|||
a pull request at GitHub. A good pull request means:
|
||||
|
||||
* commits with one logical change in each, following the
|
||||
:doc:`coding style <coding-style>`,
|
||||
:doc:`coding style </internals/contributing/writing-code/coding-style>`,
|
||||
|
||||
* well-formed messages for each commit: a summary line and then paragraphs
|
||||
wrapped at 72 characters thereafter -- see the :ref:`committing guidelines
|
||||
|
|
|
@ -158,8 +158,9 @@ Documentation quality checks
|
|||
----------------------------
|
||||
|
||||
Several checks help maintain Django's documentation quality, including
|
||||
:ref:`spelling <documentation-spelling-check>` and
|
||||
:ref:`code block formatting <documentation-code-block-format-check>`.
|
||||
:ref:`spelling <documentation-spelling-check>`,
|
||||
:ref:`code block formatting <documentation-code-block-format-check>`, and
|
||||
:ref:`documentation style <documentation-lint-check>`.
|
||||
|
||||
These checks are run automatically in CI and must pass before documentation
|
||||
changes can be merged. They can also be run locally with a single command:
|
||||
|
@ -191,7 +192,7 @@ If you encounter false-positives (error output that actually is correct), do
|
|||
one of the following:
|
||||
|
||||
* Surround inline code or brand/technology names with double grave accents
|
||||
(``).
|
||||
(\`\`)
|
||||
* Find synonyms that the spell checker recognizes.
|
||||
* If, and only if, you are sure the word you are using is correct - add it
|
||||
to ``docs/spelling_wordlist`` (please keep the list in alphabetical order).
|
||||
|
@ -205,8 +206,8 @@ All Python code blocks should be formatted using the :pypi:`blacken-docs`
|
|||
auto-formatter. This is automatically run by the :ref:`pre-commit hook
|
||||
<coding-style-pre-commit>` if configured.
|
||||
|
||||
The check can also be run manually: provided that ``blacken-docs`` is installed,
|
||||
run the following command from the ``docs`` directory:
|
||||
The check can also be run manually: provided that ``blacken-docs`` is
|
||||
installed, run the following command from the ``docs`` directory:
|
||||
|
||||
.. console::
|
||||
|
||||
|
@ -215,6 +216,31 @@ run the following command from the ``docs`` directory:
|
|||
The formatter will report any issues by printing them to the terminal and will
|
||||
reformat code blocks where possible.
|
||||
|
||||
.. _documentation-lint-check:
|
||||
|
||||
Documentation lint check
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django's documentation is checked for reStructuredText style and formal issues
|
||||
using :pypi:`sphinx-lint`. This helps catch problems like stray tabs, trailing
|
||||
whitespace, excessive line length, and similar formatting problems.
|
||||
|
||||
Once ``sphinx-lint`` is installed, the check can be run with the following
|
||||
command from the ``docs`` directory:
|
||||
|
||||
.. console::
|
||||
|
||||
$ make lint
|
||||
|
||||
The command prints any violations to the terminal in the form
|
||||
``path:line: message``. If problems are encountered:
|
||||
|
||||
* Read the message and fix the indicated issue (for example, remove trailing
|
||||
whitespace, adjust backticks, or replace tabs with spaces).
|
||||
* For long lines consider wrapping text onto new lines or breaking long inline
|
||||
links into named references. The custom line length check should already skip
|
||||
common false positives such as headings, tables and long links.
|
||||
|
||||
.. _documentation-link-check:
|
||||
|
||||
Link check
|
||||
|
@ -245,8 +271,8 @@ Entries that have a status of "broken" need to be fixed. Those that have a
|
|||
status of "redirected" may need to be updated to point to the canonical
|
||||
location, e.g. the scheme has changed ``http://`` → ``https://``. In certain
|
||||
cases, we do not want to update a "redirected" link, e.g. a rewrite to always
|
||||
point to the latest or stable version of the documentation, e.g. ``/en/stable/`` →
|
||||
``/en/3.2/``.
|
||||
point to the latest or stable version of the documentation, e.g.
|
||||
``/en/stable/`` → ``/en/3.2/``.
|
||||
|
||||
Writing style
|
||||
=============
|
||||
|
@ -523,12 +549,12 @@ General improvements or other changes to the APIs that should be emphasized
|
|||
should use the "``.. versionchanged:: X.Y``" directive (with the same format
|
||||
as the ``versionadded`` mentioned above.
|
||||
|
||||
These ``versionadded`` and ``versionchanged`` blocks should be "self-contained."
|
||||
In other words, since we only keep these annotations around for two releases,
|
||||
it's nice to be able to remove the annotation and its contents without having
|
||||
to reflow, reindent, or edit the surrounding text. For example, instead of
|
||||
putting the entire description of a new or changed feature in a block, do
|
||||
something like this:
|
||||
These ``versionadded`` and ``versionchanged`` blocks should be
|
||||
"self-contained." In other words, since we only keep these annotations around
|
||||
for two releases, it's nice to be able to remove the annotation and its
|
||||
contents without having to reflow, reindent, or edit the surrounding text. For
|
||||
example, instead of putting the entire description of a new or changed feature
|
||||
in a block, do something like this:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
|
@ -659,12 +685,12 @@ you'd like to help translate the documentation into another language.
|
|||
``django-admin`` man page
|
||||
=========================
|
||||
|
||||
Sphinx can generate a manual page for the
|
||||
:doc:`django-admin </ref/django-admin>` command. This is configured in
|
||||
``docs/conf.py``. Unlike other documentation output, this man page should be
|
||||
included in the Django repository and the releases as
|
||||
``docs/man/django-admin.1``. There isn't a need to update this file when
|
||||
updating the documentation, as it's updated once as part of the release process.
|
||||
Sphinx can generate a manual page for the :doc:`django-admin
|
||||
</ref/django-admin>` command. This is configured in ``docs/conf.py``. Unlike
|
||||
other documentation output, this man page should be included in the Django
|
||||
repository and the releases as ``docs/man/django-admin.1``. There isn't a need
|
||||
to update this file when updating the documentation, as it's updated once as
|
||||
part of the release process.
|
||||
|
||||
To generate an updated version of the man page, in the ``docs`` directory, run:
|
||||
|
||||
|
|
|
@ -64,9 +64,9 @@ details on these changes.
|
|||
* The ``all`` keyword argument of ``django.contrib.staticfiles.finders.find()``
|
||||
will be removed.
|
||||
|
||||
* The fallback to ``request.user`` when ``user`` is ``None`` in
|
||||
``django.contrib.auth.login()`` and ``django.contrib.auth.alogin()`` will be
|
||||
removed.
|
||||
* Fallbacks to ``request.user`` and ``request.auser()`` when ``user`` is
|
||||
``None`` in ``django.contrib.auth.login()`` and
|
||||
``django.contrib.auth.alogin()``, respectively, will be removed.
|
||||
|
||||
* The ``ordering`` keyword argument of the PostgreSQL specific aggregation
|
||||
functions ``django.contrib.postgres.aggregates.ArrayAgg``,
|
||||
|
@ -742,12 +742,12 @@ details on these changes.
|
|||
* The ability to use a dotted Python path for the ``LOGIN_URL`` and
|
||||
``LOGIN_REDIRECT_URL`` settings will be removed.
|
||||
|
||||
* Support for :py:mod:`optparse` will be dropped for custom management commands
|
||||
(replaced by :py:mod:`argparse`).
|
||||
* Support for :mod:`optparse` will be dropped for custom management commands
|
||||
(replaced by :mod:`argparse`).
|
||||
|
||||
* The class ``django.core.management.NoArgsCommand`` will be removed. Use
|
||||
:class:`~django.core.management.BaseCommand` instead, which takes no arguments
|
||||
by default.
|
||||
:class:`~django.core.management.BaseCommand` instead, which takes no
|
||||
arguments by default.
|
||||
|
||||
* ``django.core.context_processors`` module will be removed.
|
||||
|
||||
|
@ -777,7 +777,8 @@ details on these changes.
|
|||
* ``get_all_related_many_to_many_objects()``
|
||||
* ``get_all_related_m2m_objects_with_model()``
|
||||
|
||||
* The ``error_message`` argument of ``django.forms.RegexField`` will be removed.
|
||||
* The ``error_message`` argument of ``django.forms.RegexField`` will be
|
||||
removed.
|
||||
|
||||
* The ``unordered_list`` filter will no longer support old style lists.
|
||||
|
||||
|
@ -803,7 +804,8 @@ details on these changes.
|
|||
``django.contrib.admin.helpers.InlineAdminForm`` will be removed.
|
||||
|
||||
* The backwards compatibility shim to allow ``FormMixin.get_form()`` to be
|
||||
defined with no default value for its ``form_class`` argument will be removed.
|
||||
defined with no default value for its ``form_class`` argument will be
|
||||
removed.
|
||||
|
||||
* The following settings will be removed:
|
||||
|
||||
|
@ -821,7 +823,7 @@ details on these changes.
|
|||
:func:`~django.template.loader.get_template` and
|
||||
:func:`~django.template.loader.select_template` won't accept a
|
||||
:class:`~django.template.Context` in their
|
||||
:meth:`~django.template.backends.base.Template.render()` method anymore.
|
||||
:meth:`~django.template.backends.base.Template.render` method anymore.
|
||||
|
||||
* :doc:`Template response APIs </ref/template-response>` will enforce the use
|
||||
of :class:`dict` and backend-dependent template objects instead of
|
||||
|
@ -870,14 +872,14 @@ details on these changes.
|
|||
* Support for the legacy ``%(<foo>)s`` syntax in ``ModelFormMixin.success_url``
|
||||
will be removed.
|
||||
|
||||
* ``GeoQuerySet`` aggregate methods ``collect()``, ``extent()``, ``extent3d()``,
|
||||
``make_line()``, and ``unionagg()`` will be removed.
|
||||
* ``GeoQuerySet`` aggregate methods ``collect()``, ``extent()``,
|
||||
``extent3d()``, ``make_line()``, and ``unionagg()`` will be removed.
|
||||
|
||||
* Ability to specify ``ContentType.name`` when creating a content type instance
|
||||
will be removed.
|
||||
|
||||
* Support for the old signature of ``allow_migrate`` will be removed. It changed
|
||||
from ``allow_migrate(self, db, model)`` to
|
||||
* Support for the old signature of ``allow_migrate`` will be removed. It
|
||||
changed from ``allow_migrate(self, db, model)`` to
|
||||
``allow_migrate(self, db, app_label, model_name=None, **hints)``.
|
||||
|
||||
* Support for the syntax of ``{% cycle %}`` that uses comma-separated arguments
|
||||
|
@ -1000,8 +1002,8 @@ details on these changes.
|
|||
* ``django.utils.module_loading.import_by_path`` will be removed in favor of
|
||||
``django.utils.module_loading.import_string``.
|
||||
|
||||
* ``ssi`` and ``url`` template tags will be removed from the ``future`` template
|
||||
tag library (used during the 1.3/1.4 deprecation period).
|
||||
* ``ssi`` and ``url`` template tags will be removed from the ``future``
|
||||
template tag library (used during the 1.3/1.4 deprecation period).
|
||||
|
||||
* ``django.utils.text.javascript_quote`` will be removed.
|
||||
|
||||
|
@ -1011,9 +1013,9 @@ details on these changes.
|
|||
* The ``cache_choices`` option to :class:`~django.forms.ModelChoiceField` and
|
||||
:class:`~django.forms.ModelMultipleChoiceField` will be removed.
|
||||
|
||||
* The default value of the
|
||||
:attr:`RedirectView.permanent <django.views.generic.base.RedirectView.permanent>`
|
||||
attribute will change from ``True`` to ``False``.
|
||||
* The default value of the :attr:`RedirectView.permanent
|
||||
<django.views.generic.base.RedirectView.permanent>` attribute will change
|
||||
from ``True`` to ``False``.
|
||||
|
||||
* ``django.contrib.sitemaps.FlatPageSitemap`` will be removed in favor of
|
||||
``django.contrib.flatpages.sitemaps.FlatPageSitemap``.
|
||||
|
@ -1096,8 +1098,8 @@ details on these changes.
|
|||
* The ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting will be removed.
|
||||
|
||||
* Usage of the hardcoded *Hold down "Control", or "Command" on a Mac, to select
|
||||
more than one.* string to override or append to user-provided ``help_text`` in
|
||||
forms for ManyToMany model fields will not be performed by Django anymore
|
||||
more than one.* string to override or append to user-provided ``help_text``
|
||||
in forms for ManyToMany model fields will not be performed by Django anymore
|
||||
either at the model or forms layer.
|
||||
|
||||
* The ``Model._meta.get_(add|change|delete)_permission`` methods will
|
||||
|
@ -1110,8 +1112,9 @@ details on these changes.
|
|||
(``django.contrib.gis.sitemaps.views.index`` and
|
||||
``django.contrib.gis.sitemaps.views.sitemap``).
|
||||
|
||||
* ``django.utils.html.fix_ampersands``, the ``fix_ampersands`` template filter and
|
||||
``django.utils.html.clean_html`` will be removed following an accelerated deprecation.
|
||||
* ``django.utils.html.fix_ampersands``, the ``fix_ampersands`` template filter
|
||||
and ``django.utils.html.clean_html`` will be removed following an accelerated
|
||||
deprecation.
|
||||
|
||||
.. _deprecation-removed-in-1.7:
|
||||
|
||||
|
@ -1238,9 +1241,8 @@ details on these changes.
|
|||
``django.contrib.gis.utils`` will be removed.
|
||||
|
||||
* ``django.conf.urls.defaults`` will be removed. The functions
|
||||
``include()``, ``patterns()``, and ``url()``, plus
|
||||
:data:`~django.conf.urls.handler404` and :data:`~django.conf.urls.handler500`
|
||||
are now available through ``django.conf.urls``.
|
||||
``include()``, ``patterns()``, and ``url()``, plus ``handler404` and
|
||||
``handler500`` are now available through ``django.conf.urls``.
|
||||
|
||||
* The functions ``setup_environ()`` and ``execute_manager()`` will be removed
|
||||
from :mod:`django.core.management`. This also means that the old (pre-1.4)
|
||||
|
@ -1249,8 +1251,8 @@ details on these changes.
|
|||
* Setting the ``is_safe`` and ``needs_autoescape`` flags as attributes of
|
||||
template filter functions will no longer be supported.
|
||||
|
||||
* The attribute ``HttpRequest.raw_post_data`` was renamed to ``HttpRequest.body``
|
||||
in 1.4. The backward compatibility will be removed --
|
||||
* The attribute ``HttpRequest.raw_post_data`` was renamed to
|
||||
``HttpRequest.body`` in 1.4. The backward compatibility will be removed --
|
||||
``HttpRequest.raw_post_data`` will no longer work.
|
||||
|
||||
* The value for the ``post_url_continue`` parameter in
|
||||
|
@ -1293,7 +1295,7 @@ details on these changes.
|
|||
</topics/class-based-views/index>`.
|
||||
|
||||
* The ``django.core.servers.basehttp.AdminMediaHandler`` will be
|
||||
removed. In its place use
|
||||
removed. In its place use
|
||||
``django.contrib.staticfiles.handlers.StaticFilesHandler``.
|
||||
|
||||
* The template tags library ``adminmedia`` and the template tag ``{%
|
||||
|
@ -1335,10 +1337,10 @@ details on these changes.
|
|||
performance issues and will follow a slightly accelerated deprecation
|
||||
timeframe.
|
||||
|
||||
* Translations located under the so-called *project path* will be ignored during
|
||||
the translation building process performed at runtime. The
|
||||
:setting:`LOCALE_PATHS` setting can be used for the same task by including the
|
||||
filesystem path to a ``locale`` directory containing non-app-specific
|
||||
* Translations located under the so-called *project path* will be ignored
|
||||
during the translation building process performed at runtime. The
|
||||
:setting:`LOCALE_PATHS` setting can be used for the same task by including
|
||||
the filesystem path to a ``locale`` directory containing non-app-specific
|
||||
translations in its value.
|
||||
|
||||
* The Markup contrib app will no longer support versions of Python-Markdown
|
||||
|
@ -1356,7 +1358,7 @@ details on these changes.
|
|||
See the :ref:`Django 1.2 release notes<deprecated-features-1.2>` for more
|
||||
details on these changes.
|
||||
|
||||
* ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` will be removed. Use
|
||||
* ``CsrfResponseMiddleware`` and ``CsrfMiddleware`` will be removed. Use
|
||||
the ``{% csrf_token %}`` template tag inside forms to enable CSRF
|
||||
protection. ``CsrfViewMiddleware`` remains and is enabled by default.
|
||||
|
||||
|
@ -1384,7 +1386,7 @@ details on these changes.
|
|||
* The ``Message`` model (in ``django.contrib.auth``), its related
|
||||
manager in the ``User`` model (``user.message_set``), and the
|
||||
associated methods (``user.message_set.create()`` and
|
||||
``user.get_and_delete_messages()``), will be removed. The
|
||||
``user.get_and_delete_messages()``), will be removed. The
|
||||
:doc:`messages framework </ref/contrib/messages>` should be used
|
||||
instead. The related ``messages`` variable returned by the
|
||||
auth context processor will also be removed. Note that this
|
||||
|
@ -1396,7 +1398,7 @@ details on these changes.
|
|||
will no longer be checked and can be removed from custom backends.
|
||||
|
||||
* Authentication backends will need to support the ``AnonymousUser`` class
|
||||
being passed to all methods dealing with permissions. The
|
||||
being passed to all methods dealing with permissions. The
|
||||
``supports_anonymous_user`` variable will no longer be checked and can be
|
||||
removed from custom backends.
|
||||
|
||||
|
@ -1423,7 +1425,7 @@ details on these changes.
|
|||
``django.contrib.syndication`` will be removed. The class-based view
|
||||
``views.Feed`` should be used instead.
|
||||
|
||||
* ``django.core.context_processors.auth``. This release will
|
||||
* ``django.core.context_processors.auth``. This release will
|
||||
remove the old method in favor of the new method in
|
||||
``django.contrib.auth.context_processors.auth``.
|
||||
|
||||
|
@ -1454,7 +1456,7 @@ details on these changes.
|
|||
See the :ref:`Django 1.1 release notes<deprecated-features-1.1>` for more
|
||||
details on these changes.
|
||||
|
||||
* ``AdminSite.root()``. This method of hooking up the admin URLs will be
|
||||
* ``AdminSite.root()``. This method of hooking up the admin URLs will be
|
||||
removed in favor of including ``admin.site.urls``.
|
||||
|
||||
* Authentication backends need to define the boolean attributes
|
||||
|
|
|
@ -115,11 +115,11 @@ updates.
|
|||
committed until the final release happened.
|
||||
|
||||
For example, shortly after the release of Django 1.3 the branch
|
||||
``stable/1.3.x`` was created. Official support for that release has expired,
|
||||
and so it no longer receives direct maintenance from the Django project.
|
||||
However, that and all other similarly named branches continue to exist, and
|
||||
interested community members have occasionally used them to provide
|
||||
unofficial support for old Django releases.
|
||||
``stable/1.3.x`` was created. Official support for that release has
|
||||
expired, and so it no longer receives direct maintenance from the Django
|
||||
project. However, that and all other similarly named branches continue to
|
||||
exist, and interested community members have occasionally used them to
|
||||
provide unofficial support for old Django releases.
|
||||
|
||||
Tags
|
||||
====
|
||||
|
|
|
@ -471,7 +471,8 @@ Building the artifacts
|
|||
|
||||
.. admonition:: Optionally use helper scripts
|
||||
|
||||
You can streamline some of the steps below using helper scripts from the Wiki:
|
||||
You can streamline some of the steps below using helper scripts from the
|
||||
Wiki:
|
||||
|
||||
* `Release script
|
||||
<https://code.djangoproject.com/wiki/ReleaseScript>`_
|
||||
|
|
|
@ -5,7 +5,7 @@ Mailing lists and Forum
|
|||
.. Important::
|
||||
|
||||
Please report security issues **only** to
|
||||
security@djangoproject.com. This is a private list only open to
|
||||
security@djangoproject.com. This is a private list only open to
|
||||
long-time, highly trusted Django developers, and its archives are
|
||||
not public. For further details, please see :doc:`our security
|
||||
policies </internals/security>`.
|
||||
|
|
|
@ -72,9 +72,9 @@ to make the role of Merger sustainable.
|
|||
|
||||
The following restrictions apply to the role of Merger:
|
||||
|
||||
- A person must not simultaneously serve as a member of the steering council. If
|
||||
a Merger is elected to the steering council, they shall cease to be a Merger
|
||||
immediately upon taking up membership in the steering council.
|
||||
- A person must not simultaneously serve as a member of the steering council.
|
||||
If a Merger is elected to the steering council, they shall cease to be a
|
||||
Merger immediately upon taking up membership in the steering council.
|
||||
- A person may serve in the roles of Releaser and Merger simultaneously.
|
||||
|
||||
The selection process, when a vacancy occurs or when the steering council deems
|
||||
|
@ -122,10 +122,10 @@ upload them to the :pypi:`Python Package Index <Django>` and to the
|
|||
Membership
|
||||
----------
|
||||
|
||||
`The steering council`_ selects Releasers_ as necessary to maintain their number
|
||||
at a minimum of three, in order to spread the workload and avoid over-burdening
|
||||
or burning out any individual Releaser. There is no upper limit to the number
|
||||
of Releasers.
|
||||
`The steering council`_ selects Releasers_ as necessary to maintain their
|
||||
number at a minimum of three, in order to spread the workload and avoid
|
||||
over-burdening or burning out any individual Releaser. There is no upper limit
|
||||
to the number of Releasers.
|
||||
|
||||
It's not a requirement that a Releaser is also a Django Fellow, but the Django
|
||||
Software Foundation has the power to use funding of Fellow positions as a way
|
||||
|
@ -223,13 +223,14 @@ who demonstrate:
|
|||
years must still demonstrate an understanding of Django's changes and
|
||||
direction within those three years.
|
||||
|
||||
A new council is elected after each release cycle of Django. The election process
|
||||
works as follows:
|
||||
A new council is elected after each release cycle of Django. The election
|
||||
process works as follows:
|
||||
|
||||
#. The steering council directs one of its members to notify the Secretary of the
|
||||
Django Software Foundation, in writing, of the triggering of the election,
|
||||
and the condition which triggered it. The Secretary post to the appropriate
|
||||
venue -- the `Django Forum`_ to announce the election and its timeline.
|
||||
#. The steering council directs one of its members to notify the Secretary of
|
||||
the Django Software Foundation, in writing, of the triggering of the
|
||||
election, and the condition which triggered it. The Secretary post to the
|
||||
appropriate venue -- the `Django Forum`_ to announce the election and its
|
||||
timeline.
|
||||
#. As soon as the election is announced, the `DSF Board`_ begin a period of
|
||||
voter registration. All `individual members of the DSF`_ are automatically
|
||||
registered and need not explicitly register. All other persons who believe
|
||||
|
@ -267,12 +268,12 @@ A member of the steering council may be removed by:
|
|||
- Becoming disqualified due to actions taken by the Code of Conduct committee
|
||||
of the Django Software Foundation.
|
||||
- Determining that they did not possess the qualifications of a member of the
|
||||
steering council. This determination must be made jointly by the other members
|
||||
of the steering council, and the `DSF Board`_. A valid determination of
|
||||
ineligibility requires that all other members of the steering council and all
|
||||
members of the DSF Board vote who can vote on the issue (the affected person,
|
||||
if a DSF Board member, must not vote) vote "yes" on a motion that the person
|
||||
in question is ineligible.
|
||||
steering council. This determination must be made jointly by the other
|
||||
members of the steering council, and the `DSF Board`_. A valid determination
|
||||
of ineligibility requires that all other members of the steering council and
|
||||
all members of the DSF Board vote who can vote on the issue (the affected
|
||||
person, if a DSF Board member, must not vote) vote "yes" on a motion that the
|
||||
person in question is ineligible.
|
||||
|
||||
.. _`Django Forum`: https://forum.djangoproject.com/
|
||||
.. _`Django Git repository`: https://github.com/django/django/
|
||||
|
|
|
@ -31,7 +31,8 @@ own branch, called ``stable/A.B.x``, and bugfix/security releases will be
|
|||
issued from those branches.
|
||||
|
||||
For more information about how the Django project issues new releases for
|
||||
security purposes, please see :doc:`our security policies <security>`.
|
||||
security purposes, please see :doc:`our security policies
|
||||
</internals/security>`.
|
||||
|
||||
.. glossary::
|
||||
|
||||
|
@ -45,8 +46,8 @@ security purposes, please see :doc:`our security policies <security>`.
|
|||
bugs and/or security issues.
|
||||
|
||||
These releases will be 100% compatible with the associated feature release,
|
||||
unless this is impossible for security reasons or to prevent data loss.
|
||||
So the answer to "should I upgrade to the latest patch release?" will always
|
||||
unless this is impossible for security reasons or to prevent data loss. So
|
||||
the answer to "should I upgrade to the latest patch release?" will always
|
||||
be "yes."
|
||||
|
||||
Long-term support release
|
||||
|
@ -123,8 +124,8 @@ See also the :ref:`deprecating-a-feature` guide.
|
|||
Supported versions
|
||||
==================
|
||||
|
||||
At any moment in time, Django's developer team will support a set of releases to
|
||||
varying levels. See `the supported versions section
|
||||
At any moment in time, Django's developer team will support a set of releases
|
||||
to varying levels. See `the supported versions section
|
||||
<https://www.djangoproject.com/download/#supported-versions>`_ of the download
|
||||
page for the current state of support for each version.
|
||||
|
||||
|
|
|
@ -292,7 +292,8 @@ requires a security release:
|
|||
* Exploits which fail to follow security best practices, such as failure to
|
||||
sanitize user input. For other examples, see our :ref:`security
|
||||
documentation <cross-site-scripting>`.
|
||||
* Exploits in AI generated code that do not adhere to security best practices.
|
||||
* Exploits in AI generated code that do not adhere to security best
|
||||
practices.
|
||||
|
||||
The security team may conclude that the source of the vulnerability is within
|
||||
the Python standard library, in which case the reporter will be asked to report
|
||||
|
@ -303,8 +304,8 @@ On occasion, a security release may be issued to help resolve a security
|
|||
vulnerability within a popular third-party package. These reports should come
|
||||
from the package maintainers.
|
||||
|
||||
If you are unsure whether your finding meets these criteria, please still report
|
||||
it :ref:`privately by emailing security@djangoproject.com
|
||||
If you are unsure whether your finding meets these criteria, please still
|
||||
report it :ref:`privately by emailing security@djangoproject.com
|
||||
<reporting-security-issues>`. The security team will review your report and
|
||||
recommend the correct course of action.
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 34 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 20 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user