Showing preview only (900K chars total). Download the full file or copy to clipboard to get everything.
Repository: ilyachch/django-rest-framework-rusdoc
Branch: master
Commit: 5a30aa5af782
Files: 53
Total size: 600.5 KB
Directory structure:
gitextract_s0pt2_8x/
├── .files_cache.json
├── .github/
│ └── workflows/
│ ├── check_all_temp_data_cleared.yml
│ └── check_source_for_changes.yml
├── .gitignore
├── .scripts/
│ ├── commit_changes.py
│ └── monitor_repository.py
├── README.md
├── SUMMARY.md
├── api-guide/
│ ├── authentication.md
│ ├── caching.md
│ ├── content-negotiation.md
│ ├── exceptions.md
│ ├── fields.md
│ ├── filtering.md
│ ├── format-suffixes.md
│ ├── generic-views.md
│ ├── metadata.md
│ ├── pagination.md
│ ├── parsers.md
│ ├── permissions.md
│ ├── relations.md
│ ├── renderers.md
│ ├── requests.md
│ ├── responses.md
│ ├── reverse.md
│ ├── routers.md
│ ├── schemas.md
│ ├── serializers.md
│ ├── settings.md
│ ├── status-codes.md
│ ├── testing.md
│ ├── throttling.md
│ ├── validators.md
│ ├── versioning.md
│ ├── views.md
│ └── viewsets.md
├── monitoring_config.toml
├── quickstart.md
├── topics/
│ ├── ajax-csrf-cors.md
│ ├── browsable-api.md
│ ├── browser-enhancements.md
│ ├── documenting-your-api.md
│ ├── html-and-forms.md
│ ├── internationalization.md
│ ├── rest-hypermedia-hateoas.md
│ └── writable-nested-serializers.md
├── topics.md
└── tutorial/
├── 1-serialization.md
├── 2-requests-and-responses.md
├── 3-class-based-views.md
├── 4-authentication-and-permissions.md
├── 5-relationships-and-hyperlinked-apis.md
└── 6-viewsets-and-routers.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .files_cache.json
================================================
{
".reference/README.md": {
"commit_hash": "18c5883be8ad3c5c5c36a5e39855d79ac80de7ca",
"file_hash": "3040917d581be433da899a2b37135150"
},
".reference/api-guide/authentication.md": {
"commit_hash": "d0a5d5e7cad7f1032b4d0a36cab1596076f705ad",
"file_hash": "18084ecacf507bd6fef2c23ba2a82a56"
},
".reference/api-guide/caching.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "51b470c630a4e77a032b0ed152e130cf"
},
".reference/api-guide/content-negotiation.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "553eb6d9511eef8f816a9e8b628f321b"
},
".reference/api-guide/exceptions.md": {
"commit_hash": "c0202a0aa5cbaf8573458b932878dfd5044c93ab",
"file_hash": "659b7ca76f8efccef817591fef9d13fd"
},
".reference/api-guide/fields.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "fd8aca55b6366ed2b616b41b2ecc80ff"
},
".reference/api-guide/filtering.md": {
"commit_hash": "a323cf7c0a33d7ffd395a6805019f613fb79f985",
"file_hash": "54dc859f64dc2c74e12f6b644732c066"
},
".reference/api-guide/format-suffixes.md": {
"commit_hash": "041b88f8bbb48d9688ebd5add294eee2dfc93d1c",
"file_hash": "60ea26c1f58e88b82b31b49e524316e6"
},
".reference/api-guide/generic-views.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "2245d2ff68c620c1b85ff7aebfa31e19"
},
".reference/api-guide/metadata.md": {
"commit_hash": "e045dc465270c18689dba4a970378cd9744e57b6",
"file_hash": "a0fac04282c4c1ecff4856ff2b9920cc"
},
".reference/api-guide/pagination.md": {
"commit_hash": "f74a44e850a685ac73c819ae7b96b0d68a8f734f",
"file_hash": "10ba91c105ba55ba25b6df2d69ba8da5"
},
".reference/api-guide/parsers.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "aabe99bc6bb768328673587bfbd496fb"
},
".reference/api-guide/permissions.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "44218348009641ebc15786966c82ec5c"
},
".reference/api-guide/relations.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "b0e775bb98155170eba917bc2d563110"
},
".reference/api-guide/renderers.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "1639f64a8b74fb7a41c1cb915c75bd20"
},
".reference/api-guide/requests.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "6128e6f6fa93cff0b4970fcf6c9fd351"
},
".reference/api-guide/responses.md": {
"commit_hash": "ade172e1d5db87dc86bc616cbb4df7ccd2eb2fd3",
"file_hash": "949dd98307af0923652f9b6538ae088f"
},
".reference/api-guide/reverse.md": {
"commit_hash": "332e9560ab0b3a1b8c0ab8f68e95c09bc2d8999f",
"file_hash": "df00e0304464472fda1e97c804e1c9f9"
},
".reference/api-guide/routers.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "76e6a45fc7886c10514fa8fd9fd304df"
},
".reference/api-guide/schemas.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "db37b906afd6550a53477ecdd266f2c6"
},
".reference/api-guide/serializers.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "06cc1fe1cabdb28f42b0762eaab8cbbb"
},
".reference/api-guide/settings.md": {
"commit_hash": "f9f10e041f9b2a2c936ee54a437d4c255f76e626",
"file_hash": "e1745bd2d62b873253d4e8f338569b4f"
},
".reference/api-guide/status-codes.md": {
"commit_hash": "3e052376ace520135d42dcf52f35a51b7dcc5ac2",
"file_hash": "2c87ce34b589a196dc98c77eb3490884"
},
".reference/api-guide/testing.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "e0120b9a199bd3825e703b71d2b5dae9"
},
".reference/api-guide/throttling.md": {
"commit_hash": "d0a5d5e7cad7f1032b4d0a36cab1596076f705ad",
"file_hash": "433ab273878e04287d91b9cf0eb803e6"
},
".reference/api-guide/validators.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "86e050e68e80d8328f03ea507425c50c"
},
".reference/api-guide/versioning.md": {
"commit_hash": "0d6589cf45940bb67ace74a06b2c5b053f1c31ef",
"file_hash": "ff8cfbbbe7d70ab6bd0eac5300989be1"
},
".reference/api-guide/views.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "e4ce6ac6dd7dbb59eb5f9e94e1f54a7a"
},
".reference/api-guide/viewsets.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "32a13e45a120c83ef706f908775e5888"
},
".reference/topics/ajax-csrf-cors.md": {
"commit_hash": "605cc4f7367f58002056453d9befd3c1918f6a38",
"file_hash": "6122ed56c57770bb7d15175a6f99a598"
},
".reference/topics/browsable-api.md": {
"commit_hash": "ade172e1d5db87dc86bc616cbb4df7ccd2eb2fd3",
"file_hash": "6e26d5f207a3298f7b186c00e8a159f6"
},
".reference/topics/browser-enhancements.md": {
"commit_hash": "f5470ab9e292c7321377ae1f43f85e311d94975f",
"file_hash": "8cfe1ff6085397161e067af7bd694a6e"
},
".reference/topics/documenting-your-api.md": {
"commit_hash": "e794e5e5e43d6838d9ffb8eb0a505b5f531b261f",
"file_hash": "85ce53b26dbb566993378b57614a2eb2"
},
".reference/topics/html-and-forms.md": {
"commit_hash": "9d4ed054bf8acfac6209b7e7f837fc97517affcc",
"file_hash": "d74c3a05790e8a201b4a373d54f01267"
},
".reference/topics/internationalization.md": {
"commit_hash": "2ca3b7f9c449145156f0b0e2d9ea58a2295f8fbf",
"file_hash": "3df3355efbbdf65a01dcec2b3f0de844"
},
".reference/topics/rest-hypermedia-hateoas.md": {
"commit_hash": "ade172e1d5db87dc86bc616cbb4df7ccd2eb2fd3",
"file_hash": "2f55490daba3d605c7c75be9760a4929"
},
".reference/topics/writable-nested-serializers.md": {
"commit_hash": "f0dbf0a264677f2a53faab402ff49f442fc4383a",
"file_hash": "ae7df9cb54197166d97df23d51bdda26"
},
".reference/tutorial/1-serialization.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "87c06f99ba8316fa40da167990b20c86"
},
".reference/tutorial/2-requests-and-responses.md": {
"commit_hash": "c0f3649224117609d19e79c77242b525570d25c0",
"file_hash": "4c863e0a540e66f4034f378b23d2d4f9"
},
".reference/tutorial/3-class-based-views.md": {
"commit_hash": "c0f3649224117609d19e79c77242b525570d25c0",
"file_hash": "ed6ae64942ff177d051f7bbf90a08637"
},
".reference/tutorial/4-authentication-and-permissions.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "c3fc89a9fae949e7a27f84ef98865300"
},
".reference/tutorial/5-relationships-and-hyperlinked-apis.md": {
"commit_hash": "e221d9a1d6638b936707efc390adff59511a6605",
"file_hash": "0ca0fcc63848bbc4df6ad75739ff4077"
},
".reference/tutorial/6-viewsets-and-routers.md": {
"commit_hash": "c0f3649224117609d19e79c77242b525570d25c0",
"file_hash": "85f48f71db392be56df18888bb993530"
},
".reference/tutorial/quickstart.md": {
"commit_hash": "c0f3649224117609d19e79c77242b525570d25c0",
"file_hash": "0b31dc44cc6fb7e4a983149e96bdd175"
}
}
================================================
FILE: .github/workflows/check_all_temp_data_cleared.yml
================================================
name: Check all temp data cleared
on:
pull_request:
branches:
- master
jobs:
check_no_temp_files_left:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Check no temp files left
run: |
if [[ $(find .reference -type f -name "*.temp" -o -name "*.tmp" -o -name "*.md" | wc -l) -gt 0 ]]; then
exit 1
fi
- name: Check CHANGES.md cleared
run: |
if [[ $(find -name "CHANGES.md" | wc -l) -gt 0 ]]; then
exit 1
fi
================================================
FILE: .github/workflows/check_source_for_changes.yml
================================================
name: Check source for changes
on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run'
required: false
default: false
type: boolean
permissions:
contents: write
pull-requests: write
jobs:
check_source_for_changes:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Check for changes
id: check
run: |
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "changes<<$EOF" >> "$GITHUB_OUTPUT"
python3 ./.scripts/monitor_repository.py --details-file CHANGES.md >> "$GITHUB_OUTPUT"
echo "$EOF" >> "$GITHUB_OUTPUT"
- name: Report
env:
CHANGES: ${{ steps.check.outputs.changes }}
run: |
echo "Changes:"
echo "$CHANGES"
- name: Check if branch already exists
id: check_branch_exists
if: steps.check.outputs.changes != '' && !inputs.dry_run
run: |
git fetch
if git branch -r | grep -q "origin/sync-with-original"; then
echo
echo "branch_exists=1" >> $GITHUB_OUTPUT
exit 1
fi
- name: Open PR
if: steps.check.outputs.changes != '' && steps.check_branch_exists.outputs != '1' && !inputs.dry_run
uses: peter-evans/create-pull-request@v8
with:
title: Sync with original
body: ${{ steps.check.outputs.changes }}
labels: sync
branch: sync-with-original
branch-suffix: random
commit-message: Sync with original
delete-branch: true
assignees: ${{ github.repository_owner }}
draft: true
================================================
FILE: .gitignore
================================================
.idea
# Node rules:
## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
## Dependency directory
## Commenting this out is preferred by some people, see
## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
node_modules
# Book build output
_book
# eBook build output
*.epub
*.mobi
*.pdf
================================================
FILE: .scripts/commit_changes.py
================================================
#!/usr/bin/env python3
import argparse
import dataclasses
import logging
import subprocess
from pathlib import Path
from typing import Dict, List, Tuple
logger = logging.getLogger(__name__)
@dataclasses.dataclass
class FileStatus:
status: str
path: Path
@property
def is_modified(self) -> bool:
return 'M' in self.status
@property
def is_deleted(self) -> bool:
return 'D' in self.status
@property
def is_in_reference(self) -> bool:
return str(self.path).startswith('.reference/')
@dataclasses.dataclass
class GitChanges:
file_pairs: List[Tuple[Path, Path]]
skipped_reference_files: List[Path]
unpaired_modified_files: List[Path]
def run_command(command: List[str], cwd: Path | None = None) -> str:
"""Выполняет команду и возвращает результат"""
logger.debug(f'Running command: {" ".join(command)}')
process = subprocess.run(
command,
cwd=cwd,
capture_output=True,
text=True,
)
if process.returncode != 0:
raise RuntimeError(f'Command failed: {process.stderr}')
return process.stdout.strip()
def get_git_status() -> str:
"""Получает статус git в формате porcelain"""
return run_command(['git', 'status', '--porcelain'])
def parse_status_line(line: str) -> FileStatus | None:
"""Разбирает строку статуса git и возвращает FileStatus"""
if not line:
return None
# Формат вывода git status --porcelain:
# XY PATH или XY "PATH с пробелами"
parts = line.split(maxsplit=1)
if len(parts) != 2:
return None
status = parts[0].strip()
file_path = parts[1].strip()
return FileStatus(status=status, path=Path(file_path))
def get_changes_to_commit() -> GitChanges:
"""
Находит файлы для коммитов:
- пары: измененный файл + удаленный файл в .reference с тем же относительным путем
- одиночные удаленные файлы из .reference (без измененного аналога)
- измененные файлы без удаленного файла в .reference
"""
status_output = get_git_status()
modified_files: Dict[Path, FileStatus] = {}
deleted_reference_files: Dict[Path, FileStatus] = {}
for line in status_output.split('\n'):
if not line:
continue
file_status = parse_status_line(line)
if not file_status:
continue
# Измененные файлы (не в .reference)
if file_status.is_modified and not file_status.is_in_reference:
modified_files[file_status.path] = file_status
# Удаленные файлы в .reference
if file_status.is_deleted and file_status.is_in_reference:
deleted_reference_files[file_status.path] = file_status
matched_reference_files: set[Path] = set()
# Находим пары файлов и отмечаем соответствующие файлы в .reference
file_pairs = []
unpaired_modified_files: List[Path] = []
for modified_path in modified_files:
reference_path = Path('.reference') / modified_path
if reference_path in deleted_reference_files:
file_pairs.append((modified_path, reference_path))
matched_reference_files.add(reference_path)
else:
logger.warning(f'No matching deleted reference file for {modified_path}')
unpaired_modified_files.append(modified_path)
# Находим удаленные файлы в .reference без пары
skipped_reference_files = [
ref_path for ref_path in deleted_reference_files if ref_path not in matched_reference_files
]
return GitChanges(
file_pairs=file_pairs,
skipped_reference_files=skipped_reference_files,
unpaired_modified_files=unpaired_modified_files,
)
def commit_file_pair(modified_file: Path, reference_file: Path, dry_run: bool = False) -> None:
"""Создает коммит для пары файлов"""
file_name = modified_file.name
commit_message = f'update {file_name}'
logger.info(f'Committing changes for {file_name}...')
if dry_run:
logger.info(f'[DRY RUN] Would add: {modified_file} {reference_file}')
logger.info(f'[DRY RUN] Would commit with message: {commit_message}')
return
# Добавляем файлы в индекс
run_command(['git', 'add', str(modified_file), str(reference_file)])
# Создаем коммит
run_command(['git', 'commit', '-m', commit_message])
logger.info(f'Committed: {commit_message}')
def commit_skipped_reference_file(reference_file: Path, dry_run: bool = False) -> None:
"""Создает коммит для удаленного файла из .reference без пары"""
file_name = reference_file.name
commit_message = f'skipped {file_name}'
logger.info(f'Committing skipped reference file {file_name}...')
if dry_run:
logger.info(f'[DRY RUN] Would add: {reference_file}')
logger.info(f'[DRY RUN] Would commit with message: {commit_message}')
return
run_command(['git', 'add', str(reference_file)])
run_command(['git', 'commit', '-m', commit_message])
logger.info(f'Committed: {commit_message}')
def main() -> None:
parser = argparse.ArgumentParser(
description='Create separate commits for each pair of related files'
)
parser.add_argument(
'--dry-run',
action='store_true',
default=False,
help='Run without making actual commits',
)
parser.add_argument(
'-v',
'--verbose',
action='count',
default=0,
help='Increase verbosity (can be used multiple times)',
)
args = parser.parse_args()
dry_run, verbose = args.dry_run, args.verbose
log_level = (5 - verbose) * 10
logging.basicConfig(
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)
logger.info('Starting file pair commit process')
logger.debug(f'Arguments: dry_run={dry_run}, verbose={verbose}')
try:
changes = get_changes_to_commit()
if changes.unpaired_modified_files:
logger.warning(
'Modified files without matching deleted reference: %s',
', '.join(str(path) for path in changes.unpaired_modified_files),
)
if not changes.file_pairs and not changes.skipped_reference_files:
logger.warning('No matching file pairs or skipped reference files found')
return
if changes.file_pairs:
logger.info(f'Found {len(changes.file_pairs)} file pairs to commit')
if changes.skipped_reference_files:
logger.info(
f'Found {len(changes.skipped_reference_files)} skipped reference files to commit'
)
for modified_file, reference_file in changes.file_pairs:
commit_file_pair(modified_file, reference_file, dry_run)
for reference_file in changes.skipped_reference_files:
commit_skipped_reference_file(reference_file, dry_run)
logger.info('All commits created successfully!')
except Exception as e:
logger.error(f'Error: {e}')
exit(1)
if __name__ == '__main__':
main()
================================================
FILE: .scripts/monitor_repository.py
================================================
import argparse
import dataclasses
import hashlib
import json
import logging
import subprocess
import tempfile
from functools import cache, cached_property
from pathlib import Path
import tomllib
logger = logging.getLogger(__name__)
@dataclasses.dataclass
class Config:
repo_url: str
repo_branch: str
original_link: str
paths: list[tuple[str, str]]
cache_file: Path
@classmethod
def from_config_file(cls, config_file_path: Path) -> 'Config':
content = config_file_path.read_text()
raw_config = tomllib.loads(content)
logger.debug('Loaded config: %s', raw_config)
return cls(
repo_url=raw_config['repository']['repo_url'],
repo_branch=raw_config['repository']['repo_branch'],
original_link=raw_config['repository']['original_link'],
paths=raw_config['repository']['paths'],
cache_file=Path(raw_config['repository']['cache_file']),
)
@dataclasses.dataclass
class CacheData:
commit_hash: str | None
file_hash: str | None
def read_files_json(path: Path) -> dict[str, CacheData]:
if not path.exists():
return {}
raw_content = json.loads(path.read_text())
return {
key: CacheData(commit_hash=value.get('commit_hash'), file_hash=value.get('file_hash'))
for key, value in raw_content.items()
}
def write_files_json(path: Path, data: dict[str, CacheData]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
keys = sorted(data.keys())
path.write_text(
json.dumps(
{
key: {'commit_hash': data[key].commit_hash, 'file_hash': data[key].file_hash}
for key in keys
},
indent=2,
)
)
@dataclasses.dataclass
class FileInfo:
src: Path
dst: Path
_repo: 'GitRepository'
def __hash__(self):
return hash(f'{self.src}{self.dst}')
@cached_property
def file_hash(self) -> str:
return hashlib.md5(self.src.read_bytes()).hexdigest()
@cached_property
def commit_hash(self) -> str:
result = subprocess.run(
['git', 'rev-list', '-1', 'HEAD', '--', self.src.resolve()],
cwd=self._repo.path,
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()
@cache
def get_file_diff(
self,
old_commit: str,
) -> str:
result = subprocess.run(
['git', 'diff', old_commit, self.commit_hash, '--', self.src.resolve()],
cwd=self._repo.path,
capture_output=True,
text=True,
check=True,
)
clear_result = result.stdout.strip()
clear_result = '\n'.join(clear_result.split('\n')[4:])
clear_result = f'{old_commit} -> {self.commit_hash}\n{clear_result}'
return clear_result
def copy(self) -> None:
self.dst.parent.mkdir(parents=True, exist_ok=True)
self.dst.write_text(self.src.read_text())
class GitRepository:
def __init__(self, config: 'Config', path: Path) -> None:
self.config = config
self.path = path
@classmethod
def get(cls, config: 'Config', temp_dir: Path | None) -> 'GitRepository':
if temp_dir is None:
logger.info('Creating temporary directory')
temp_dir = Path(tempfile.mkdtemp())
logger.debug('Temporary directory: %s', temp_dir)
if not temp_dir.exists() or not any(temp_dir.iterdir()):
logger.debug('Cloning repository')
subprocess.run(
['git', 'clone', config.repo_url, temp_dir.resolve()],
check=True,
capture_output=True,
)
subprocess.run(
['git', 'checkout', config.repo_branch],
cwd=temp_dir,
check=True,
capture_output=True,
)
return cls(config, temp_dir)
if any(temp_dir.iterdir()) and '.git' not in [x.name for x in temp_dir.iterdir()]:
raise ValueError(f'{temp_dir} is not empty and not a git repository')
check_result = subprocess.run(
['git', 'config', '--get', 'remote.origin.url'],
cwd=temp_dir,
capture_output=True,
text=True,
)
if check_result.stdout.strip() != config.repo_url:
raise ValueError(
f'Expected repository url {config.repo_url}, got {check_result.stdout.strip()}'
)
subprocess.run(
['git', 'checkout', config.repo_branch],
cwd=temp_dir,
check=True,
capture_output=True,
)
subprocess.run(
['git', 'pull'],
cwd=temp_dir,
check=True,
capture_output=True,
)
return cls(config, temp_dir)
def get_files_info(self) -> list[FileInfo]:
file_infos: list[FileInfo] = []
for src_raw, dst_raw in self.config.paths:
src, dst = self.path / Path(src_raw), Path(dst_raw)
if not src.exists():
logging.warning(f'Path {src.resolve()} does not exist')
continue
if src.is_dir():
for folder, _, files in src.walk():
for src_file in files:
src_file = Path(folder) / src_file
dst_file = dst / src_file.relative_to(src)
file_infos.append(FileInfo(src=src_file, dst=dst_file, _repo=self))
else:
src_file = self.path / Path(src_raw)
dst_file = Path(dst_raw)
file_infos.append(FileInfo(src=src_file, dst=dst_file, _repo=self))
logger.debug('File infos: %s', file_infos)
return file_infos
def delete(self) -> None:
logger.debug('Deleting folder %s', self.path)
self.delete_folder(self.path)
logger.debug('Folder %s deleted', self.path)
def delete_folder(self, path: Path) -> None:
for sub in path.iterdir():
if sub.is_dir():
self.delete_folder(sub)
else:
sub.unlink()
path.rmdir()
def process_repository(
save: bool,
tmp_folder_path: Path | None,
dry_run: bool,
config_file: Path,
cache_file: Path,
details_file: Path | None,
) -> None:
config = Config.from_config_file(config_file)
files_cache = read_files_json(cache_file or config.cache_file)
obsolete_files = set(files_cache.keys())
changed_files: dict[Path, str] = {}
repo = GitRepository.get(config, tmp_folder_path)
file_infos = repo.get_files_info()
for file_info in file_infos:
file_cache_data = files_cache.get(str(file_info.dst))
if file_cache_data is None:
if not dry_run:
file_info.copy()
changed_files[file_info.dst] = 'New file'
else:
if file_cache_data.commit_hash != file_info.commit_hash:
if file_cache_data.file_hash != file_info.file_hash:
if not dry_run:
file_info.copy()
changed_files[file_info.dst] = file_info.get_file_diff(
file_cache_data.commit_hash
)
files_cache[str(file_info.dst)] = CacheData(
commit_hash=file_info.commit_hash,
file_hash=file_info.file_hash,
)
obsolete_files.discard(str(file_info.dst))
for obsolete_file in obsolete_files:
logging.info('Removing obsolete file %s', ', '.join(obsolete_file))
del files_cache[obsolete_file]
if not dry_run:
logger.info('Writing cache file %s', cache_file)
write_files_json(cache_file, files_cache)
if changed_files:
summary_lines = [f'Sync with [original]({config.original_link})\n']
details_lines = [f'Sync with [original]({config.original_link})\n']
for file, change in changed_files.items():
summary_lines.append(f'- [ ] `{file}`')
change_message = change if change != 'New file' else 'New file'
details_lines.append(f' - `{file}`\n')
details_lines.append(f'```\n{change_message}\n```\n\n\n')
summary_text = "\n".join(summary_lines)
details_text = "".join(details_lines)
print(summary_text)
if details_file and not dry_run:
details_file.write_text(details_text)
if not save:
logger.info('Deleting temporary folder %s', repo.path)
repo.delete()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
'--config',
default='monitoring_config.toml',
required=False,
help='Path to the configuration file',
type=Path,
)
parser.add_argument(
'--cache',
default='.files_cache.json',
required=False,
help='Path to the cache file',
type=Path,
)
parser.add_argument(
'--details-file',
default=None,
required=False,
help='Path to the file where detailed changes will be written',
type=Path,
)
parser.add_argument(
'--temp-folder',
default=None,
required=False,
help='Temporary folder to store files',
type=Path,
)
parser.add_argument(
'-s',
'--save',
action='store_true',
default=False,
help='Save files in temporary folder',
)
parser.add_argument(
'--dry-run',
action='store_true',
default=False,
help='Run without making changes',
)
parser.add_argument(
'-v',
'--verbose',
action='count',
default=0,
)
args = parser.parse_args()
verbose, save, temp_folder_path, dry_run = (
args.verbose,
args.save,
args.temp_folder,
args.dry_run,
)
config_file, cache_file = args.config, args.cache
logging.basicConfig(level=(5 - verbose) * 10)
logger.info('Processing repository')
logger.debug(
'Running with params %s', ', '.join(f'{key}={value}' for key, value in vars(args).items())
)
process_repository(save, temp_folder_path, dry_run, config_file, cache_file, args.details_file)
================================================
FILE: README.md
================================================
<!-- TRANSLATED by md-translate -->
# Django REST framework

Django REST framework - это мощный и гибкий набор инструментов для создания Web API.
Некоторые причины, по которым вы можете захотеть использовать REST framework:
* Web-интерфейс API - огромный выигрыш в удобстве использования для ваших разработчиков.
* [Политики аутентификации](api-guide/authentication.md), включая пакеты для [OAuth1a](api-guide/authentication.md#django-rest-framework-oauth) и [OAuth2](api-guide/authentication.md#django-oauth-toolkit).
* [Сериализация](api-guide/serializers.md), поддерживающая как [ORM](api-guide/serializers.md#modelserializer), так и [non-ORM](api-guide/serializers.md#сериализаторы) источники данных.
* Настраивается все - просто используйте [обычные представления на основе функций](api-guide/views#Представления-на-основе-функций), если вам не нужны [более](api-guide/generic-views.md) [мощные](api-guide/viewsets.md) [возможности](api-guide/routers.md).
* Обширная документация и [отличная поддержка сообщества](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework).
* Используется и пользуется доверием всемирно известных компаний, включая [Mozilla](https://www.mozilla.org/en-us/about/), [Red Hat](https://www.redhat.com/), [Heroku](https://www.heroku.com/) и [Eventbrite](https://www.eventbrite.co.uk/about/).
---
## Требования
REST framework требует следующего:
* Django (4.2, 5.0, 5.1, 5.2)
* Python (3.10, 3.11, 3.12, 3.13, 3.14)
Мы **настоятельно рекомендуем** и официально поддерживаем только последние выпуски патчей каждой версии Python и Django.
Следующие пакеты являются необязательными:
* [PyYAML](https://pypi.org/project/pyyaml/), [uritemplate](https://pypi.org/project/uritemplate/) (5.1+, 3.0.0+) - Поддержка генерации схем.
* [Markdown](https://pypi.org/project/markdown/) (3.3.0+) - Поддержка Markdown для Web-интерфейса API.
* [Pygments](https://pypi.org/project/pygments/) (2.7.0+) - Добавление подсветки синтаксиса в обработку Markdown.
* [django-filter](https://pypi.org/project/django-filter/) (1.0.1+) - Поддержка фильтрации.
* [django-guardian](https://github.com/django-guardian/django-guardian) (1.1.1+) - Поддержка разрешений на уровне объектов.
## Установка
Установите с помощью `pip`, включая все дополнительные пакеты, которые вы хотите...
```bash
pip install djangorestframework
pip install markdown # Markdown support for the browsable API.
pip install django-filter # Filtering support
```
...или клонируйте проект с github.
```bash
git clone https://github.com/encode/django-rest-framework
```
Добавьте `'rest_framework'` в настройку `INSTALLED_APPS`.
```python
INSTALLED_APPS = [
...
'rest_framework',
]
```
Если вы планируете использовать Web-интерфейс API, вы, вероятно, также захотите добавить представления входа и выхода из системы REST framework. Добавьте следующее в ваш корневой файл `urls.py`.
```python
urlpatterns = [
...
path('api-auth/', include('rest_framework.urls'))
]
```
Обратите внимание, что путь URL может быть любым, какой вы захотите.
## Пример
Давайте рассмотрим быстрый пример использования REST-фреймворка для создания простого API с поддержкой модели.
Мы создадим API с функцией чтения-записи для доступа к информации о пользователях нашего проекта.
Все глобальные настройки для API REST-фреймворка хранятся в одном конфигурационном словаре с именем `REST_FRAMEWORK`. Начните с добавления следующих параметров в модуль `settings.py`:
```python
REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
```
Не забудьте убедиться, что вы также добавили `rest_framework` в `INSTALLED_APPS`.
Теперь мы готовы к созданию нашего API. Вот корневой модуль нашего проекта `urls.py`:
```python
from django.urls import path, include
from django.contrib.auth.models import User
from rest_framework import routers, serializers, viewsets
# Serializers define the API representation.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ['url', 'username', 'email', 'is_staff']
# ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('', include(router.urls)),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
```
Теперь вы можете открыть API в браузере по адресу [http://127.0.0.1:8000/](http://127.0.0.1:8000/) и просмотреть новых "пользователей" API. Если вы используете элемент управления входом в систему в правом верхнем углу, вы также сможете добавлять, создавать и удалять пользователей из системы.
## Быстрый старт
Не можете дождаться начала работы? Руководство [quickstart](quickstart.md) - это самый быстрый способ начать работу и создавать API с помощью REST framework.
## Руководство
Руководство проведет вас через все этапы настройки DRF. Это займет не очень много времени, однако вы получите полное понимание того, как все компоненты работают друг с другом и данное руководство крайне рекомендовано к прочтению.
* [Сериализация](tutorial/1-serialization.md)
* [Запросы-ответы](tutorial/2-requests-and-responses.md)
* [Представления-классы](tutorial/3-class-based-views.md)
* [Аутентификация/права доступа](tutorial/4-authentication-and-permissions.md)
* [Отношения и связи](tutorial/5-relationships-and-hyperlinked-apis.md)
* [Наборы представлений и роутеры](tutorial/6-viewsets-and-routers.md)
Так же есть пример работающего API законченного руководства для тестовых целей, [доступен здесь](http://restframework.herokuapp.com/).
## Навигатор по API
Навигатор по API - исчерпывающее руководство по всему функционалу, предоставляемому DRF.
* [Запросы](api-guide/requests.md)
* [Ответы](api-guide/responses.md)
* [Представления](api-guide/views.md)
* [Общие представления](api-guide/generic-views.md)
* [Viewsets](api-guide/viewsets.md)
* [Маршрутизаторы](api-guide/routers.md)
* [Парсеры](api-guide/parsers.md)
* [Рендереры](api-guide/renderers.md)
* [Сериализаторы](api-guide/serializers.md)
* [Поля сериализатора](api-guide/fields.md)
* [Отношения сериализаторов](api-guide/relations.md)
* [Валидаторы](api-guide/validators.md)
* [Аутентификация](api-guide/authentication.md)
* [Разрешения](api-guide/permissions.md)
* [Кэширование](api-guide/caching.md)
* [Дросселирование](api-guide/throttling.md)
* [Фильтрация](api-guide/filtering.md)
* [Пагинация](api-guide/pagination.md)
* [Версионирование](api-guide/versioning.md)
* [Согласование контента](api-guide/content-negotiation.md)
* [Метаданные](api-guide/metadata.md)
* [Schemas](api-guide/schemas.md)
* [Cуффиксы формата](api-guide/format-suffixes.md)
* [Возвращение URL-адресов](api-guide/reverse.md)
* [Исключения](api-guide/exceptions.md)
* [Коды состояния](api-guide/status-codes.md)
* [Тестирование](api-guide/testing.md)
* [Настройки](api-guide/settings.md)
## Статьи
Основные руководства для использующих DRF.
* [AJAX, CSRF & CORS](topics/ajax-csrf-cors.md)
* [The Browsable API](topics/browsable-api.md)
* [Улучшения в браузере](topics/browser-enhancements.md)
* [Документирование вашего API](topics/documenting-your-api.md)
* [HTML и формы](topics/html-and-forms.md)
* [Интернационализация](topics/internationalization.md)
* [REST, гипермедиа и HATEOAS](topics/rest-hypermedia-hateoas.md)
* [Вложенные сериализаторы с возможностью записи](topics/writable-nested-serializers.md)
## Разработка
Смотрите [руководство для разработчиков](https://www.django-rest-framework.org/community/contributing/) для получения информации о том, как клонировать репозиторий, запустить набор тестов и внести изменения в REST Framework.
## Поддержка
За поддержкой обращайтесь в [REST framework discussion group](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework), попробуйте использовать канал `#restframework` на `irc.libera.chat`, или задайте вопрос на [Stack Overflow](https://stackoverflow.com/), обязательно указав тег ['django-rest-framework'](https://stackoverflow.com/questions/tagged/django-rest-framework).
## Безопасность
**Пожалуйста, сообщайте о проблемах безопасности по электронной почте security@encode.io**.
После этого сопровождающие проекта будут работать с вами над решением любых вопросов, если потребуется, до обнародования информации.
---
## Авторы перевода
* [Ilya Chichak](https://github.com/ilyachch/)
## Помощь в переводе
* [https://github.com/pymq](https://github.com/pymq)
* [https://github.com/rufatpro](https://github.com/rufatpro)
* [Dmitry Plaxunov](https://github.com/fojetin)
Спасибо всем за помощь в переводе!
Перевод производится с помощью утилиты [md_docs-trans-app](https://github.com/ilyachch/md_docs-trans-app)
================================================
FILE: SUMMARY.md
================================================
# Summary
## Overview
* [Django REST framework](README.md)
* [Быстрый старт](quickstart.md)
* [Сериализация](tutorial/1-serialization.md)
* [Запросы-ответы](tutorial/2-requests-and-responses.md)
* [Представления-классы](tutorial/3-class-based-views.md)
* [Аутентификация/права доступа](tutorial/4-authentication-and-permissions.md)
* [Отношения и связи](tutorial/5-relationships-and-hyperlinked-apis.md)
* [Наборы представлений и роутеры](tutorial/6-viewsets-and-routers.md)
* Навигация по API:
* [Запросы](api-guide/requests.md)
* [Ответы](api-guide/responses.md)
* [Представления](api-guide/views.md)
* [Общие представления](api-guide/generic-views.md)
* [Viewsets](api-guide/viewsets.md)
* [Маршрутизаторы](api-guide/routers.md)
* [Парсеры](api-guide/parsers.md)
* [Рендереры](api-guide/renderers.md)
* [Сериализаторы](api-guide/serializers.md)
* [Поля сериализатора](api-guide/fields.md)
* [Отношения сериализаторов](api-guide/relations.md)
* [Валидаторы](api-guide/validators.md)
* [Аутентификация](api-guide/authentication.md)
* [Разрешения](api-guide/permissions.md)
* [Кэширование](api-guide/caching.md)
* [Дросселирование](api-guide/throttling.md)
* [Фильтрация](api-guide/filtering.md)
* [Пагинация](api-guide/pagination.md)
* [Версионирование](api-guide/versioning.md)
* [Согласование контента](api-guide/content-negotiation.md)
* [Метаданные](api-guide/metadata.md)
* [Schemas](api-guide/schemas.md)
* [Cуффиксы формата](api-guide/format-suffixes.md)
* [Возвращение URL-адресов](api-guide/reverse.md)
* [Исключения](api-guide/exceptions.md)
* [Коды состояния](api-guide/status-codes.md)
* [Тестирование](api-guide/testing.md)
* [Настройки](api-guide/settings.md)
## Статьи
* [Статьи](topics.md)
* [AJAX, CSRF & CORS](topics/ajax-csrf-cors.md)
* [The Browsable API](topics/browsable-api.md)
* [Улучшения в браузере](topics/browser-enhancements.md)
* [Документирование вашего API](topics/documenting-your-api.md)
* [HTML и формы](topics/html-and-forms.md)
* [Интернационализация](topics/internationalization.md)
* [REST, гипермедиа и HATEOAS](topics/rest-hypermedia-hateoas.md)
* [Вложенные сериализаторы с возможностью записи](topics/writable-nested-serializers.md)
================================================
FILE: api-guide/authentication.md
================================================
<!-- TRANSLATED by md-translate -->
# Аутентификация
> Аутентификация должна быть подключаемой.
>
> - Джейкоб Каплан-Мосс, ["Худшие практики REST"](https://jacobian.org/writing/rest-worst-practices/)
Аутентификация - это механизм связывания входящего запроса с набором идентификационных данных, таких как пользователь, от которого пришел запрос, или токен, которым он был подписан. Политики [permission](permissions.md) и [throttling](throttling.md) могут затем использовать эти учетные данные, чтобы определить, должен ли запрос быть разрешен.
DRF предоставляет несколько схем аутентификации из коробки, а также позволяет реализовать пользовательские схемы.
Аутентификация всегда выполняется в самом начале представления, до того, как произойдет проверка разрешений и дросселирование, и до того, как будет разрешено выполнение любого другого кода.
Свойство `request.user` обычно устанавливается на экземпляр класса `User` пакета `contrib.auth`.
Свойство `request.auth` используется для любой дополнительной информации об аутентификации, например, оно может быть использовано для представления маркера аутентификации, которым был подписан запрос.
---
**Примечание:** Не забывайте, что **аутентификация сама по себе не разрешает и не запрещает входящий запрос**, она просто идентифицирует учетные данные, с которыми был сделан запрос.
Информацию о том, как настроить политику разрешений для вашего API, смотрите в документации [permissions](permissions.md).
---
## Как определяется аутентификация
Схемы аутентификации всегда определяются как список классов. DRF попытается выполнить аутентификацию с каждым классом в списке, и установит `request.user` и `request.auth`, используя возвращаемое значение первого класса, который успешно аутентифицируется.
Если ни один класс не выполнит аутентификацию, `request.user` будет установлен в экземпляр `django.contrib.auth.models.AnonymousUser`, а `request.auth` будет установлен в `None`.
Значение `request.user` и `request.auth` для неаутентифицированных запросов можно изменить с помощью параметров `UNAUTHENTICATED_USER` и `UNAUTHENTICATED_TOKEN`.
## Установка схемы аутентификации
Схемы аутентификации по умолчанию можно установить глобально, используя параметр `DEFAULT_AUTHENTICATION_CLASSES`. Например.
```python
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
```
Вы также можете установить схему аутентификации для каждого представления или каждого набора представлений, используя класс `APIView`, основанный на представлениях.
```python
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
```
Или, если вы используете декоратор `@api_view` с представлениями, основанными на функциях.
```python
@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
```
## Неавторизованные и запрещенные ответы
Когда неаутентифицированному запросу отказано в разрешении, существует два различных кода ошибок, которые могут быть уместны.
* [HTTP 401 Unauthorized](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2)
* [HTTP 403 Permission Denied](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4)
Ответы HTTP 401 всегда должны содержать заголовок `WWW-Authenticate`, который указывает клиенту, как пройти аутентификацию. Ответы HTTP 403 не включают заголовок `WWW-Authenticate`.
Тип ответа, который будет использоваться, зависит от схемы аутентификации. Хотя может использоваться несколько схем аутентификации, для определения типа ответа может использоваться только одна схема. **Первый класс аутентификации, установленный в представлении, используется при определении типа ответа**.
Обратите внимание, что когда запрос может успешно пройти аутентификацию, но при этом получить отказ в разрешении на выполнение запроса, в этом случае всегда будет использоваться ответ `403 Permission Denied`, независимо от схемы аутентификации.
## Django 5.1+ `LoginRequiredMiddleware`
Если вы работаете с Django 5.1+ и используете [`LoginRequiredMiddleware`](https://docs.djangoproject.com/en/stable/ref/middleware/#django.contrib.auth.middleware.LoginRequiredMiddleware), обратите внимание, что все представления из DRF отказываются от этого промежуточного ПО. Это связано с тем, что аутентификация в DRF основана на классах аутентификации и разрешений, которые могут быть определены после применения промежуточного ПО. Кроме того, если запрос не аутентифицирован, промежуточное ПО перенаправляет пользователя на страницу входа в систему, что не подходит для API-запросов, где предпочтительнее возвращать код состояния `401 Unauthorized`.
DRF предлагает эквивалентный механизм для представлений DRF через глобальные настройки `DEFAULT_AUTHENTICATION_CLASSES` и `DEFAULT_PERMISSION_CLASSES`. Их следует изменить соответствующим образом, если вам нужно обеспечить вход в систему при запросах API.
## Специфическая конфигурация Apache mod_wsgi
Обратите внимание, что при развертывании на [Apache using mod_wsgi](https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html) заголовок авторизации по умолчанию не передается приложению WSGI, так как предполагается, что аутентификация будет обрабатываться Apache, а не на уровне приложения.
Если вы развертываете на Apache и используете любую аутентификацию, не основанную на сеансах, вам необходимо явно настроить mod_wsgi для передачи необходимых заголовков приложению. Это можно сделать, указав директиву `WSGIPassAuthorization` в соответствующем контексте и установив ее в значение `'On'`.
```apache
# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On
```
---
# API Reference
## BasicAuthentication
Эта схема аутентификации использует [HTTP Basic Authentication](https://tools.ietf.org/html/rfc2617), подписанную именем пользователя и паролем. Базовая аутентификация обычно подходит только для тестирования.
При успешной аутентификации `BasicAuthentication` предоставляет следующие учетные данные.
* `request.user` будет экземпляром Django `User`.
* `request.auth` будет `None`.
Ответы без аутентификации, которым отказано в разрешении, приведут к ответу `HTTP 401 Unauthorized` с соответствующим заголовком WWW-Authenticate. Например:
```http
WWW-Authenticate: Basic realm="api"
```
---
**Примечание:** Если вы используете `BasicAuthentication` в реальном проекте, вы должны убедиться, что ваш API доступен только через `https`. Вы также должны убедиться, что клиенты вашего API всегда будут повторно запрашивать имя пользователя и пароль при входе в систему и никогда не будут сохранять эти данные в постоянном хранилище.
---
## TokenAuthentication
---
**Примечание:** Аутентификация с помощью токенов, предоставляемая DRF, является довольно простой реализацией.
Для реализации, которая позволяет использовать более одного токена на пользователя, имеет некоторые более жесткие детали реализации безопасности и поддерживает истечение срока действия токена, пожалуйста, обратитесь к стороннему пакету [Django REST Knox](https://github.com/James1345/django-rest-knox).
---
Эта схема аутентификации использует простую схему аутентификации HTTP на основе токенов. Токен-аутентификация подходит для клиент-серверных установок, таких как собственные настольные и мобильные клиенты.
Для использования схемы `TokenAuthentication` вам необходимо [настроить классы аутентификации](#установка-схемы-аутентификации), чтобы включить `TokenAuthentication`, и дополнительно включить `rest_framework.authtoken` в настройку `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
...
'rest_framework.authtoken'
]
```
Обязательно запустите `manage.py migrate` после изменения настроек.
Приложение `rest_framework.authtoken` обеспечивает миграцию баз данных Django.
Вам также потребуется создать токены для своих пользователей.
```python
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=...)
print(token.key)
```
Для аутентификации клиентов ключ токена должен быть включен в HTTP-заголовок `Authorization`. Ключ должен иметь префикс в виде строкового литерала "Token", с пробелами, разделяющими эти две строки. Например:
```http
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
```
*Если вы хотите использовать другое ключевое слово в заголовке, например `Bearer`, просто создайте подкласс `TokenAuthentication` и установите переменную класса `keyword`.
При успешной аутентификации `TokenAuthentication` предоставляет следующие учетные данные.
* `request.user` будет экземпляром Django `User`.
* `request.auth` будет экземпляром `rest_framework.authtoken.models.Token`.
Ответы без аутентификации, которым отказано в разрешении, приведут к ответу `HTTP 401 Unauthorized` с соответствующим заголовком WWW-Authenticate. Например:
```http
WWW-Authenticate: Token
```
Инструмент командной строки `curl` может быть полезен для тестирования API с аутентификацией токенов. Например:
```http
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
```
---
**Примечание:** Если вы используете `TokenAuthentication` в реальном проекте, вы должны убедиться, что ваш API доступен только через `https`.
---
### Генерация токенов
#### С помощью сигналов
Если вы хотите, чтобы у каждого пользователя был автоматически сгенерированный токен, вы можете просто перехватить сигнал `post_save` пользователя.
```python
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
```
Обратите внимание, что вам нужно убедиться, что вы поместили этот фрагмент кода в установленный модуль `models.py` или в другое место, которое будет импортироваться Django при запуске.
Если вы уже создали несколько пользователей, вы можете сгенерировать токены для всех существующих пользователей следующим образом:
```python
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
for user in User.objects.all():
Token.objects.get_or_create(user=user)
```
#### Посредством конечной точки API
При использовании `TokenAuthentication` вы можете захотеть предоставить клиентам механизм для получения токена, заданного именем пользователя и паролем. DRF предоставляет встроенное представление для обеспечения такого поведения. Чтобы использовать его, добавьте представление `obtain_auth_token` в URLconf:
```python
from rest_framework.authtoken import views
urlpatterns += [
path('api-token-auth/', views.obtain_auth_token)
]
```
Обратите внимание, что URL часть шаблона может быть любой, которую вы хотите использовать.
Представление `obtain_auth_token` вернет ответ в формате JSON, если действительные поля `username` и `password` будут отправлены в представление с помощью данных формы или JSON:
```python
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
```
Обратите внимание, что представление по умолчанию `obtain_auth_token` явно использует JSON запросы и ответы, а не использует классы рендерера и парсера по умолчанию в ваших настройках.
По умолчанию к представлению `obtain_auth_token` не применяется никаких разрешений или дросселирования. Если вы хотите применить дросселирование, вам нужно переопределить класс представления и включить их с помощью атрибута `throttle_classes`.
Если вам нужна настраиваемая версия представления `obtain_auth_token`, вы можете сделать это, создав подкласс класса представления `ObtainAuthToken` и используя его в url conf.
Например, вы можете возвращать дополнительную информацию о пользователе помимо значения `token`:
```python
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})
```
И в вашем `urls.py`:
```python
urlpatterns += [
path('api-token-auth/', CustomAuthToken.as_view())
]
```
#### С администратором Django
Токены также можно создавать вручную через интерфейс администратора. В случае, если вы используете большую базу пользователей, мы рекомендуем вам пропатчить класс `TokenAdmin`, чтобы настроить его под свои нужды, в частности, объявив поле `user` как `raw_field`.
`ваше_приложение/admin.py`:
```python
from rest_framework.authtoken.admin import TokenAdmin
TokenAdmin.raw_id_fields = ['user']
```
#### Использование команды Django manage.py
Начиная с версии 3.6.4 можно сгенерировать пользовательский токен с помощью следующей команды:
```bash
./manage.py drf_create_token <username>
```
эта команда вернет API-токен для данного пользователя, создав его, если он не существует:
```bash
Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1
```
Если вы хотите восстановить токен (например, если он был скомпрометирован или произошла утечка), вы можете передать дополнительный параметр:
```bash
./manage.py drf_create_token -r <username>
```
## SessionAuthentication
Эта схема аутентификации использует бэкенд сессий Django по умолчанию для аутентификации. Сеансовая аутентификация подходит для клиентов AJAX, которые работают в том же сеансовом контексте, что и ваш сайт.
При успешной аутентификации `SessionAuthentication` предоставляет следующие учетные данные.
* `request.user` будет экземпляром Django `User`.
* `request.auth` будет `None`.
Ответы без аутентификации, которым отказано в разрешении, приведут к ответу `HTTP 403 Forbidden`.
Если вы используете API в стиле AJAX с `SessionAuthentication`, вам нужно убедиться, что вы включаете действительный CSRF токен для любых "небезопасных" вызовов HTTP методов, таких как `PUT`, `PATCH`, `POST` или `DELETE` запросы. Более подробную информацию смотрите в [Django CSRF documentation](https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax).
---
**Предупреждение**: Всегда используйте стандартное представление входа Django при создании страниц входа. Это обеспечит надлежащую защиту ваших представлений входа.
---
Проверка CSRF в DRF работает несколько иначе, чем в стандартном Django, из-за необходимости поддерживать как сеансовую, так и несеансовую аутентификацию для одних и тех же представлений. Это означает, что только аутентифицированные запросы требуют CSRF-токенов, а анонимные запросы могут быть отправлены без CSRF-токенов. Такое поведение не подходит для представлений входа в систему, к которым всегда должна применяться проверка CSRF.
## RemoteUserAuthentication
Эта схема аутентификации позволяет вам делегировать аутентификацию вашему веб-серверу, который устанавливает переменную окружения `REMOTE_USER`.
Чтобы использовать его, вы должны иметь `django.contrib.auth.backends.RemoteUserBackend` (или его подкласс) в настройке `AUTHENTICATION_BACKENDS`. По умолчанию `RemoteUserBackend` создает объекты `User` для имен пользователей, которые еще не существуют. Чтобы изменить это и другое поведение, обратитесь к [документации Django](https://docs.djangoproject.com/en/stable/howto/auth-remote-user/).
При успешной аутентификации `RemoteUserAuthentication` предоставляет следующие учетные данные:
* `request.user` будет экземпляром Django `User`.
* `request.auth` будет `None`.
Обратитесь к документации вашего веб-сервера за информацией о настройке метода аутентификации, например:
* [Apache Authentication How-To](https://httpd.apache.org/docs/2.4/howto/auth.html)
* [NGINX (ограничение доступа)](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/)
# Пользовательская аутентификация
Чтобы реализовать собственную схему аутентификации, создайте подкласс `BaseAuthentication` и переопределите метод `.authenticate(self, request)`. Метод должен возвращать кортеж `(user, auth)`, если аутентификация прошла успешно, или `None` в противном случае.
В некоторых случаях вместо возврата `None` вы можете захотеть вызвать исключение `AuthenticationFailed` из метода `.authenticate()`.
Как правило, вам следует придерживаться следующего подхода:
* Если попытка аутентификации не была предпринята, верните `None`. Любые другие схемы аутентификации, которые также используются, будут проверены.
* Если попытка аутентификации была предпринята, но не удалась, вызовите исключение `AuthenticationFailed`. Ответ об ошибке будет возвращен немедленно, независимо от любых проверок разрешений и без проверки других схем аутентификации.
Вы *можете* также переопределить метод `.authenticate_header(self, request)`. Если он реализован, он должен возвращать строку, которая будет использоваться в качестве значения заголовка `WWW-Authenticate` в ответе `HTTP 401 Unauthorized`.
Если метод `.authenticate_header()` не переопределен, схема аутентификации будет возвращать ответы `HTTP 403 Forbidden`, когда неаутентифицированному запросу будет отказано в доступе.
---
**Примечание:** Когда ваш пользовательский аутентификатор вызывается свойствами `.user` или `.auth` объекта запроса, вы можете увидеть, как `AttributeError` преобразуется в `WrappedAttributeError`. Это необходимо для того, чтобы исходное исключение не было подавлено доступом к внешнему свойству. Python не распознает, что `AttributeError` исходит от вашего пользовательского аутентификатора, и вместо этого будет считать, что объект запроса не имеет свойства `.user` или `.auth`. Эти ошибки должны быть исправлены или иным образом обработаны вашим аутентификатором.
---
## Пример
Следующий пример аутентифицирует любой входящий запрос как пользователя, указанного в имени пользователя в пользовательском заголовке запроса 'X-USERNAME'.
```python
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
username = request.META.get('HTTP_X_USERNAME')
if not username:
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('No such user')
return (user, None)
```
---
# Пакеты сторонних производителей
Также доступны следующие пакеты сторонних производителей.
## django-rest-knox
Библиотека [Django-rest-knox](https://github.com/James1345/django-rest-knox) предоставляет модели и представления для обработки аутентификации на основе токенов более безопасным и расширяемым способом, чем встроенная схема `TokenAuthentication` - с учетом одностраничных приложений и мобильных клиентов. Она предоставляет токены для каждого клиента, а также представления для их генерации при предоставлении другой аутентификации (обычно базовой), для удаления токена (обеспечивая принудительный выход с сервера) и для удаления всех токенов (выход из всех клиентов, в которые вошел пользователь).
## Django OAuth Toolkit
Пакет [Django OAuth Toolkit](https://github.com/evonove/django-oauth-toolkit) обеспечивает поддержку OAuth 2.0 и работает с Python 3.4+. Пакет поддерживается [jazzband](https://github.com/jazzband/) и использует превосходный [OAuthLib](https://github.com/idan/oauthlib). Пакет хорошо документирован, хорошо поддерживается и в настоящее время является нашим **рекомендованным пакетом для поддержки OAuth 2.0**.
### Установка и настройка
Установите с помощью `pip`.
```bash
pip install django-oauth-toolkit
```
Добавьте пакет в `INSTALLED_APPS` и измените настройки DRF.
```python
INSTALLED_APPS = [
...
'oauth2_provider',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
]
}
```
Более подробную информацию можно найти в документации [Django REST framework - Getting started](https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html).
## Django REST framework OAuth
Пакет [Django REST framework OAuth](https://jpadilla.github.io/django-rest-framework-oauth/) обеспечивает поддержку OAuth1 и OAuth2 для DRF.
Ранее этот пакет был включен непосредственно в DRF, но теперь поддерживается и сопровождается как пакет стороннего разработчика.
### Установка и настройка
Установите пакет с помощью `pip`.
```bash
pip install djangorestframework-oauth
```
Подробнее о настройке и использовании смотрите документацию по OAuth фреймворку Django REST для [authentication](https://jpadilla.github.io/django-rest-framework-oauth/authentication/) и [permissions](https://jpadilla.github.io/django-rest-framework-oauth/permissions/).
## JSON Web Token Authentication
JSON Web Token - это довольно новый стандарт, который можно использовать для аутентификации на основе токенов. В отличие от встроенной схемы TokenAuthentication, аутентификация JWT не требует использования базы данных для проверки токена. Пакет для JWT-аутентификации - [djangorestframework-simplejwt](https://github.com/davesque/django-rest-framework-simplejwt), который предоставляет некоторые возможности, а также подключаемое приложение черного списка токенов.
## Аутентификация Hawk HTTP
Библиотека [HawkREST](https://hawkrest.readthedocs.io/en/latest/) основана на библиотеке [Mohawk](https://mohawk.readthedocs.io/en/latest/) и позволяет вам работать с подписанными запросами и ответами [Hawk](https://github.com/hueniverse/hawk) в вашем API. [Hawk](https://github.com/hueniverse/hawk) позволяет двум сторонам безопасно общаться друг с другом, используя сообщения, подписанные общим ключом. Она основана на [HTTP MAC аутентификации доступа](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-05) (которая была основана на части [OAuth 1.0](https://oauth.net/core/1.0a/)).
## Аутентификация подписью HTTP
HTTP Signature (в настоящее время [проект IETF](https://datatracker.ietf.org/doc/draft-cavage-http-signatures/)) предоставляет способ достижения аутентификации происхождения и целостности сообщений HTTP. Подобно схеме [Amazon's HTTP Signature scheme](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html), используемой многими сервисами компании, она допускает безэталонную аутентификацию по каждому запросу. [Elvio Toccalino](https://github.com/etoccalino/) поддерживает пакет [djangorestframework-httpsignature](https://github.com/etoccalino/django-rest-framework-httpsignature) (устаревший), который предоставляет простой в использовании механизм аутентификации HTTP Signature. Вы можете использовать обновленную версию-форк [djangorestframework-httpsignature](https://github.com/etoccalino/django-rest-framework-httpsignature), которой является [drf-httpsig](https://github.com/ahknight/drf-httpsig).
## Djoser
[Djoser](https://github.com/sunscrapers/djoser) библиотека предоставляет набор представлений для обработки основных действий, таких как регистрация, вход в систему, выход из системы, сброс пароля и активация учетной записи. Пакет работает с пользовательской моделью пользователя и использует аутентификацию на основе токенов. Это готовая к использованию REST-реализация системы аутентификации Django.
## DRF Auth Kit
Библиотека [DRF Auth Kit](https://github.com/huynguyengl99/drf-auth-kit) предоставляет современное решение для аутентификации REST с помощью файлов cookie JWT, входа через социальные сети, многофакторной аутентификации и комплексного управления пользователями. Пакет предлагает полную типобезопасность, автоматическую генерацию схемы OpenAPI с DRF Spectacular. Он поддерживает несколько типов аутентификации (JWT, DRF Token или Custom) и включает встроенную интернационализацию для более 50 языков.
## django-rest-auth / dj-rest-auth
Эта библиотека предоставляет набор конечных точек REST API для регистрации, аутентификации (включая аутентификацию в социальных сетях), сброса пароля, получения и обновления данных пользователя и т.д. Имея эти конечные точки API, ваши клиентские приложения, такие как AngularJS, iOS, Android и другие, могут самостоятельно общаться с вашим бэкендом Django через REST API для управления пользователями.
В настоящее время существует два форка этого проекта.
* [Django-rest-auth](https://github.com/Tivix/django-rest-auth) - оригинальный проект, [но в настоящее время не получает обновлений](https://github.com/Tivix/django-rest-auth/issues/568).
* [Dj-rest-auth](https://github.com/jazzband/dj-rest-auth) - более новый форк проекта.
## drf-social-oauth2
[Drf-social-oauth2](https://github.com/wagnerdelima/drf-social-oauth2) - это фреймворк, который поможет вам аутентифицироваться у основных поставщиков социального oauth2, таких как Facebook, Google, Twitter, Orcid и др. Он генерирует токены в виде JWT с простой настройкой.
## drfpasswordless
[drfpasswordless](https://github.com/aaronn/django-rest-framework-passwordless) добавляет (по мотивам Medium, Square Cash) поддержку беспарольного входа в схему TokenAuthentication платформы DRF. Пользователи входят в систему и регистрируются с помощью токена, отправленного на контактную точку, например, адрес электронной почты или номер мобильного телефона.
## django-rest-authemail
[django-rest-authemail](https://github.com/celiao/django-rest-authemail) предоставляет RESTful API интерфейс для регистрации и аутентификации пользователей. Для аутентификации используются адреса электронной почты, а не имена пользователей. Доступны конечные точки API для регистрации, проверки электронной почты при регистрации, входа в систему, выхода из системы, сброса пароля, проверки сброса пароля, изменения электронной почты, проверки изменения электронной почты, изменения пароля и детализации пользователя. Полностью функциональный пример проекта и подробные инструкции прилагаются.
## Django-Rest-Durin
[Django-Rest-Durin](https://github.com/eshaan7/django-rest-durin) создана с идеей иметь одну библиотеку, которая делает аутентификацию токенов для нескольких Web/CLI/Mobile API клиентов через один интерфейс, но позволяет различную конфигурацию токенов для каждого API клиента, который потребляет API. Она обеспечивает поддержку нескольких токенов для каждого пользователя через пользовательские модели, представления, разрешения, которые работают с Django-Rest-Framework. Время истечения срока действия токена может быть разным для каждого API-клиента и настраивается через интерфейс администратора Django.
Более подробную информацию можно найти в [Документации](https://django-rest-durin.readthedocs.io/en/latest/index.html).
## django_pyoidc
[dango_pyoidc](https://github.com/makinacorpus/django_pyoidc) добавляет поддержку аутентификации OpenID Connect (OIDC). Это позволяет делегировать управление пользователями провайдеру идентификации, который может быть использован для реализации Single-Sign-On (SSO). Он обеспечивает поддержку большинства сценариев использования, таких как настройка сопоставления информации о токенах с моделями пользователей, использование аудиторий OIDC для контроля доступа и т. д.
Более подробную информацию можно найти в [Документации](https://django-pyoidc.readthedocs.io/latest/index.html).
================================================
FILE: api-guide/caching.md
================================================
<!-- TRANSLATED by md-translate -->
# Кэширование
> У одной женщины было очень острое сознание, но почти не было памяти... Она помнила достаточно, чтобы работать, и она много работала.
>
> * Лидия Дэвис
Кэширование в DRF хорошо работает с утилитами кэширования, предоставляемыми в Django.
---
## Использование кэша с apiview и наборами представлений
Django предоставляет [`method_decorator`](https://docs.djangoproject.com/en/stable/topics/class-based-views/intro/#decorating-the-class) для использования декораторов с представлениями, основанными на классах. Его можно использовать с другими декораторами кэша, такими как [`cache_page`](https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cache), [`vary_on_cookie`](https://docs.djangoproject.com/en/stable/topics/http/decorators/#django.views.decorators.vary.vary_on_cookie) и [`vary_on_headers`](https://docs.djangoproject.com/en/stable/topics/http/decorators/#django.views.decorators.vary.vary_on_headers).
```python
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import viewsets
class UserViewSet(viewsets.ViewSet):
# With cookie: cache requested url for each user for 2 hours
@method_decorator(cache_page(60 * 60 * 2))
@method_decorator(vary_on_cookie)
def list(self, request, format=None):
content = {
"user_feed": request.user.get_user_feed(),
}
return Response(content)
class ProfileView(APIView):
# With auth: cache requested url for each user for 2 hours
@method_decorator(cache_page(60 * 60 * 2))
@method_decorator(vary_on_headers("Authorization"))
def get(self, request, format=None):
content = {
"user_feed": request.user.get_user_feed(),
}
return Response(content)
class PostView(APIView):
# Cache page for the requested url
@method_decorator(cache_page(60 * 60 * 2))
def get(self, request, format=None):
content = {
"title": "Post title",
"body": "Post content",
}
return Response(content)
```
## Использование кэша с декоратором @api_view
При использовании декоратора `@api_view` декораторы кэша, основанные на методах, такие как [`cache_page`](https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cache), [`vary_on_cookie`](https://docs.djangoproject.com/en/stable/topics/http/decorators/#django.views.decorators.vary.vary_on_cookie) и [`vary_on_headers`](https://docs.djangoproject.com/en/stable/topics/http/decorators/#django.views.decorators.vary.vary_on_headers) могут быть вызваны напрямую.
```python
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
from rest_framework.decorators import api_view
from rest_framework.response import Response
@cache_page(60 * 15)
@vary_on_cookie
@api_view(["GET"])
def get_user_list(request):
content = {"user_feed": request.user.get_user_feed()}
return Response(content)
```
---
**Обратите внимание:** Декоратор [`cache_page`](https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cache) кэширует только ответы `GET` и `HEAD` со статусом 200.
---
================================================
FILE: api-guide/content-negotiation.md
================================================
<!-- TRANSLATED by md-translate -->
# Согласование контента
> HTTP предусматривает несколько механизмов "согласования контента" - процесса выбора наилучшего представления для данного ответа при наличии нескольких представлений.
>
> - [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html), Fielding et al.
Согласование контента - это процесс выбора одного из нескольких возможных форматов ответа для возврата клиенту, основанный на предпочтениях клиента или сервера.
## Определение выбранного рендерера
DRF использует простой стиль согласования контента для определения того, какой формат данных должен быть возвращен клиенту, основываясь на доступных рендерерах, приоритетах каждого из них и заголовке клиента `Accept:`. Используемый стиль частично зависит от клиента, а частично от сервера.
1. Более конкретным типам носителей отдается предпочтение перед менее конкретными типами носителей.
2. Если несколько типов медиа имеют одинаковую специфичность, то предпочтение отдается на основе порядка рендеринга, настроенного для данного представления.
Например, при следующем заголовке `Accept`:
```http
application/json; indent=4, application/json, application/yaml, text/html, */*
```
Приоритеты для каждого из указанных типов носителей будут следующими:
* `'application/json; indent=4'`
* `'application/json'`, `'application/yaml'` и `'text/html'`
* `*/*`
Если запрашиваемое представление было настроено только с рендерерами для `YAML` и `HTML`, то DRF будет выбирать тот рендерер, который указан первым в списке `renderer_classes` или настройке `DEFAULT_RENDERER_CLASSES`.
Более подробную информацию о заголовке `HTTP Accept` смотрите в [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html).
---
**Примечание**: Значения "q" не учитываются DRF при определении предпочтений. Использование значений "q" негативно влияет на кэширование, и, по мнению автора, это ненужный и слишком сложный подход к согласованию контента.
Это верный подход, поскольку спецификация HTTP намеренно не определяет, как сервер должен взвешивать предпочтения, основанные на сервере, против предпочтений, основанных на клиенте.
---
# Переговоры по пользовательскому контенту
Маловероятно, что вы захотите предоставить пользовательскую схему согласования контента для DRF, но вы можете сделать это при необходимости. Для реализации пользовательской схемы согласования контента переопределите `BaseContentNegotiation`.
Классы согласования контента DRF обрабатывают выбор как подходящего парсера для запроса, так и подходящего рендерера для ответа, поэтому вы должны реализовать оба метода `.select_parser(request, parsers)` и `.select_renderer(request, renderers, format_suffix)`.
Метод `select_parser()` должен вернуть один экземпляр парсера из списка доступных парсеров, или `None`, если ни один из парсеров не может обработать входящий запрос.
Метод `select_renderer()` должен возвращать кортеж из (экземпляр рендерера, тип медиа), либо вызывать исключение `NotAcceptable`.
## Пример
Ниже представлен пользовательский класс согласования контента, который игнорирует запрос клиента при выборе подходящего парсера или рендерера.
```python
from rest_framework.negotiation import BaseContentNegotiation
class IgnoreClientContentNegotiation(BaseContentNegotiation):
def select_parser(self, request, parsers):
"""
Select the first parser in the `.parser_classes` list.
"""
return parsers[0]
def select_renderer(self, request, renderers, format_suffix):
"""
Select the first renderer in the `.renderer_classes` list.
"""
return (renderers[0], renderers[0].media_type)
```
## Указание согласования контента
Класс согласования контента по умолчанию можно установить глобально, используя настройку `DEFAULT_CONTENT_NEGOTIATION_CLASS`. Например, следующие настройки будут использовать наш пример класса `IgnoreClientContentNegotiation`.
```python
REST_FRAMEWORK = {
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation',
}
```
Вы также можете указать согласование контента, используемое для отдельного представления или набора представлений, используя представления на основе класса `APIView`.
```python
from myapp.negotiation import IgnoreClientContentNegotiation
from rest_framework.response import Response
from rest_framework.views import APIView
class NoNegotiationView(APIView):
"""
An example view that does not perform content negotiation.
"""
content_negotiation_class = IgnoreClientContentNegotiation
def get(self, request, format=None):
return Response({
'accepted media type': request.accepted_renderer.media_type
})
```
================================================
FILE: api-guide/exceptions.md
================================================
<!-- TRANSLATED by md-translate -->
# Исключения
> Исключения... позволяют чисто организовать обработку ошибок в центральном или высокоуровневом месте в структуре программы.
>
> — Даг Хеллманн, [Python Exception Handling Techniques](https://doughellmann.com/blog/2009/06/19/python-exception-handling-techniques/)
## Обработка исключений в представлениях DRF
Представления DRF обрабатывают различные исключения и возвращают соответствующие ответы на ошибки.
Обрабатываемыми исключениями являются:
* Подклассы `APIException`, возникающие внутри DRF.
* Исключение Django `Http404`.
* Исключение Django `PermissionDenied`.
В каждом случае DRF вернет ответ с соответствующим кодом состояния и типом содержимого. В теле ответа будут содержаться любые дополнительные сведения о характере ошибки.
Большинство ответов на ошибки будут содержать ключ `detail` в теле ответа.
Например, следующий запрос:
```http
DELETE http://api.example.com/foo/bar HTTP/1.1
Accept: application/json
```
Может быть получен ответ об ошибке, указывающий на то, что метод `DELETE` не разрешен для данного ресурса:
```http
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 42
{"detail": "Method 'DELETE' not allowed."}
```
Ошибки валидации обрабатываются несколько иначе, и в качестве ключей в ответе будут указаны имена полей. Если ошибка валидации не относится к конкретному полю, то будет использоваться ключ "non_field_errors", или любое строковое значение, установленное для параметра `NON_FIELD_ERRORS_KEY`.
Пример ошибки валидации может выглядеть следующим образом:
```http
HTTP/1.1 400 Bad Request
Content-Type: application/json
Content-Length: 94
{"amount": ["A valid integer is required."], "description": ["This field may not be blank."]}
```
## Пользовательская обработка исключений
Вы можете реализовать пользовательскую обработку исключений, создав функцию-обработчик, которая преобразует исключения, возникающие в ваших представлениях API, в объекты ответа. Это позволяет вам контролировать стиль ответов на ошибки, используемый вашим API.
Функция должна принимать пару аргументов, первый из которых - обрабатываемое исключение, а второй - словарь, содержащий любой дополнительный контекст, например, обрабатываемое в данный момент представление. Функция обработчика исключения должна либо возвращать объект `Response`, либо возвращать `None`, если исключение не может быть обработано. Если обработчик возвращает `None`, то исключение будет повторно поднято, и Django вернет стандартный ответ HTTP `500 "Server error"`.
Например, вы можете захотеть убедиться, что все ответы на ошибки включают код состояния HTTP в теле ответа, например, так:
```http
HTTP/1.1 405 Method Not Allowed
Content-Type: application/json
Content-Length: 62
{"status_code": 405, "detail": "Method 'DELETE' not allowed."}
```
Чтобы изменить стиль ответа, вы можете написать следующий пользовательский обработчик исключений:
```python
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
# Now add the HTTP status code to the response.
if response is not None:
response.data['status_code'] = response.status_code
return response
```
Аргумент `context` не используется обработчиком по умолчанию, но может быть полезен, если обработчику исключений нужна дополнительная информация, например, обрабатываемое в данный момент представление, доступ к которому можно получить как `context['view']`.
Обработчик исключений также должен быть настроен в ваших настройках, используя ключ настройки `EXCEPTION_HANDLER`. Например:
```python
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
```
Если параметр `'EXCEPTION_HANDLER'` не указан, по умолчанию используется стандартный обработчик исключений, предоставляемый DRF:
```python
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
```
Обратите внимание, что обработчик исключений будет вызываться только для ответов, сгенерированных поднятыми исключениями. Он не будет использоваться для ответов, возвращаемых непосредственно представлением, таких как ответы `HTTP_400_BAD_REQUEST`, которые возвращаются общими представлениями при неудачной проверке сериализатора.
---
# API Reference
## APIException
**Сигнатура:** `APIException()`.
**базовый класс** для всех исключений, возникающих внутри класса `APIView` или `@api_view`.
Чтобы предоставить пользовательское исключение, подкласс `APIException` и установите атрибуты `.status_code`, `.default_detail` и `.default_code` для класса.
Например, если ваш API полагается на сторонний сервис, который иногда может быть недоступен, вы можете захотеть реализовать исключение для кода ответа HTTP "503 Service Unavailable". Это можно сделать следующим образом:
```python
from rest_framework.exceptions import APIException
class ServiceUnavailable(APIException):
status_code = 503
default_detail = 'Service temporarily unavailable, try again later.'
default_code = 'service_unavailable'
```
#### Проверка исключений API
Существует ряд различных свойств, доступных для проверки состояния исключения API. Вы можете использовать их для создания пользовательской обработки исключений для вашего проекта.
Доступными атрибутами и методами являются:
* `.detail` - Возвращает текстовое описание ошибки.
* `.get_codes()` - Возвращает идентификатор кода ошибки.
* `.get_full_details()` - Возвращает как текстовое описание, так и идентификатор кода.
В большинстве случаев деталь ошибки будет простым элементом:
```python
>>> print(exc.detail)
You do not have permission to perform this action.
>>> print(exc.get_codes())
permission_denied
>>> print(exc.get_full_details())
{'message':'You do not have permission to perform this action.','code':'permission_denied'}
```
В случае ошибок валидации деталь ошибки будет представлять собой либо список, либо словарь элементов:
```python
>>> print(exc.detail)
{"name":"This field is required.","age":"A valid integer is required."}
>>> print(exc.get_codes())
{"name":"required","age":"invalid"}
>>> print(exc.get_full_details())
{"name":{"message":"This field is required.","code":"required"},"age":{"message":"A valid integer is required.","code":"invalid"}}
```
## ParseError
**Сигнатура:** `ParseError(detail=None, code=None)`.
Возникает, если запрос содержит неправильно сформированные данные при доступе к `request.data`.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "400 Bad Request".
## AuthenticationFailed
**Сигнатура:** `AuthenticationFailed(detail=None, code=None)`.
Возникает, когда входящий запрос содержит неправильную аутентификацию.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "401 Unauthenticated", но оно также может привести к ответу "403 Forbidden", в зависимости от используемой схемы аутентификации. Более подробную информацию см. в документации [authentication documentation](authentication.md).
## NotAuthenticated
**Сигнатура:** `NotAuthenticated(detail=None, code=None)`.
Возникает, когда неаутентифицированный запрос не прошел проверку на разрешение.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "401 Unauthenticated", но оно также может привести к ответу "403 Forbidden", в зависимости от используемой схемы аутентификации. Более подробную информацию см. в документации [authentication documentation](authentication.md).
## PermissionDenied
**Сигнатура:** `PermissionDenied(detail=None, code=None)`.
Возникает, когда аутентифицированный запрос не прошел проверку на разрешение.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "403 Forbidden".
## NotFound
**Сигнатура:** `NotFound(detail=None, code=None)`.
Возникает, когда ресурс не существует по указанному URL. Это исключение эквивалентно стандартному исключению `Http404` Django.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "404 Not Found".
## MethodNotAllowed
**Сигнатура:** `MethodNotAllowed(method, detail=None, code=None)`.
Возникает, когда происходит входящий запрос, который не сопоставлен с методом-обработчиком на представлении.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "405 Method Not Allowed".
## Неприемлемо
**Сигнатура:** `NotAcceptable(detail=None, code=None)`.
Возникает, когда поступает запрос с заголовком `Accept`, который не может быть удовлетворен ни одним из доступных рендереров.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "406 Not Acceptable".
## UnsupportedMediaType
**Сигнатура:** `UnsupportedMediaType(media_type, detail=None, code=None)`.
Возникает, если при обращении к `request.data` нет парсеров, способных обработать тип содержимого данных запроса.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "415 Unsupported Media Type".
## Дроссель
**Сигнатура:** `Throttled(wait=None, detail=None, code=None)`.
Возникает, когда входящий запрос не проходит проверку на дросселирование.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "429 Too Many Requests".
## ValidationError
**Сигнатура:** `ValidationError(detail=None, code=None)`.
Исключение `ValidationError` немного отличается от других классов `APIException`:
* Аргумент `detail` может быть списком или словарем деталей ошибки, а также может быть вложенной структурой данных. Используя словарь, вы можете указать ошибки на уровне полей при выполнении проверки на уровне объектов в методе `validate()` сериализатора. Например. `raise serializers.ValidationError({'name': 'Please enter a valid name.'})`.
* По соглашению вы должны импортировать модуль `serializers` и использовать полностью определенный `ValidationError`, чтобы отличить его от встроенной ошибки валидации Django. Например. `raise serializers.ValidationError('Это поле должно быть целочисленным значением.')`.
Класс `ValidationError` должен использоваться для сериализатора и валидации полей, а также классами валидаторов. Он также вызывается при вызове `serializer.is_valid` с именованным аргументом `raise_exception`:
```python
serializer.is_valid(raise_exception=True)
```
Общие представления используют флаг `raise_exception=True`, что означает, что вы можете переопределить стиль ответов на ошибки валидации глобально в вашем API. Для этого используйте пользовательский обработчик исключений, как описано выше.
По умолчанию это исключение приводит к ответу с кодом состояния HTTP "400 Bad Request".
---
# Общие представления об ошибках
DRF предоставляет два представления ошибок, подходящих для предоставления общих JSON ответов `500` Server Error и `400` Bad Request. (Стандартные представления ошибок Django предоставляют HTML-ответы, которые могут не подойти для приложения, использующего только API).
Используйте их согласно [документации по настройке представлений ошибок в Django](https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views).
## `rest_framework.exceptions.server_error`
Возвращает ответ с кодом состояния `500` и типом содержимого `application/json`.
Устанавливается как `handler500`:
```python
handler500 = 'rest_framework.exceptions.server_error'
```
## `rest_framework.exceptions.bad_request`
Возвращает ответ с кодом статуса `400` и типом содержимого `application/json`.
Устанавливается как `handler400`:
```python
handler400 = 'rest_framework.exceptions.bad_request'
```
# Пакеты сторонних производителей
Также доступны следующие пакеты сторонних производителей.
## Стандартизированные ошибки ДРФ
Пакет [drf-standardized-errors](https://github.com/ghazi-git/drf-standardized-errors) предоставляет обработчик исключений, который генерирует одинаковый формат для всех ответов 4xx и 5xx. Он является заменой стандартного обработчика исключений и позволяет настраивать формат ответа на ошибку без переписывания всего обработчика исключений. Стандартизированный формат ответа на ошибку легче документировать и проще обрабатывать потребителям API.
================================================
FILE: api-guide/fields.md
================================================
<!-- TRANSLATED by md-translate -->
# Поля сериализатора
> Каждое поле в классе Form отвечает не только за проверку данных, но и за их "очистку" — приведение их к единообразному формату.
>
> — [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/api/#django.forms.Form.cleaned_data)
Поля сериализатора выполняют преобразование между примитивными значениями и внутренними типами данных. Они также занимаются проверкой входных значений, а также получением и установкой значений из родительских объектов.
---
**Примечание:** Поля сериализатора объявляются в `fields.py`, но по соглашению вы должны импортировать их с помощью `from rest_framework import serializers` и обращаться к полям как `serializers.<FieldName>`.
---
## Основные аргументы
Каждый конструктор класса поля сериализатора принимает как минимум эти аргументы. Некоторые классы `Field` принимают дополнительные, специфические для поля аргументы, но следующие должны приниматься всегда:
### `read_only`
Поля, доступные только для чтения, включаются в выходные данные API, но не должны включаться во входные данные при операциях создания или обновления. Любые поля `read_only`, по ошибке включенные во входные данные сериализатора, будут проигнорированы.
Установите значение `True`, чтобы гарантировать, что поле используется при сериализации представления, но не используется при создании или обновлении экземпляра во время десериализации.
По умолчанию `False`.
### `write_only`
Установите значение `True`, чтобы поле могло использоваться при обновлении или создании экземпляра, но не включалось при сериализации представления.
По умолчанию `False`.
### `required`
Обычно ошибка возникает, если поле не предоставлено во время десериализации. Установите значение `False`, если это поле не обязательно должно присутствовать при десериализации.
Установка этого значения в `False` также позволяет не выводить атрибут объекта или ключ словаря при сериализации экземпляра. Если ключ не присутствует, он просто не будет включен в выходное представление.
По умолчанию имеет значение `True`. Если вы используете [Model Serializer](../api-guide/serializers.md#modelserializer), значение по умолчанию будет `False`, если вы указали `default` или если соответствующее поле `Model` имеет `blank=True` или `null=True` и в то же время не является частью ограничения по уникальности. (Обратите внимание, что без значения `default` [ограничения по уникальности приведут к тому, что поле станет обязательным.](../api-guide/validators.md#необязательные-поля).)
### `default`
Если установлено, это значение по умолчанию, которое будет использоваться для поля, если значение не передано. Если значение не задано, то по умолчанию атрибут вообще не заполняется.
Значение `default` не применяется во время операций частичного обновления. В случае частичного обновления только те поля, которые указаны во входящих данных, получат подтвержденное значение.
Может быть задана как функция или другой вызываемый объект, в этом случае значение будет выполняться каждый раз при его использовании. При вызове оно не получает никаких аргументов. Если вызываемая функция имеет атрибут `requires_context = True`, то поле сериализатора будет передано в качестве аргумента.
Например:
```python
class CurrentUserDefault:
"""
May be applied as a `default=...` value on a serializer field.
Returns the current user.
"""
requires_context = True
def __call__(self, serializer_field):
return serializer_field.context['request'].user
```
При сериализации экземпляра будет использоваться значение по умолчанию, если атрибут объекта или ключ словаря не присутствует в экземпляре.
Обратите внимание, что установка значения `default` подразумевает, что поле не является обязательным. Включение обоих именованных аргументов `default` и `required` является недопустимым и приведет к ошибке.
### `allow_null`
Обычно, если в поле сериализатора передается `None`, возникает ошибка. Установите этот именованный аргумент в `True`, если `None` должно считаться допустимым значением.
Обратите внимание, что без явного указания `default` установка этого аргумента в `True` подразумевает `default` значение `null` для вывода сериализации, но не подразумевает значение по умолчанию для десериализации ввода.
По умолчанию `False`.
### `source`
Имя атрибута, который будет использоваться для заполнения поля. Может быть методом, который принимает только аргумент `self`, например `URLField(source='get_absolute_url')`, или может использовать точечную нотацию для обхода атрибутов, например `EmailField(source='user.email')`.
При сериализации полей с точечной нотацией может потребоваться предоставить значение `default`, если какой-либо объект отсутствует или пуст при обходе атрибута. Остерегайтесь возможных проблем n+1 при использовании атрибута source, если вы обращаетесь к реляционной модели orm. Например:
```python
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField(source="user.email")
```
В этом случае объект пользователя должен быть извлечен из базы данных, если он не был предварительно извлечен. Если это нежелательно, убедитесь, что вы используете методы `prefetch_related` и `select_related` соответствующим образом. Более подробную информацию об этих методах можно найти в [документации django](https://docs.djangoproject.com/en/stable/ref/models/querysets/#django.db.models.query.QuerySet.select_related).
Значение `source='*'` имеет особое значение и используется для указания на то, что в поле должен быть передан весь объект. Это может быть полезно для создания вложенных представлений или для полей, которым требуется доступ к полному объекту для определения выходного представления.
По умолчанию используется имя поля.
### `validators`
Список функций валидаторов, которые должны быть применены к вводимому полю и которые либо выдают ошибку валидации, либо просто возвращаются. Функции валидатора обычно должны вызывать `serializers.ValidationError`, но встроенный в Django `ValidationError` также поддерживается для совместимости с валидаторами, определенными в кодовой базе Django или в сторонних пакетах Django.
### `error_messages`
Словарь кодов ошибок и сообщений об ошибках.
### `label`
Короткая текстовая строка, которая может быть использована в качестве имени поля в полях HTML-формы или других описательных элементах.
### `help_text`
Текстовая строка, которая может быть использована в качестве описания поля в полях HTML-формы или других описательных элементах.
### `initial`
Значение, которое должно использоваться для предварительного заполнения значений полей HTML-формы. Вы можете передать ему вызываемый объект, как и в случае с любым обычным полем Django `Field`:
```python
import datetime
from rest_framework import serializers
class ExampleSerializer(serializers.Serializer):
day = serializers.DateField(initial=datetime.date.today)
```
### `style`
Словарь пар ключ-значение, которые можно использовать для управления тем, как рендереры должны отображать поле.
Двумя примерами здесь являются `'input_type'` и `'base_template'`:
```python
# Use <input type="password"> for the input.
password = serializers.CharField(
style={'input_type': 'password'}
)
# Use a radio input instead of a select input.
color_channel = serializers.ChoiceField(
choices=['red', 'green', 'blue'],
style={'base_template': 'radio.html'}
)
```
Более подробную информацию можно найти в документации [HTML & Forms](../topics/html-and-forms.md).
---
# Булевы поля
## BooleanField
Булево представление.
При использовании HTML-кодированных форм ввода имейте в виду, что отсутствие значения всегда будет рассматриваться как установка поля в `False`, даже если для него указана опция `default=True`. Это происходит потому, что чекбоксы HTML представляют состояние без флажка в виде отсутствия значение, поэтому DRF воспринимает отсутствие значения как `False`.
Обратите внимание, что в Django 2.1 из `models.BooleanField` был удален именованный аргумент `blank`. До Django 2.1 поля `models.BooleanField` всегда имели значение `blank=True`. Таким образом, начиная с Django 2.1 экземпляры `serializers.BooleanField` по умолчанию будут генерироваться без kwarg `required` (т.е. эквивалентно `required=True`), тогда как в предыдущих версиях Django экземпляры `BooleanField` по умолчанию будут генерироваться с опцией `required=False`. Если вы хотите управлять этим поведением вручную, явно объявите `BooleanField` в классе сериализатора или используйте опцию `extra_kwargs` для установки флага `required`.
Соответствует `django.db.models.fields.BooleanField`.
**Сигнатура:** `BooleanField()`.
---
# Строковые поля
## CharField
Текстовое представление. Опционально проверяет, чтобы текст был короче `max_length` и длиннее `min_length`.
Соответствует `django.db.models.fields.CharField` или `django.db.models.fields.TextField`.
**Сигнатура:** `CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)`.
* `max_length` - Проверяет, что вводимые данные содержат не более этого количества символов.
* `min_length` - Проверяет, что вводимые данные содержат не менее этого количества символов.
* `allow_blank` - Если установлено значение `True`, то пустая строка будет считаться допустимым значением. Если установлено значение `False`, то пустая строка будет считаться недопустимой и вызовет ошибку валидации. По умолчанию `False`.
* `trim_whitespace` - Если установлено значение `True`, то пробельные символы в начале и в конце будут обрезаны. По умолчанию `True`.
Опция `allow_null` также доступна для строковых полей, хотя ее использование не рекомендуется в пользу `allow_blank`. Можно установить и `allow_blank=True`, и `allow_null=True`, но это означает, что для строковых представлений будут допустимы два разных типа пустых значений, что может привести к несоответствию данных и тонким ошибкам в работе приложения.
## EmailField
Текстовое представление, проверяющее, является ли этот текст действительным адресом электронной почты.
Соответствует `django.db.models.fields.EmailField`.
**Сигнатура:** `EmailField(max_length=None, min_length=None, allow_blank=False)`.
## RegexField
Текстовое представление, которое проверяет соответствие заданного значения определенному регулярному выражению.
Соответствует `django.forms.fields.RegexField`.
**Сигнатура:** `RegexField(regex, max_length=None, min_length=None, allow_blank=False)`.
Обязательный аргумент `regex` может быть либо строкой, либо скомпилированным объектом регулярного выражения python.
Для проверки используется `django.core.validators.RegexValidator` от Django.
## SlugField
Поле `RegexField`, которое проверяет вводимые данные на соответствие шаблону `[a-zA-Z0-9_-]+`.
Соответствует `django.db.models.fields.SlugField`.
**Сигнатура:** `SlugField(max_length=50, min_length=None, allow_blank=False)`.
## URLField
Поле `RegexField`, которое проверяет вводимые данные на соответствие шаблону URL. Ожидаются полностью определенные URL вида `http://<host>/<path>`.
Соответствует `django.db.models.fields.URLField`. Для проверки используется `django.core.validators.URLValidator`.
**Сигнатура:** `URLField(max_length=200, min_length=None, allow_blank=False)`.
## UUIDField
Поле, которое гарантирует, что вводимые данные являются правильной строкой UUID. Метод `to_internal_value` возвращает экземпляр `uuid.UUID`. На выходе поле вернет строку в каноническом дефисном формате, например:
```
"de305d54-75b4-431b-adb2-eb6b9e546013"
```
**Сигнатура:** `UUIDField(format='hex_verbose')`.
* `format`: Определяет формат представления значения uuid
- `'hex_verbose'` - Каноническое шестнадцатеричное представление, включая дефисы: `"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"`.
- `'hex'` - Компактное шестнадцатеричное представление UUID, не включая дефисы: `"5ce0e9a55ffa654bcee01238041fb31a"`.
- `'int'` - 128-битное целочисленное представление UUID: `"123456789012312313134124512351145145114"`.
- `'urn'` - RFC 4122 URN-представление UUID: `'urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a'`.
Изменение параметров `формата` влияет только на значения представления. Все форматы принимаются функцией `to_internal_value`.
## FilePathField
Поле, выбор которого ограничен именами файлов в определенном каталоге файловой системы
Соответствует `django.forms.fields.FilePathField`.
**Сигнатура:** `FilePathField(path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs)`.
* `path` - Абсолютный путь файловой системы к директории, из которой это поле FilePathField должно получить свой выбор.
* `match` - Регулярное выражение в виде строки, которое FilePathField будет использовать для фильтрации имен файлов.
* `recursive` - Указывает, должны ли включаться все подкаталоги пути. По умолчанию `False`.
* `allow_files` - Указывает, должны ли включаться файлы в указанном месте. По умолчанию `True`. Либо это, либо `allow_folders` должно быть `True`.
* `allow_folders` - Указывает, должны ли включаться папки в указанном месте. По умолчанию `False`. Либо это, либо `allow_files` должно быть `True`.
## IPAddressField
Поле, гарантирующее, что вводимые данные являются действительной строкой IPv4 или IPv6.
Соответствует `django.forms.fields.IPAddressField` и `django.forms.fields.GenericIPAddressField`.
**Сигнатура**: `IPAddressField(protocol='both', unpack_ipv4=False, **options)`.
* `protocol` Ограничивает допустимые входы указанным протоколом. Принимаемые значения: `'both'` (по умолчанию), `'IPv4'` или `'IPv6'`. Соответствие не зависит от регистра.
* `unpack_ipv4` Распаковывает сопоставленные IPv4-адреса, например `::ffff:192.0.2.1`. Если эта опция включена, то адрес будет распакован в `192.0.2.1`. По умолчанию отключена. Может использоваться только в том случае, если для протокола установлено значение `'both'`.
---
# Числовые поля
## IntegerField
Целочисленное представление.
Соответствует `django.db.models.fields.IntegerField`, `django.db.models.fields.SmallIntegerField`, `django.db.models.fields.PositiveIntegerField` и `django.db.models.fields.PositiveSmallIntegerField`.
**Сигнатура**: `IntegerField(max_value=None, min_value=None)`.
* `max_value` Проверяет, что предоставленное число не больше этого значения.
* `min_value` Проверяет, что предоставленное число не меньше этого значения.
## BigIntegerField
Представление большого целого числа.
Соответствует `django.db.models.fields.BigIntegerField`.
**Сигнатура**: `BigIntegerField(max_value=None, min_value=None, coerce_to_string=None)`
* `max_value` Проверяет, что предоставленное число не больше этого значения.
* `min_value` Проверяет, что предоставленное число не меньше этого значения.
* `coerce_to_string` Устанавливается в `True`, если для представления должны возвращаться строковые значения, или в `False`, если должны возвращаться объекты `BigInteger`. По умолчанию используется то же значение, что и для ключа настройки `COERCE_BIGINT_TO_STRING`, которое будет `False`, если не переопределено. Если сериализатор возвращает объекты `BigInteger`, то окончательный формат вывода будет определяться рендерером.
## FloatField
Представление числа с плавающей точкой.
Соответствует `django.db.models.fields.FloatField`.
**Сигнатура**: `FloatField(max_value=None, min_value=None)`.
* `max_value` Проверяет, что предоставленное число не больше этого значения.
* `min_value` Проверяет, что предоставленное число не меньше этого значения.
## DecimalField
Десятичное представление, представленное в Python экземпляром `Decimal`.
Соответствует `django.db.models.fields.DecimalField`.
**Сигнатура**: `DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)`.
* `max_digits` Максимальное количество цифр, допустимое в числе. Оно должно быть либо `None`, либо целым числом, большим или равным `decimal_places`.
* `decimal_places` Количество десятичных знаков, которые следует хранить в числе.
* `coerce_to_string` Установите значение `True`, если для представления должны быть возвращены строковые значения, или `False`, если должны быть возвращены объекты `Decimal`. По умолчанию имеет то же значение, что и ключ настроек `COERCE_DECIMAL_TO_STRING`, который будет `True`, если его не переопределить. Если сериализатор возвращает `Decimal` объекты, то окончательный формат вывода будет определяться рендерером. Обратите внимание, что установка `localize` заставит значение быть `True`.
* `max_value` Проверяет, что предоставленное число не больше этого значения. Должно быть целым числом или объектом `Decimal`.
* `min_value` Проверяет, что предоставленное число не меньше этого значения. Должно быть целым числом или объектом `Decimal`.
* `localize` Установите значение `True`, чтобы включить локализацию ввода и вывода на основе текущей локали. Это также заставит `coerce_to_string` принять значение `True`. По умолчанию установлено значение `False`. Обратите внимание, что форматирование данных будет включено, если вы установили `USE_L10N=True` в вашем файле настроек.
* `rounding` Устанавливает режим округления, используемый при квантовании с заданной точностью. Допустимые значения: [`decimal` module rounding modes](https://docs.python.org/3/library/decimal.html#rounding-modes). По умолчанию `None`.
* `normalize_output` Нормализует десятичное значение при сериализации. При этом удаляются все нули в конце строки и точность значения изменяется до минимально необходимой, чтобы можно было представить значение без потери данных. По умолчанию имеет значение `False`.
#### Пример использования
Для проверки чисел до 999 с разрешением 2 знака после запятой можно использовать:
```python
serializers.DecimalField(max_digits=5, decimal_places=2)
```
А также для проверки чисел вплоть до миллиарда с разрешением 10 знаков после запятой:
```python
serializers.DecimalField(max_digits=19, decimal_places=10)
```
---
# Поля даты и времени
## DateTimeField
Представление даты и времени.
Соответствует `django.db.models.fields.DateTimeField`.
**Сигнатура:** `DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None, default_timezone=None)`.
* `format` - Строка, представляющая формат вывода. Если он не указан, то по умолчанию принимает значение, равное ключу настроек `DATETIME_FORMAT`, который будет `'iso-8601'`, если он не установлен. Установка в строку формата указывает на то, что возвращаемые значения `to_representation` должны быть принудительно приведены к строковому виду. Строки формата описаны ниже. Установка этого значения в `None` указывает, что `to_representation` должен возвращать объекты Python `datetime`. В этом случае кодировка времени будет определяться рендерером.
* `input_formats` - Список строк, представляющих входные форматы, которые могут быть использованы для разбора даты. Если он не указан, будет использоваться настройка `DATETIME_INPUT_FORMATS`, которая по умолчанию принимает значение `['iso-8601']`.
* `default_timezone` - Подкласс `tzinfo` (`zoneinfo` или `pytz`), представляющий часовой пояс. Если он не указан и включен параметр `USE_TZ`, то по умолчанию используется [текущий часовой пояс](https://docs.djangoproject.com/en/stable/topics/i18n/timezones/#default-time-zone-and-current-time-zone). Если `USE_TZ` отключена, то объекты datetime будут наивными.
#### Строки формата `DateTimeField`.
Строки формата могут быть либо [Python strftime formats](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior), которые явно указывают формат, либо специальной строкой `'iso-8601'`, которая указывает на то, что следует использовать значения в стиле [ISO 8601](https://www.w3.org/TR/NOTE-datetime). (Например, `'2013-01-29T12:34:56.000000Z'`)
Если для формата используется значение `None`, то объекты `datetime` будут возвращаться методом `to_representation`, а окончательное представление на выходе будет определяться классом renderer.
#### Поля модели `auto_now` и `auto_now_add`.
При использовании `ModelSerializer` или `HyperlinkedModelSerializer` обратите внимание, что любые поля модели с `auto_now=True` или `auto_now_add=True` будут использовать поля сериализатора, которые по умолчанию имеют значение `read_only=True`.
Если вы хотите переопределить это поведение, вам нужно будет явно объявить `DateTimeField` в сериализаторе. Например:
```python
class CommentSerializer(serializers.ModelSerializer):
created = serializers.DateTimeField()
class Meta:
model = Comment
```
## DateField
Представление даты.
Соответствует `django.db.models.fields.DateField`.
**Сигнатура:** `DateField(format=api_settings.DATE_FORMAT, input_formats=None)`.
* `format` - Строка, представляющая формат вывода. Если он не указан, то по умолчанию принимает значение, равное ключу настроек `DATE_FORMAT`, который будет `'iso-8601'`, если он не установлен. Установка в строку формата указывает на то, что возвращаемые значения `to_representation` должны быть преобразованы в строковый вывод. Строки формата описаны ниже. Установка этого значения в `None` указывает, что `to_representation` должен возвращать объекты Python `date`. В этом случае кодировка даты будет определяться рендерером.
* `input_formats` - Список строк, представляющих входные форматы, которые могут быть использованы для разбора даты. Если он не указан, будет использоваться настройка `DATE_INPUT_FORMATS`, которая по умолчанию принимает значение `['iso-8601']`.
#### Строки формата `DateField`.
Строки формата могут быть либо [Python strftime formats](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior), которые явно указывают формат, либо специальной строкой `'iso-8601'`, которая указывает, что должны использоваться даты в стиле [ISO 8601](https://www.w3.org/TR/NOTE-datetime). (например, `'2013-01-29'`)
## TimeField
Представление времени.
Соответствует `django.db.models.fields.TimeField`.
**Сигнатура:** `TimeField(format=api_settings.TIME_FORMAT, input_formats=None)`.
* `format` - строка, представляющая формат вывода. Если он не указан, то по умолчанию принимает значение, равное ключу настроек `TIME_FORMAT`, который будет `'iso-8601'`, если он не установлен. Установка в строку формата указывает на то, что возвращаемые значения `to_representation` должны быть принудительно выведены в строку. Строки формата описаны ниже. Установка этого значения в `None` указывает, что `to_representation` должен возвращать объекты Python `time`. В этом случае кодировка времени будет определяться рендерером.
* `input_formats` - Список строк, представляющих входные форматы, которые могут быть использованы для разбора даты. Если он не указан, будет использоваться настройка `TIME_INPUT_FORMATS`, которая по умолчанию принимает значение `['iso-8601']`.
#### Строки формата `TimeField`.
Строки формата могут быть либо [Python strftime formats](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior), которые явно указывают формат, либо специальной строкой `'iso-8601'`, которая указывает, что должно использоваться время в стиле [ISO 8601](https://www.w3.org/TR/NOTE-datetime). (например, `'12:34:56.000000'`)
## DurationField
Представление длительности. Соответствует `django.db.models.fields.DurationField`.
В `validated_data` для этих полей будет содержаться экземпляр `datetime.timedelta`.
**Сигнатура:** `DurationField(format=api_settings.DURATION_FORMAT, max_value=None, min_value=None)`.
* `format` - строка, представляющая формат вывода. Если не указано, по умолчанию используется то же значение, что и в ключе настроек `DURATION_FORMAT`, которое будет `'django'`, если не установлено. Форматы описаны ниже. Установка этого значения в `None` указывает, что объекты Python `timedelta` должны возвращаться `to_representation`. В этом случае кодировка даты будет определяться рендерером.
* `max_value` Проверяет, что предоставленная продолжительность не больше этого значения.
* `min_value` Проверяет, что предоставленная продолжительность не меньше этого значения.
#### Форматы `DurationField`
Формат может быть либо специальной строкой `'iso-8601'`, которая указывает, что должны использоваться интервалы в стиле [ISO 8601](https://www.w3.org/TR/NOTE-datetime) (например, `'P4DT1H15M20S'`), либо `'django'`, что указывает на то, что должен использоваться формат интервала Django `'[DD] [HH:[MM:]]ss[.uuuuuu]'` (например, `'4 1:15:20'`).
---
# Поля выбора
## ChoiceField
Поле, которое может принимать значение из ограниченного набора вариантов.
Используется `ModelSerializer` для автоматической генерации полей, если соответствующее поле модели содержит аргумент `choices=...`.
**Сигнатура:** `ChoiceField(choices)`.
* `choices` - Список допустимых значений, или список кортежей `(key, display_name)`.
* `allow_blank` - Если установлено значение `True`, то пустая строка будет считаться допустимым значением. Если установлено значение `False`, то пустая строка будет считаться недопустимой и вызовет ошибку валидации. По умолчанию `False`.
* `html_cutoff` - Если установлено, то это будет максимальное количество вариантов, которые будут отображаться в выпадающем списке HTML select. Может использоваться для того, чтобы автоматически генерируемые поля выбора с очень большим количеством возможных вариантов выбора не мешали отрисовке шаблона. По умолчанию `None`.
* `html_cutoff_text` - Если установлено, то будет отображаться текстовый индикатор, если максимальное количество элементов было отсечено в выпадающем списке HTML select. По умолчанию `"More than {count} items..."`.
Оба параметра `allow_blank` и `allow_null` являются допустимыми для `ChoiceField`, хотя настоятельно рекомендуется использовать только один из них, а не оба. `allow_blank` следует предпочесть для текстовых вариантов, а `allow_null` - для числовых или других нетекстовых вариантов.
## MultipleChoiceField
Поле, которое может принимать нулевое, одно или множество значений, выбираемых из ограниченного набора вариантов. Принимает один обязательный аргумент. `to_internal_value` возвращает `set`, содержащий выбранные значения.
**Сигнатура:** `MultipleChoiceField(choices)`.
* `choices` - Список допустимых значений, или список кортежей `(key, display_name)`.
* `allow_blank` - Если установлено значение `True`, то пустая строка будет считаться допустимым значением. Если установлено значение `False`, то пустая строка будет считаться недопустимой и вызовет ошибку валидации. По умолчанию `False`.
* `html_cutoff` - Если установлено, то это будет максимальное количество вариантов, которые будут отображаться в выпадающем списке HTML select. Может использоваться для того, чтобы автоматически генерируемые поля выбора с очень большим количеством возможных вариантов выбора не мешали отрисовке шаблона. По умолчанию `None`.
* `html_cutoff_text` - Если установлено, то будет отображаться текстовый индикатор, если максимальное количество элементов было отсечено в выпадающем списке HTML select. По умолчанию `"More than {count} items..."`.
Как и в случае с `ChoiceField`, оба параметра `allow_blank` и `allow_null` являются допустимыми, хотя настоятельно рекомендуется использовать только один из них, а не оба. `allow_blank` следует предпочесть для текстовых вариантов, а `allow_null` - для числовых или других нетекстовых вариантов.
---
# Поля для загрузки файлов
#### Парсеры и загрузка файлов.
Классы `FileField` и `ImageField` подходят только для использования с `MultiPartParser` или `FileUploadParser`. Большинство парсеров, например, JSON, не поддерживают загрузку файлов. Для работы с загруженными файлами в Django используются штатные [FILE_UPLOAD_HANDLERS](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS).
## FileField
Представление файла. Выполняет стандартную для Django проверку `FileField`.
Соответствует `django.forms.fields.FileField`.
**Сигнатура:** `FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)`.
* `max_length` - Указывает максимальную длину имени файла.
* `allow_empty_file` - Указывает, разрешены ли пустые файлы.
* `use_url` - Если установлено значение `True`, то для выходного представления будут использоваться строковые значения URL. Если установлено значение `False`, то для вывода будут использоваться строковые значения имен файлов. По умолчанию принимает значение ключа настроек `UPLOADED_FILES_USE_URL`, которое равно `True`, если не установлено иное.
## ImageField
Представление изображения. Проверяет соответствие содержимого загруженного файла известному формату изображения.
Соответствует `django.forms.fields.ImageField`.
**Сигнатура:** `ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)`.
* `max_length` - Указывает максимальную длину имени файла.
* `allow_empty_file` - Указывает, разрешены ли пустые файлы.
* `use_url` - Если установлено значение `True`, то для выходного представления будут использоваться строковые значения URL. Если установлено значение `False`, то для вывода будут использоваться строковые значения имен файлов. По умолчанию принимает значение ключа настроек `UPLOADED_FILES_USE_URL`, которое равно `True`, если не установлено иное.
Требуется либо пакет `Pillow`, либо пакет `PIL`. Рекомендуется использовать пакет `Pillow`, так как пакет `PIL` больше активно не поддерживается.
---
# Составные поля
## ListField
Класс поля, который проверяет список объектов.
**Сигнатура**: `ListField(child=<A_FIELD_INSTANCE>, allow_empty=True, min_length=None, max_length=None)`.
* `child` - Экземпляр поля, который должен использоваться для проверки объектов в списке. Если этот аргумент не указан, то объекты в списке не будут проверяться.
* `allow_empty` - Указывает, разрешены ли пустые списки.
* `min_length` - Проверяет, что список содержит не менее этого количества элементов.
* `max_length` - Проверяет, что список содержит не более этого количества элементов.
Например, для проверки списка целых чисел вы можете использовать что-то вроде следующего:
```python
scores = serializers.ListField(
child=serializers.IntegerField(min_value=0, max_value=100)
)
```
Класс `ListField` также поддерживает декларативный стиль, который позволяет писать многократно используемые классы полей списков.
```python
class StringListField(serializers.ListField):
child = serializers.CharField()
```
Теперь мы можем повторно использовать наш пользовательский класс `StringListField` во всем нашем приложении, без необходимости указывать для него аргумент `child`.
## DictField
Класс поля, который проверяет словарь объектов. Ключи в `DictField` всегда предполагаются как строковые значения.
**Сигнатура**: `DictField(child=<A_FIELD_INSTANCE>, allow_empty=True)`.
* `child` - Экземпляр поля, который должен использоваться для проверки значений в словаре. Если этот аргумент не указан, то значения в отображении не будут проверяться.
* `allow_empty` - Указывает, разрешены ли пустые словари.
Например, чтобы создать поле, которое проверяет сопоставление строк со строками, вы должны написать что-то вроде этого:
```python
document = DictField(child=CharField())
```
Вы также можете использовать декларативный стиль, как в случае с `ListField`. Например:
```python
class DocumentField(DictField):
child = CharField()
```
## HStoreField
Предварительно настроенное `DictField`, совместимое с `HStoreField` от Django для postgres.
**Сигнатура**: `HStoreField(child=<A_FIELD_INSTANCE>, allow_empty=True)`.
* `child` - экземпляр поля, который используется для проверки значений в словаре. По умолчанию дочернее поле принимает как пустые строки, так и нулевые значения.
* `allow_empty` - Указывает, разрешены ли пустые словари.
Обратите внимание, что дочернее поле **должно** быть экземпляром `CharField`, так как расширение hstore хранит значения в виде строк.
## JSONField
Класс поля, который проверяет, что входящая структура данных состоит из корректных примитивов JSON. В альтернативном двоичном режиме он представляет и проверяет двоичные строки, закодированные в JSON.
**Сигнатура**: `JSONField(binary, encoder)`.
* `binary` - Если установлено значение `True`, то поле будет выводить и проверять строку, закодированную в JSON, а не примитивную структуру данных. По умолчанию `False`.
* `encoder` - Используйте этот JSON-кодер для сериализации входного объекта. По умолчанию `None`.
---
# Разные поля
## ReadOnlyField
Класс поля, который просто возвращает значение поля без изменений.
Это поле используется по умолчанию в `ModelSerializer` при включении имен полей, относящихся к атрибуту, а не к полю модели.
**Сигнатура**: `ReadOnlyField()`.
Например, если бы `has_expired` было свойством модели `Account`, то следующий сериализатор автоматически сгенерировал бы его как `ReadOnlyField`:
```python
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'has_expired']
```
## HiddenField
Класс поля, которое не принимает значение на основе пользовательского ввода, а берет его из значения по умолчанию или вызываемого объекта.
**Сигнатура**: `HiddenField()`.
Например, чтобы включить поле, которое всегда предоставляет текущее время как часть проверяемых сериализатором данных, вы должны использовать следующее:
```python
modified = serializers.HiddenField(default=timezone.now)
```
Класс `HiddenField` обычно нужен только в том случае, если у вас есть валидация, которая должна выполняться на основе некоторых предварительно предоставленных значений полей, но вы не хотите открывать все эти поля конечному пользователю.
Другие примеры по `HiddenField` смотрите в документации [validators](validators.md).
---
**Примечание:** `HiddenField()` не появляется в сериализаторе `partial=True` (при выполнении запроса `PATCH`).
---
## ModelField
Общее поле, которое может быть привязано к любому произвольному полю модели. Класс `ModelField` делегирует задачу сериализации/десериализации связанному с ним полю модели. Это поле можно использовать для создания полей сериализатора для пользовательских полей модели, без необходимости создавать новое пользовательское поле сериализатора.
Это поле используется `ModelSerializer` для соответствия классам полей пользовательской модели.
**Сигнатура:** `ModelField(model_field=<Django ModelField instance>)`.
Класс `ModelField` обычно предназначен для внутреннего использования, но при необходимости может быть использован в вашем API. Чтобы правильно инстанцировать `ModelField`, ему должно быть передано поле, привязанное к инстанцированной модели. Например: `ModelField(model_field=MyModel()._meta.get_field('custom_field'))`.
## SerializerMethodField
Это поле, доступное только для чтения. Оно получает свое значение путем вызова метода класса сериализатора, к которому оно присоединено. Его можно использовать для добавления любых данных в сериализованное представление вашего объекта.
**Сигнатура**: `SerializerMethodField(method_name=None)`.
* `method_name` - Имя метода в сериализаторе, который будет вызван. Если оно не включено, то по умолчанию используется `get_<имя_поля>`.
Метод сериализатора, на который ссылается аргумент `имя_метода`, должен принимать единственный аргумент (в дополнение к `self`), которым является сериализуемый объект. Он должен возвращать все, что вы хотите включить в сериализованное представление объекта. Например:
```python
from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
days_since_joined = serializers.SerializerMethodField()
class Meta:
model = User
fields = '__all__'
def get_days_since_joined(self, obj):
return (now() - obj.date_joined).days
```
---
# Пользовательские поля
Если вы хотите создать пользовательское поле, вам нужно создать подкласс `Field`, а затем переопределить один или оба метода `.to_representation()` и `.to_internal_value()`. Эти два метода используются для преобразования между исходным типом данных и примитивным, сериализуемым типом данных. Примитивными типами данных обычно являются число, строка, булево значение, `date`/`time`/`datetime` или `None`. Также это может быть любой список или словарь, содержащий только другие примитивные объекты. Могут поддерживаться и другие типы, в зависимости от используемого рендерера.
Метод `.to_representation()` вызывается для преобразования исходного типа данных в примитивный, сериализуемый тип данных.
Метод `.to_internal_value()` вызывается для восстановления примитивного типа данных в его внутреннее python-представление. Этот метод должен вызвать ошибку `serializers.ValidationError`, если данные недействительны.
## Примеры
### Базовое пользовательское поле
Давайте рассмотрим пример сериализации класса, представляющего значение цвета RGB:
```python
class Color:
"""
A color represented in the RGB colorspace.
"""
def __init__(self, red, green, blue):
assert(red >= 0 and green >= 0 and blue >= 0)
assert(red < 256 and green < 256 and blue < 256)
self.red, self.green, self.blue = red, green, blue
class ColorField(serializers.Field):
"""
Color objects are serialized into 'rgb(#, #, #)' notation.
"""
def to_representation(self, value):
return "rgb(%d, %d, %d)" % (value.red, value.green, value.blue)
def to_internal_value(self, data):
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
return Color(red, green, blue)
```
По умолчанию значения полей рассматриваются как сопоставление с атрибутом объекта. Если вам нужно настроить доступ к значению поля и его установку, вам нужно переопределить `.get_attribute()` и/или `.get_value()`.
В качестве примера создадим поле, которое может быть использовано для представления имени класса сериализуемого объекта:
```python
class ClassNameField(serializers.Field):
def get_attribute(self, instance):
# We pass the object instance onto `to_representation`,
# not just the field attribute.
return instance
def to_representation(self, value):
"""
Serialize the value's class name.
"""
return value.__class__.__name__
```
### Вызов ошибок проверки
Наш класс `ColorField`, описанный выше, в настоящее время не выполняет никакой проверки данных. Чтобы указать на недопустимые данные, мы должны вызвать ошибку `serializers.ValidationError`, как показано ниже:
```python
def to_internal_value(self, data):
if not isinstance(data, str):
msg = 'Incorrect type. Expected a string, but got %s'
raise ValidationError(msg % type(data).__name__)
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
raise ValidationError('Incorrect format. Expected `rgb(#,#,#)`.')
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
if any([col > 255 or col < 0 for col in (red, green, blue)]):
raise ValidationError('Value out of range. Must be between 0 and 255.')
return Color(red, green, blue)
```
Метод `.fail()` - это ярлык для вызова `ValidationError`, который принимает строку сообщения из словаря `error_messages`. Например:
```python
default_error_messages = {
'incorrect_type': 'Incorrect type. Expected a string, but got {input_type}',
'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
'out_of_range': 'Value out of range. Must be between 0 and 255.'
}
def to_internal_value(self, data):
if not isinstance(data, str):
self.fail('incorrect_type', input_type=type(data).__name__)
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
self.fail('incorrect_format')
data = data.strip('rgb(').rstrip(')')
red, green, blue = [int(col) for col in data.split(',')]
if any([col > 255 or col < 0 for col in (red, green, blue)]):
self.fail('out_of_range')
return Color(red, green, blue)
```
Этот стиль делает сообщения об ошибках более чистыми и отделенными от кода, поэтому его следует предпочесть.
### Использование `source='*'`
Здесь мы рассмотрим пример _плоской_ модели `DataPoint` с атрибутами `x_coordinate` и `y_coordinate`.
```python
class DataPoint(models.Model):
label = models.CharField(max_length=50)
x_coordinate = models.SmallIntegerField()
y_coordinate = models.SmallIntegerField()
```
Используя пользовательское поле и `source='*'`, мы можем предоставить вложенное представление пары координат:
```python
class CoordinateField(serializers.Field):
def to_representation(self, value):
ret = {
"x": value.x_coordinate,
"y": value.y_coordinate
}
return ret
def to_internal_value(self, data):
ret = {
"x_coordinate": data["x"],
"y_coordinate": data["y"],
}
return ret
class DataPointSerializer(serializers.ModelSerializer):
coordinates = CoordinateField(source='*')
class Meta:
model = DataPoint
fields = ['label', 'coordinates']
```
Обратите внимание, что в этом примере не предусмотрена валидация. Отчасти по этой причине в реальном проекте вложенность координат может быть лучше обработана с помощью вложенного сериализатора используя `source='*'`, с двумя экземплярами `IntegerField`, каждый из которых имеет свой собственный `source` указывающий на соответствующее поле.
Ключевыми моментами из этого примера являются следующие:
* `to_representation` передается весь объект `DataPoint`, который должен быть отображен в нужный вывод.
```python
>>> instance = DataPoint(label='Example', x_coordinate=1, y_coordinate=2)
>>> out_serializer = DataPointSerializer(instance)
>>> out_serializer.data
ReturnDict([('label', 'Example'), ('coordinates', {'x': 1, 'y': 2})])
```
* Если только наше поле не будет доступно только для чтения, `to_internal_value` должно возвращать дикту, подходящую для обновления целевого объекта. При использовании `source='*'`, возврат из `to_internal_value` будет обновлять корневой словарь данных, а не один ключ.
```python
>>> data = {
... "label": "Second Example",
... "coordinates": {
... "x": 3,
... "y": 4,
... }
... }
>>> in_serializer = DataPointSerializer(data=data)
>>> in_serializer.is_valid()
True
>>> in_serializer.validated_data
OrderedDict([('label', 'Second Example'),
('y_coordinate', 4),
('x_coordinate', 3)])
```
Для полноты картины повторим то же самое, но с использованием вложенного сериализатора, предложенного выше:
```python
class NestedCoordinateSerializer(serializers.Serializer):
x = serializers.IntegerField(source='x_coordinate')
y = serializers.IntegerField(source='y_coordinate')
class DataPointSerializer(serializers.ModelSerializer):
coordinates = NestedCoordinateSerializer(source='*')
class Meta:
model = DataPoint
fields = ['label', 'coordinates']
```
Здесь отображение между парами атрибутов цели и источника (`x` и `x_coordinate`, `y` и `y_coordinate`) обрабатывается в объявлениях `IntegerField`. Это наш `NestedCoordinateSerializer`, который принимает `source='*'`.
Наш новый `DataPointSerializer` демонстрирует то же поведение, что и подход с пользовательскими полями.
Сериализация:
```python
>>> out_serializer = DataPointSerializer(instance)
>>> out_serializer.data
ReturnDict([('label', 'testing'),
('coordinates', OrderedDict([('x', 1), ('y', 2)]))])
```
Десериализация:
```python
>>> in_serializer = DataPointSerializer(data=data)
>>> in_serializer.is_valid()
True
>>> in_serializer.validated_data
OrderedDict([('label', 'still testing'),
('x_coordinate', 3),
('y_coordinate', 4)])
```
Но мы также получаем встроенную валидацию бесплатно:
```python
>>> invalid_data = {
... "label": "still testing",
... "coordinates": {
... "x": 'a',
... "y": 'b',
... }
... }
>>> invalid_serializer = DataPointSerializer(data=invalid_data)
>>> invalid_serializer.is_valid()
False
>>> invalid_serializer.errors
ReturnDict([('coordinates',
{'x': ['A valid integer is required.'],
'y': ['A valid integer is required.']})])
```
По этой причине подход с использованием вложенного сериализатора следует попробовать в первую очередь. Вы будете использовать подход с пользовательскими полями, когда вложенный сериализатор станет невыполнимым или слишком сложным.
# Пакеты сторонних производителей
Также доступны следующие пакеты сторонних производителей.
## Составные поля DRF
Пакет [drf-compound-fields](https://drf-compound-fields.readthedocs.io) предоставляет "составные" поля сериализатора, такие как списки простых значений, которые могут быть описаны другими полями, а не сериализаторами с опцией `many=True`. Также предоставляются поля для типизированных словарей и значений, которые могут быть либо определенным типом, либо списком элементов этого типа.
## Дополнительные поля DRF
Пакет [drf-extra-fields](https://github.com/Hipo/drf-extra-fields) предоставляет дополнительные поля сериализатора для DRF, включая классы `Base64ImageField` и `PointField`.
## djangorestframework-recursive
Пакет [djangorestframework-recursive](https://github.com/heywbj/django-rest-framework-recursive) предоставляет `RecursiveField` для сериализации и десериализации рекурсивных структур.
## django-rest-framework-gis
Пакет [django-rest-framework-gis](https://github.com/djangonauts/django-rest-framework-gis) предоставляет географические дополнения для DRF, такие как поле `GeometryField` и сериализатор GeoJSON.
## django-rest-framework-hstore
Пакет [django-rest-framework-hstore](https://github.com/djangonauts/django-rest-framework-hstore) предоставляет `HStoreField` для поддержки поля модели [django-hstore](https://github.com/djangonauts/django-hstore) `DictionaryField`.
================================================
FILE: api-guide/filtering.md
================================================
<!-- TRANSLATED by md-translate -->
---
источник:
- filters.py
---
# Фильтрация
> Корневой QuerySet, предоставляемый менеджером, описывает все объекты в таблице базы данных. Однако обычно требуется выбрать только подмножество из полного набора объектов.
>
> — [Django documentation](https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-specific-objects-with-filters)
По умолчанию общие списочные представления DRF возвращают весь `QuerySet` для менеджера модели. Часто требуется, чтобы API ограничивал количество элементов, возвращаемых `QuerySet`.
Простейшим способом фильтрации `QuerySet` любого представления, подкласса `GenericAPIView`, является переопределение метода `.get_queryset()`.
Переопределение этого метода позволяет настраивать `QuerySet`, возвращаемый представлением, различными способами.
## Фильтрация по текущему пользователю
Возможно, потребуется отфильтровать `QuerySet`, чтобы возвращать только результаты, относящиеся к текущему аутентифицированному пользователю, сделавшему запрос.
Это можно сделать с помощью фильтрации по значению `request.user`.
Например:
```python
from myapp.models import Purchase
from myapp.serializers import PurchaseSerializer
from rest_framework import generics
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases
for the currently authenticated user.
"""
user = self.request.user
return Purchase.objects.filter(purchaser=user)
```
## Фильтрация по URL
Другой стиль фильтрации может включать ограничение `QuerySet` на основе некоторой части URL.
Например, если в конфигурации URL содержится запись следующего вида:
```python
re_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),
```
Затем вы можете написать представление, возвращающее `QuerySet` покупок, отфильтрованный по имени пользователя в части URL:
```python
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
This view should return a list of all the purchases for
the user as determined by the username portion of the URL.
"""
username = self.kwargs['username']
return Purchase.objects.filter(purchaser__username=username)
```
## Фильтрация по параметрам запроса
Следующим примером фильтрации исходного QuerySet может быть определение исходного QuerySet на основе параметров запроса в url.
Мы можем переопределить `.get_queryset()` для работы с такими URL, как `http://example.com/api/purchases?username=denvercoder9`, и фильтровать QuerySet только в том случае, если в URL включен параметр `username`:
```python
class PurchaseList(generics.ListAPIView):
serializer_class = PurchaseSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Purchase.objects.all()
username = self.request.query_params.get('username')
if username is not None:
queryset = queryset.filter(purchaser__username=username)
return queryset
```
---
# Общая фильтрация
Помимо возможности переопределения стандартного QuerySet, DRF также включает поддержку общих бэкендов фильтрации, которые позволяют легко строить сложные поисковые запросы и фильтры.
Общие фильтры также могут быть представлены в виде элементов управления HTML в API просмотра и API администрирования.

## Настройка бэкендов фильтров
Бэкенды фильтров по умолчанию могут быть заданы глобально, с помощью параметра `DEFAULT_FILTER_BACKENDS`. Например:
```python
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
```
Вы также можете задать бэкенды фильтров для каждого вида или для каждого набора видов, используя класс `GenericAPIView`, основанный на представлениях.
```python
import django_filters.rest_framework
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
```
## Фильтрация и поиск объектов
Обратите внимание, что если для представления настроен бэкэнд фильтрации, то он будет использоваться не только для фильтрации списочных представлений, но и для фильтрации `QuerySet`, используемых для возврата одного объекта.
Например, если взять предыдущий пример и товар с идентификатором `4675`, то следующий URL будет либо возвращать соответствующий объект, либо возвращать ответ 404, в зависимости от того, были ли выполнены условия фильтрации для данного экземпляра товара:
```bash
http://example.com/api/products/4675/?category=clothing&max_price=10.00
```
## Переопределение исходного QuerySet
Обратите внимание, что можно использовать и переопределенный `.get_queryset()`, и общую фильтрацию, и все будет работать так, как ожидается. Например, если у `Product` есть отношение "многие-ко-многим" с `User`, названное `purchase`, вы можете написать представление следующим образом:
```python
class PurchasedProductsList(generics.ListAPIView):
"""
Return a list of all the products that the authenticated
user has ever purchased, with optional filtering.
"""
model = Product
serializer_class = ProductSerializer
filterset_class = ProductFilter
def get_queryset(self):
user = self.request.user
return user.purchase_set.all()
```
---
# Руководство по API
## DjangoFilterBackend
Библиотека [`django-filter`](https://django-filter.readthedocs.io/en/latest/index.html) включает класс `DjangoFilterBackend`, который поддерживает высоконастраиваемую фильтрацию полей для DRF.
Чтобы использовать `DjangoFilterBackend`, сначала установите `django-filter`.
```bash
pip install django-filter
```
Затем добавьте `'django_filters'` в `INSTALLED_APPS` Django:
```python
INSTALLED_APPS = [
...
'django_filters',
...
]
```
Теперь необходимо либо добавить бэкэнд фильтра в настройки:
```python
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
```
Или добавить бэкэнд фильтрации в отдельный `View` или `ViewSet`.
```python
from django_filters.rest_framework import DjangoFilterBackend
class UserListView(generics.ListAPIView):
...
filter_backends = [DjangoFilterBackend]
```
Если вам нужна простая фильтрация на основе равенства, вы можете установить атрибут `filterset_fields` для представления или набора представлений, перечислив набор полей, по которым вы хотите осуществлять фильтрацию.
```python
class ProductList(generics.ListAPIView):
queryset = Product.objects.all()
serializer_class = ProductSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['category', 'in_stock']
```
Это автоматически создаст класс `FilterSet` для заданных полей и позволит выполнять такие запросы, как:
```bash
http://example.com/api/products?category=clothing&in_stock=True
```
Для более сложных требований к фильтрации можно указать класс `FilterSet`, который должен использоваться представлением. Более подробно о `FilterSet` можно прочитать в [документации django-filter](https://django-filter.readthedocs.io/en/latest/index.html). Также рекомендуется прочитать раздел [Интеграция DRF](https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html).
## SearchFilter
Класс `SearchFilter` поддерживает простой поиск по одному параметру запроса и основан на функциональности [поиска в админ-панели Django](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields).
При использовании в состав Web-интерфейса API будет входить элемент управления `SearchFilter`:

Класс `SearchFilter` будет применяться только в том случае, если у представления установлен атрибут `search_fields`. Атрибут `search_fields` должен представлять собой список имен полей текстового типа в модели, например `CharField` или `TextField`.
```python
from rest_framework import filters
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.SearchFilter]
search_fields = ['username', 'email']
```
Это позволит клиенту фильтровать элементы списка, задавая такие запросы, как:
```bash
http://example.com/api/users?search=russell
```
Также можно выполнить связанный поиск по полю `ForeignKey` или `ManyToManyField` с помощью нотации двойного подчеркивания в API поиска:
```python
search_fields = ['username', 'email', 'profile__profession']
```
Для полей [JSONField](https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.JSONField) и [HStoreField](https://docs.djangoproject.com/en/stable/ref/contrib/postgres/fields/#hstorefield) можно осуществлять фильтрацию по вложенным значениям внутри структуры данных, используя ту же нотацию двойного подчеркивания:
```python
search_fields = ['data__breed', 'data__owner__other_pets__0__name']
```
По умолчанию в поиске используются частичные совпадения без учета регистра. Параметр `search` может содержать несколько условий поиска, которые должны быть разделены пробелами и/или запятыми. Если используется несколько условий поиска, то объекты будут возвращены в списке только при совпадении всех указанных условий. Поиск может содержать _цитируемые фразы_ с пробелами, каждая фраза рассматривается как один поисковый термин.
Поведение поиска может быть задано путем добавления префикса к именам полей в `search_fields` одним из следующих символов (что эквивалентно добавлению `__<lookup>` к полю):
| Префикс | Поиск | |
|---------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `^` | `istartswith` | Начинается с поиска. |
| `=` | `iexact` | Точные совпадения. |
| `$` | `iregex` | Regex-поиск. |
| `@` | `search` | Полнотекстовый поиск (в настоящее время поддерживается только бэкенд Django [PostgreSQL](https://docs.djangoproject.com/en/stable/ref/contrib/postgres/search/)). |
| None | `icontains` | Содержит поиск (по умолчанию). |
Например:
```python
search_fields = ['=username', '=email']
```
По умолчанию параметр поиска называется `'search'`, но это можно переопределить с помощью параметра `SEARCH_PARAM` в секции настроек `REST_FRAMEWORK`.
Для динамического изменения полей поиска в зависимости от содержимого запроса можно подклассифицировать `SearchFilter` и переопределить функцию `get_search_fields()`. Например, следующий подкласс будет искать по `title`, только если в запросе присутствует параметр запроса `title_only`:
```python
from rest_framework import filters
class CustomSearchFilter(filters.SearchFilter):
def get_search_fields(self, view, request):
if request.query_params.get('title_only'):
return ['title']
return super().get_search_fields(view, request)
```
Более подробную информацию можно найти в [документации Django](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields).
---
## OrderingFilter
Класс `OrderingFilter` поддерживает простое упорядочивание результатов, управляемое параметрами запроса.

По умолчанию параметр запроса называется `'ordering'`, но это можно переопределить с помощью параметра `ORDERING_PARAM` в секции настроек `REST_FRAMEWORK`.
Например, чтобы упорядочить пользователей по имени пользователя:
```bash
http://example.com/api/users?ordering=username
```
Клиент может задать и обратный порядок, добавив к имени поля префикс '-', например, так:
```bash
http://example.com/api/users?ordering=-username
```
Также может быть задано несколько порядков:
```bash
http://example.com/api/users?ordering=account,username
```
### Указание того, какие поля могут быть использованы для упорядочивания
Рекомендуется явно указывать, какие поля API должен разрешать в фильтре упорядочивания. Это можно сделать, установив атрибут `ordering_fields` на представлении, например, так:
```python
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['username', 'email']
```
Это позволяет предотвратить непредвиденную утечку данных, например, разрешить пользователям заказывать по хэш-полю пароли или другие конфиденциальные данные.
Если _не_ указать атрибут `orderering_fields` для представления, то класс фильтра будет по умолчанию позволять пользователю фильтровать по любым читаемым полям на сериализаторе, указанном атрибутом `serializer_class`.
Если вы уверены, что используемый представлением `Queryset` не содержит конфиденциальных данных, вы также можете явно указать, что представление должно разрешать упорядочивание по _любому_ полю модели или агрегату `Queryset`, используя специальное значение `'__all__'`.
```python
class BookingsListView(generics.ListAPIView):
queryset = Booking.objects.all()
serializer_class = BookingSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = '__all__'
```
### Указание порядка по умолчанию
Если у представления установлен атрибут `ordering`, то он будет использоваться в качестве упорядочивания по умолчанию.
Обычно для этого используется параметр `order_by` в исходном `Queryset`, но использование параметра `ordering` в представлении позволяет указать порядок таким образом, что он может быть автоматически передан в качестве контекста в шаблон рендеринга. Это позволяет автоматически отображать заголовки столбцов по-разному, если они используются для упорядочивания результатов.
```python
class UserListView(generics.ListAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
filter_backends = [filters.OrderingFilter]
ordering_fields = ['username', 'email']
ordering = ['username']
```
Атрибут `orderering` может быть как строкой, так и списком/кортежем строк.
---
# Пользовательская общая фильтрация
Вы также можете предоставить свой собственный бэкэнд фильтрации или написать устанавливаемое приложение для использования другими разработчиками.
Для этого переопределите `BaseFilterBackend` и переопределите метод `.filter_queryset(self, request, queryset, view)`. Метод должен возвращать новый, отфильтрованный `QuerySet`.
Помимо того, что клиенты могут выполнять поиск и фильтрацию, общие бэкенды фильтров могут быть полезны для ограничения того, какие объекты должны быть видны для каждого конкретного запроса или пользователя.
## Пример
Например, может потребоваться ограничить доступ пользователей только к созданным ими объектам.
```python
class IsOwnerFilterBackend(filters.BaseFilterBackend):
"""
Filter that only allows users to see their own objects.
"""
def filter_queryset(self, request, queryset, view):
return queryset.filter(owner=request.user)
```
Мы могли бы добиться такого же поведения, переопределив `get_queryset()` в представлениях, но использование бэкенда фильтров позволяет более просто добавить это ограничение к нескольким представлениям или применить его ко всему API.
## Настройка интерфейса
Общие фильтры также могут представлять интерфейс в Web-интерфейсе API. Для этого необходимо реализовать метод `to_html()`, который возвращает отрендеренное HTML-представление фильтра. Этот метод должен иметь следующую сигнатуру:
`to_html(self, request, queryset, view)`.
Метод должен возвращать отрендеренную HTML-строку.
# Пакеты сторонних производителей
Следующие пакеты сторонних производителей предоставляют дополнительные реализации фильтров.
## Django-rest-framework-filters
[django-rest-framework-filters](https://github.com/philipn/django-rest-framework-filters) работает совместно с классом `DjangoFilterBackend` и позволяет легко создавать фильтры по отношениям, а также создавать несколько типов фильтров для поиска по заданному полю.
## Djangorestframework-word-filter
[djangorestframework-word-filter](https://github.com/trollknurr/django-rest-framework-word-search-filter) разработан как альтернатива `filters.SearchFilter`, который будет искать полное слово в тексте, либо точное совпадение.
## Django-url-filter
[django-url-filter](https://github.com/miki725/django-url-filter) предоставляет безопасный способ фильтрации данных по удобным для человека URL-адресам. Он работает очень похоже на сериализаторы и поля DRF в том смысле, что они могут быть вложенными, за исключением того, что они называются `filtersets` и `filters`. Это обеспечивает простой способ фильтрации связанных данных. Кроме того, эта библиотека является универсальной, поэтому ее можно использовать для фильтрации других источников данных, а не только Django `QuerySet`.
## drf-url-filters
[drf-url-filter](https://github.com/manjitkumar/drf-url-filters) - это простое Django-приложение для применения фильтров к `Queryset` в `ModelViewSet` чистым, простым и настраиваемым способом. Оно также поддерживает валидацию входящих параметров запроса и их значений. Для валидации входящих параметров запроса используется красивый python-пакет `Voluptuous`. Самое приятное в `Voluptuous` то, что вы можете определить свои собственные валидации в соответствии с требованиями к параметрам запроса.
================================================
FILE: api-guide/format-suffixes.md
================================================
<!-- TRANSLATED by md-translate -->
# Cуффиксы формата
> В разделе 6.2.1 не говорится, что согласование содержания должно использоваться постоянно.
>
> - Рой Филдинг, [список рассылки REST discuss](http://tech.groups.yahoo.com/group/rest-discuss/message/5857)
Общим шаблоном для веб-интерфейсов является использование расширений имен файлов в URL-адресах для предоставления конечной точки для данного типа носителя. Например, 'http://example.com/api/users.json' для предоставления представления JSON.
Добавление шаблонов суффиксов формата к каждой отдельной записи в URLconf для вашего API чревато ошибками и не соответствует стандарту DRY, поэтому DRF предоставляет быстрый способ добавления этих шаблонов в URLConf.
## format_suffix_patterns
**Сигнатура**: `format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None)`
Возвращает список шаблонов URL, который включает шаблоны суффиксов формата, добавленные к каждому из предоставленных шаблонов URL.
Аргументы:
* **urlpatterns**: Обязательно. Список шаблонов URL.
* **suffix_required**: Необязательно. Булево значение, указывающее, должны ли суффиксы в URL быть необязательными или обязательными. По умолчанию `False`, что означает, что суффиксы по умолчанию необязательны.
* **allowed**: Необязательно. Список или кортеж допустимых суффиксов формата. Если не указан, будет использоваться шаблон суффикса формата.
Пример:
```python
from rest_framework.urlpatterns import format_suffix_patterns
from blog import views
urlpatterns = [
path('', views.apt_root),
path('comments/', views.comment_list),
path('comments/<int:pk>/', views.comment_detail)
]
urlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
```
При использовании `format_suffix_patterns`, вы должны убедиться, что добавили именованный аргумент `'format'` в соответствующие представления. Например:
```python
@api_view(['GET', 'POST'])
def comment_list(request, format=None):
# do stuff...
```
Или с помощью представлений, основанных на классах:
```python
class CommentList(APIView):
def get(self, request, format=None):
# do stuff...
def post(self, request, format=None):
# do stuff...
```
Имя используемого именнованного аргумента можно изменить с помощью параметра `FORMAT_SUFFIX_KWARG`.
Также обратите внимание, что `format_suffix_patterns` не поддерживает применение к шаблонам URL `include`.
### Использование с `i18n_patterns`.
При использовании функции `i18n_patterns`, предоставляемой Django, а также `format_suffix_patterns` вы должны убедиться, что функция `i18n_patterns` применяется как конечная, или крайняя функция. Например:
```python
urlpatterns = [
…
]
urlpatterns = i18n_patterns(
format_suffix_patterns(urlpatterns, allowed=['json', 'html'])
)
```
---
## Форматы параметров запроса
Альтернативой суффиксам формата является включение запрашиваемого формата в параметр запроса. DRF предоставляет этот параметр по умолчанию, и он используется в Web-интерфейсе API для переключения между различными доступными представлениями.
Чтобы выбрать представление по его краткому формату, используйте параметр запроса `format`. Например: `http://example.com/organizations/?format=csv`.
Имя этого параметра запроса можно изменить с помощью параметра `URL_FORMAT_OVERRIDE`. Установите значение `None`, чтобы отключить это поведение.
---
## Принимать заголовки против суффиксов формата
Похоже, некоторые представители веб-сообщества считают, что расширения имен файлов не являются шаблоном RESTful, и что вместо них всегда следует использовать заголовки `HTTP Accept`.
На самом деле это заблуждение. Например, возьмем следующую цитату Роя Филдинга, обсуждающего относительные достоинства индикаторов медиатипа параметров запроса по сравнению с индикаторами медиатипа расширений файлов:
> Вот почему я всегда предпочитаю расширения. Ни тот, ни другой выбор не имеют никакого отношения к REST".
>
> - Рой Филдинг, [Список рассылки REST discuss](https://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/14844)
В цитате не упоминаются заголовки Accept, но она ясно дает понять, что суффиксы формата следует считать приемлемым шаблоном.
================================================
FILE: api-guide/generic-views.md
================================================
<!-- TRANSLATED by md-translate -->
# Общие представления
> Общие представления в Django... были разработаны как кратчайший путь к общим шаблонам использования... Они берут определенные общие идиомы и паттерны, встречающиеся в разработке представлений, и абстрагируют их, чтобы вы могли быстро писать общие представления данных без необходимости повторяться.
>
> - [Документация Django](https://docs.djangoproject.com/en/stable/ref/class-based-views/#base-vs-generic-views)
Одним из ключевых преимуществ представлений, основанных на классах, является то, что они позволяют составлять фрагменты многократно используемого поведения. DRF использует это преимущество, предоставляя ряд готовых представлений, которые обеспечивают часто используемые шаблоны.
Типовые представления, предоставляемые DRF, позволяют быстро создавать представления API, которые тесно связаны с моделями вашей базы данных.
Если типовые представления не удовлетворяют потребностям вашего API, вы можете перейти к использованию обычного класса `APIView` или повторно использовать миксины и базовые классы, используемые типовыми представлениями, для создания собственного набора многократно используемых типовых представлений.
## Примеры
Обычно при использовании общих представлений вы переопределяете представление и устанавливаете несколько атрибутов класса.
```python
from django.contrib.auth.models import User
from myapp.serializers import UserSerializer
from rest_framework import generics
from rest_framework.permissions import IsAdminUser
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
```
Для более сложных случаев вы также можете захотеть переопределить различные методы класса представления. Например.
```python
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAdminUser]
def list(self, request):
# Note the use of `get_queryset()` instead of `self.queryset`
queryset = self.get_queryset()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
```
Для очень простых случаев вы можете передать любые атрибуты класса с помощью метода `.as_view()`. Например, ваша URLconf может включать что-то вроде следующей записи:
```python
path('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')
```
---
# API Reference
## GenericAPIView
Этот класс расширяет класс `APIView` DRF, добавляя часто требуемое поведение для стандартных представлений списка и детализации.
Каждое из конкретных типовых представлений создается путем объединения `GenericAPIView` с одним или несколькими классами-миксинами.
### Атрибуты
**Основные настройки**:
Следующие атрибуты управляют основным поведением представления.
* `queryset` - Набор queryset, который должен использоваться для возврата объектов из этого представления. Как правило, вы должны либо установить этот атрибут, либо переопределить метод `get_queryset()`. Если вы переопределяете метод представления, важно вызвать `get_queryset()`, а не обращаться к этому свойству напрямую, так как `queryset` будет оценен один раз, и эти результаты будут кэшироваться для всех последующих запросов.
* `serializer_class` - Класс сериализатора, который должен использоваться для проверки и десериализации входных данных, а также для сериализации выходных данных. Как правило, вы должны либо установить этот атрибут, либо переопределить метод `get_serializer_class()`.
* `lookup_field` - Поле модели, которое должно использоваться для выполнения поиска объектов в отдельных экземплярах модели. По умолчанию используется значение `'pk'`. Обратите внимание, что при использовании API с гиперссылками вам нужно убедиться, что *и* представления API, *и* классы сериализатора устанавливают поля поиска, если вам нужно использовать пользовательское значение.
* `lookup_url_kwarg` - Именованный аргумент URL, который должен использоваться для поиска объекта. URL conf должен включать именованный аргумент, соответствующий этому значению. Если значение не установлено, по умолчанию используется то же значение, что и `lookup_field`.
**Пагинация**:
Следующие атрибуты используются для управления пагинацией при использовании представлений списка.
* `pagination_class` - Класс пагинации, который должен использоваться при пагинации результатов списка. По умолчанию имеет то же значение, что и параметр `DEFAULT_PAGINATION_CLASS`, который является `'rest_framework.pagination.PageNumberPagination'`. Установка `pagination_class=None` отключит пагинацию в этом представлении.
**Фильтрация**:
* `filter_backends` - Список классов бэкендов фильтра, которые должны использоваться для фильтрации набора запросов. По умолчанию имеет то же значение, что и параметр `DEFAULT_FILTER_BACKENDS`.
### Методы
**Базовые методы**:
#### `get_queryset(self)`.
Возвращает набор запросов, который должен использоваться для представлений списка и который должен использоваться в качестве базы для поиска в детальных представлениях. По умолчанию возвращается кверисет, указанный атрибутом `queryset`.
Этот метод всегда следует использовать вместо прямого обращения к `self.queryset`, поскольку `self.queryset` оценивается только один раз, и эти результаты кэшируются для всех последующих запросов.
Может быть переопределена для обеспечения динамического поведения, например, возврата набора запросов, специфичного для пользователя, делающего запрос.
Например:
```python
def get_queryset(self):
user = self.request.user
return user.accounts.all()
```
---
**Примечание:** Если класс `serializer_class`, используемый в общем представлении, охватывает несколько отношений, что приводит к проблеме n+1, вы можете оптимизировать ваш набор запросов в этом методе, используя `select_related` и `prefetch_related`. Для получения дополнительной информации о проблеме n+1 и случаях использования упомянутых методов обратитесь к разделу related в [документации django](https://docs.djangoproject.com/en/stable/ref/models/querysets/#django.db.models.query.QuerySet.select_related).
---
### Избегание запросов N+1
При перечислении объектов (например, с помощью `ListAPIView` или `ModelViewSet`) сериализаторы могут вызвать паттерн запросов N+1, если доступ к связанным объектам осуществляется индивидуально для каждого элемента.
Чтобы этого не произошло, оптимизируйте набор запросов в `get_queryset()` или установите атрибут класса `queryset` с помощью [`select_related()`](https://docs.djangoproject.com/en/stable/ref/models/querysets/#select-related) и [`prefetch_related()`](https://docs.djangoproject.com/en/stable/ref/models/querysets/#prefetch-related), в зависимости от типа отношения.
**Для ForeignKey и OneToOneField**:
Используйте `select_related()`, чтобы получить связанные объекты в одном запросе:
```python
def get_queryset(self):
return Order.objects.select_related("customer", "billing_address")
```
**Для обратных и many-to-many отношений**:
Используйте `prefetch_related()`, чтобы эффективно загрузить коллекции связанных объектов:
```python
def get_queryset(self):
return Book.objects.prefetch_related("categories", "reviews__user")
```
**Для сложных наборов запросов с несколькими типами отношений**:
```python
def get_queryset(self):
return (
Order.objects
.select_related("customer")
.prefetch_related("items__product")
)
```
Эти оптимизации уменьшают повторный доступ к базе данных и улучшают производительность представлений списка.
---
#### `get_object(self)`.
Возвращает экземпляр объекта, который должен использоваться для детальных представлений. По умолчанию используется параметр `lookup_field` для фильтрации базового набора запросов.
Может быть переопределена для обеспечения более сложного поведения, например, поиска объектов на основе более чем одного именованного аргумента URL.
Например:
```python
def get_object(self):
queryset = self.get_queryset()
filter = {}
for field in self.multiple_lookup_fields:
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter)
self.check_object_permissions(self.request, obj)
return obj
```
Обратите внимание, что если ваш API не включает разрешения на уровне объекта, вы можете исключить `self.check_object_permissions`, и просто вернуть объект из поиска `get_object_or_404`.
#### `filter_queryset(self, queryset)`.
Получив набор запросов, отфильтруйте его с помощью тех бэкендов фильтрации, которые используются, и верните новый набор запросов.
Например:
```python
def filter_queryset(self, queryset):
filter_backends = [CategoryFilter]
if 'geo_route' in self.request.query_params:
filter_backends = [GeoRouteFilter, CategoryFilter]
elif 'geo_point' in self.request.query_params:
filter_backends = [GeoPointFilter, CategoryFilter]
for backend in list(filter_backends):
queryset = backend().filter_queryset(self.request, queryset, view=self)
return queryset
```
#### `get_serializer_class(self)`.
Возвращает класс, который должен быть использован для сериализатора. По умолчанию возвращается атрибут `serializer_class`.
Может быть переопределен для обеспечения динамического поведения, например, использования различных сериализаторов для операций чтения и записи, или предоставления различных сериализаторов различным типам пользователей.
Например:
```python
def get_serializer_class(self):
if self.request.user.is_staff:
return FullAccountSerializer
return BasicAccountSerializer
```
**Хуки для сохранения и удаления**:
Следующие методы предоставляются mixin-классами и обеспечивают легкое переопределение поведения сохранения или удаления объекта.
* `perform_create(self, serializer)` - Вызывается `CreateModelMixin` при сохранении нового экземпляра объекта.
* `perform_update(self, serializer)` - Вызывается `UpdateModelMixin` при сохранении существующего экземпляра объекта.
* `perform_destroy(self, instance)` - Вызывается `DestroyModelMixin` при удалении экземпляра объекта.
Эти крючки особенно полезны для установки атрибутов, которые подразумеваются в запросе, но не являются частью данных запроса. Например, вы можете установить атрибут объекта на основе пользователя запроса или на основе именованного аргумента URL.
```python
def perform_create(self, serializer):
serializer.save(user=self.request.user)
```
Эти точки переопределения также особенно полезны для добавления поведения, которое происходит до или после сохранения объекта, например, отправки подтверждения по электронной почте или регистрации обновления.
```python
def perform_update(self, serializer):
instance = serializer.save()
send_email_confirmation(user=self.request.user, modified=instance)
```
Вы также можете использовать эти крючки для обеспечения дополнительной проверки, вызывая `ValidationError()`. Это может быть полезно, если вам нужно применить логику валидации в момент сохранения базы данных. Например:
```python
def perform_create(self, serializer):
queryset = SignupRequest.objects.filter(user=self.request.user)
if queryset.exists():
raise ValidationError('You have already signed up')
serializer.save(user=self.request.user)
```
**Другие методы**:
Обычно вам не нужно переопределять следующие методы, хотя вам может понадобиться обращаться к ним, если вы пишете пользовательские представления, используя `GenericAPIView`.
* `get_serializer_context(self)` - Возвращает словарь, содержащий любой дополнительный контекст, который должен быть предоставлен сериализатору. По умолчанию включает ключи `'request'`, `'view'` и `'format'`.
* `get_serializer(self, instance=None, data=None, many=False, partial=False)` - Возвращает экземпляр сериализатора.
* `get_paginated_response(self, data)` - Возвращает объект `Response` в стиле paginated.
* `paginate_queryset(self, queryset)` - Пагинация набора запросов, если требуется, возвращает либо объект страницы, либо `None`, если пагинация не настроена для этого представления.
* `filter_queryset(self, queryset)` - Получив набор запросов, отфильтровать его с помощью используемых бэкендов фильтрации, возвращая новый набор запросов.
---
# Миксины
Mixin-классы предоставляют действия, которые используются для обеспечения базового поведения представления. Обратите внимание, что mixin-классы предоставляют методы действий, а не определяют методы обработчиков, такие как `.get()` и `.post()`, напрямую. Это позволяет более гибко компоновать поведение.
Mixin-классы могут быть импортированы из `rest_framework.mixins`.
## ListModelMixin
Предоставляет метод `.list(request, *args, **kwargs)`, который реализует перечисление набора запросов.
Если набор запросов заполнен, возвращается ответ `200 OK` с сериализованным представлением набора запросов в качестве тела ответа. По желанию данные ответа могут быть постраничными.
## CreateModelMixin
Предоставляет метод `.create(request, *args, **kwargs)`, который реализует создание и сохранение нового экземпляра модели.
Если объект создан, возвращается ответ `201 Created` с сериализованным представлением объекта в качестве тела ответа. Если представление содержит ключ с именем `url`, то заголовок `Location` ответа будет заполнен этим значением.
Если данные запроса, предоставленные для создания объекта, были недействительными, будет возвращен ответ `400 Bad Request`, а в теле ответа будет содержаться информация об ошибке.
## RetrieveModelMixin
Предоставляет метод `.retrieve(request, *args, **kwargs)`, который реализует возврат существующего экземпляра модели в ответ.
Если объект может быть получен, то возвращается ответ `200 OK` с сериализованным представлением объекта в качестве тела ответа. В противном случае будет возвращен ответ `404 Not Found`.
## UpdateModelMixin
Предоставляет метод `.update(request, *args, **kwargs)`, который реализует обновление и сохранение существующего экземпляра модели.
Также предоставляет метод `.partial_update(request, *args, **kwargs)`, который похож на метод `update`, за исключением того, что все поля для обновления будут необязательными. Это позволяет поддерживать HTTP-запросы `PATCH`.
Если объект обновлен, возвращается ответ `200 OK` с сериализованным представлением объекта в качестве тела ответа.
Если данные запроса, предоставленные для обновления объекта, были недействительными, будет возвращен ответ `400 Bad Request`, в теле которого будет содержаться информация об ошибке.
## DestroyModelMixin
Предоставляет метод `.destroy(request, *args, **kwargs)`, который реализует удаление существующего экземпляра модели.
Если объект удален, возвращается ответ `204 No Content`, в противном случае возвращается ответ `404 Not Found`.
---
# Классы специфичных представлений
Следующие классы являются специфичными общими представлениями. Если вы используете общие представления, то обычно вы работаете именно на этом уровне, если только вам не нужно сильно измененное поведение.
Классы представления могут быть импортированы из `rest_framework.generics`.
## CreateAPIView
Используется только для **создания** конечных точек.
Предоставляет обработчик метода `post`.
Расширяет: [GenericAPIView](#genericapiview), [CreateModelMixin](#createmodelmixin)
## ListAPIView
Используется для конечных точек **только для чтения** для представления **коллекции экземпляров модели**.
Предоставляет обработчик метода `get`.
Расширяет: [GenericAPIView](#genericapiview), [ListModelMixin](#listmodelmixin)
## RetrieveAPIView
Используется для конечных точек **только для чтения** для представления **одного экземпляра модели**.
Предоставляет обработчик метода `get`.
Расширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin)
## DestroyAPIView
Используется для **только для удаления** конечных точек для **одного экземпляра модели**.
Предоставляет обработчик метода `delete`.
Расширяет: [GenericAPIView](#genericapiview), [DestroyModelMixin](#destroymodelmixin)
## UpdateAPIView
Используется для **только для обновления** конечных точек для **одного экземпляра модели**.
Предоставляет обработчики методов `put` и `patch`.
Расширяет: [GenericAPIView](#genericapiview), [UpdateModelMixin](#updatemodelmixin)
## ListCreateAPIView
Используется для конечных точек **чтения-записи** для представления **коллекции экземпляров модели**.
Предоставляет обработчики методов `get` и `post`.
Расширяет: [GenericAPIView](#genericapiview), [ListModelMixin](#listmodelmixin), [CreateModelMixin](#createmodelmixin)
## RetrieveUpdateAPIView
Используется для **чтения или обновления** конечных точек для представления **одного экземпляра модели**.
Предоставляет обработчики методов `get`, `put` и `patch`.
Расширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin), [UpdateModelMixin](#updatemodelmixin)
## RetrieveDestroyAPIView
Используется для конечных точек **чтения или удаления** для представления **одного экземпляра модели**.
Предоставляет обработчики методов `get` и `delete`.
Расширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin), [DestroyModelMixin](#destroymodelmixin)
## RetrieveUpdateDestroyAPIView
Используется для конечных точек **чтение-запись-удаление** для представления **одного экземпляра модели**.
Предоставляет обработчики методов `get`, `put`, `patch` и `delete`.
Расширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin), [UpdateModelMixin](#updatemodelmixin), [DestroyModelMixin](#destroymodelmixin)
---
# Настройка общих представлений
Часто вы хотите использовать существующие типовые представления, но использовать несколько измененное поведение. Если вы столкнулись с повторным использованием некоторого настроенного поведения в нескольких местах, вы можете захотеть отрефакторить это поведение в общий класс, который затем можно просто применить к любому представлению или набору представлений по мере необходимости.
## Создание пользовательских миксинов
Например, если вам нужно искать объекты на основе нескольких полей в URL conf, вы можете создать mixin-класс, подобный следующему:
```python
class MultipleFieldLookupMixin:
"""
Apply this mixin to any view or viewset to get multiple field filtering
based on a `lookup_fields` attribute, instead of the default single field filtering.
"""
def get_object(self):
queryset = self.get_queryset() # Get the base queryset
queryset = self.filter_queryset(queryset) # Apply any filter backends
filter = {}
for field in self.lookup_fields:
if self.kwargs.get(field): # Ignore empty fields.
filter[field] = self.kwargs[field]
obj = get_object_or_404(queryset, **filter) # Lookup the object
self.check_object_permissions(self.request, obj)
return obj
```
Затем вы можете просто применить этот миксин к представлению или набору представлений в любое время, когда вам нужно применить пользовательское поведение.
```python
class RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
lookup_fields = ['account', 'username']
```
Использование пользовательских миксинов - хороший вариант, если у вас есть пользовательское поведение, которое необходимо использовать.
## Создание пользовательских базовых классов
Если вы используете миксин в нескольких представлениях, вы можете пойти дальше и создать свой собственный набор базовых представлений, которые затем можно использовать во всем проекте. Например:
```python
class BaseRetrieveView(MultipleFieldLookupMixin,
generics.RetrieveAPIView):
pass
class BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,
generics.RetrieveUpdateDestroyAPIView):
pass
```
Использование пользовательских базовых классов является хорошим вариантом, если у вас есть пользовательское поведение, которое последовательно должно повторяться в большом количестве представлений в вашем проекте.
---
# PUT как создание
До версии 3.0 миксины DRF рассматривали `PUT` как операцию обновления или создания, в зависимости от того, существовал ли уже объект или нет.
Разрешение `PUT` в качестве операций создания является проблематичным, поскольку оно обязательно раскрывает информацию о существовании или несуществовании объектов. Также не очевидно, что прозрачное разрешение повторного создания ранее удаленных экземпляров обязательно является лучшим поведением по умолчанию, чем простое возвращение ответов `404`.
Оба стиля "`PUT` как 404" и "`PUT` как создание" могут быть действительны в различных обстоятельствах, но начиная с версии 3.0 мы теперь используем поведение 404 по умолчанию, поскольку оно проще и очевиднее.
---
# Пакеты сторонних производителей
Следующие пакеты сторонних производителей предоставляют дополнительные реализации общих представлений.
## Django Rest Multiple Models
[Django Rest Multiple Models](https://github.com/MattBroach/DjangoRestMultipleModels) предоставляет общее представление (и миксин) для отправки нескольких сериализованных моделей и/или наборов запросов через один запрос API.
================================================
FILE: api-guide/metadata.md
================================================
<!-- TRANSLATED by md-translate -->
# Метаданные
> [Метод `OPTIONS`] позволяет клиенту определить опции и/или требования, связанные с ресурсом, или возможности сервера, не подразумевая действия с ресурсом и не инициируя поиск ресурса.
>
> - [RFC7231, раздел 4.3.7.](https://tools.ietf.org/html/rfc7231#section-4.3.7)
DRF включает настраиваемый механизм для определения того, как ваш API должен отвечать на запросы `OPTIONS`. Это позволяет вам возвращать схему API или другую информацию о ресурсе.
В настоящее время не существует широко принятых соглашений о том, какой именно стиль ответа должен быть возвращен для HTTP `OPTIONS` запросов, поэтому мы предоставляем специальный стиль, который возвращает некоторую полезную информацию.
Вот пример ответа, который демонстрирует информацию, возвращаемую по умолчанию.
```http
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
{
"name": "To Do List",
"description": "List existing 'To Do' items, or create a new item.",
"renders": [
"application/json",
"text/html"
],
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"actions": {
"POST": {
"note": {
"type": "string",
"required": false,
"read_only": false,
"label": "title",
"max_length": 100
}
}
}
}
```
## Установка схемы метаданных
Вы можете установить класс метаданных глобально, используя ключ настройки `'DEFAULT_METADATA_CLASS'`:
```python
REST_FRAMEWORK = {
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'
}
```
Или вы можете установить класс метаданных индивидуально для представления:
```python
class APIRoot(APIView):
metadata_class = APIRootMetadata
def get(self, request, format=None):
return Response({
...
})
```
Пакет DRF включает только одну реализацию класса метаданных, названную `SimpleMetadata`. Если вы хотите использовать альтернативный стиль, вам нужно будет реализовать собственный класс метаданных.
## Создание конечных точек схемы
Если у вас есть особые требования к созданию конечных точек схемы, доступ к которым осуществляется с помощью обычных запросов `GET`, вы можете рассмотреть возможность повторного использования API метаданных для этого.
Например, следующий дополнительный маршрут может быть использован в наборе представлений для обеспечения конечной точки схемы со ссылкой.
```python
@action(methods=['GET'], detail=False)
def api_schema(self, request):
meta = self.metadata_class()
data = meta.determine_metadata(request, self)
return Response(data)
```
Есть несколько причин, по которым вы можете выбрать такой подход, включая то, что ответы `OPTIONS` [не подлежат кэшированию](https://www.mnot.net/blog/2012/10/29/NO_OPTIONS).
---
# Пользовательские классы метаданных
Если вы хотите предоставить собственный класс метаданных, вам следует переопределить `BaseMetadata` и реализовать метод `determine_metadata(self, request, view)`.
Полезные вещи, которые вы, возможно, захотите сделать, могут включать возврат информации о схеме, используя такой формат, как [JSON schema](https://json-schema.org/), или возврат отладочной информации для пользователей-администраторов.
## Пример
Следующий класс может быть использован для ограничения информации, возвращаемой на запросы `OPTIONS`.
```python
class MinimalMetadata(BaseMetadata):
"""
Don't include field and other information for `OPTIONS` requests.
Just return the name and description.
"""
def determine_metadata(self, request, view):
return {
'name': view.get_view_name(),
'description': view.get_view_description()
}
```
Затем настройте свои параметры для использования этого пользовательского класса:
```python
REST_FRAMEWORK = {
'DEFAULT_METADATA_CLASS': 'myproject.apps.core.MinimalMetadata'
}
```
# Пакеты сторонних производителей
Следующие пакеты сторонних производителей предоставляют дополнительные реализации метаданных.
## DRF-schema-adapter
[drf-schema-adapter](https://github.com/drf-forms/drf-schema-adapter) - это набор инструментов, облегчающих предоставление информации о схемах фронтенд-фреймворкам и библиотекам. Он предоставляет миксин метаданных, а также 2 класса метаданных и несколько адаптеров, подходящих для генерации [json-schema](https://json-schema.org/), а также информации о схемах, читаемой различными библиотеками.
Вы также можете написать свой собственный адаптер для работы с вашим конкретным фронтендом. Если вы захотите это сделать, он также предоставляет экспортер, который может экспортировать информацию о схеме в json-файлы.
================================================
FILE: api-guide/pagination.md
================================================
<!-- TRANSLATED by md-translate -->
---
источник:
- pagination.py
---
# Пагинация
> Django предоставляет несколько классов, которые помогают вам управлять постраничными данными - то есть данными, разделенными на несколько страниц, со ссылками "Предыдущая/Следующая".
>
> — [Django documentation](https://docs.djangoproject.com/en/stable/topics/pagination/)
DRF включает поддержку настраиваемых стилей пагинации. Это позволяет изменять, как большие наборы результатов разбиваются на отдельные страницы данных.
API пагинации может поддерживать любую из этих функций:
* Ссылки пагинации, которые предоставляются как часть содержимого ответа.
* Ссылки пагинации, включенные в заголовки ответа, такие как `Content-Range` или `Link`.
В настоящее время все встроенные стили используют ссылки, включенные как часть содержимого ответа. Этот стиль более доступен при использовании API с возможностью просмотра.
Пагинация выполняется автоматически, только если вы используете общие представления или наборы представлений. Если вы используете обычное `APIView`, вам нужно будет самостоятельно обратиться к API пагинации, чтобы убедиться, что вы возвращаете ответ с пагинацией. Пример смотрите в исходном коде классов `mixins.ListModelMixin` и `generics.GenericAPIView`.
Пагинацию можно отключить, установив для класса пагинации значение `None`.
## Установка стиля пагинации
Стиль пагинации можно задать глобально, используя ключи настройки `DEFAULT_PAGINATION_CLASS` и `PAGE_SIZE`. Например, чтобы использовать встроенную пагинацию с ограничением/смещением, вы должны сделать следующее:
```python
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
```
Обратите внимание, что необходимо задать как класс пагинации, так и размер страницы, которая будет использоваться. По умолчанию и `DEFAULT_PAGINATION_CLASS`, и `PAGE_SIZE` имеют значение `None`.
Вы также можете установить класс пагинации для отдельного представления с помощью атрибута `pagination_class`. Обычно вы хотите использовать один и тот же стиль пагинации во всем API, хотя вы можете захотеть варьировать отдельные аспекты пагинации, такие как размер страницы по умолчанию или максимальный размер страницы, на основе каждого представления.
## Изменение стиля пагинации
Если вы хотите изменить определенные аспекты стиля пагинации, вам нужно переопределить один из классов пагинации и установить атрибуты, которые вы хотите изменить.
```python
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'page_size'
max_page_size = 10000
class StandardResultsSetPagination(PageNumberPagination):
page_size = 100
page_size_query_param = 'page_size'
max_page_size = 1000
```
Затем вы можете применить ваш новый стиль к представлению с помощью атрибута `pagination_class`:
```python
class BillingRecordsView(generics.ListAPIView):
queryset = Billing.objects.all()
serializer_class = BillingRecordsSerializer
pagination_class = LargeResultsSetPagination
```
Или примените стиль глобально, используя ключ настройки `DEFAULT_PAGINATION_CLASS`. Например:
```python
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}
```
---
# Описание API
## PageNumberPagination
Этот стиль пагинации принимает номер страницы с одним номером в параметрах запроса.
**Запрос**:
```http
GET https://api.example.org/accounts/?page=4
```
**Ответ**:
```http
HTTP 200 OK
{
"count": 1023,
"next": "https://api.example.org/accounts/?page=5",
"previous": "https://api.example.org/accounts/?page=3",
"results": [
…
]
}
```
#### Настройка
Чтобы включить стиль `PageNumberPagination` глобально, используйте следующую конфигурацию и установите `PAGE_SIZE` по желанию:
```python
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100
}
```
В подклассах `GenericAPIView` вы также можете установить атрибут `pagination_class` для выбора `PageNumberPagination` на основе каждого вида.
#### Конфигурация
Класс `PageNumberPagination` включает ряд атрибутов, которые могут быть переопределены для изменения стиля пагинации.
Чтобы установить эти атрибуты, необходимо переопределить класс `PageNumberPagination`, а затем включить свой собственный класс пагинации, как указано выше.
* `django_paginator_class` - Класс Django Paginator, который будет использоваться. По умолчанию это `django.core.paginator.Paginator`, что должно быть хорошо для большинства случаев использования.
* `page_size` - Числовое значение, указывающее размер страницы. Если установлено, оно отменяет настройку `PAGE_SIZE`. По умолчанию имеет то же значение, что и ключ настройки `PAGE_SIZE`.
* `page_query_param` - Строковое значение, указывающее имя параметра запроса, который будет использоваться для управления пагинацией.
* `page_size_query_param` - Если установлено, это строковое значение, указывающее имя параметра запроса, который позволяет клиенту устанавливать размер страницы на основе каждого запроса. По умолчанию `None`, что означает, что клиент не может контролировать размер запрашиваемой страницы.
* `max_page_size` - Если установлено, это числовое значение, указывающее на максимально допустимый размер запрашиваемой страницы. Этот атрибут действителен, только если `page_size_query_param` также установлен.
* `last_page_strings` - Список или кортеж строковых значений, указывающих на значения, которые могут быть использованы с `page_query_param` для запроса последней страницы в наборе. По умолчанию `('last',)`. Например, используйте `?page=last` для запроса сразу последней страницы.
* `template` - Имя шаблона для использования при отображении элементов управления пагинацией в Web-интерфейсе API. Может быть переопределено для изменения стиля рендеринга или установлено в `None` для полного отключения HTML элементов управления пагинацией. По умолчанию используется `"rest_framework/pagination/numbers.html"`.
---
## LimitOffsetPagination
Этот стиль пагинации повторяет синтаксис, используемый при поиске нескольких записей в базе данных. Клиент включает в себя как `'limit'`, так и параметр запроса `'offset'`. Лимит указывает на максимальное количество возвращаемых элементов и эквивалентен `page_size` в других стилях. Смещение указывает начальную позицию запроса по отношению к полному набору непагинированных элементов.
**Запрос**:
```http
GET https://api.example.org/accounts/?limit=100&offset=400
```
**Ответ**:
```http
HTTP 200 OK
{
"count": 1023,
"next": "https://api.example.org/accounts/?limit=100&offset=500",
"previous": "https://api.example.org/accounts/?limit=100&offset=300",
"results": [
…
]
}
```
#### Подключение
Чтобы включить стиль `LimitOffsetPagination` глобально, используйте следующую конфигурацию:
```python
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
}
```
По желанию вы также можете задать ключ `PAGE_SIZE`. Если параметр `PAGE_SIZE` также используется, то параметр запроса `limit` будет необязательным и может быть опущен клиентом.
В подклассах `GenericAPIView` вы также можете установить атрибут `pagination_class` для выбора `LimitOffsetPagination` для каждого представления.
#### Настройка
Класс `LimitOffsetPagination` включает ряд атрибутов, которые могут быть переопределены для изменения стиля пагинации.
Чтобы установить эти атрибуты, вы должны переопределить класс `LimitOffsetPagination`, а затем включить свой собственный класс пагинации, как указано выше.
* `default_limit` - Числовое значение, указывающее предел, который следует использовать, если он не указан клиентом в параметре запроса. По умолчанию имеет то же значение, что и ключ настройки `PAGE_SIZE`.
* `limit_query_param` - Строковое значение, указывающее имя параметра запроса "limit". По умолчанию имеет значение `'limit'`.
* `offset_query_param` - Строковое значение, указывающее имя параметра запроса "offset". По умолчанию имеет значение `'offset'`.
* `max_limit` - Если установлено, то это числовое значение, указывающее на максимально допустимый лимит, который может быть запрошен клиентом. По умолчанию `None`.
* `template` - Имя шаблона, который будет использоваться при отображении элементов управления пагинацией в Web-интерфейсе API. Может быть переопределено для изменения стиля рендеринга или установлено в `None` для полного отключения HTML элементов управления пагинацией. По умолчанию используется `"rest_framework/pagination/numbers.html"`.
---
## CursorPagination
Пагинация на основе курсора представляет непрозрачный индикатор `cursor`, который клиент может использовать для просмотра набора результатов. Этот стиль пагинации представляет только элементы управления перемоткой вперед и назад и не позволяет клиенту переходить к произвольным позициям.
Пагинация на основе курсора требует наличия уникального, неизменного порядка следования элементов в наборе результатов. Обычно таким упорядочиванием может быть временная метка создания записей, так как она представляет собой последовательный порядок для постраничного просмотра.
Пагинация на основе курсора является более сложной, чем другие схемы. Она также требует, чтобы набор результатов представлял фиксированный порядок, и не позволяет клиенту произвольно индексировать набор результатов. Однако она обеспечивает следующие преимущества:
* Обеспечивает последовательное представление пагинации. При правильном использовании `CursorPagination` гарантирует, что клиент никогда не увидит один и тот же элемент дважды при листании записей, даже если новые элементы вставляются другими клиентами во время процесса пагинации.
* Поддержка использования с очень большими наборами данных. При работе с очень большими наборами данных пагинация с использованием стилей пагинации на основе смещения может стать неэффективной или непригодной для использования. Вместо этого схемы пагинации на основе курсора имеют свойства фиксированного времени и не замедляются при увеличении размера набора данных.
#### Подробности и ограничения
Правильное использование пагинации на основе курсора требует некоторого внимания к деталям. Вам нужно подумать о том, в каком порядке вы хотите применять схему. По умолчанию используется порядок по `"-created"`. Это предполагает, что **в экземплярах модели должно быть поле временной метки "created"**, и будет представлено постраничное представление в стиле "временной шкалы", где первыми будут самые последние добавленные элементы.
Вы можете изменить порядок, переопределив атрибут `'ordering'` класса пагинации, или используя класс фильтра `OrderingFilter` вместе с `CursorPagination`. При использовании `OrderingFilter` следует тщательно продумать ограничение полей, по которым пользователь может делать заказ.
Правильное использование пагинации курсора должно иметь поле упорядочивания, которое удовлетворяет следующим требованиям:
* Должно быть неизменным значением, таким как временная метка, slug или другое поле, которое устанавливается только один раз, при создании.
* Должно быть уникальным или почти уникальным. Хорошим примером являются временные метки с точностью до миллисекунды. Эта реализация пагинации курсора использует интеллектуальный стиль "позиция плюс смещение", что позволяет ей правильно поддерживать не строго уникальные значения в качестве упорядочивания.
* Должно быть не нулевым значением, которое можно принудительно преобразовать в строку.
* Не должно быть плавающей точкой. Ошибки точности легко приводят к неправильным результатам.
**Совет**: используйте вместо этого десятичные числа. (Если у вас уже есть поле с плавающей запятой и вам нужно сделать постраничную запись по нему, можно воспользоваться командой [пример подкласса `CursorPagination`, который использует десятичные числа для ограничения точности, доступен здесь] (https://gist.github.com/keturn/8bc88525a183fd41c73ffb729b8865be#file-fpcursorpagination-py).)
* Поле должно иметь индекс базы данных.
Использование поля упорядочивания, которое не удовлетворяет этим ограничениям, как правило, будет работать, но вы потеряете некоторые преимущества пагинации курсора.
Для получения более подробной технической информации о реализации, которую мы используем для пагинации курсоров, в статье ["Building cursors for the Disqus API"](https://cra.mr/2011/03/08/building-cursors-for-the-disqus-api) блога дается хороший обзор основного подхода.
#### Подключение
Чтобы включить стиль `CursorPagination` глобально, используйте следующую конфигурацию, изменяя `PAGE_SIZE` по желанию:
```python
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 100
}
```
В подклассах `GenericAPIView` вы также можете установить атрибут `pagination_class` для выбора `CursorPagination` на основе каждого вида.
#### Настройка
Класс `CursorPagination` включает ряд атрибутов, которые могут быть переопределены для изменения стиля пагинации.
Чтобы установить эти атрибуты, необходимо переопределить класс `CursorPagination`, а затем включить свой собственный класс пагинации, как указано выше.
* `page_size` - числовое значение, указывающее размер страницы. Если установлено, оно отменяет настройку `PAGE_SIZE`. По умолчанию имеет то же значение, что и ключ настройки `PAGE_SIZE`.
* `cursor_query_param` - Строковое значение, указывающее имя параметра запроса "cursor". По умолчанию `'cursor'`.
* `orderering` - Это должна быть строка или список строк, указывающих на поле, к которому будет применяться пагинация на основе курсора. Например: `ordering = 'slug'`. По умолчанию используется `-created`. Это значение также может быть переопределено с помощью `OrderingFilter` в представлении.
* `template` - Имя шаблона, который будет использоваться при отображении элементов управления пагинацией в API просмотра. Может быть переопределено для изменения стиля рендеринга или установлено в `None` для полного отключения HTML элементов управления пагинацией. По умолчанию используется `"rest_framework/pagination/previous_and_next.html"`.
---
# Пользовательские стили пагинации
Чтобы создать собственный класс сериализатора пагинации, необходимо унаследовать подкласс `pagination.BasePagination`, переопределить методы `paginate_queryset(self, queryset, request, view=None)` и `get_paginated_response(self, data)`:
* Метод `paginate_queryset` передается начальному кверисету и должен возвращать итерируемый объект. Этот объект содержит только данные запрашиваемой страницы.
* Метод `get_paginated_response` передается сериализованным данным страницы и должен возвращать экземпляр `Response`.
Обратите внимание, что метод `paginate_queryset` может установить состояние экземпляра пагинации, которое впоследствии может быть использовано методом `get_paginated_response`.
## Пример
Предположим, мы хотим заменить стандартный стиль вывода пагинации на модифицированный формат, котор
gitextract_s0pt2_8x/
├── .files_cache.json
├── .github/
│ └── workflows/
│ ├── check_all_temp_data_cleared.yml
│ └── check_source_for_changes.yml
├── .gitignore
├── .scripts/
│ ├── commit_changes.py
│ └── monitor_repository.py
├── README.md
├── SUMMARY.md
├── api-guide/
│ ├── authentication.md
│ ├── caching.md
│ ├── content-negotiation.md
│ ├── exceptions.md
│ ├── fields.md
│ ├── filtering.md
│ ├── format-suffixes.md
│ ├── generic-views.md
│ ├── metadata.md
│ ├── pagination.md
│ ├── parsers.md
│ ├── permissions.md
│ ├── relations.md
│ ├── renderers.md
│ ├── requests.md
│ ├── responses.md
│ ├── reverse.md
│ ├── routers.md
│ ├── schemas.md
│ ├── serializers.md
│ ├── settings.md
│ ├── status-codes.md
│ ├── testing.md
│ ├── throttling.md
│ ├── validators.md
│ ├── versioning.md
│ ├── views.md
│ └── viewsets.md
├── monitoring_config.toml
├── quickstart.md
├── topics/
│ ├── ajax-csrf-cors.md
│ ├── browsable-api.md
│ ├── browser-enhancements.md
│ ├── documenting-your-api.md
│ ├── html-and-forms.md
│ ├── internationalization.md
│ ├── rest-hypermedia-hateoas.md
│ └── writable-nested-serializers.md
├── topics.md
└── tutorial/
├── 1-serialization.md
├── 2-requests-and-responses.md
├── 3-class-based-views.md
├── 4-authentication-and-permissions.md
├── 5-relationships-and-hyperlinked-apis.md
└── 6-viewsets-and-routers.md
SYMBOL INDEX (30 symbols across 2 files)
FILE: .scripts/commit_changes.py
class FileStatus (line 14) | class FileStatus:
method is_modified (line 19) | def is_modified(self) -> bool:
method is_deleted (line 23) | def is_deleted(self) -> bool:
method is_in_reference (line 27) | def is_in_reference(self) -> bool:
class GitChanges (line 32) | class GitChanges:
function run_command (line 38) | def run_command(command: List[str], cwd: Path | None = None) -> str:
function get_git_status (line 52) | def get_git_status() -> str:
function parse_status_line (line 57) | def parse_status_line(line: str) -> FileStatus | None:
function get_changes_to_commit (line 74) | def get_changes_to_commit() -> GitChanges:
function commit_file_pair (line 130) | def commit_file_pair(modified_file: Path, reference_file: Path, dry_run:...
function commit_skipped_reference_file (line 151) | def commit_skipped_reference_file(reference_file: Path, dry_run: bool = ...
function main (line 169) | def main() -> None:
FILE: .scripts/monitor_repository.py
class Config (line 17) | class Config:
method from_config_file (line 25) | def from_config_file(cls, config_file_path: Path) -> 'Config':
class CacheData (line 41) | class CacheData:
function read_files_json (line 46) | def read_files_json(path: Path) -> dict[str, CacheData]:
function write_files_json (line 56) | def write_files_json(path: Path, data: dict[str, CacheData]) -> None:
class FileInfo (line 73) | class FileInfo:
method __hash__ (line 79) | def __hash__(self):
method file_hash (line 83) | def file_hash(self) -> str:
method commit_hash (line 87) | def commit_hash(self) -> str:
method get_file_diff (line 98) | def get_file_diff(
method copy (line 114) | def copy(self) -> None:
class GitRepository (line 119) | class GitRepository:
method __init__ (line 120) | def __init__(self, config: 'Config', path: Path) -> None:
method get (line 125) | def get(cls, config: 'Config', temp_dir: Path | None) -> 'GitRepository':
method get_files_info (line 175) | def get_files_info(self) -> list[FileInfo]:
method delete (line 197) | def delete(self) -> None:
method delete_folder (line 202) | def delete_folder(self, path: Path) -> None:
function process_repository (line 211) | def process_repository(
Condensed preview — 53 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (909K chars).
[
{
"path": ".files_cache.json",
"chars": 7230,
"preview": "{\n \".reference/README.md\": {\n \"commit_hash\": \"18c5883be8ad3c5c5c36a5e39855d79ac80de7ca\",\n \"file_hash\": \"3040917d5"
},
{
"path": ".github/workflows/check_all_temp_data_cleared.yml",
"chars": 565,
"preview": "name: Check all temp data cleared\non:\n pull_request:\n branches:\n - master\n\njobs:\n check_no_temp_files_left:\n "
},
{
"path": ".github/workflows/check_source_for_changes.yml",
"chars": 1873,
"preview": "name: Check source for changes\non:\n schedule:\n - cron: '0 0 * * 0'\n workflow_dispatch:\n inputs:\n dry_run:\n "
},
{
"path": ".gitignore",
"chars": 360,
"preview": ".idea\n\n# Node rules:\n## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n## D"
},
{
"path": ".scripts/commit_changes.py",
"chars": 7079,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport dataclasses\nimport logging\nimport subprocess\nfrom pathlib import Path\nfro"
},
{
"path": ".scripts/monitor_repository.py",
"chars": 10409,
"preview": "import argparse\nimport dataclasses\nimport hashlib\nimport json\nimport logging\nimport subprocess\nimport tempfile\nfrom func"
},
{
"path": "README.md",
"chars": 9266,
"preview": "<!-- TRANSLATED by md-translate -->\n# Django REST framework\n\n\n* [Быстрый старт](quickstart.md)\n * [Сериализация](tutoria"
},
{
"path": "api-guide/authentication.md",
"chars": 28498,
"preview": "<!-- TRANSLATED by md-translate -->\n# Аутентификация\n\n> Аутентификация должна быть подключаемой.\n>\n> - Джейкоб Каплан-Мо"
},
{
"path": "api-guide/caching.md",
"chars": 3391,
"preview": "<!-- TRANSLATED by md-translate -->\n# Кэширование\n\n> У одной женщины было очень острое сознание, но почти не было памяти"
},
{
"path": "api-guide/content-negotiation.md",
"chars": 4714,
"preview": "<!-- TRANSLATED by md-translate -->\n# Согласование контента\n\n> HTTP предусматривает несколько механизмов \"согласования к"
},
{
"path": "api-guide/exceptions.md",
"chars": 12159,
"preview": "<!-- TRANSLATED by md-translate -->\n# Исключения\n\n> Исключения... позволяют чисто организовать обработку ошибок в центра"
},
{
"path": "api-guide/fields.md",
"chars": 45929,
"preview": "<!-- TRANSLATED by md-translate -->\n# Поля сериализатора\n\n> Каждое поле в классе Form отвечает не только за проверку дан"
},
{
"path": "api-guide/filtering.md",
"chars": 18645,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- filters.py\n\n---\n\n# Фильтрация\n\n> Корневой QuerySet, предоставляемый"
},
{
"path": "api-guide/format-suffixes.md",
"chars": 4179,
"preview": "<!-- TRANSLATED by md-translate -->\n# Cуффиксы формата\n\n> В разделе 6.2.1 не говорится, что согласование содержания долж"
},
{
"path": "api-guide/generic-views.md",
"chars": 21323,
"preview": "<!-- TRANSLATED by md-translate -->\n# Общие представления\n\n> Общие представления в Django... были разработаны как кратча"
},
{
"path": "api-guide/metadata.md",
"chars": 4781,
"preview": "<!-- TRANSLATED by md-translate -->\n# Метаданные\n\n> [Метод `OPTIONS`] позволяет клиенту определить опции и/или требовани"
},
{
"path": "api-guide/pagination.md",
"chars": 19358,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- pagination.py\n\n---\n\n# Пагинация\n\n> Django предоставляет несколько к"
},
{
"path": "api-guide/parsers.md",
"chars": 9030,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- parsers.py\n\n---\n\n# Парсеры\n\n> Взаимодействующие с машинами веб-серв"
},
{
"path": "api-guide/permissions.md",
"chars": 22950,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- permissions.py\n\n---\n\n# Разрешения\n\n> Аутентификация или идентификац"
},
{
"path": "api-guide/relations.md",
"chars": 27836,
"preview": "<!-- TRANSLATED by md-translate -->\n# Отношения сериализаторов\n\n> Структуры данных, а не алгоритмы, занимают центральное"
},
{
"path": "api-guide/renderers.md",
"chars": 24637,
"preview": "<!-- TRANSLATED by md-translate -->\n# Рендереры\n\n> Прежде чем экземпляр `TemplateResponse` будет возвращен клиенту, он д"
},
{
"path": "api-guide/requests.md",
"chars": 6801,
"preview": "<!-- TRANSLATED by md-translate -->\n# Запросы\n\n> Если вы занимаетесь веб-сервисами на основе REST... вам следует игнорир"
},
{
"path": "api-guide/responses.md",
"chars": 4825,
"preview": "<!-- TRANSLATED by md-translate -->\n# Ответы\n\n> В отличие от базовых объектов HttpResponse, объекты TemplateResponse сох"
},
{
"path": "api-guide/reverse.md",
"chars": 2494,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- reverse.py\n\n---\n\n# Возвращение URL-адресов\n\n> Центральной особеннос"
},
{
"path": "api-guide/routers.md",
"chars": 19071,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- routers.py\n\n---\n\n# Маршрутизаторы\n\n> Маршрутизация ресурсов позволя"
},
{
"path": "api-guide/schemas.md",
"chars": 16851,
"preview": "<!-- TRANSLATED by md-translate -->\n# Schemas\n\n> Машиночитаемая \\[схема\\] описывает, какие ресурсы доступны через API, к"
},
{
"path": "api-guide/serializers.md",
"chars": 59339,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- serializers.py\n\n---\n\n# Сериализаторы\n\n> Расширение полезности сериа"
},
{
"path": "api-guide/settings.md",
"chars": 18204,
"preview": "<!-- TRANSLATED by md-translate -->\n# Настройки\n\n> Пространства имен - это отличная идея - давайте делать их больше!\n>\n>"
},
{
"path": "api-guide/status-codes.md",
"chars": 4430,
"preview": "<!-- TRANSLATED by md-translate -->\n# Коды состояния\n\n> 418 Я чайник - Любая попытка сварить кофе с помощью чайника долж"
},
{
"path": "api-guide/testing.md",
"chars": 18431,
"preview": "<!-- TRANSLATED by md-translate -->\n# Тестирование\n\n> Код без тестов сломан по умолчанию.\n>\n> - [Джейкоб Каплан-Мосс](ht"
},
{
"path": "api-guide/throttling.md",
"chars": 11715,
"preview": "<!-- TRANSLATED by md-translate -->\n# Дросселирование\n\n> HTTP/1.1 420 Повышение спокойствия\n>\n> [Twitter API ограничение"
},
{
"path": "api-guide/validators.md",
"chars": 14851,
"preview": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n - validators.py\n\n---\n\n# Валидаторы\n\n> Валидаторы могут быть полез"
},
{
"path": "api-guide/versioning.md",
"chars": 10625,
"preview": "<!-- TRANSLATED by md-translate -->\n# Версионирование\n\n> Версионирование интерфейса - это просто \"вежливый\" способ убить"
},
{
"path": "api-guide/views.md",
"chars": 9140,
"preview": "<!-- TRANSLATED by md-translate -->\n# Представления, основанные на классах\n\n> Представления Django, основанные на класса"
},
{
"path": "api-guide/viewsets.md",
"chars": 15604,
"preview": "<!-- TRANSLATED by md-translate -->\n# ViewSets\n\n> После того, как маршрутизация определила, какой контроллер использоват"
},
{
"path": "monitoring_config.toml",
"chars": 461,
"preview": "[repository]\nrepo_url = 'https://github.com/encode/django-rest-framework.git'\nrepo_branch = 'main'\noriginal_link = 'http"
},
{
"path": "quickstart.md",
"chars": 7436,
"preview": "<!-- TRANSLATED by md-translate -->\n# Быстрый старт\n\nМы создадим простой API, который позволит администраторам просматри"
},
{
"path": "topics/ajax-csrf-cors.md",
"chars": 3129,
"preview": "<!-- TRANSLATED by md-translate -->\n# Работа с AJAX, CSRF и CORS\n\n> \"Внимательно изучите возможные уязвимости CSRF / XSR"
},
{
"path": "topics/browsable-api.md",
"chars": 10544,
"preview": "<!-- TRANSLATED by md-translate -->\n# Browsable API\n\n> Это глубоко ошибочный трюизм... что мы должны культивировать прив"
},
{
"path": "topics/browser-enhancements.md",
"chars": 4172,
"preview": "<!-- TRANSLATED by md-translate -->\n# Улучшения в браузере\n\n> \"Есть два не вызывающих споров варианта использования пере"
},
{
"path": "topics/documenting-your-api.md",
"chars": 9963,
"preview": "<!-- TRANSLATED by md-translate -->\n# Документирование вашего API\n\n> REST API должен тратить почти все свои усилия по оп"
},
{
"path": "topics/html-and-forms.md",
"chars": 9261,
"preview": "<!-- TRANSLATED by md-translate -->\n# HTML и формы\n\nDRF подходит для возврата как ответов в стиле API, так и обычных HTM"
},
{
"path": "topics/internationalization.md",
"chars": 5953,
"preview": "<!-- TRANSLATED by md-translate -->\n# Интернационализация\n\n> Поддержка интернационализации не является необязательной. О"
},
{
"path": "topics/rest-hypermedia-hateoas.md",
"chars": 3318,
"preview": "<!-- TRANSLATED by md-translate -->\n# REST, гипермедиа и HATEOAS\n\n> Вы продолжаете использовать это слово \"REST\". Я не д"
},
{
"path": "topics/writable-nested-serializers.md",
"chars": 1815,
"preview": "<!-- TRANSLATED by md-translate -->\n# Вложенные сериализаторы с возможностью записи\n\n> Для экономии HTTP-запросов может "
},
{
"path": "topics.md",
"chars": 480,
"preview": "# Статьи\n\n* [AJAX, CSRF & CORS](topics/ajax-csrf-cors.md)\n* [The Browsable API](topics/browsable-api.md)\n* [Улучшения в "
},
{
"path": "tutorial/1-serialization.md",
"chars": 16604,
"preview": "<!-- TRANSLATED by md-translate -->\n# Урок 1: Сериализация\n\n## Введение\n\nВ этом уроке мы рассмотрим создание простого We"
},
{
"path": "tutorial/2-requests-and-responses.md",
"chars": 8714,
"preview": "<!-- TRANSLATED by md-translate -->\n# Учебник 2: Запросы и ответы\n\nНачиная с этого момента мы действительно начнем освещ"
},
{
"path": "tutorial/3-class-based-views.md",
"chars": 6559,
"preview": "<!-- TRANSLATED by md-translate -->\n# Урок 3: Представления на основе классов\n\nМы также можем писать наши представления "
},
{
"path": "tutorial/4-authentication-and-permissions.md",
"chars": 11430,
"preview": "<!-- TRANSLATED by md-translate -->\n# Урок 4: Аутентификация и разрешения\n\nВ настоящее время наш API не имеет ограничени"
},
{
"path": "tutorial/5-relationships-and-hyperlinked-apis.md",
"chars": 9024,
"preview": "<!-- TRANSLATED by md-translate -->\n# Урок 5: Отношения и API с гиперссылками\n\nВ настоящее время отношения в нашем API п"
},
{
"path": "tutorial/6-viewsets-and-routers.md",
"chars": 7161,
"preview": "<!-- TRANSLATED by md-translate -->\n# Урок 6: Наборы представлений и маршрутизаторы\n\nDRF включает в себя абстракцию для "
}
]
About this extraction
This page contains the full source code of the ilyachch/django-rest-framework-rusdoc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 53 files (600.5 KB), approximately 146.7k tokens, and a symbol index with 30 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.