[
  {
    "path": ".files_cache.json",
    "content": "{\n  \".reference/README.md\": {\n    \"commit_hash\": \"18c5883be8ad3c5c5c36a5e39855d79ac80de7ca\",\n    \"file_hash\": \"3040917d581be433da899a2b37135150\"\n  },\n  \".reference/api-guide/authentication.md\": {\n    \"commit_hash\": \"d0a5d5e7cad7f1032b4d0a36cab1596076f705ad\",\n    \"file_hash\": \"18084ecacf507bd6fef2c23ba2a82a56\"\n  },\n  \".reference/api-guide/caching.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"51b470c630a4e77a032b0ed152e130cf\"\n  },\n  \".reference/api-guide/content-negotiation.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"553eb6d9511eef8f816a9e8b628f321b\"\n  },\n  \".reference/api-guide/exceptions.md\": {\n    \"commit_hash\": \"c0202a0aa5cbaf8573458b932878dfd5044c93ab\",\n    \"file_hash\": \"659b7ca76f8efccef817591fef9d13fd\"\n  },\n  \".reference/api-guide/fields.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"fd8aca55b6366ed2b616b41b2ecc80ff\"\n  },\n  \".reference/api-guide/filtering.md\": {\n    \"commit_hash\": \"a323cf7c0a33d7ffd395a6805019f613fb79f985\",\n    \"file_hash\": \"54dc859f64dc2c74e12f6b644732c066\"\n  },\n  \".reference/api-guide/format-suffixes.md\": {\n    \"commit_hash\": \"041b88f8bbb48d9688ebd5add294eee2dfc93d1c\",\n    \"file_hash\": \"60ea26c1f58e88b82b31b49e524316e6\"\n  },\n  \".reference/api-guide/generic-views.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"2245d2ff68c620c1b85ff7aebfa31e19\"\n  },\n  \".reference/api-guide/metadata.md\": {\n    \"commit_hash\": \"e045dc465270c18689dba4a970378cd9744e57b6\",\n    \"file_hash\": \"a0fac04282c4c1ecff4856ff2b9920cc\"\n  },\n  \".reference/api-guide/pagination.md\": {\n    \"commit_hash\": \"f74a44e850a685ac73c819ae7b96b0d68a8f734f\",\n    \"file_hash\": \"10ba91c105ba55ba25b6df2d69ba8da5\"\n  },\n  \".reference/api-guide/parsers.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"aabe99bc6bb768328673587bfbd496fb\"\n  },\n  \".reference/api-guide/permissions.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"44218348009641ebc15786966c82ec5c\"\n  },\n  \".reference/api-guide/relations.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"b0e775bb98155170eba917bc2d563110\"\n  },\n  \".reference/api-guide/renderers.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"1639f64a8b74fb7a41c1cb915c75bd20\"\n  },\n  \".reference/api-guide/requests.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"6128e6f6fa93cff0b4970fcf6c9fd351\"\n  },\n  \".reference/api-guide/responses.md\": {\n    \"commit_hash\": \"ade172e1d5db87dc86bc616cbb4df7ccd2eb2fd3\",\n    \"file_hash\": \"949dd98307af0923652f9b6538ae088f\"\n  },\n  \".reference/api-guide/reverse.md\": {\n    \"commit_hash\": \"332e9560ab0b3a1b8c0ab8f68e95c09bc2d8999f\",\n    \"file_hash\": \"df00e0304464472fda1e97c804e1c9f9\"\n  },\n  \".reference/api-guide/routers.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"76e6a45fc7886c10514fa8fd9fd304df\"\n  },\n  \".reference/api-guide/schemas.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"db37b906afd6550a53477ecdd266f2c6\"\n  },\n  \".reference/api-guide/serializers.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"06cc1fe1cabdb28f42b0762eaab8cbbb\"\n  },\n  \".reference/api-guide/settings.md\": {\n    \"commit_hash\": \"f9f10e041f9b2a2c936ee54a437d4c255f76e626\",\n    \"file_hash\": \"e1745bd2d62b873253d4e8f338569b4f\"\n  },\n  \".reference/api-guide/status-codes.md\": {\n    \"commit_hash\": \"3e052376ace520135d42dcf52f35a51b7dcc5ac2\",\n    \"file_hash\": \"2c87ce34b589a196dc98c77eb3490884\"\n  },\n  \".reference/api-guide/testing.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"e0120b9a199bd3825e703b71d2b5dae9\"\n  },\n  \".reference/api-guide/throttling.md\": {\n    \"commit_hash\": \"d0a5d5e7cad7f1032b4d0a36cab1596076f705ad\",\n    \"file_hash\": \"433ab273878e04287d91b9cf0eb803e6\"\n  },\n  \".reference/api-guide/validators.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"86e050e68e80d8328f03ea507425c50c\"\n  },\n  \".reference/api-guide/versioning.md\": {\n    \"commit_hash\": \"0d6589cf45940bb67ace74a06b2c5b053f1c31ef\",\n    \"file_hash\": \"ff8cfbbbe7d70ab6bd0eac5300989be1\"\n  },\n  \".reference/api-guide/views.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"e4ce6ac6dd7dbb59eb5f9e94e1f54a7a\"\n  },\n  \".reference/api-guide/viewsets.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"32a13e45a120c83ef706f908775e5888\"\n  },\n  \".reference/topics/ajax-csrf-cors.md\": {\n    \"commit_hash\": \"605cc4f7367f58002056453d9befd3c1918f6a38\",\n    \"file_hash\": \"6122ed56c57770bb7d15175a6f99a598\"\n  },\n  \".reference/topics/browsable-api.md\": {\n    \"commit_hash\": \"ade172e1d5db87dc86bc616cbb4df7ccd2eb2fd3\",\n    \"file_hash\": \"6e26d5f207a3298f7b186c00e8a159f6\"\n  },\n  \".reference/topics/browser-enhancements.md\": {\n    \"commit_hash\": \"f5470ab9e292c7321377ae1f43f85e311d94975f\",\n    \"file_hash\": \"8cfe1ff6085397161e067af7bd694a6e\"\n  },\n  \".reference/topics/documenting-your-api.md\": {\n    \"commit_hash\": \"e794e5e5e43d6838d9ffb8eb0a505b5f531b261f\",\n    \"file_hash\": \"85ce53b26dbb566993378b57614a2eb2\"\n  },\n  \".reference/topics/html-and-forms.md\": {\n    \"commit_hash\": \"9d4ed054bf8acfac6209b7e7f837fc97517affcc\",\n    \"file_hash\": \"d74c3a05790e8a201b4a373d54f01267\"\n  },\n  \".reference/topics/internationalization.md\": {\n    \"commit_hash\": \"2ca3b7f9c449145156f0b0e2d9ea58a2295f8fbf\",\n    \"file_hash\": \"3df3355efbbdf65a01dcec2b3f0de844\"\n  },\n  \".reference/topics/rest-hypermedia-hateoas.md\": {\n    \"commit_hash\": \"ade172e1d5db87dc86bc616cbb4df7ccd2eb2fd3\",\n    \"file_hash\": \"2f55490daba3d605c7c75be9760a4929\"\n  },\n  \".reference/topics/writable-nested-serializers.md\": {\n    \"commit_hash\": \"f0dbf0a264677f2a53faab402ff49f442fc4383a\",\n    \"file_hash\": \"ae7df9cb54197166d97df23d51bdda26\"\n  },\n  \".reference/tutorial/1-serialization.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"87c06f99ba8316fa40da167990b20c86\"\n  },\n  \".reference/tutorial/2-requests-and-responses.md\": {\n    \"commit_hash\": \"c0f3649224117609d19e79c77242b525570d25c0\",\n    \"file_hash\": \"4c863e0a540e66f4034f378b23d2d4f9\"\n  },\n  \".reference/tutorial/3-class-based-views.md\": {\n    \"commit_hash\": \"c0f3649224117609d19e79c77242b525570d25c0\",\n    \"file_hash\": \"ed6ae64942ff177d051f7bbf90a08637\"\n  },\n  \".reference/tutorial/4-authentication-and-permissions.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"c3fc89a9fae949e7a27f84ef98865300\"\n  },\n  \".reference/tutorial/5-relationships-and-hyperlinked-apis.md\": {\n    \"commit_hash\": \"e221d9a1d6638b936707efc390adff59511a6605\",\n    \"file_hash\": \"0ca0fcc63848bbc4df6ad75739ff4077\"\n  },\n  \".reference/tutorial/6-viewsets-and-routers.md\": {\n    \"commit_hash\": \"c0f3649224117609d19e79c77242b525570d25c0\",\n    \"file_hash\": \"85f48f71db392be56df18888bb993530\"\n  },\n  \".reference/tutorial/quickstart.md\": {\n    \"commit_hash\": \"c0f3649224117609d19e79c77242b525570d25c0\",\n    \"file_hash\": \"0b31dc44cc6fb7e4a983149e96bdd175\"\n  }\n}"
  },
  {
    "path": ".github/workflows/check_all_temp_data_cleared.yml",
    "content": "name: Check all temp data cleared\non:\n  pull_request:\n    branches:\n      - master\n\njobs:\n  check_no_temp_files_left:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Check no temp files left\n        run: |\n          if [[ $(find .reference -type f -name \"*.temp\" -o -name \"*.tmp\" -o -name \"*.md\" | wc -l) -gt 0 ]]; then\n            exit 1\n          fi\n      - name: Check CHANGES.md cleared\n        run: |\n          if [[ $(find -name \"CHANGES.md\" | wc -l) -gt 0 ]]; then\n            exit 1\n          fi\n"
  },
  {
    "path": ".github/workflows/check_source_for_changes.yml",
    "content": "name: Check source for changes\non:\n  schedule:\n    - cron: '0 0 * * 0'\n  workflow_dispatch:\n    inputs:\n      dry_run:\n        description: 'Dry run'\n        required: false\n        default: false\n        type: boolean\n\npermissions:\n  contents: write\n  pull-requests: write\n\njobs:\n  check_source_for_changes:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '3.12'\n\n      - name: Check for changes\n        id: check\n        run: |\n          EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)\n          echo \"changes<<$EOF\" >> \"$GITHUB_OUTPUT\"\n          python3 ./.scripts/monitor_repository.py --details-file CHANGES.md >> \"$GITHUB_OUTPUT\"\n          echo \"$EOF\" >> \"$GITHUB_OUTPUT\"\n\n      - name: Report\n        env:\n          CHANGES: ${{ steps.check.outputs.changes }}\n        run: |\n          echo \"Changes:\"\n          echo \"$CHANGES\"\n\n      - name: Check if branch already exists\n        id: check_branch_exists\n        if: steps.check.outputs.changes != '' && !inputs.dry_run\n        run: |\n          git fetch\n          if git branch -r | grep -q \"origin/sync-with-original\"; then\n          echo\n            echo \"branch_exists=1\" >> $GITHUB_OUTPUT\n            exit 1\n          fi\n\n      - name: Open PR\n        if: steps.check.outputs.changes != '' && steps.check_branch_exists.outputs != '1' && !inputs.dry_run\n        uses: peter-evans/create-pull-request@v8\n        with:\n          title: Sync with original\n          body: ${{ steps.check.outputs.changes }}\n          labels: sync\n          branch: sync-with-original\n          branch-suffix: random\n          commit-message: Sync with original\n          delete-branch: true\n          assignees: ${{ github.repository_owner }}\n          draft: true\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n\n# Node rules:\n## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n## Dependency directory\n## Commenting this out is preferred by some people, see\n## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git\nnode_modules\n\n# Book build output\n_book\n\n# eBook build output\n*.epub\n*.mobi\n*.pdf\n"
  },
  {
    "path": ".scripts/commit_changes.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport dataclasses\nimport logging\nimport subprocess\nfrom pathlib import Path\nfrom typing import Dict, List, Tuple\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclasses.dataclass\nclass FileStatus:\n    status: str\n    path: Path\n\n    @property\n    def is_modified(self) -> bool:\n        return 'M' in self.status\n\n    @property\n    def is_deleted(self) -> bool:\n        return 'D' in self.status\n\n    @property\n    def is_in_reference(self) -> bool:\n        return str(self.path).startswith('.reference/')\n\n\n@dataclasses.dataclass\nclass GitChanges:\n    file_pairs: List[Tuple[Path, Path]]\n    skipped_reference_files: List[Path]\n    unpaired_modified_files: List[Path]\n\n\ndef run_command(command: List[str], cwd: Path | None = None) -> str:\n    \"\"\"Выполняет команду и возвращает результат\"\"\"\n    logger.debug(f'Running command: {\" \".join(command)}')\n    process = subprocess.run(\n        command,\n        cwd=cwd,\n        capture_output=True,\n        text=True,\n    )\n    if process.returncode != 0:\n        raise RuntimeError(f'Command failed: {process.stderr}')\n    return process.stdout.strip()\n\n\ndef get_git_status() -> str:\n    \"\"\"Получает статус git в формате porcelain\"\"\"\n    return run_command(['git', 'status', '--porcelain'])\n\n\ndef parse_status_line(line: str) -> FileStatus | None:\n    \"\"\"Разбирает строку статуса git и возвращает FileStatus\"\"\"\n    if not line:\n        return None\n\n    # Формат вывода git status --porcelain:\n    # XY PATH или XY \"PATH с пробелами\"\n    parts = line.split(maxsplit=1)\n    if len(parts) != 2:\n        return None\n\n    status = parts[0].strip()\n    file_path = parts[1].strip()\n\n    return FileStatus(status=status, path=Path(file_path))\n\n\ndef get_changes_to_commit() -> GitChanges:\n    \"\"\"\n    Находит файлы для коммитов:\n    - пары: измененный файл + удаленный файл в .reference с тем же относительным путем\n    - одиночные удаленные файлы из .reference (без измененного аналога)\n    - измененные файлы без удаленного файла в .reference\n    \"\"\"\n    status_output = get_git_status()\n\n    modified_files: Dict[Path, FileStatus] = {}\n    deleted_reference_files: Dict[Path, FileStatus] = {}\n\n    for line in status_output.split('\\n'):\n        if not line:\n            continue\n\n        file_status = parse_status_line(line)\n        if not file_status:\n            continue\n\n        # Измененные файлы (не в .reference)\n        if file_status.is_modified and not file_status.is_in_reference:\n            modified_files[file_status.path] = file_status\n\n        # Удаленные файлы в .reference\n        if file_status.is_deleted and file_status.is_in_reference:\n            deleted_reference_files[file_status.path] = file_status\n\n    matched_reference_files: set[Path] = set()\n\n    # Находим пары файлов и отмечаем соответствующие файлы в .reference\n    file_pairs = []\n    unpaired_modified_files: List[Path] = []\n\n    for modified_path in modified_files:\n        reference_path = Path('.reference') / modified_path\n\n        if reference_path in deleted_reference_files:\n            file_pairs.append((modified_path, reference_path))\n            matched_reference_files.add(reference_path)\n        else:\n            logger.warning(f'No matching deleted reference file for {modified_path}')\n            unpaired_modified_files.append(modified_path)\n\n    # Находим удаленные файлы в .reference без пары\n    skipped_reference_files = [\n        ref_path for ref_path in deleted_reference_files if ref_path not in matched_reference_files\n    ]\n\n    return GitChanges(\n        file_pairs=file_pairs,\n        skipped_reference_files=skipped_reference_files,\n        unpaired_modified_files=unpaired_modified_files,\n    )\n\n\ndef commit_file_pair(modified_file: Path, reference_file: Path, dry_run: bool = False) -> None:\n    \"\"\"Создает коммит для пары файлов\"\"\"\n    file_name = modified_file.name\n    commit_message = f'update {file_name}'\n\n    logger.info(f'Committing changes for {file_name}...')\n\n    if dry_run:\n        logger.info(f'[DRY RUN] Would add: {modified_file} {reference_file}')\n        logger.info(f'[DRY RUN] Would commit with message: {commit_message}')\n        return\n\n    # Добавляем файлы в индекс\n    run_command(['git', 'add', str(modified_file), str(reference_file)])\n\n    # Создаем коммит\n    run_command(['git', 'commit', '-m', commit_message])\n\n    logger.info(f'Committed: {commit_message}')\n\n\ndef commit_skipped_reference_file(reference_file: Path, dry_run: bool = False) -> None:\n    \"\"\"Создает коммит для удаленного файла из .reference без пары\"\"\"\n    file_name = reference_file.name\n    commit_message = f'skipped {file_name}'\n\n    logger.info(f'Committing skipped reference file {file_name}...')\n\n    if dry_run:\n        logger.info(f'[DRY RUN] Would add: {reference_file}')\n        logger.info(f'[DRY RUN] Would commit with message: {commit_message}')\n        return\n\n    run_command(['git', 'add', str(reference_file)])\n    run_command(['git', 'commit', '-m', commit_message])\n\n    logger.info(f'Committed: {commit_message}')\n\n\ndef main() -> None:\n    parser = argparse.ArgumentParser(\n        description='Create separate commits for each pair of related files'\n    )\n    parser.add_argument(\n        '--dry-run',\n        action='store_true',\n        default=False,\n        help='Run without making actual commits',\n    )\n    parser.add_argument(\n        '-v',\n        '--verbose',\n        action='count',\n        default=0,\n        help='Increase verbosity (can be used multiple times)',\n    )\n\n    args = parser.parse_args()\n\n    dry_run, verbose = args.dry_run, args.verbose\n\n    log_level = (5 - verbose) * 10\n    logging.basicConfig(\n        level=log_level,\n        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',\n    )\n\n    logger.info('Starting file pair commit process')\n    logger.debug(f'Arguments: dry_run={dry_run}, verbose={verbose}')\n\n    try:\n        changes = get_changes_to_commit()\n\n        if changes.unpaired_modified_files:\n            logger.warning(\n                'Modified files without matching deleted reference: %s',\n                ', '.join(str(path) for path in changes.unpaired_modified_files),\n            )\n\n        if not changes.file_pairs and not changes.skipped_reference_files:\n            logger.warning('No matching file pairs or skipped reference files found')\n            return\n\n        if changes.file_pairs:\n            logger.info(f'Found {len(changes.file_pairs)} file pairs to commit')\n        if changes.skipped_reference_files:\n            logger.info(\n                f'Found {len(changes.skipped_reference_files)} skipped reference files to commit'\n            )\n\n        for modified_file, reference_file in changes.file_pairs:\n            commit_file_pair(modified_file, reference_file, dry_run)\n\n        for reference_file in changes.skipped_reference_files:\n            commit_skipped_reference_file(reference_file, dry_run)\n\n        logger.info('All commits created successfully!')\n\n    except Exception as e:\n        logger.error(f'Error: {e}')\n        exit(1)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": ".scripts/monitor_repository.py",
    "content": "import argparse\nimport dataclasses\nimport hashlib\nimport json\nimport logging\nimport subprocess\nimport tempfile\nfrom functools import cache, cached_property\nfrom pathlib import Path\n\nimport tomllib\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclasses.dataclass\nclass Config:\n    repo_url: str\n    repo_branch: str\n    original_link: str\n    paths: list[tuple[str, str]]\n    cache_file: Path\n\n    @classmethod\n    def from_config_file(cls, config_file_path: Path) -> 'Config':\n        content = config_file_path.read_text()\n        raw_config = tomllib.loads(content)\n\n        logger.debug('Loaded config: %s', raw_config)\n\n        return cls(\n            repo_url=raw_config['repository']['repo_url'],\n            repo_branch=raw_config['repository']['repo_branch'],\n            original_link=raw_config['repository']['original_link'],\n            paths=raw_config['repository']['paths'],\n            cache_file=Path(raw_config['repository']['cache_file']),\n        )\n\n\n@dataclasses.dataclass\nclass CacheData:\n    commit_hash: str | None\n    file_hash: str | None\n\n\ndef read_files_json(path: Path) -> dict[str, CacheData]:\n    if not path.exists():\n        return {}\n    raw_content = json.loads(path.read_text())\n    return {\n        key: CacheData(commit_hash=value.get('commit_hash'), file_hash=value.get('file_hash'))\n        for key, value in raw_content.items()\n    }\n\n\ndef write_files_json(path: Path, data: dict[str, CacheData]) -> None:\n    path.parent.mkdir(parents=True, exist_ok=True)\n\n    keys = sorted(data.keys())\n\n    path.write_text(\n        json.dumps(\n            {\n                key: {'commit_hash': data[key].commit_hash, 'file_hash': data[key].file_hash}\n                for key in keys\n            },\n            indent=2,\n        )\n    )\n\n\n@dataclasses.dataclass\nclass FileInfo:\n    src: Path\n    dst: Path\n\n    _repo: 'GitRepository'\n\n    def __hash__(self):\n        return hash(f'{self.src}{self.dst}')\n\n    @cached_property\n    def file_hash(self) -> str:\n        return hashlib.md5(self.src.read_bytes()).hexdigest()\n\n    @cached_property\n    def commit_hash(self) -> str:\n        result = subprocess.run(\n            ['git', 'rev-list', '-1', 'HEAD', '--', self.src.resolve()],\n            cwd=self._repo.path,\n            capture_output=True,\n            text=True,\n            check=True,\n        )\n        return result.stdout.strip()\n\n    @cache\n    def get_file_diff(\n        self,\n        old_commit: str,\n    ) -> str:\n        result = subprocess.run(\n            ['git', 'diff', old_commit, self.commit_hash, '--', self.src.resolve()],\n            cwd=self._repo.path,\n            capture_output=True,\n            text=True,\n            check=True,\n        )\n        clear_result = result.stdout.strip()\n        clear_result = '\\n'.join(clear_result.split('\\n')[4:])\n        clear_result = f'{old_commit} -> {self.commit_hash}\\n{clear_result}'\n        return clear_result\n\n    def copy(self) -> None:\n        self.dst.parent.mkdir(parents=True, exist_ok=True)\n        self.dst.write_text(self.src.read_text())\n\n\nclass GitRepository:\n    def __init__(self, config: 'Config', path: Path) -> None:\n        self.config = config\n        self.path = path\n\n    @classmethod\n    def get(cls, config: 'Config', temp_dir: Path | None) -> 'GitRepository':\n        if temp_dir is None:\n            logger.info('Creating temporary directory')\n            temp_dir = Path(tempfile.mkdtemp())\n\n        logger.debug('Temporary directory: %s', temp_dir)\n\n        if not temp_dir.exists() or not any(temp_dir.iterdir()):\n            logger.debug('Cloning repository')\n            subprocess.run(\n                ['git', 'clone', config.repo_url, temp_dir.resolve()],\n                check=True,\n                capture_output=True,\n            )\n            subprocess.run(\n                ['git', 'checkout', config.repo_branch],\n                cwd=temp_dir,\n                check=True,\n                capture_output=True,\n            )\n            return cls(config, temp_dir)\n\n        if any(temp_dir.iterdir()) and '.git' not in [x.name for x in temp_dir.iterdir()]:\n            raise ValueError(f'{temp_dir} is not empty and not a git repository')\n\n        check_result = subprocess.run(\n            ['git', 'config', '--get', 'remote.origin.url'],\n            cwd=temp_dir,\n            capture_output=True,\n            text=True,\n        )\n        if check_result.stdout.strip() != config.repo_url:\n            raise ValueError(\n                f'Expected repository url {config.repo_url}, got {check_result.stdout.strip()}'\n            )\n\n        subprocess.run(\n            ['git', 'checkout', config.repo_branch],\n            cwd=temp_dir,\n            check=True,\n            capture_output=True,\n        )\n        subprocess.run(\n            ['git', 'pull'],\n            cwd=temp_dir,\n            check=True,\n            capture_output=True,\n        )\n        return cls(config, temp_dir)\n\n    def get_files_info(self) -> list[FileInfo]:\n        file_infos: list[FileInfo] = []\n\n        for src_raw, dst_raw in self.config.paths:\n            src, dst = self.path / Path(src_raw), Path(dst_raw)\n            if not src.exists():\n                logging.warning(f'Path {src.resolve()} does not exist')\n                continue\n            if src.is_dir():\n                for folder, _, files in src.walk():\n                    for src_file in files:\n                        src_file = Path(folder) / src_file\n                        dst_file = dst / src_file.relative_to(src)\n                        file_infos.append(FileInfo(src=src_file, dst=dst_file, _repo=self))\n            else:\n                src_file = self.path / Path(src_raw)\n                dst_file = Path(dst_raw)\n                file_infos.append(FileInfo(src=src_file, dst=dst_file, _repo=self))\n\n        logger.debug('File infos: %s', file_infos)\n        return file_infos\n\n    def delete(self) -> None:\n        logger.debug('Deleting folder %s', self.path)\n        self.delete_folder(self.path)\n        logger.debug('Folder %s deleted', self.path)\n\n    def delete_folder(self, path: Path) -> None:\n        for sub in path.iterdir():\n            if sub.is_dir():\n                self.delete_folder(sub)\n            else:\n                sub.unlink()\n        path.rmdir()\n\n\ndef process_repository(\n    save: bool,\n    tmp_folder_path: Path | None,\n    dry_run: bool,\n    config_file: Path,\n    cache_file: Path,\n    details_file: Path | None,\n) -> None:\n    config = Config.from_config_file(config_file)\n    files_cache = read_files_json(cache_file or config.cache_file)\n    obsolete_files = set(files_cache.keys())\n\n    changed_files: dict[Path, str] = {}\n\n    repo = GitRepository.get(config, tmp_folder_path)\n\n    file_infos = repo.get_files_info()\n\n    for file_info in file_infos:\n        file_cache_data = files_cache.get(str(file_info.dst))\n        if file_cache_data is None:\n            if not dry_run:\n                file_info.copy()\n            changed_files[file_info.dst] = 'New file'\n        else:\n            if file_cache_data.commit_hash != file_info.commit_hash:\n                if file_cache_data.file_hash != file_info.file_hash:\n                    if not dry_run:\n                        file_info.copy()\n                    changed_files[file_info.dst] = file_info.get_file_diff(\n                        file_cache_data.commit_hash\n                    )\n\n        files_cache[str(file_info.dst)] = CacheData(\n            commit_hash=file_info.commit_hash,\n            file_hash=file_info.file_hash,\n        )\n        obsolete_files.discard(str(file_info.dst))\n\n    for obsolete_file in obsolete_files:\n        logging.info('Removing obsolete file %s', ', '.join(obsolete_file))\n        del files_cache[obsolete_file]\n\n    if not dry_run:\n        logger.info('Writing cache file %s', cache_file)\n        write_files_json(cache_file, files_cache)\n\n    if changed_files:\n        summary_lines = [f'Sync with [original]({config.original_link})\\n']\n        details_lines = [f'Sync with [original]({config.original_link})\\n']\n\n        for file, change in changed_files.items():\n            summary_lines.append(f'- [ ] `{file}`')\n\n            change_message = change if change != 'New file' else 'New file'\n            details_lines.append(f' - `{file}`\\n')\n            details_lines.append(f'```\\n{change_message}\\n```\\n\\n\\n')\n\n        summary_text = \"\\n\".join(summary_lines)\n        details_text = \"\".join(details_lines)\n\n        print(summary_text)\n\n        if details_file and not dry_run:\n            details_file.write_text(details_text)\n\n    if not save:\n        logger.info('Deleting temporary folder %s', repo.path)\n        repo.delete()\n\n\nif __name__ == '__main__':\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        '--config',\n        default='monitoring_config.toml',\n        required=False,\n        help='Path to the configuration file',\n        type=Path,\n    )\n    parser.add_argument(\n        '--cache',\n        default='.files_cache.json',\n        required=False,\n        help='Path to the cache file',\n        type=Path,\n    )\n    parser.add_argument(\n        '--details-file',\n        default=None,\n        required=False,\n        help='Path to the file where detailed changes will be written',\n        type=Path,\n    )\n    parser.add_argument(\n        '--temp-folder',\n        default=None,\n        required=False,\n        help='Temporary folder to store files',\n        type=Path,\n    )\n    parser.add_argument(\n        '-s',\n        '--save',\n        action='store_true',\n        default=False,\n        help='Save files in temporary folder',\n    )\n    parser.add_argument(\n        '--dry-run',\n        action='store_true',\n        default=False,\n        help='Run without making changes',\n    )\n    parser.add_argument(\n        '-v',\n        '--verbose',\n        action='count',\n        default=0,\n    )\n    args = parser.parse_args()\n    verbose, save, temp_folder_path, dry_run = (\n        args.verbose,\n        args.save,\n        args.temp_folder,\n        args.dry_run,\n    )\n    config_file, cache_file = args.config, args.cache\n    logging.basicConfig(level=(5 - verbose) * 10)\n\n    logger.info('Processing repository')\n    logger.debug(\n        'Running with params %s', ', '.join(f'{key}={value}' for key, value in vars(args).items())\n    )\n\n    process_repository(save, temp_folder_path, dry_run, config_file, cache_file, args.details_file)\n\n"
  },
  {
    "path": "README.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Django REST framework\n\n![Logo by Jake 'Sid' Smith](https://github.com/encode/django-rest-framework/raw/main/docs/img/logo.png)\n\nDjango REST framework - это мощный и гибкий набор инструментов для создания Web API.\n\nНекоторые причины, по которым вы можете захотеть использовать REST framework:\n\n* Web-интерфейс API - огромный выигрыш в удобстве использования для ваших разработчиков.\n* [Политики аутентификации](api-guide/authentication.md), включая пакеты для [OAuth1a](api-guide/authentication.md#django-rest-framework-oauth) и [OAuth2](api-guide/authentication.md#django-oauth-toolkit).\n* [Сериализация](api-guide/serializers.md), поддерживающая как [ORM](api-guide/serializers.md#modelserializer), так и [non-ORM](api-guide/serializers.md#сериализаторы) источники данных.\n* Настраивается все - просто используйте [обычные представления на основе функций](api-guide/views#Представления-на-основе-функций), если вам не нужны [более](api-guide/generic-views.md) [мощные](api-guide/viewsets.md) [возможности](api-guide/routers.md).\n* Обширная документация и [отличная поддержка сообщества](https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework).\n* Используется и пользуется доверием всемирно известных компаний, включая [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/).\n\n---\n\n## Требования\n\nREST framework требует следующего:\n\n* Django (4.2, 5.0, 5.1, 5.2)\n* Python (3.10, 3.11, 3.12, 3.13, 3.14)\n\nМы **настоятельно рекомендуем** и официально поддерживаем только последние выпуски патчей каждой версии Python и Django.\n\nСледующие пакеты являются необязательными:\n\n* [PyYAML](https://pypi.org/project/pyyaml/), [uritemplate](https://pypi.org/project/uritemplate/) (5.1+, 3.0.0+) - Поддержка генерации схем.\n* [Markdown](https://pypi.org/project/markdown/) (3.3.0+) - Поддержка Markdown для Web-интерфейса API.\n* [Pygments](https://pypi.org/project/pygments/) (2.7.0+) - Добавление подсветки синтаксиса в обработку Markdown.\n* [django-filter](https://pypi.org/project/django-filter/) (1.0.1+) - Поддержка фильтрации.\n* [django-guardian](https://github.com/django-guardian/django-guardian) (1.1.1+) - Поддержка разрешений на уровне объектов.\n\n## Установка\n\nУстановите с помощью `pip`, включая все дополнительные пакеты, которые вы хотите...\n\n```bash\npip install djangorestframework\npip install markdown       # Markdown support for the browsable API.\npip install django-filter  # Filtering support\n```\n\n...или клонируйте проект с github.\n\n```bash\ngit clone https://github.com/encode/django-rest-framework\n```\n\nДобавьте `'rest_framework'` в настройку `INSTALLED_APPS`.\n\n```python\nINSTALLED_APPS = [\n    ...\n    'rest_framework',\n]\n```\n\nЕсли вы планируете использовать Web-интерфейс API, вы, вероятно, также захотите добавить представления входа и выхода из системы REST framework. Добавьте следующее в ваш корневой файл `urls.py`.\n\n```python\nurlpatterns = [\n    ...\n    path('api-auth/', include('rest_framework.urls'))\n]\n```\n\nОбратите внимание, что путь URL может быть любым, какой вы захотите.\n\n## Пример\n\nДавайте рассмотрим быстрый пример использования REST-фреймворка для создания простого API с поддержкой модели.\n\nМы создадим API с функцией чтения-записи для доступа к информации о пользователях нашего проекта.\n\nВсе глобальные настройки для API REST-фреймворка хранятся в одном конфигурационном словаре с именем `REST_FRAMEWORK`. Начните с добавления следующих параметров в модуль `settings.py`:\n\n```python\nREST_FRAMEWORK = {\n    # Use Django's standard `django.contrib.auth` permissions,\n    # or allow read-only access for unauthenticated users.\n    'DEFAULT_PERMISSION_CLASSES': [\n        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'\n    ]\n}\n```\n\nНе забудьте убедиться, что вы также добавили `rest_framework` в `INSTALLED_APPS`.\n\nТеперь мы готовы к созданию нашего API. Вот корневой модуль нашего проекта `urls.py`:\n\n```python\nfrom django.urls import path, include\nfrom django.contrib.auth.models import User\nfrom rest_framework import routers, serializers, viewsets\n\n# Serializers define the API representation.\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n    class Meta:\n        model = User\n        fields = ['url', 'username', 'email', 'is_staff']\n\n# ViewSets define the view behavior.\nclass UserViewSet(viewsets.ModelViewSet):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n# Routers provide an easy way of automatically determining the URL conf.\nrouter = routers.DefaultRouter()\nrouter.register(r'users', UserViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n    path('', include(router.urls)),\n    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n```\n\nТеперь вы можете открыть API в браузере по адресу [http://127.0.0.1:8000/](http://127.0.0.1:8000/) и просмотреть новых \"пользователей\" API. Если вы используете элемент управления входом в систему в правом верхнем углу, вы также сможете добавлять, создавать и удалять пользователей из системы.\n\n## Быстрый старт\n\nНе можете дождаться начала работы? Руководство [quickstart](quickstart.md) - это самый быстрый способ начать работу и создавать API с помощью REST framework.\n\n## Руководство\n\nРуководство проведет вас через все этапы настройки DRF. Это займет не очень много времени, однако вы получите полное понимание того, как все компоненты работают друг с другом и данное руководство крайне рекомендовано к прочтению.\n\n* [Сериализация](tutorial/1-serialization.md)\n* [Запросы-ответы](tutorial/2-requests-and-responses.md)\n* [Представления-классы](tutorial/3-class-based-views.md)\n* [Аутентификация/права доступа](tutorial/4-authentication-and-permissions.md)\n* [Отношения и связи](tutorial/5-relationships-and-hyperlinked-apis.md)\n* [Наборы представлений и роутеры](tutorial/6-viewsets-and-routers.md)\n\nТак же есть пример работающего API законченного руководства для тестовых целей, [доступен здесь](http://restframework.herokuapp.com/).\n\n## Навигатор по API\n\nНавигатор по API - исчерпывающее руководство по всему функционалу, предоставляемому DRF.\n\n* [Запросы](api-guide/requests.md)\n* [Ответы](api-guide/responses.md)\n* [Представления](api-guide/views.md)\n* [Общие представления](api-guide/generic-views.md)\n* [Viewsets](api-guide/viewsets.md)\n* [Маршрутизаторы](api-guide/routers.md)\n* [Парсеры](api-guide/parsers.md)\n* [Рендереры](api-guide/renderers.md)\n* [Сериализаторы](api-guide/serializers.md)\n* [Поля сериализатора](api-guide/fields.md)\n* [Отношения сериализаторов](api-guide/relations.md)\n* [Валидаторы](api-guide/validators.md)\n* [Аутентификация](api-guide/authentication.md)\n* [Разрешения](api-guide/permissions.md)\n* [Кэширование](api-guide/caching.md)\n* [Дросселирование](api-guide/throttling.md)\n* [Фильтрация](api-guide/filtering.md)\n* [Пагинация](api-guide/pagination.md)\n* [Версионирование](api-guide/versioning.md)\n* [Согласование контента](api-guide/content-negotiation.md)\n* [Метаданные](api-guide/metadata.md)\n* [Schemas](api-guide/schemas.md)\n* [Cуффиксы формата](api-guide/format-suffixes.md)\n* [Возвращение URL-адресов](api-guide/reverse.md)\n* [Исключения](api-guide/exceptions.md)\n* [Коды состояния](api-guide/status-codes.md)\n* [Тестирование](api-guide/testing.md)\n* [Настройки](api-guide/settings.md)\n\n## Статьи\n\nОсновные руководства для использующих DRF.\n\n* [AJAX, CSRF & CORS](topics/ajax-csrf-cors.md)\n* [The Browsable API](topics/browsable-api.md)\n* [Улучшения в браузере](topics/browser-enhancements.md)\n* [Документирование вашего API](topics/documenting-your-api.md)\n* [HTML и формы](topics/html-and-forms.md)\n* [Интернационализация](topics/internationalization.md)\n* [REST, гипермедиа и HATEOAS](topics/rest-hypermedia-hateoas.md)\n* [Вложенные сериализаторы с возможностью записи](topics/writable-nested-serializers.md)\n\n## Разработка\n\nСмотрите [руководство для разработчиков](https://www.django-rest-framework.org/community/contributing/) для получения информации о том, как клонировать репозиторий, запустить набор тестов и внести изменения в REST Framework.\n\n## Поддержка\n\nЗа поддержкой обращайтесь в [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).\n\n## Безопасность\n\n**Пожалуйста, сообщайте о проблемах безопасности по электронной почте security@encode.io**.\n\nПосле этого сопровождающие проекта будут работать с вами над решением любых вопросов, если потребуется, до обнародования информации.\n\n---\n## Авторы перевода\n\n* [Ilya Chichak](https://github.com/ilyachch/)\n\n## Помощь в переводе\n* [https://github.com/pymq](https://github.com/pymq)\n* [https://github.com/rufatpro](https://github.com/rufatpro)\n* [Dmitry Plaxunov](https://github.com/fojetin)\n\nСпасибо всем за помощь в переводе!\n\nПеревод производится с помощью утилиты [md_docs-trans-app](https://github.com/ilyachch/md_docs-trans-app)\n"
  },
  {
    "path": "SUMMARY.md",
    "content": "# Summary\n\n## Overview\n\n* [Django REST framework](README.md)\n* [Быстрый старт](quickstart.md)\n  * [Сериализация](tutorial/1-serialization.md)\n  * [Запросы-ответы](tutorial/2-requests-and-responses.md)\n  * [Представления-классы](tutorial/3-class-based-views.md)\n  * [Аутентификация/права доступа](tutorial/4-authentication-and-permissions.md)\n  * [Отношения и связи](tutorial/5-relationships-and-hyperlinked-apis.md)\n  * [Наборы представлений и роутеры](tutorial/6-viewsets-and-routers.md)\n* Навигация по API:\n  * [Запросы](api-guide/requests.md)\n  * [Ответы](api-guide/responses.md)\n  * [Представления](api-guide/views.md)\n  * [Общие представления](api-guide/generic-views.md)\n  * [Viewsets](api-guide/viewsets.md)\n  * [Маршрутизаторы](api-guide/routers.md)\n  * [Парсеры](api-guide/parsers.md)\n  * [Рендереры](api-guide/renderers.md)\n  * [Сериализаторы](api-guide/serializers.md)\n  * [Поля сериализатора](api-guide/fields.md)\n  * [Отношения сериализаторов](api-guide/relations.md)\n  * [Валидаторы](api-guide/validators.md)\n  * [Аутентификация](api-guide/authentication.md)\n  * [Разрешения](api-guide/permissions.md)\n  * [Кэширование](api-guide/caching.md)\n  * [Дросселирование](api-guide/throttling.md)\n  * [Фильтрация](api-guide/filtering.md)\n  * [Пагинация](api-guide/pagination.md)\n  * [Версионирование](api-guide/versioning.md)\n  * [Согласование контента](api-guide/content-negotiation.md)\n  * [Метаданные](api-guide/metadata.md)\n  * [Schemas](api-guide/schemas.md)\n  * [Cуффиксы формата](api-guide/format-suffixes.md)\n  * [Возвращение URL-адресов](api-guide/reverse.md)\n  * [Исключения](api-guide/exceptions.md)\n  * [Коды состояния](api-guide/status-codes.md)\n  * [Тестирование](api-guide/testing.md)\n  * [Настройки](api-guide/settings.md)\n\n## Статьи\n\n* [Статьи](topics.md)\n  * [AJAX, CSRF & CORS](topics/ajax-csrf-cors.md)\n  * [The Browsable API](topics/browsable-api.md)\n  * [Улучшения в браузере](topics/browser-enhancements.md)\n  * [Документирование вашего API](topics/documenting-your-api.md)\n  * [HTML и формы](topics/html-and-forms.md)\n  * [Интернационализация](topics/internationalization.md)\n  * [REST, гипермедиа и HATEOAS](topics/rest-hypermedia-hateoas.md)\n  * [Вложенные сериализаторы с возможностью записи](topics/writable-nested-serializers.md)\n"
  },
  {
    "path": "api-guide/authentication.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Аутентификация\n\n> Аутентификация должна быть подключаемой.\n>\n> - Джейкоб Каплан-Мосс, [\"Худшие практики REST\"](https://jacobian.org/writing/rest-worst-practices/)\n\nАутентификация - это механизм связывания входящего запроса с набором идентификационных данных, таких как пользователь, от которого пришел запрос, или токен, которым он был подписан. Политики [permission](permissions.md) и [throttling](throttling.md) могут затем использовать эти учетные данные, чтобы определить, должен ли запрос быть разрешен.\n\nDRF предоставляет несколько схем аутентификации из коробки, а также позволяет реализовать пользовательские схемы.\n\nАутентификация всегда выполняется в самом начале представления, до того, как произойдет проверка разрешений и дросселирование, и до того, как будет разрешено выполнение любого другого кода.\n\nСвойство `request.user` обычно устанавливается на экземпляр класса `User` пакета `contrib.auth`.\n\nСвойство `request.auth` используется для любой дополнительной информации об аутентификации, например, оно может быть использовано для представления маркера аутентификации, которым был подписан запрос.\n\n---\n\n**Примечание:** Не забывайте, что **аутентификация сама по себе не разрешает и не запрещает входящий запрос**, она просто идентифицирует учетные данные, с которыми был сделан запрос.\n\nИнформацию о том, как настроить политику разрешений для вашего API, смотрите в документации [permissions](permissions.md).\n\n---\n\n## Как определяется аутентификация\n\nСхемы аутентификации всегда определяются как список классов. DRF попытается выполнить аутентификацию с каждым классом в списке, и установит `request.user` и `request.auth`, используя возвращаемое значение первого класса, который успешно аутентифицируется.\n\nЕсли ни один класс не выполнит аутентификацию, `request.user` будет установлен в экземпляр `django.contrib.auth.models.AnonymousUser`, а `request.auth` будет установлен в `None`.\n\nЗначение `request.user` и `request.auth` для неаутентифицированных запросов можно изменить с помощью параметров `UNAUTHENTICATED_USER` и `UNAUTHENTICATED_TOKEN`.\n\n## Установка схемы аутентификации\n\nСхемы аутентификации по умолчанию можно установить глобально, используя параметр `DEFAULT_AUTHENTICATION_CLASSES`. Например.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_AUTHENTICATION_CLASSES': [\n        'rest_framework.authentication.BasicAuthentication',\n        'rest_framework.authentication.SessionAuthentication',\n    ]\n}\n```\n\nВы также можете установить схему аутентификации для каждого представления или каждого набора представлений, используя класс `APIView`, основанный на представлениях.\n\n```python\nfrom rest_framework.authentication import SessionAuthentication, BasicAuthentication\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n    authentication_classes = [SessionAuthentication, BasicAuthentication]\n    permission_classes = [IsAuthenticated]\n\n    def get(self, request, format=None):\n        content = {\n            'user': str(request.user),  # `django.contrib.auth.User` instance.\n            'auth': str(request.auth),  # None\n        }\n        return Response(content)\n```\n\nИли, если вы используете декоратор `@api_view` с представлениями, основанными на функциях.\n\n```python\n@api_view(['GET'])\n@authentication_classes([SessionAuthentication, BasicAuthentication])\n@permission_classes([IsAuthenticated])\ndef example_view(request, format=None):\n    content = {\n        'user': str(request.user),  # `django.contrib.auth.User` instance.\n        'auth': str(request.auth),  # None\n    }\n    return Response(content)\n```\n\n## Неавторизованные и запрещенные ответы\n\nКогда неаутентифицированному запросу отказано в разрешении, существует два различных кода ошибок, которые могут быть уместны.\n\n* [HTTP 401 Unauthorized](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2)\n* [HTTP 403 Permission Denied](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.4)\n\nОтветы HTTP 401 всегда должны содержать заголовок `WWW-Authenticate`, который указывает клиенту, как пройти аутентификацию. Ответы HTTP 403 не включают заголовок `WWW-Authenticate`.\n\nТип ответа, который будет использоваться, зависит от схемы аутентификации. Хотя может использоваться несколько схем аутентификации, для определения типа ответа может использоваться только одна схема. **Первый класс аутентификации, установленный в представлении, используется при определении типа ответа**.\n\nОбратите внимание, что когда запрос может успешно пройти аутентификацию, но при этом получить отказ в разрешении на выполнение запроса, в этом случае всегда будет использоваться ответ `403 Permission Denied`, независимо от схемы аутентификации.\n\n## Django 5.1+ `LoginRequiredMiddleware`\n\nЕсли вы работаете с Django 5.1+ и используете [`LoginRequiredMiddleware`](https://docs.djangoproject.com/en/stable/ref/middleware/#django.contrib.auth.middleware.LoginRequiredMiddleware), обратите внимание, что все представления из DRF отказываются от этого промежуточного ПО. Это связано с тем, что аутентификация в DRF основана на классах аутентификации и разрешений, которые могут быть определены после применения промежуточного ПО. Кроме того, если запрос не аутентифицирован, промежуточное ПО перенаправляет пользователя на страницу входа в систему, что не подходит для API-запросов, где предпочтительнее возвращать код состояния `401 Unauthorized`.\n\nDRF предлагает эквивалентный механизм для представлений DRF через глобальные настройки `DEFAULT_AUTHENTICATION_CLASSES` и `DEFAULT_PERMISSION_CLASSES`. Их следует изменить соответствующим образом, если вам нужно обеспечить вход в систему при запросах API.\n\n## Специфическая конфигурация Apache mod_wsgi\n\nОбратите внимание, что при развертывании на [Apache using mod_wsgi](https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIPassAuthorization.html) заголовок авторизации по умолчанию не передается приложению WSGI, так как предполагается, что аутентификация будет обрабатываться Apache, а не на уровне приложения.\n\nЕсли вы развертываете на Apache и используете любую аутентификацию, не основанную на сеансах, вам необходимо явно настроить mod_wsgi для передачи необходимых заголовков приложению. Это можно сделать, указав директиву `WSGIPassAuthorization` в соответствующем контексте и установив ее в значение `'On'`.\n\n```apache\n# this can go in either server config, virtual host, directory or .htaccess\nWSGIPassAuthorization On\n```\n\n---\n\n# API Reference\n\n## BasicAuthentication\n\nЭта схема аутентификации использует [HTTP Basic Authentication](https://tools.ietf.org/html/rfc2617), подписанную именем пользователя и паролем. Базовая аутентификация обычно подходит только для тестирования.\n\nПри успешной аутентификации `BasicAuthentication` предоставляет следующие учетные данные.\n\n* `request.user` будет экземпляром Django `User`.\n* `request.auth` будет `None`.\n\nОтветы без аутентификации, которым отказано в разрешении, приведут к ответу `HTTP 401 Unauthorized` с соответствующим заголовком WWW-Authenticate. Например:\n\n```http\nWWW-Authenticate: Basic realm=\"api\"\n```\n\n---\n\n**Примечание:** Если вы используете `BasicAuthentication` в реальном проекте, вы должны убедиться, что ваш API доступен только через `https`. Вы также должны убедиться, что клиенты вашего API всегда будут повторно запрашивать имя пользователя и пароль при входе в систему и никогда не будут сохранять эти данные в постоянном хранилище.\n\n---\n\n## TokenAuthentication\n\n---\n\n**Примечание:** Аутентификация с помощью токенов, предоставляемая DRF, является довольно простой реализацией.\n\nДля реализации, которая позволяет использовать более одного токена на пользователя, имеет некоторые более жесткие детали реализации безопасности и поддерживает истечение срока действия токена, пожалуйста, обратитесь к стороннему пакету [Django REST Knox](https://github.com/James1345/django-rest-knox).\n\n---\n\nЭта схема аутентификации использует простую схему аутентификации HTTP на основе токенов. Токен-аутентификация подходит для клиент-серверных установок, таких как собственные настольные и мобильные клиенты.\n\nДля использования схемы `TokenAuthentication` вам необходимо [настроить классы аутентификации](#установка-схемы-аутентификации), чтобы включить `TokenAuthentication`, и дополнительно включить `rest_framework.authtoken` в настройку `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n    ...\n    'rest_framework.authtoken'\n]\n```\n\nОбязательно запустите `manage.py migrate` после изменения настроек.\n\nПриложение `rest_framework.authtoken` обеспечивает миграцию баз данных Django.\n\nВам также потребуется создать токены для своих пользователей.\n\n```python\nfrom rest_framework.authtoken.models import Token\n\ntoken = Token.objects.create(user=...)\nprint(token.key)\n```\n\nДля аутентификации клиентов ключ токена должен быть включен в HTTP-заголовок `Authorization`. Ключ должен иметь префикс в виде строкового литерала \"Token\", с пробелами, разделяющими эти две строки. Например:\n\n```http\nAuthorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b\n```\n\n*Если вы хотите использовать другое ключевое слово в заголовке, например `Bearer`, просто создайте подкласс `TokenAuthentication` и установите переменную класса `keyword`.\n\nПри успешной аутентификации `TokenAuthentication` предоставляет следующие учетные данные.\n\n* `request.user` будет экземпляром Django `User`.\n* `request.auth` будет экземпляром `rest_framework.authtoken.models.Token`.\n\nОтветы без аутентификации, которым отказано в разрешении, приведут к ответу `HTTP 401 Unauthorized` с соответствующим заголовком WWW-Authenticate. Например:\n\n```http\nWWW-Authenticate: Token\n```\n\nИнструмент командной строки `curl` может быть полезен для тестирования API с аутентификацией токенов. Например:\n\n```http\ncurl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'\n```\n\n---\n\n**Примечание:** Если вы используете `TokenAuthentication` в реальном проекте, вы должны убедиться, что ваш API доступен только через `https`.\n\n---\n\n### Генерация токенов\n\n#### С помощью сигналов\n\nЕсли вы хотите, чтобы у каждого пользователя был автоматически сгенерированный токен, вы можете просто перехватить сигнал `post_save` пользователя.\n\n```python\nfrom django.conf import settings\nfrom django.db.models.signals import post_save\nfrom django.dispatch import receiver\nfrom rest_framework.authtoken.models import Token\n\n@receiver(post_save, sender=settings.AUTH_USER_MODEL)\ndef create_auth_token(sender, instance=None, created=False, **kwargs):\n    if created:\n        Token.objects.create(user=instance)\n```\n\nОбратите внимание, что вам нужно убедиться, что вы поместили этот фрагмент кода в установленный модуль `models.py` или в другое место, которое будет импортироваться Django при запуске.\n\nЕсли вы уже создали несколько пользователей, вы можете сгенерировать токены для всех существующих пользователей следующим образом:\n\n```python\nfrom django.contrib.auth.models import User\nfrom rest_framework.authtoken.models import Token\n\nfor user in User.objects.all():\n    Token.objects.get_or_create(user=user)\n```\n\n#### Посредством конечной точки API\n\nПри использовании `TokenAuthentication` вы можете захотеть предоставить клиентам механизм для получения токена, заданного именем пользователя и паролем. DRF предоставляет встроенное представление для обеспечения такого поведения. Чтобы использовать его, добавьте представление `obtain_auth_token` в URLconf:\n\n```python\nfrom rest_framework.authtoken import views\nurlpatterns += [\n    path('api-token-auth/', views.obtain_auth_token)\n]\n```\n\nОбратите внимание, что URL часть шаблона может быть любой, которую вы хотите использовать.\n\nПредставление `obtain_auth_token` вернет ответ в формате JSON, если действительные поля `username` и `password` будут отправлены в представление с помощью данных формы или JSON:\n\n```python\n{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }\n```\n\nОбратите внимание, что представление по умолчанию `obtain_auth_token` явно использует JSON запросы и ответы, а не использует классы рендерера и парсера по умолчанию в ваших настройках.\n\nПо умолчанию к представлению `obtain_auth_token` не применяется никаких разрешений или дросселирования. Если вы хотите применить дросселирование, вам нужно переопределить класс представления и включить их с помощью атрибута `throttle_classes`.\n\nЕсли вам нужна настраиваемая версия представления `obtain_auth_token`, вы можете сделать это, создав подкласс класса представления `ObtainAuthToken` и используя его в url conf.\n\nНапример, вы можете возвращать дополнительную информацию о пользователе помимо значения `token`:\n\n```python\nfrom rest_framework.authtoken.views import ObtainAuthToken\nfrom rest_framework.authtoken.models import Token\nfrom rest_framework.response import Response\n\nclass CustomAuthToken(ObtainAuthToken):\n\n    def post(self, request, *args, **kwargs):\n        serializer = self.serializer_class(data=request.data,\n                                           context={'request': request})\n        serializer.is_valid(raise_exception=True)\n        user = serializer.validated_data['user']\n        token, created = Token.objects.get_or_create(user=user)\n        return Response({\n            'token': token.key,\n            'user_id': user.pk,\n            'email': user.email\n        })\n```\n\nИ в вашем `urls.py`:\n\n```python\nurlpatterns += [\n    path('api-token-auth/', CustomAuthToken.as_view())\n]\n```\n\n#### С администратором Django\n\nТокены также можно создавать вручную через интерфейс администратора. В случае, если вы используете большую базу пользователей, мы рекомендуем вам пропатчить класс `TokenAdmin`, чтобы настроить его под свои нужды, в частности, объявив поле `user` как `raw_field`.\n\n`ваше_приложение/admin.py`:\n\n```python\nfrom rest_framework.authtoken.admin import TokenAdmin\n\nTokenAdmin.raw_id_fields = ['user']\n```\n\n#### Использование команды Django manage.py\n\nНачиная с версии 3.6.4 можно сгенерировать пользовательский токен с помощью следующей команды:\n\n```bash\n./manage.py drf_create_token <username>\n```\n\nэта команда вернет API-токен для данного пользователя, создав его, если он не существует:\n\n```bash\nGenerated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1\n```\n\nЕсли вы хотите восстановить токен (например, если он был скомпрометирован или произошла утечка), вы можете передать дополнительный параметр:\n\n```bash\n./manage.py drf_create_token -r <username>\n```\n\n## SessionAuthentication\n\nЭта схема аутентификации использует бэкенд сессий Django по умолчанию для аутентификации. Сеансовая аутентификация подходит для клиентов AJAX, которые работают в том же сеансовом контексте, что и ваш сайт.\n\nПри успешной аутентификации `SessionAuthentication` предоставляет следующие учетные данные.\n\n* `request.user` будет экземпляром Django `User`.\n* `request.auth` будет `None`.\n\nОтветы без аутентификации, которым отказано в разрешении, приведут к ответу `HTTP 403 Forbidden`.\n\nЕсли вы используете 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).\n\n---\n\n**Предупреждение**: Всегда используйте стандартное представление входа Django при создании страниц входа. Это обеспечит надлежащую защиту ваших представлений входа.\n\n---\n\nПроверка CSRF в DRF работает несколько иначе, чем в стандартном Django, из-за необходимости поддерживать как сеансовую, так и несеансовую аутентификацию для одних и тех же представлений. Это означает, что только аутентифицированные запросы требуют CSRF-токенов, а анонимные запросы могут быть отправлены без CSRF-токенов. Такое поведение не подходит для представлений входа в систему, к которым всегда должна применяться проверка CSRF.\n\n## RemoteUserAuthentication\n\nЭта схема аутентификации позволяет вам делегировать аутентификацию вашему веб-серверу, который устанавливает переменную окружения `REMOTE_USER`.\n\nЧтобы использовать его, вы должны иметь `django.contrib.auth.backends.RemoteUserBackend` (или его подкласс) в настройке `AUTHENTICATION_BACKENDS`. По умолчанию `RemoteUserBackend` создает объекты `User` для имен пользователей, которые еще не существуют. Чтобы изменить это и другое поведение, обратитесь к [документации Django](https://docs.djangoproject.com/en/stable/howto/auth-remote-user/).\n\nПри успешной аутентификации `RemoteUserAuthentication` предоставляет следующие учетные данные:\n\n* `request.user` будет экземпляром Django `User`.\n* `request.auth` будет `None`.\n\nОбратитесь к документации вашего веб-сервера за информацией о настройке метода аутентификации, например:\n\n* [Apache Authentication How-To](https://httpd.apache.org/docs/2.4/howto/auth.html)\n* [NGINX (ограничение доступа)](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/)\n\n# Пользовательская аутентификация\n\nЧтобы реализовать собственную схему аутентификации, создайте подкласс `BaseAuthentication` и переопределите метод `.authenticate(self, request)`. Метод должен возвращать кортеж `(user, auth)`, если аутентификация прошла успешно, или `None` в противном случае.\n\nВ некоторых случаях вместо возврата `None` вы можете захотеть вызвать исключение `AuthenticationFailed` из метода `.authenticate()`.\n\nКак правило, вам следует придерживаться следующего подхода:\n\n* Если попытка аутентификации не была предпринята, верните `None`. Любые другие схемы аутентификации, которые также используются, будут проверены.\n* Если попытка аутентификации была предпринята, но не удалась, вызовите исключение `AuthenticationFailed`. Ответ об ошибке будет возвращен немедленно, независимо от любых проверок разрешений и без проверки других схем аутентификации.\n\nВы *можете* также переопределить метод `.authenticate_header(self, request)`. Если он реализован, он должен возвращать строку, которая будет использоваться в качестве значения заголовка `WWW-Authenticate` в ответе `HTTP 401 Unauthorized`.\n\nЕсли метод `.authenticate_header()` не переопределен, схема аутентификации будет возвращать ответы `HTTP 403 Forbidden`, когда неаутентифицированному запросу будет отказано в доступе.\n\n---\n\n**Примечание:** Когда ваш пользовательский аутентификатор вызывается свойствами `.user` или `.auth` объекта запроса, вы можете увидеть, как `AttributeError` преобразуется в `WrappedAttributeError`. Это необходимо для того, чтобы исходное исключение не было подавлено доступом к внешнему свойству. Python не распознает, что `AttributeError` исходит от вашего пользовательского аутентификатора, и вместо этого будет считать, что объект запроса не имеет свойства `.user` или `.auth`. Эти ошибки должны быть исправлены или иным образом обработаны вашим аутентификатором.\n\n---\n\n## Пример\n\nСледующий пример аутентифицирует любой входящий запрос как пользователя, указанного в имени пользователя в пользовательском заголовке запроса 'X-USERNAME'.\n\n```python\nfrom django.contrib.auth.models import User\nfrom rest_framework import authentication\nfrom rest_framework import exceptions\n\nclass ExampleAuthentication(authentication.BaseAuthentication):\n    def authenticate(self, request):\n        username = request.META.get('HTTP_X_USERNAME')\n        if not username:\n            return None\n\n        try:\n            user = User.objects.get(username=username)\n        except User.DoesNotExist:\n            raise exceptions.AuthenticationFailed('No such user')\n\n        return (user, None)\n```\n\n---\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## django-rest-knox\n\nБиблиотека [Django-rest-knox](https://github.com/James1345/django-rest-knox) предоставляет модели и представления для обработки аутентификации на основе токенов более безопасным и расширяемым способом, чем встроенная схема `TokenAuthentication` - с учетом одностраничных приложений и мобильных клиентов. Она предоставляет токены для каждого клиента, а также представления для их генерации при предоставлении другой аутентификации (обычно базовой), для удаления токена (обеспечивая принудительный выход с сервера) и для удаления всех токенов (выход из всех клиентов, в которые вошел пользователь).\n\n## Django OAuth Toolkit\n\nПакет [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**.\n\n### Установка и настройка\n\nУстановите с помощью `pip`.\n\n```bash\npip install django-oauth-toolkit\n```\n\nДобавьте пакет в `INSTALLED_APPS` и измените настройки DRF.\n\n```python\nINSTALLED_APPS = [\n    ...\n    'oauth2_provider',\n]\n\nREST_FRAMEWORK = {\n    'DEFAULT_AUTHENTICATION_CLASSES': [\n        'oauth2_provider.contrib.rest_framework.OAuth2Authentication',\n    ]\n}\n```\n\nБолее подробную информацию можно найти в документации [Django REST framework - Getting started](https://django-oauth-toolkit.readthedocs.io/en/latest/rest-framework/getting_started.html).\n\n## Django REST framework OAuth\n\nПакет [Django REST framework OAuth](https://jpadilla.github.io/django-rest-framework-oauth/) обеспечивает поддержку OAuth1 и OAuth2 для DRF.\n\nРанее этот пакет был включен непосредственно в DRF, но теперь поддерживается и сопровождается как пакет стороннего разработчика.\n\n### Установка и настройка\n\nУстановите пакет с помощью `pip`.\n\n```bash\npip install djangorestframework-oauth\n```\n\nПодробнее о настройке и использовании смотрите документацию по OAuth фреймворку Django REST для [authentication](https://jpadilla.github.io/django-rest-framework-oauth/authentication/) и [permissions](https://jpadilla.github.io/django-rest-framework-oauth/permissions/).\n\n## JSON Web Token Authentication\n\nJSON Web Token - это довольно новый стандарт, который можно использовать для аутентификации на основе токенов. В отличие от встроенной схемы TokenAuthentication, аутентификация JWT не требует использования базы данных для проверки токена. Пакет для JWT-аутентификации - [djangorestframework-simplejwt](https://github.com/davesque/django-rest-framework-simplejwt), который предоставляет некоторые возможности, а также подключаемое приложение черного списка токенов.\n\n## Аутентификация Hawk HTTP\n\nБиблиотека [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/)).\n\n## Аутентификация подписью HTTP\n\nHTTP 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).\n\n## Djoser\n\n[Djoser](https://github.com/sunscrapers/djoser) библиотека предоставляет набор представлений для обработки основных действий, таких как регистрация, вход в систему, выход из системы, сброс пароля и активация учетной записи. Пакет работает с пользовательской моделью пользователя и использует аутентификацию на основе токенов. Это готовая к использованию REST-реализация системы аутентификации Django.\n\n## DRF Auth Kit\n\nБиблиотека [DRF Auth Kit](https://github.com/huynguyengl99/drf-auth-kit) предоставляет современное решение для аутентификации REST с помощью файлов cookie JWT, входа через социальные сети, многофакторной аутентификации и комплексного управления пользователями. Пакет предлагает полную типобезопасность, автоматическую генерацию схемы OpenAPI с DRF Spectacular. Он поддерживает несколько типов аутентификации (JWT, DRF Token или Custom) и включает встроенную интернационализацию для более 50 языков.\n\n## django-rest-auth / dj-rest-auth\n\nЭта библиотека предоставляет набор конечных точек REST API для регистрации, аутентификации (включая аутентификацию в социальных сетях), сброса пароля, получения и обновления данных пользователя и т.д. Имея эти конечные точки API, ваши клиентские приложения, такие как AngularJS, iOS, Android и другие, могут самостоятельно общаться с вашим бэкендом Django через REST API для управления пользователями.\n\nВ настоящее время существует два форка этого проекта.\n\n* [Django-rest-auth](https://github.com/Tivix/django-rest-auth) - оригинальный проект, [но в настоящее время не получает обновлений](https://github.com/Tivix/django-rest-auth/issues/568).\n* [Dj-rest-auth](https://github.com/jazzband/dj-rest-auth) - более новый форк проекта.\n\n## drf-social-oauth2\n\n[Drf-social-oauth2](https://github.com/wagnerdelima/drf-social-oauth2) - это фреймворк, который поможет вам аутентифицироваться у основных поставщиков социального oauth2, таких как Facebook, Google, Twitter, Orcid и др. Он генерирует токены в виде JWT с простой настройкой.\n\n## drfpasswordless\n\n[drfpasswordless](https://github.com/aaronn/django-rest-framework-passwordless) добавляет (по мотивам Medium, Square Cash) поддержку беспарольного входа в схему TokenAuthentication платформы DRF. Пользователи входят в систему и регистрируются с помощью токена, отправленного на контактную точку, например, адрес электронной почты или номер мобильного телефона.\n\n## django-rest-authemail\n\n[django-rest-authemail](https://github.com/celiao/django-rest-authemail) предоставляет RESTful API интерфейс для регистрации и аутентификации пользователей. Для аутентификации используются адреса электронной почты, а не имена пользователей. Доступны конечные точки API для регистрации, проверки электронной почты при регистрации, входа в систему, выхода из системы, сброса пароля, проверки сброса пароля, изменения электронной почты, проверки изменения электронной почты, изменения пароля и детализации пользователя. Полностью функциональный пример проекта и подробные инструкции прилагаются.\n\n## Django-Rest-Durin\n\n[Django-Rest-Durin](https://github.com/eshaan7/django-rest-durin) создана с идеей иметь одну библиотеку, которая делает аутентификацию токенов для нескольких Web/CLI/Mobile API клиентов через один интерфейс, но позволяет различную конфигурацию токенов для каждого API клиента, который потребляет API. Она обеспечивает поддержку нескольких токенов для каждого пользователя через пользовательские модели, представления, разрешения, которые работают с Django-Rest-Framework. Время истечения срока действия токена может быть разным для каждого API-клиента и настраивается через интерфейс администратора Django.\n\nБолее подробную информацию можно найти в [Документации](https://django-rest-durin.readthedocs.io/en/latest/index.html).\n\n## django_pyoidc\n\n[dango_pyoidc](https://github.com/makinacorpus/django_pyoidc) добавляет поддержку аутентификации OpenID Connect (OIDC). Это позволяет делегировать управление пользователями провайдеру идентификации, который может быть использован для реализации Single-Sign-On (SSO). Он обеспечивает поддержку большинства сценариев использования, таких как настройка сопоставления информации о токенах с моделями пользователей, использование аудиторий OIDC для контроля доступа и т. д.\n\nБолее подробную информацию можно найти в [Документации](https://django-pyoidc.readthedocs.io/latest/index.html).\n"
  },
  {
    "path": "api-guide/caching.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Кэширование\n\n> У одной женщины было очень острое сознание, но почти не было памяти... Она помнила достаточно, чтобы работать, и она много работала.\n>\n> * Лидия Дэвис\n\nКэширование в DRF хорошо работает с утилитами кэширования, предоставляемыми в Django.\n\n---\n\n## Использование кэша с apiview и наборами представлений\n\nDjango предоставляет [`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).\n\n```python\nfrom django.utils.decorators import method_decorator\nfrom django.views.decorators.cache import cache_page\nfrom django.views.decorators.vary import vary_on_cookie, vary_on_headers\n\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\nfrom rest_framework import viewsets\n\n\nclass UserViewSet(viewsets.ViewSet):\n    # With cookie: cache requested url for each user for 2 hours\n    @method_decorator(cache_page(60 * 60 * 2))\n    @method_decorator(vary_on_cookie)\n    def list(self, request, format=None):\n        content = {\n            \"user_feed\": request.user.get_user_feed(),\n        }\n        return Response(content)\n\n\nclass ProfileView(APIView):\n    # With auth: cache requested url for each user for 2 hours\n    @method_decorator(cache_page(60 * 60 * 2))\n    @method_decorator(vary_on_headers(\"Authorization\"))\n    def get(self, request, format=None):\n        content = {\n            \"user_feed\": request.user.get_user_feed(),\n        }\n        return Response(content)\n\n\nclass PostView(APIView):\n    # Cache page for the requested url\n    @method_decorator(cache_page(60 * 60 * 2))\n    def get(self, request, format=None):\n        content = {\n            \"title\": \"Post title\",\n            \"body\": \"Post content\",\n        }\n        return Response(content)\n```\n\n## Использование кэша с декоратором @api_view\n\nПри использовании декоратора `@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) могут быть вызваны напрямую.\n\n```python\nfrom django.views.decorators.cache import cache_page\nfrom django.views.decorators.vary import vary_on_cookie\n\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\n\n\n@cache_page(60 * 15)\n@vary_on_cookie\n@api_view([\"GET\"])\ndef get_user_list(request):\n    content = {\"user_feed\": request.user.get_user_feed()}\n    return Response(content)\n```\n\n---\n\n**Обратите внимание:** Декоратор [`cache_page`](https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cache) кэширует только ответы `GET` и `HEAD` со статусом 200.\n\n---\n"
  },
  {
    "path": "api-guide/content-negotiation.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Согласование контента\n\n> HTTP предусматривает несколько механизмов \"согласования контента\" - процесса выбора наилучшего представления для данного ответа при наличии нескольких представлений.\n>\n> - [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html), Fielding et al.\n\nСогласование контента - это процесс выбора одного из нескольких возможных форматов ответа для возврата клиенту, основанный на предпочтениях клиента или сервера.\n\n## Определение выбранного рендерера\n\nDRF использует простой стиль согласования контента для определения того, какой формат данных должен быть возвращен клиенту, основываясь на доступных рендерерах, приоритетах каждого из них и заголовке клиента `Accept:`. Используемый стиль частично зависит от клиента, а частично от сервера.\n\n1. Более конкретным типам носителей отдается предпочтение перед менее конкретными типами носителей.\n2. Если несколько типов медиа имеют одинаковую специфичность, то предпочтение отдается на основе порядка рендеринга, настроенного для данного представления.\n\nНапример, при следующем заголовке `Accept`:\n\n```http\napplication/json; indent=4, application/json, application/yaml, text/html, */*\n```\n\nПриоритеты для каждого из указанных типов носителей будут следующими:\n\n* `'application/json; indent=4'`\n* `'application/json'`, `'application/yaml'` и `'text/html'`\n* `*/*`\n\nЕсли запрашиваемое представление было настроено только с рендерерами для `YAML` и `HTML`, то DRF будет выбирать тот рендерер, который указан первым в списке `renderer_classes` или настройке `DEFAULT_RENDERER_CLASSES`.\n\nБолее подробную информацию о заголовке `HTTP Accept` смотрите в [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html).\n\n---\n\n**Примечание**: Значения \"q\" не учитываются DRF при определении предпочтений. Использование значений \"q\" негативно влияет на кэширование, и, по мнению автора, это ненужный и слишком сложный подход к согласованию контента.\n\nЭто верный подход, поскольку спецификация HTTP намеренно не определяет, как сервер должен взвешивать предпочтения, основанные на сервере, против предпочтений, основанных на клиенте.\n\n---\n\n# Переговоры по пользовательскому контенту\n\nМаловероятно, что вы захотите предоставить пользовательскую схему согласования контента для DRF, но вы можете сделать это при необходимости. Для реализации пользовательской схемы согласования контента переопределите `BaseContentNegotiation`.\n\nКлассы согласования контента DRF обрабатывают выбор как подходящего парсера для запроса, так и подходящего рендерера для ответа, поэтому вы должны реализовать оба метода `.select_parser(request, parsers)` и `.select_renderer(request, renderers, format_suffix)`.\n\nМетод `select_parser()` должен вернуть один экземпляр парсера из списка доступных парсеров, или `None`, если ни один из парсеров не может обработать входящий запрос.\n\nМетод `select_renderer()` должен возвращать кортеж из (экземпляр рендерера, тип медиа), либо вызывать исключение `NotAcceptable`.\n\n## Пример\n\nНиже представлен пользовательский класс согласования контента, который игнорирует запрос клиента при выборе подходящего парсера или рендерера.\n\n```python\nfrom rest_framework.negotiation import BaseContentNegotiation\n\nclass IgnoreClientContentNegotiation(BaseContentNegotiation):\n    def select_parser(self, request, parsers):\n        \"\"\"\n        Select the first parser in the `.parser_classes` list.\n        \"\"\"\n        return parsers[0]\n\n    def select_renderer(self, request, renderers, format_suffix):\n        \"\"\"\n        Select the first renderer in the `.renderer_classes` list.\n        \"\"\"\n        return (renderers[0], renderers[0].media_type)\n```\n\n## Указание согласования контента\n\nКласс согласования контента по умолчанию можно установить глобально, используя настройку `DEFAULT_CONTENT_NEGOTIATION_CLASS`. Например, следующие настройки будут использовать наш пример класса `IgnoreClientContentNegotiation`.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'myapp.negotiation.IgnoreClientContentNegotiation',\n}\n```\n\nВы также можете указать согласование контента, используемое для отдельного представления или набора представлений, используя представления на основе класса `APIView`.\n\n```python\nfrom myapp.negotiation import IgnoreClientContentNegotiation\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass NoNegotiationView(APIView):\n    \"\"\"\n    An example view that does not perform content negotiation.\n    \"\"\"\n    content_negotiation_class = IgnoreClientContentNegotiation\n\n    def get(self, request, format=None):\n        return Response({\n            'accepted media type': request.accepted_renderer.media_type\n        })\n```\n"
  },
  {
    "path": "api-guide/exceptions.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Исключения\n\n> Исключения... позволяют чисто организовать обработку ошибок в центральном или высокоуровневом месте в структуре программы.\n>\n> &mdash; Даг Хеллманн, [Python Exception Handling Techniques](https://doughellmann.com/blog/2009/06/19/python-exception-handling-techniques/)\n\n## Обработка исключений в представлениях DRF\n\nПредставления DRF обрабатывают различные исключения и возвращают соответствующие ответы на ошибки.\n\nОбрабатываемыми исключениями являются:\n\n* Подклассы `APIException`, возникающие внутри DRF.\n* Исключение Django `Http404`.\n* Исключение Django `PermissionDenied`.\n\nВ каждом случае DRF вернет ответ с соответствующим кодом состояния и типом содержимого. В теле ответа будут содержаться любые дополнительные сведения о характере ошибки.\n\nБольшинство ответов на ошибки будут содержать ключ `detail` в теле ответа.\n\nНапример, следующий запрос:\n\n```http\nDELETE http://api.example.com/foo/bar HTTP/1.1\nAccept: application/json\n```\n\nМожет быть получен ответ об ошибке, указывающий на то, что метод `DELETE` не разрешен для данного ресурса:\n\n```http\nHTTP/1.1 405 Method Not Allowed\nContent-Type: application/json\nContent-Length: 42\n\n{\"detail\": \"Method 'DELETE' not allowed.\"}\n```\n\nОшибки валидации обрабатываются несколько иначе, и в качестве ключей в ответе будут указаны имена полей. Если ошибка валидации не относится к конкретному полю, то будет использоваться ключ \"non_field_errors\", или любое строковое значение, установленное для параметра `NON_FIELD_ERRORS_KEY`.\n\nПример ошибки валидации может выглядеть следующим образом:\n\n```http\nHTTP/1.1 400 Bad Request\nContent-Type: application/json\nContent-Length: 94\n\n{\"amount\": [\"A valid integer is required.\"], \"description\": [\"This field may not be blank.\"]}\n```\n\n## Пользовательская обработка исключений\n\nВы можете реализовать пользовательскую обработку исключений, создав функцию-обработчик, которая преобразует исключения, возникающие в ваших представлениях API, в объекты ответа. Это позволяет вам контролировать стиль ответов на ошибки, используемый вашим API.\n\nФункция должна принимать пару аргументов, первый из которых - обрабатываемое исключение, а второй - словарь, содержащий любой дополнительный контекст, например, обрабатываемое в данный момент представление. Функция обработчика исключения должна либо возвращать объект `Response`, либо возвращать `None`, если исключение не может быть обработано. Если обработчик возвращает `None`, то исключение будет повторно поднято, и Django вернет стандартный ответ HTTP `500 \"Server error\"`.\n\nНапример, вы можете захотеть убедиться, что все ответы на ошибки включают код состояния HTTP в теле ответа, например, так:\n\n```http\nHTTP/1.1 405 Method Not Allowed\nContent-Type: application/json\nContent-Length: 62\n\n{\"status_code\": 405, \"detail\": \"Method 'DELETE' not allowed.\"}\n```\n\nЧтобы изменить стиль ответа, вы можете написать следующий пользовательский обработчик исключений:\n\n```python\nfrom rest_framework.views import exception_handler\n\ndef custom_exception_handler(exc, context):\n    # Call REST framework's default exception handler first,\n    # to get the standard error response.\n    response = exception_handler(exc, context)\n\n    # Now add the HTTP status code to the response.\n    if response is not None:\n        response.data['status_code'] = response.status_code\n\n    return response\n```\n\nАргумент `context` не используется обработчиком по умолчанию, но может быть полезен, если обработчику исключений нужна дополнительная информация, например, обрабатываемое в данный момент представление, доступ к которому можно получить как `context['view']`.\n\nОбработчик исключений также должен быть настроен в ваших настройках, используя ключ настройки `EXCEPTION_HANDLER`. Например:\n\n```python\nREST_FRAMEWORK = {\n    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'\n}\n```\n\nЕсли параметр `'EXCEPTION_HANDLER'` не указан, по умолчанию используется стандартный обработчик исключений, предоставляемый DRF:\n\n```python\nREST_FRAMEWORK = {\n    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'\n}\n```\n\nОбратите внимание, что обработчик исключений будет вызываться только для ответов, сгенерированных поднятыми исключениями. Он не будет использоваться для ответов, возвращаемых непосредственно представлением, таких как ответы `HTTP_400_BAD_REQUEST`, которые возвращаются общими представлениями при неудачной проверке сериализатора.\n\n---\n\n# API Reference\n\n## APIException\n\n**Сигнатура:** `APIException()`.\n\n**базовый класс** для всех исключений, возникающих внутри класса `APIView` или `@api_view`.\n\nЧтобы предоставить пользовательское исключение, подкласс `APIException` и установите атрибуты `.status_code`, `.default_detail` и `.default_code` для класса.\n\nНапример, если ваш API полагается на сторонний сервис, который иногда может быть недоступен, вы можете захотеть реализовать исключение для кода ответа HTTP \"503 Service Unavailable\". Это можно сделать следующим образом:\n\n```python\nfrom rest_framework.exceptions import APIException\n\nclass ServiceUnavailable(APIException):\n    status_code = 503\n    default_detail = 'Service temporarily unavailable, try again later.'\n    default_code = 'service_unavailable'\n```\n\n#### Проверка исключений API\n\nСуществует ряд различных свойств, доступных для проверки состояния исключения API. Вы можете использовать их для создания пользовательской обработки исключений для вашего проекта.\n\nДоступными атрибутами и методами являются:\n\n* `.detail` - Возвращает текстовое описание ошибки.\n* `.get_codes()` - Возвращает идентификатор кода ошибки.\n* `.get_full_details()` - Возвращает как текстовое описание, так и идентификатор кода.\n\nВ большинстве случаев деталь ошибки будет простым элементом:\n\n```python\n>>> print(exc.detail)\nYou do not have permission to perform this action.\n>>> print(exc.get_codes())\npermission_denied\n>>> print(exc.get_full_details())\n{'message':'You do not have permission to perform this action.','code':'permission_denied'}\n```\n\nВ случае ошибок валидации деталь ошибки будет представлять собой либо список, либо словарь элементов:\n\n```python\n>>> print(exc.detail)\n{\"name\":\"This field is required.\",\"age\":\"A valid integer is required.\"}\n>>> print(exc.get_codes())\n{\"name\":\"required\",\"age\":\"invalid\"}\n>>> print(exc.get_full_details())\n{\"name\":{\"message\":\"This field is required.\",\"code\":\"required\"},\"age\":{\"message\":\"A valid integer is required.\",\"code\":\"invalid\"}}\n```\n\n## ParseError\n\n**Сигнатура:** `ParseError(detail=None, code=None)`.\n\nВозникает, если запрос содержит неправильно сформированные данные при доступе к `request.data`.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"400 Bad Request\".\n\n## AuthenticationFailed\n\n**Сигнатура:** `AuthenticationFailed(detail=None, code=None)`.\n\nВозникает, когда входящий запрос содержит неправильную аутентификацию.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"401 Unauthenticated\", но оно также может привести к ответу \"403 Forbidden\", в зависимости от используемой схемы аутентификации. Более подробную информацию см. в документации [authentication documentation](authentication.md).\n\n## NotAuthenticated\n\n**Сигнатура:** `NotAuthenticated(detail=None, code=None)`.\n\nВозникает, когда неаутентифицированный запрос не прошел проверку на разрешение.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"401 Unauthenticated\", но оно также может привести к ответу \"403 Forbidden\", в зависимости от используемой схемы аутентификации. Более подробную информацию см. в документации [authentication documentation](authentication.md).\n\n## PermissionDenied\n\n**Сигнатура:** `PermissionDenied(detail=None, code=None)`.\n\nВозникает, когда аутентифицированный запрос не прошел проверку на разрешение.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"403 Forbidden\".\n\n## NotFound\n\n**Сигнатура:** `NotFound(detail=None, code=None)`.\n\nВозникает, когда ресурс не существует по указанному URL. Это исключение эквивалентно стандартному исключению `Http404` Django.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"404 Not Found\".\n\n## MethodNotAllowed\n\n**Сигнатура:** `MethodNotAllowed(method, detail=None, code=None)`.\n\nВозникает, когда происходит входящий запрос, который не сопоставлен с методом-обработчиком на представлении.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"405 Method Not Allowed\".\n\n## Неприемлемо\n\n**Сигнатура:** `NotAcceptable(detail=None, code=None)`.\n\nВозникает, когда поступает запрос с заголовком `Accept`, который не может быть удовлетворен ни одним из доступных рендереров.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"406 Not Acceptable\".\n\n## UnsupportedMediaType\n\n**Сигнатура:** `UnsupportedMediaType(media_type, detail=None, code=None)`.\n\nВозникает, если при обращении к `request.data` нет парсеров, способных обработать тип содержимого данных запроса.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"415 Unsupported Media Type\".\n\n## Дроссель\n\n**Сигнатура:** `Throttled(wait=None, detail=None, code=None)`.\n\nВозникает, когда входящий запрос не проходит проверку на дросселирование.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"429 Too Many Requests\".\n\n## ValidationError\n\n**Сигнатура:** `ValidationError(detail=None, code=None)`.\n\nИсключение `ValidationError` немного отличается от других классов `APIException`:\n\n* Аргумент `detail` может быть списком или словарем деталей ошибки, а также может быть вложенной структурой данных. Используя словарь, вы можете указать ошибки на уровне полей при выполнении проверки на уровне объектов в методе `validate()` сериализатора. Например. `raise serializers.ValidationError({'name': 'Please enter a valid name.'})`.\n* По соглашению вы должны импортировать модуль `serializers` и использовать полностью определенный `ValidationError`, чтобы отличить его от встроенной ошибки валидации Django. Например. `raise serializers.ValidationError('Это поле должно быть целочисленным значением.')`.\n\nКласс `ValidationError` должен использоваться для сериализатора и валидации полей, а также классами валидаторов. Он также вызывается при вызове `serializer.is_valid` с именованным аргументом `raise_exception`:\n\n```python\nserializer.is_valid(raise_exception=True)\n```\n\nОбщие представления используют флаг `raise_exception=True`, что означает, что вы можете переопределить стиль ответов на ошибки валидации глобально в вашем API. Для этого используйте пользовательский обработчик исключений, как описано выше.\n\nПо умолчанию это исключение приводит к ответу с кодом состояния HTTP \"400 Bad Request\".\n\n---\n\n# Общие представления об ошибках\n\nDRF предоставляет два представления ошибок, подходящих для предоставления общих JSON ответов `500` Server Error и `400` Bad Request. (Стандартные представления ошибок Django предоставляют HTML-ответы, которые могут не подойти для приложения, использующего только API).\n\nИспользуйте их согласно [документации по настройке представлений ошибок в Django](https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views).\n\n## `rest_framework.exceptions.server_error`\n\nВозвращает ответ с кодом состояния `500` и типом содержимого `application/json`.\n\nУстанавливается как `handler500`:\n\n```python\nhandler500 = 'rest_framework.exceptions.server_error'\n```\n\n## `rest_framework.exceptions.bad_request`\n\nВозвращает ответ с кодом статуса `400` и типом содержимого `application/json`.\n\nУстанавливается как `handler400`:\n\n```python\nhandler400 = 'rest_framework.exceptions.bad_request'\n```\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## Стандартизированные ошибки ДРФ\n\nПакет [drf-standardized-errors](https://github.com/ghazi-git/drf-standardized-errors) предоставляет обработчик исключений, который генерирует одинаковый формат для всех ответов 4xx и 5xx. Он является заменой стандартного обработчика исключений и позволяет настраивать формат ответа на ошибку без переписывания всего обработчика исключений. Стандартизированный формат ответа на ошибку легче документировать и проще обрабатывать потребителям API.\n"
  },
  {
    "path": "api-guide/fields.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Поля сериализатора\n\n> Каждое поле в классе Form отвечает не только за проверку данных, но и за их \"очистку\" &mdash; приведение их к единообразному формату.\n>\n> &mdash; [Django documentation](https://docs.djangoproject.com/en/stable/ref/forms/api/#django.forms.Form.cleaned_data)\n\nПоля сериализатора выполняют преобразование между примитивными значениями и внутренними типами данных. Они также занимаются проверкой входных значений, а также получением и установкой значений из родительских объектов.\n\n---\n\n**Примечание:** Поля сериализатора объявляются в `fields.py`, но по соглашению вы должны импортировать их с помощью `from rest_framework import serializers` и обращаться к полям как `serializers.<FieldName>`.\n\n---\n\n## Основные аргументы\n\nКаждый конструктор класса поля сериализатора принимает как минимум эти аргументы. Некоторые классы `Field` принимают дополнительные, специфические для поля аргументы, но следующие должны приниматься всегда:\n\n### `read_only`\n\nПоля, доступные только для чтения, включаются в выходные данные API, но не должны включаться во входные данные при операциях создания или обновления. Любые поля `read_only`, по ошибке включенные во входные данные сериализатора, будут проигнорированы.\n\nУстановите значение `True`, чтобы гарантировать, что поле используется при сериализации представления, но не используется при создании или обновлении экземпляра во время десериализации.\n\nПо умолчанию `False`.\n\n### `write_only`\n\nУстановите значение `True`, чтобы поле могло использоваться при обновлении или создании экземпляра, но не включалось при сериализации представления.\n\nПо умолчанию `False`.\n\n### `required`\n\nОбычно ошибка возникает, если поле не предоставлено во время десериализации. Установите значение `False`, если это поле не обязательно должно присутствовать при десериализации.\n\nУстановка этого значения в `False` также позволяет не выводить атрибут объекта или ключ словаря при сериализации экземпляра. Если ключ не присутствует, он просто не будет включен в выходное представление.\n\nПо умолчанию имеет значение `True`. Если вы используете [Model Serializer](../api-guide/serializers.md#modelserializer), значение по умолчанию будет `False`, если вы указали `default` или если соответствующее поле `Model` имеет `blank=True` или `null=True` и в то же время не является частью ограничения по уникальности. (Обратите внимание, что без значения `default` [ограничения по уникальности приведут к тому, что поле станет обязательным.](../api-guide/validators.md#необязательные-поля).)\n\n### `default`\n\nЕсли установлено, это значение по умолчанию, которое будет использоваться для поля, если значение не передано. Если значение не задано, то по умолчанию атрибут вообще не заполняется.\n\nЗначение `default` не применяется во время операций частичного обновления. В случае частичного обновления только те поля, которые указаны во входящих данных, получат подтвержденное значение.\n\nМожет быть задана как функция или другой вызываемый объект, в этом случае значение будет выполняться каждый раз при его использовании. При вызове оно не получает никаких аргументов. Если вызываемая функция имеет атрибут `requires_context = True`, то поле сериализатора будет передано в качестве аргумента.\n\nНапример:\n\n```python\nclass CurrentUserDefault:\n    \"\"\"\n    May be applied as a `default=...` value on a serializer field.\n    Returns the current user.\n    \"\"\"\n    requires_context = True\n\n    def __call__(self, serializer_field):\n        return serializer_field.context['request'].user\n```\n\nПри сериализации экземпляра будет использоваться значение по умолчанию, если атрибут объекта или ключ словаря не присутствует в экземпляре.\n\nОбратите внимание, что установка значения `default` подразумевает, что поле не является обязательным. Включение обоих именованных аргументов `default` и `required` является недопустимым и приведет к ошибке.\n\n### `allow_null`\n\nОбычно, если в поле сериализатора передается `None`, возникает ошибка. Установите этот именованный аргумент в `True`, если `None` должно считаться допустимым значением.\n\nОбратите внимание, что без явного указания `default` установка этого аргумента в `True` подразумевает `default` значение `null` для вывода сериализации, но не подразумевает значение по умолчанию для десериализации ввода.\n\nПо умолчанию `False`.\n\n### `source`\n\nИмя атрибута, который будет использоваться для заполнения поля. Может быть методом, который принимает только аргумент `self`, например `URLField(source='get_absolute_url')`, или может использовать точечную нотацию для обхода атрибутов, например `EmailField(source='user.email')`.\n\nПри сериализации полей с точечной нотацией может потребоваться предоставить значение `default`, если какой-либо объект отсутствует или пуст при обходе атрибута. Остерегайтесь возможных проблем n+1 при использовании атрибута source, если вы обращаетесь к реляционной модели orm. Например:\n\n```python\nclass CommentSerializer(serializers.Serializer):\n    email = serializers.EmailField(source=\"user.email\")\n```\n\nВ этом случае объект пользователя должен быть извлечен из базы данных, если он не был предварительно извлечен. Если это нежелательно, убедитесь, что вы используете методы `prefetch_related` и `select_related` соответствующим образом. Более подробную информацию об этих методах можно найти в [документации django](https://docs.djangoproject.com/en/stable/ref/models/querysets/#django.db.models.query.QuerySet.select_related).\n\nЗначение `source='*'` имеет особое значение и используется для указания на то, что в поле должен быть передан весь объект. Это может быть полезно для создания вложенных представлений или для полей, которым требуется доступ к полному объекту для определения выходного представления.\n\nПо умолчанию используется имя поля.\n\n### `validators`\n\nСписок функций валидаторов, которые должны быть применены к вводимому полю и которые либо выдают ошибку валидации, либо просто возвращаются. Функции валидатора обычно должны вызывать `serializers.ValidationError`, но встроенный в Django `ValidationError` также поддерживается для совместимости с валидаторами, определенными в кодовой базе Django или в сторонних пакетах Django.\n\n### `error_messages`\n\nСловарь кодов ошибок и сообщений об ошибках.\n\n### `label`\n\nКороткая текстовая строка, которая может быть использована в качестве имени поля в полях HTML-формы или других описательных элементах.\n\n### `help_text`\n\nТекстовая строка, которая может быть использована в качестве описания поля в полях HTML-формы или других описательных элементах.\n\n### `initial`\n\nЗначение, которое должно использоваться для предварительного заполнения значений полей HTML-формы. Вы можете передать ему вызываемый объект, как и в случае с любым обычным полем Django `Field`:\n\n```python\nimport datetime\nfrom rest_framework import serializers\nclass ExampleSerializer(serializers.Serializer):\n    day = serializers.DateField(initial=datetime.date.today)\n```\n\n### `style`\n\nСловарь пар ключ-значение, которые можно использовать для управления тем, как рендереры должны отображать поле.\n\nДвумя примерами здесь являются `'input_type'` и `'base_template'`:\n\n```python\n# Use <input type=\"password\"> for the input.\npassword = serializers.CharField(\n    style={'input_type': 'password'}\n)\n\n# Use a radio input instead of a select input.\ncolor_channel = serializers.ChoiceField(\n    choices=['red', 'green', 'blue'],\n    style={'base_template': 'radio.html'}\n)\n```\n\nБолее подробную информацию можно найти в документации [HTML & Forms](../topics/html-and-forms.md).\n\n---\n\n# Булевы поля\n\n## BooleanField\n\nБулево представление.\n\nПри использовании HTML-кодированных форм ввода имейте в виду, что отсутствие значения всегда будет рассматриваться как установка поля в `False`, даже если для него указана опция `default=True`. Это происходит потому, что чекбоксы HTML представляют состояние без флажка в виде отсутствия значение, поэтому DRF воспринимает отсутствие значения как `False`.\n\nОбратите внимание, что в 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`.\n\nСоответствует `django.db.models.fields.BooleanField`.\n\n**Сигнатура:** `BooleanField()`.\n\n---\n\n# Строковые поля\n\n## CharField\n\nТекстовое представление. Опционально проверяет, чтобы текст был короче `max_length` и длиннее `min_length`.\n\nСоответствует `django.db.models.fields.CharField` или `django.db.models.fields.TextField`.\n\n**Сигнатура:** `CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)`.\n\n* `max_length` - Проверяет, что вводимые данные содержат не более этого количества символов.\n* `min_length` - Проверяет, что вводимые данные содержат не менее этого количества символов.\n* `allow_blank` - Если установлено значение `True`, то пустая строка будет считаться допустимым значением. Если установлено значение `False`, то пустая строка будет считаться недопустимой и вызовет ошибку валидации. По умолчанию `False`.\n* `trim_whitespace` - Если установлено значение `True`, то пробельные символы в начале и в конце будут обрезаны. По умолчанию `True`.\n\nОпция `allow_null` также доступна для строковых полей, хотя ее использование не рекомендуется в пользу `allow_blank`. Можно установить и `allow_blank=True`, и `allow_null=True`, но это означает, что для строковых представлений будут допустимы два разных типа пустых значений, что может привести к несоответствию данных и тонким ошибкам в работе приложения.\n\n## EmailField\n\nТекстовое представление, проверяющее, является ли этот текст действительным адресом электронной почты.\n\nСоответствует `django.db.models.fields.EmailField`.\n\n**Сигнатура:** `EmailField(max_length=None, min_length=None, allow_blank=False)`.\n\n## RegexField\n\nТекстовое представление, которое проверяет соответствие заданного значения определенному регулярному выражению.\n\nСоответствует `django.forms.fields.RegexField`.\n\n**Сигнатура:** `RegexField(regex, max_length=None, min_length=None, allow_blank=False)`.\n\nОбязательный аргумент `regex` может быть либо строкой, либо скомпилированным объектом регулярного выражения python.\n\nДля проверки используется `django.core.validators.RegexValidator` от Django.\n\n## SlugField\n\nПоле `RegexField`, которое проверяет вводимые данные на соответствие шаблону `[a-zA-Z0-9_-]+`.\n\nСоответствует `django.db.models.fields.SlugField`.\n\n**Сигнатура:** `SlugField(max_length=50, min_length=None, allow_blank=False)`.\n\n## URLField\n\nПоле `RegexField`, которое проверяет вводимые данные на соответствие шаблону URL. Ожидаются полностью определенные URL вида `http://<host>/<path>`.\n\nСоответствует `django.db.models.fields.URLField`. Для проверки используется `django.core.validators.URLValidator`.\n\n**Сигнатура:** `URLField(max_length=200, min_length=None, allow_blank=False)`.\n\n## UUIDField\n\nПоле, которое гарантирует, что вводимые данные являются правильной строкой UUID. Метод `to_internal_value` возвращает экземпляр `uuid.UUID`. На выходе поле вернет строку в каноническом дефисном формате, например:\n\n```\n\"de305d54-75b4-431b-adb2-eb6b9e546013\"\n```\n\n**Сигнатура:** `UUIDField(format='hex_verbose')`.\n\n* `format`: Определяет формат представления значения uuid\n    - `'hex_verbose'` - Каноническое шестнадцатеричное представление, включая дефисы: `\"5ce0e9a5-5ffa-654b-cee0-1238041fb31a\"`.\n    - `'hex'` - Компактное шестнадцатеричное представление UUID, не включая дефисы: `\"5ce0e9a55ffa654bcee01238041fb31a\"`.\n    - `'int'` - 128-битное целочисленное представление UUID: `\"123456789012312313134124512351145145114\"`.\n    - `'urn'` - RFC 4122 URN-представление UUID: `'urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a'`.\n    Изменение параметров `формата` влияет только на значения представления. Все форматы принимаются функцией `to_internal_value`.\n\n## FilePathField\n\nПоле, выбор которого ограничен именами файлов в определенном каталоге файловой системы\n\nСоответствует `django.forms.fields.FilePathField`.\n\n**Сигнатура:** `FilePathField(path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs)`.\n\n* `path` - Абсолютный путь файловой системы к директории, из которой это поле FilePathField должно получить свой выбор.\n* `match` - Регулярное выражение в виде строки, которое FilePathField будет использовать для фильтрации имен файлов.\n* `recursive` - Указывает, должны ли включаться все подкаталоги пути. По умолчанию `False`.\n* `allow_files` - Указывает, должны ли включаться файлы в указанном месте. По умолчанию `True`. Либо это, либо `allow_folders` должно быть `True`.\n* `allow_folders` - Указывает, должны ли включаться папки в указанном месте. По умолчанию `False`. Либо это, либо `allow_files` должно быть `True`.\n\n## IPAddressField\n\nПоле, гарантирующее, что вводимые данные являются действительной строкой IPv4 или IPv6.\n\nСоответствует `django.forms.fields.IPAddressField` и `django.forms.fields.GenericIPAddressField`.\n\n**Сигнатура**: `IPAddressField(protocol='both', unpack_ipv4=False, **options)`.\n\n* `protocol` Ограничивает допустимые входы указанным протоколом. Принимаемые значения: `'both'` (по умолчанию), `'IPv4'` или `'IPv6'`. Соответствие не зависит от регистра.\n* `unpack_ipv4` Распаковывает сопоставленные IPv4-адреса, например `::ffff:192.0.2.1`. Если эта опция включена, то адрес будет распакован в `192.0.2.1`. По умолчанию отключена. Может использоваться только в том случае, если для протокола установлено значение `'both'`.\n\n---\n\n# Числовые поля\n\n## IntegerField\n\nЦелочисленное представление.\n\nСоответствует `django.db.models.fields.IntegerField`, `django.db.models.fields.SmallIntegerField`, `django.db.models.fields.PositiveIntegerField` и `django.db.models.fields.PositiveSmallIntegerField`.\n\n**Сигнатура**: `IntegerField(max_value=None, min_value=None)`.\n\n* `max_value` Проверяет, что предоставленное число не больше этого значения.\n* `min_value` Проверяет, что предоставленное число не меньше этого значения.\n\n## BigIntegerField\n\nПредставление большого целого числа.\n\nСоответствует `django.db.models.fields.BigIntegerField`.\n\n**Сигнатура**: `BigIntegerField(max_value=None, min_value=None, coerce_to_string=None)`\n\n* `max_value` Проверяет, что предоставленное число не больше этого значения.\n* `min_value` Проверяет, что предоставленное число не меньше этого значения.\n* `coerce_to_string` Устанавливается в `True`, если для представления должны возвращаться строковые значения, или в `False`, если должны возвращаться объекты `BigInteger`. По умолчанию используется то же значение, что и для ключа настройки `COERCE_BIGINT_TO_STRING`, которое будет `False`, если не переопределено. Если сериализатор возвращает объекты `BigInteger`, то окончательный формат вывода будет определяться рендерером.\n\n## FloatField\n\nПредставление числа с плавающей точкой.\n\nСоответствует `django.db.models.fields.FloatField`.\n\n**Сигнатура**: `FloatField(max_value=None, min_value=None)`.\n\n* `max_value` Проверяет, что предоставленное число не больше этого значения.\n* `min_value` Проверяет, что предоставленное число не меньше этого значения.\n\n## DecimalField\n\nДесятичное представление, представленное в Python экземпляром `Decimal`.\n\nСоответствует `django.db.models.fields.DecimalField`.\n\n**Сигнатура**: `DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)`.\n\n* `max_digits` Максимальное количество цифр, допустимое в числе. Оно должно быть либо `None`, либо целым числом, большим или равным `decimal_places`.\n* `decimal_places` Количество десятичных знаков, которые следует хранить в числе.\n* `coerce_to_string` Установите значение `True`, если для представления должны быть возвращены строковые значения, или `False`, если должны быть возвращены объекты `Decimal`. По умолчанию имеет то же значение, что и ключ настроек `COERCE_DECIMAL_TO_STRING`, который будет `True`, если его не переопределить. Если сериализатор возвращает `Decimal` объекты, то окончательный формат вывода будет определяться рендерером. Обратите внимание, что установка `localize` заставит значение быть `True`.\n* `max_value` Проверяет, что предоставленное число не больше этого значения. Должно быть целым числом или объектом `Decimal`.\n* `min_value` Проверяет, что предоставленное число не меньше этого значения. Должно быть целым числом или объектом `Decimal`.\n* `localize` Установите значение `True`, чтобы включить локализацию ввода и вывода на основе текущей локали. Это также заставит `coerce_to_string` принять значение `True`. По умолчанию установлено значение `False`. Обратите внимание, что форматирование данных будет включено, если вы установили `USE_L10N=True` в вашем файле настроек.\n* `rounding` Устанавливает режим округления, используемый при квантовании с заданной точностью. Допустимые значения: [`decimal` module rounding modes](https://docs.python.org/3/library/decimal.html#rounding-modes). По умолчанию `None`.\n* `normalize_output` Нормализует десятичное значение при сериализации. При этом удаляются все нули в конце строки и точность значения изменяется до минимально необходимой, чтобы можно было представить значение без потери данных. По умолчанию имеет значение `False`.\n\n#### Пример использования\n\nДля проверки чисел до 999 с разрешением 2 знака после запятой можно использовать:\n\n```python\nserializers.DecimalField(max_digits=5, decimal_places=2)\n```\n\nА также для проверки чисел вплоть до миллиарда с разрешением 10 знаков после запятой:\n\n```python\nserializers.DecimalField(max_digits=19, decimal_places=10)\n```\n\n---\n\n# Поля даты и времени\n\n## DateTimeField\n\nПредставление даты и времени.\n\nСоответствует `django.db.models.fields.DateTimeField`.\n\n**Сигнатура:** `DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None, default_timezone=None)`.\n\n* `format` - Строка, представляющая формат вывода. Если он не указан, то по умолчанию принимает значение, равное ключу настроек `DATETIME_FORMAT`, который будет `'iso-8601'`, если он не установлен. Установка в строку формата указывает на то, что возвращаемые значения `to_representation` должны быть принудительно приведены к строковому виду. Строки формата описаны ниже. Установка этого значения в `None` указывает, что `to_representation` должен возвращать объекты Python `datetime`. В этом случае кодировка времени будет определяться рендерером.\n* `input_formats` - Список строк, представляющих входные форматы, которые могут быть использованы для разбора даты. Если он не указан, будет использоваться настройка `DATETIME_INPUT_FORMATS`, которая по умолчанию принимает значение `['iso-8601']`.\n* `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 будут наивными.\n\n#### Строки формата `DateTimeField`.\n\nСтроки формата могут быть либо [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'`)\n\nЕсли для формата используется значение `None`, то объекты `datetime` будут возвращаться методом `to_representation`, а окончательное представление на выходе будет определяться классом renderer.\n\n#### Поля модели `auto_now` и `auto_now_add`.\n\nПри использовании `ModelSerializer` или `HyperlinkedModelSerializer` обратите внимание, что любые поля модели с `auto_now=True` или `auto_now_add=True` будут использовать поля сериализатора, которые по умолчанию имеют значение `read_only=True`.\n\nЕсли вы хотите переопределить это поведение, вам нужно будет явно объявить `DateTimeField` в сериализаторе. Например:\n\n```python\nclass CommentSerializer(serializers.ModelSerializer):\n    created = serializers.DateTimeField()\n\n    class Meta:\n        model = Comment\n```\n\n## DateField\n\nПредставление даты.\n\nСоответствует `django.db.models.fields.DateField`.\n\n**Сигнатура:** `DateField(format=api_settings.DATE_FORMAT, input_formats=None)`.\n\n* `format` - Строка, представляющая формат вывода. Если он не указан, то по умолчанию принимает значение, равное ключу настроек `DATE_FORMAT`, который будет `'iso-8601'`, если он не установлен. Установка в строку формата указывает на то, что возвращаемые значения `to_representation` должны быть преобразованы в строковый вывод. Строки формата описаны ниже. Установка этого значения в `None` указывает, что `to_representation` должен возвращать объекты Python `date`. В этом случае кодировка даты будет определяться рендерером.\n* `input_formats` - Список строк, представляющих входные форматы, которые могут быть использованы для разбора даты. Если он не указан, будет использоваться настройка `DATE_INPUT_FORMATS`, которая по умолчанию принимает значение `['iso-8601']`.\n\n#### Строки формата `DateField`.\n\nСтроки формата могут быть либо [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'`)\n\n## TimeField\n\nПредставление времени.\n\nСоответствует `django.db.models.fields.TimeField`.\n\n**Сигнатура:** `TimeField(format=api_settings.TIME_FORMAT, input_formats=None)`.\n\n* `format` - строка, представляющая формат вывода. Если он не указан, то по умолчанию принимает значение, равное ключу настроек `TIME_FORMAT`, который будет `'iso-8601'`, если он не установлен. Установка в строку формата указывает на то, что возвращаемые значения `to_representation` должны быть принудительно выведены в строку. Строки формата описаны ниже. Установка этого значения в `None` указывает, что `to_representation` должен возвращать объекты Python `time`. В этом случае кодировка времени будет определяться рендерером.\n* `input_formats` - Список строк, представляющих входные форматы, которые могут быть использованы для разбора даты. Если он не указан, будет использоваться настройка `TIME_INPUT_FORMATS`, которая по умолчанию принимает значение `['iso-8601']`.\n\n#### Строки формата `TimeField`.\n\nСтроки формата могут быть либо [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'`)\n\n## DurationField\n\nПредставление длительности. Соответствует `django.db.models.fields.DurationField`.\n\nВ `validated_data` для этих полей будет содержаться экземпляр `datetime.timedelta`.\n\n**Сигнатура:** `DurationField(format=api_settings.DURATION_FORMAT, max_value=None, min_value=None)`.\n\n* `format` - строка, представляющая формат вывода.  Если не указано, по умолчанию используется то же значение, что и в ключе настроек `DURATION_FORMAT`, которое будет `'django'`, если не установлено. Форматы описаны ниже. Установка этого значения в `None` указывает, что объекты Python `timedelta` должны возвращаться `to_representation`. В этом случае кодировка даты будет определяться рендерером.\n* `max_value` Проверяет, что предоставленная продолжительность не больше этого значения.\n* `min_value` Проверяет, что предоставленная продолжительность не меньше этого значения.\n\n#### Форматы `DurationField`\n\nФормат может быть либо специальной строкой `'iso-8601'`, которая указывает, что должны использоваться интервалы в стиле [ISO 8601](https://www.w3.org/TR/NOTE-datetime) (например, `'P4DT1H15M20S'`), либо `'django'`, что указывает на то, что должен использоваться формат интервала Django `'[DD] [HH:[MM:]]ss[.uuuuuu]'` (например, `'4 1:15:20'`).\n\n---\n\n# Поля выбора\n\n## ChoiceField\n\nПоле, которое может принимать значение из ограниченного набора вариантов.\n\nИспользуется `ModelSerializer` для автоматической генерации полей, если соответствующее поле модели содержит аргумент `choices=...`.\n\n**Сигнатура:** `ChoiceField(choices)`.\n\n* `choices` - Список допустимых значений, или список кортежей `(key, display_name)`.\n* `allow_blank` - Если установлено значение `True`, то пустая строка будет считаться допустимым значением. Если установлено значение `False`, то пустая строка будет считаться недопустимой и вызовет ошибку валидации. По умолчанию `False`.\n* `html_cutoff` - Если установлено, то это будет максимальное количество вариантов, которые будут отображаться в выпадающем списке HTML select. Может использоваться для того, чтобы автоматически генерируемые поля выбора с очень большим количеством возможных вариантов выбора не мешали отрисовке шаблона. По умолчанию `None`.\n* `html_cutoff_text` - Если установлено, то будет отображаться текстовый индикатор, если максимальное количество элементов было отсечено в выпадающем списке HTML select. По умолчанию `\"More than {count} items...\"`.\n\nОба параметра `allow_blank` и `allow_null` являются допустимыми для `ChoiceField`, хотя настоятельно рекомендуется использовать только один из них, а не оба. `allow_blank` следует предпочесть для текстовых вариантов, а `allow_null` - для числовых или других нетекстовых вариантов.\n\n## MultipleChoiceField\n\nПоле, которое может принимать нулевое, одно или множество значений, выбираемых из ограниченного набора вариантов. Принимает один обязательный аргумент. `to_internal_value` возвращает `set`, содержащий выбранные значения.\n\n**Сигнатура:** `MultipleChoiceField(choices)`.\n\n* `choices` - Список допустимых значений, или список кортежей `(key, display_name)`.\n* `allow_blank` - Если установлено значение `True`, то пустая строка будет считаться допустимым значением. Если установлено значение `False`, то пустая строка будет считаться недопустимой и вызовет ошибку валидации. По умолчанию `False`.\n* `html_cutoff` - Если установлено, то это будет максимальное количество вариантов, которые будут отображаться в выпадающем списке HTML select. Может использоваться для того, чтобы автоматически генерируемые поля выбора с очень большим количеством возможных вариантов выбора не мешали отрисовке шаблона. По умолчанию `None`.\n* `html_cutoff_text` - Если установлено, то будет отображаться текстовый индикатор, если максимальное количество элементов было отсечено в выпадающем списке HTML select. По умолчанию `\"More than {count} items...\"`.\n\nКак и в случае с `ChoiceField`, оба параметра `allow_blank` и `allow_null` являются допустимыми, хотя настоятельно рекомендуется использовать только один из них, а не оба. `allow_blank` следует предпочесть для текстовых вариантов, а `allow_null` - для числовых или других нетекстовых вариантов.\n\n---\n\n# Поля для загрузки файлов\n\n#### Парсеры и загрузка файлов.\n\nКлассы `FileField` и `ImageField` подходят только для использования с `MultiPartParser` или `FileUploadParser`. Большинство парсеров, например, JSON, не поддерживают загрузку файлов. Для работы с загруженными файлами в Django используются штатные [FILE_UPLOAD_HANDLERS](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-FILE_UPLOAD_HANDLERS).\n\n## FileField\n\nПредставление файла. Выполняет стандартную для Django проверку `FileField`.\n\nСоответствует `django.forms.fields.FileField`.\n\n**Сигнатура:** `FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)`.\n\n* `max_length` - Указывает максимальную длину имени файла.\n* `allow_empty_file` - Указывает, разрешены ли пустые файлы.\n* `use_url` - Если установлено значение `True`, то для выходного представления будут использоваться строковые значения URL. Если установлено значение `False`, то для вывода будут использоваться строковые значения имен файлов. По умолчанию принимает значение ключа настроек `UPLOADED_FILES_USE_URL`, которое равно `True`, если не установлено иное.\n\n## ImageField\n\nПредставление изображения. Проверяет соответствие содержимого загруженного файла известному формату изображения.\n\nСоответствует `django.forms.fields.ImageField`.\n\n**Сигнатура:** `ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)`.\n\n* `max_length` - Указывает максимальную длину имени файла.\n* `allow_empty_file` - Указывает, разрешены ли пустые файлы.\n* `use_url` - Если установлено значение `True`, то для выходного представления будут использоваться строковые значения URL. Если установлено значение `False`, то для вывода будут использоваться строковые значения имен файлов. По умолчанию принимает значение ключа настроек `UPLOADED_FILES_USE_URL`, которое равно `True`, если не установлено иное.\n\nТребуется либо пакет `Pillow`, либо пакет `PIL`. Рекомендуется использовать пакет `Pillow`, так как пакет `PIL` больше активно не поддерживается.\n\n---\n\n# Составные поля\n\n## ListField\n\nКласс поля, который проверяет список объектов.\n\n**Сигнатура**: `ListField(child=<A_FIELD_INSTANCE>, allow_empty=True, min_length=None, max_length=None)`.\n\n* `child` - Экземпляр поля, который должен использоваться для проверки объектов в списке. Если этот аргумент не указан, то объекты в списке не будут проверяться.\n* `allow_empty` - Указывает, разрешены ли пустые списки.\n* `min_length` - Проверяет, что список содержит не менее этого количества элементов.\n* `max_length` - Проверяет, что список содержит не более этого количества элементов.\n\nНапример, для проверки списка целых чисел вы можете использовать что-то вроде следующего:\n\n```python\nscores = serializers.ListField(\n   child=serializers.IntegerField(min_value=0, max_value=100)\n)\n```\n\nКласс `ListField` также поддерживает декларативный стиль, который позволяет писать многократно используемые классы полей списков.\n\n```python\nclass StringListField(serializers.ListField):\n    child = serializers.CharField()\n```\n\nТеперь мы можем повторно использовать наш пользовательский класс `StringListField` во всем нашем приложении, без необходимости указывать для него аргумент `child`.\n\n## DictField\n\nКласс поля, который проверяет словарь объектов. Ключи в `DictField` всегда предполагаются как строковые значения.\n\n**Сигнатура**: `DictField(child=<A_FIELD_INSTANCE>, allow_empty=True)`.\n\n* `child` - Экземпляр поля, который должен использоваться для проверки значений в словаре. Если этот аргумент не указан, то значения в отображении не будут проверяться.\n* `allow_empty` - Указывает, разрешены ли пустые словари.\n\nНапример, чтобы создать поле, которое проверяет сопоставление строк со строками, вы должны написать что-то вроде этого:\n\n```python\ndocument = DictField(child=CharField())\n```\n\nВы также можете использовать декларативный стиль, как в случае с `ListField`. Например:\n\n```python\nclass DocumentField(DictField):\n    child = CharField()\n```\n\n## HStoreField\n\nПредварительно настроенное `DictField`, совместимое с `HStoreField` от Django для postgres.\n\n**Сигнатура**: `HStoreField(child=<A_FIELD_INSTANCE>, allow_empty=True)`.\n\n* `child` - экземпляр поля, который используется для проверки значений в словаре. По умолчанию дочернее поле принимает как пустые строки, так и нулевые значения.\n* `allow_empty` - Указывает, разрешены ли пустые словари.\n\nОбратите внимание, что дочернее поле **должно** быть экземпляром `CharField`, так как расширение hstore хранит значения в виде строк.\n\n## JSONField\n\nКласс поля, который проверяет, что входящая структура данных состоит из корректных примитивов JSON. В альтернативном двоичном режиме он представляет и проверяет двоичные строки, закодированные в JSON.\n\n**Сигнатура**: `JSONField(binary, encoder)`.\n\n* `binary` - Если установлено значение `True`, то поле будет выводить и проверять строку, закодированную в JSON, а не примитивную структуру данных. По умолчанию `False`.\n* `encoder` - Используйте этот JSON-кодер для сериализации входного объекта. По умолчанию `None`.\n\n---\n\n# Разные поля\n\n## ReadOnlyField\n\nКласс поля, который просто возвращает значение поля без изменений.\n\nЭто поле используется по умолчанию в `ModelSerializer` при включении имен полей, относящихся к атрибуту, а не к полю модели.\n\n**Сигнатура**: `ReadOnlyField()`.\n\nНапример, если бы `has_expired` было свойством модели `Account`, то следующий сериализатор автоматически сгенерировал бы его как `ReadOnlyField`:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['id', 'account_name', 'has_expired']\n```\n\n## HiddenField\n\nКласс поля, которое не принимает значение на основе пользовательского ввода, а берет его из значения по умолчанию или вызываемого объекта.\n\n**Сигнатура**: `HiddenField()`.\n\nНапример, чтобы включить поле, которое всегда предоставляет текущее время как часть проверяемых сериализатором данных, вы должны использовать следующее:\n\n```python\nmodified = serializers.HiddenField(default=timezone.now)\n```\n\nКласс `HiddenField` обычно нужен только в том случае, если у вас есть валидация, которая должна выполняться на основе некоторых предварительно предоставленных значений полей, но вы не хотите открывать все эти поля конечному пользователю.\n\nДругие примеры по `HiddenField` смотрите в документации [validators](validators.md).\n\n---\n\n**Примечание:** `HiddenField()` не появляется в сериализаторе `partial=True` (при выполнении запроса `PATCH`).\n\n---\n\n## ModelField\n\nОбщее поле, которое может быть привязано к любому произвольному полю модели. Класс `ModelField` делегирует задачу сериализации/десериализации связанному с ним полю модели. Это поле можно использовать для создания полей сериализатора для пользовательских полей модели, без необходимости создавать новое пользовательское поле сериализатора.\n\nЭто поле используется `ModelSerializer` для соответствия классам полей пользовательской модели.\n\n**Сигнатура:** `ModelField(model_field=<Django ModelField instance>)`.\n\nКласс `ModelField` обычно предназначен для внутреннего использования, но при необходимости может быть использован в вашем API. Чтобы правильно инстанцировать `ModelField`, ему должно быть передано поле, привязанное к инстанцированной модели. Например: `ModelField(model_field=MyModel()._meta.get_field('custom_field'))`.\n\n## SerializerMethodField\n\nЭто поле, доступное только для чтения. Оно получает свое значение путем вызова метода класса сериализатора, к которому оно присоединено. Его можно использовать для добавления любых данных в сериализованное представление вашего объекта.\n\n**Сигнатура**: `SerializerMethodField(method_name=None)`.\n\n* `method_name` - Имя метода в сериализаторе, который будет вызван. Если оно не включено, то по умолчанию используется `get_<имя_поля>`.\n\nМетод сериализатора, на который ссылается аргумент `имя_метода`, должен принимать единственный аргумент (в дополнение к `self`), которым является сериализуемый объект. Он должен возвращать все, что вы хотите включить в сериализованное представление объекта. Например:\n\n```python\nfrom django.contrib.auth.models import User\nfrom django.utils.timezone import now\nfrom rest_framework import serializers\n\nclass UserSerializer(serializers.ModelSerializer):\n    days_since_joined = serializers.SerializerMethodField()\n\n    class Meta:\n        model = User\n        fields = '__all__'\n\n    def get_days_since_joined(self, obj):\n        return (now() - obj.date_joined).days\n```\n\n---\n\n# Пользовательские поля\n\nЕсли вы хотите создать пользовательское поле, вам нужно создать подкласс `Field`, а затем переопределить один или оба метода `.to_representation()` и `.to_internal_value()`. Эти два метода используются для преобразования между исходным типом данных и примитивным, сериализуемым типом данных. Примитивными типами данных обычно являются число, строка, булево значение, `date`/`time`/`datetime` или `None`. Также это может быть любой список или словарь, содержащий только другие примитивные объекты. Могут поддерживаться и другие типы, в зависимости от используемого рендерера.\n\nМетод `.to_representation()` вызывается для преобразования исходного типа данных в примитивный, сериализуемый тип данных.\n\nМетод `.to_internal_value()` вызывается для восстановления примитивного типа данных в его внутреннее python-представление. Этот метод должен вызвать ошибку `serializers.ValidationError`, если данные недействительны.\n\n## Примеры\n\n### Базовое пользовательское поле\n\nДавайте рассмотрим пример сериализации класса, представляющего значение цвета RGB:\n\n```python\nclass Color:\n    \"\"\"\n    A color represented in the RGB colorspace.\n    \"\"\"\n    def __init__(self, red, green, blue):\n        assert(red >= 0 and green >= 0 and blue >= 0)\n        assert(red < 256 and green < 256 and blue < 256)\n        self.red, self.green, self.blue = red, green, blue\n\nclass ColorField(serializers.Field):\n    \"\"\"\n    Color objects are serialized into 'rgb(#, #, #)' notation.\n    \"\"\"\n    def to_representation(self, value):\n        return \"rgb(%d, %d, %d)\" % (value.red, value.green, value.blue)\n\n    def to_internal_value(self, data):\n        data = data.strip('rgb(').rstrip(')')\n        red, green, blue = [int(col) for col in data.split(',')]\n        return Color(red, green, blue)\n```\n\nПо умолчанию значения полей рассматриваются как сопоставление с атрибутом объекта. Если вам нужно настроить доступ к значению поля и его установку, вам нужно переопределить `.get_attribute()` и/или `.get_value()`.\n\nВ качестве примера создадим поле, которое может быть использовано для представления имени класса сериализуемого объекта:\n\n```python\nclass ClassNameField(serializers.Field):\n    def get_attribute(self, instance):\n        # We pass the object instance onto `to_representation`,\n        # not just the field attribute.\n        return instance\n\n    def to_representation(self, value):\n        \"\"\"\n        Serialize the value's class name.\n        \"\"\"\n        return value.__class__.__name__\n```\n\n### Вызов ошибок проверки\n\nНаш класс `ColorField`, описанный выше, в настоящее время не выполняет никакой проверки данных. Чтобы указать на недопустимые данные, мы должны вызвать ошибку `serializers.ValidationError`, как показано ниже:\n\n```python\ndef to_internal_value(self, data):\n    if not isinstance(data, str):\n        msg = 'Incorrect type. Expected a string, but got %s'\n        raise ValidationError(msg % type(data).__name__)\n\n    if not re.match(r'^rgb\\([0-9]+,[0-9]+,[0-9]+\\)$', data):\n        raise ValidationError('Incorrect format. Expected `rgb(#,#,#)`.')\n\n    data = data.strip('rgb(').rstrip(')')\n    red, green, blue = [int(col) for col in data.split(',')]\n\n    if any([col > 255 or col < 0 for col in (red, green, blue)]):\n        raise ValidationError('Value out of range. Must be between 0 and 255.')\n\n    return Color(red, green, blue)\n```\n\nМетод `.fail()` - это ярлык для вызова `ValidationError`, который принимает строку сообщения из словаря `error_messages`. Например:\n\n```python\ndefault_error_messages = {\n    'incorrect_type': 'Incorrect type. Expected a string, but got {input_type}',\n    'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',\n    'out_of_range': 'Value out of range. Must be between 0 and 255.'\n}\n\ndef to_internal_value(self, data):\n    if not isinstance(data, str):\n        self.fail('incorrect_type', input_type=type(data).__name__)\n\n    if not re.match(r'^rgb\\([0-9]+,[0-9]+,[0-9]+\\)$', data):\n        self.fail('incorrect_format')\n\n    data = data.strip('rgb(').rstrip(')')\n    red, green, blue = [int(col) for col in data.split(',')]\n\n    if any([col > 255 or col < 0 for col in (red, green, blue)]):\n        self.fail('out_of_range')\n\n    return Color(red, green, blue)\n```\n\nЭтот стиль делает сообщения об ошибках более чистыми и отделенными от кода, поэтому его следует предпочесть.\n\n### Использование `source='*'`\n\nЗдесь мы рассмотрим пример _плоской_ модели `DataPoint` с атрибутами `x_coordinate` и `y_coordinate`.\n\n```python\nclass DataPoint(models.Model):\n    label = models.CharField(max_length=50)\n    x_coordinate = models.SmallIntegerField()\n    y_coordinate = models.SmallIntegerField()\n```\n\nИспользуя пользовательское поле и `source='*'`, мы можем предоставить вложенное представление пары координат:\n\n```python\nclass CoordinateField(serializers.Field):\n\n    def to_representation(self, value):\n        ret = {\n            \"x\": value.x_coordinate,\n            \"y\": value.y_coordinate\n        }\n        return ret\n\n    def to_internal_value(self, data):\n        ret = {\n            \"x_coordinate\": data[\"x\"],\n            \"y_coordinate\": data[\"y\"],\n        }\n        return ret\n\nclass DataPointSerializer(serializers.ModelSerializer):\n    coordinates = CoordinateField(source='*')\n\n    class Meta:\n        model = DataPoint\n        fields = ['label', 'coordinates']\n```\n\nОбратите внимание, что в этом примере не предусмотрена валидация. Отчасти по этой причине в реальном проекте вложенность координат может быть лучше обработана с помощью вложенного сериализатора используя `source='*'`, с двумя экземплярами `IntegerField`, каждый из которых имеет свой собственный `source` указывающий на соответствующее поле.\n\nКлючевыми моментами из этого примера являются следующие:\n\n* `to_representation` передается весь объект `DataPoint`, который должен быть отображен в нужный вывод.\n\n    ```python\n    >>> instance = DataPoint(label='Example', x_coordinate=1, y_coordinate=2)\n    >>> out_serializer = DataPointSerializer(instance)\n    >>> out_serializer.data\n    ReturnDict([('label', 'Example'), ('coordinates', {'x': 1, 'y': 2})])\n    ```\n\n* Если только наше поле не будет доступно только для чтения, `to_internal_value` должно возвращать дикту, подходящую для обновления целевого объекта. При использовании `source='*'`, возврат из `to_internal_value` будет обновлять корневой словарь данных, а не один ключ.\n\n    ```python\n    >>> data = {\n    ...     \"label\": \"Second Example\",\n    ...     \"coordinates\": {\n    ...         \"x\": 3,\n    ...         \"y\": 4,\n    ...     }\n    ... }\n    >>> in_serializer = DataPointSerializer(data=data)\n    >>> in_serializer.is_valid()\n    True\n    >>> in_serializer.validated_data\n    OrderedDict([('label', 'Second Example'),\n                ('y_coordinate', 4),\n                ('x_coordinate', 3)])\n    ```\n\nДля полноты картины повторим то же самое, но с использованием вложенного сериализатора, предложенного выше:\n\n```python\nclass NestedCoordinateSerializer(serializers.Serializer):\n    x = serializers.IntegerField(source='x_coordinate')\n    y = serializers.IntegerField(source='y_coordinate')\n\nclass DataPointSerializer(serializers.ModelSerializer):\n    coordinates = NestedCoordinateSerializer(source='*')\n\n    class Meta:\n        model = DataPoint\n        fields = ['label', 'coordinates']\n```\n\nЗдесь отображение между парами атрибутов цели и источника (`x` и `x_coordinate`, `y` и `y_coordinate`) обрабатывается в объявлениях `IntegerField`. Это наш `NestedCoordinateSerializer`, который принимает `source='*'`.\n\nНаш новый `DataPointSerializer` демонстрирует то же поведение, что и подход с пользовательскими полями.\n\nСериализация:\n\n```python\n>>> out_serializer = DataPointSerializer(instance)\n>>> out_serializer.data\nReturnDict([('label', 'testing'),\n            ('coordinates', OrderedDict([('x', 1), ('y', 2)]))])\n```\n\nДесериализация:\n\n```python\n>>> in_serializer = DataPointSerializer(data=data)\n>>> in_serializer.is_valid()\nTrue\n>>> in_serializer.validated_data\nOrderedDict([('label', 'still testing'),\n             ('x_coordinate', 3),\n             ('y_coordinate', 4)])\n```\n\nНо мы также получаем встроенную валидацию бесплатно:\n\n```python\n>>> invalid_data = {\n...     \"label\": \"still testing\",\n...     \"coordinates\": {\n...         \"x\": 'a',\n...         \"y\": 'b',\n...     }\n... }\n>>> invalid_serializer = DataPointSerializer(data=invalid_data)\n>>> invalid_serializer.is_valid()\nFalse\n>>> invalid_serializer.errors\nReturnDict([('coordinates',\n             {'x': ['A valid integer is required.'],\n              'y': ['A valid integer is required.']})])\n```\n\nПо этой причине подход с использованием вложенного сериализатора следует попробовать в первую очередь. Вы будете использовать подход с пользовательскими полями, когда вложенный сериализатор станет невыполнимым или слишком сложным.\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## Составные поля DRF\n\nПакет [drf-compound-fields](https://drf-compound-fields.readthedocs.io) предоставляет \"составные\" поля сериализатора, такие как списки простых значений, которые могут быть описаны другими полями, а не сериализаторами с опцией `many=True`. Также предоставляются поля для типизированных словарей и значений, которые могут быть либо определенным типом, либо списком элементов этого типа.\n\n## Дополнительные поля DRF\n\nПакет [drf-extra-fields](https://github.com/Hipo/drf-extra-fields) предоставляет дополнительные поля сериализатора для DRF, включая классы `Base64ImageField` и `PointField`.\n\n## djangorestframework-recursive\n\nПакет [djangorestframework-recursive](https://github.com/heywbj/django-rest-framework-recursive) предоставляет `RecursiveField` для сериализации и десериализации рекурсивных структур.\n\n## django-rest-framework-gis\n\nПакет [django-rest-framework-gis](https://github.com/djangonauts/django-rest-framework-gis) предоставляет географические дополнения для DRF, такие как поле `GeometryField` и сериализатор GeoJSON.\n\n## django-rest-framework-hstore\n\nПакет [django-rest-framework-hstore](https://github.com/djangonauts/django-rest-framework-hstore) предоставляет `HStoreField` для поддержки поля модели [django-hstore](https://github.com/djangonauts/django-hstore) `DictionaryField`.\n"
  },
  {
    "path": "api-guide/filtering.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- filters.py\n\n---\n\n# Фильтрация\n\n> Корневой QuerySet, предоставляемый менеджером, описывает все объекты в таблице базы данных. Однако обычно требуется выбрать только подмножество из полного набора объектов.\n>\n> &mdash; [Django documentation](https://docs.djangoproject.com/en/stable/topics/db/queries/#retrieving-specific-objects-with-filters)\n\nПо умолчанию общие списочные представления DRF возвращают весь `QuerySet` для менеджера модели. Часто требуется, чтобы API ограничивал количество элементов, возвращаемых `QuerySet`.\n\nПростейшим способом фильтрации `QuerySet` любого представления, подкласса `GenericAPIView`, является переопределение метода `.get_queryset()`.\n\nПереопределение этого метода позволяет настраивать `QuerySet`, возвращаемый представлением, различными способами.\n\n## Фильтрация по текущему пользователю\n\nВозможно, потребуется отфильтровать `QuerySet`, чтобы возвращать только результаты, относящиеся к текущему аутентифицированному пользователю, сделавшему запрос.\n\nЭто можно сделать с помощью фильтрации по значению `request.user`.\n\nНапример:\n\n```python\nfrom myapp.models import Purchase\nfrom myapp.serializers import PurchaseSerializer\nfrom rest_framework import generics\n\nclass PurchaseList(generics.ListAPIView):\n    serializer_class = PurchaseSerializer\n\n    def get_queryset(self):\n        \"\"\"\n        This view should return a list of all the purchases\n        for the currently authenticated user.\n        \"\"\"\n        user = self.request.user\n        return Purchase.objects.filter(purchaser=user)\n```\n\n## Фильтрация по URL\n\nДругой стиль фильтрации может включать ограничение `QuerySet` на основе некоторой части URL.\n\nНапример, если в конфигурации URL содержится запись следующего вида:\n\n```python\nre_path('^purchases/(?P<username>.+)/$', PurchaseList.as_view()),\n```\n\nЗатем вы можете написать представление, возвращающее `QuerySet` покупок, отфильтрованный по имени пользователя в части URL:\n\n```python\nclass PurchaseList(generics.ListAPIView):\n    serializer_class = PurchaseSerializer\n\n    def get_queryset(self):\n        \"\"\"\n        This view should return a list of all the purchases for\n        the user as determined by the username portion of the URL.\n        \"\"\"\n        username = self.kwargs['username']\n        return Purchase.objects.filter(purchaser__username=username)\n```\n\n## Фильтрация по параметрам запроса\n\nСледующим примером фильтрации исходного QuerySet может быть определение исходного QuerySet на основе параметров запроса в url.\n\nМы можем переопределить `.get_queryset()` для работы с такими URL, как `http://example.com/api/purchases?username=denvercoder9`, и фильтровать QuerySet только в том случае, если в URL включен параметр `username`:\n\n```python\nclass PurchaseList(generics.ListAPIView):\n    serializer_class = PurchaseSerializer\n\n    def get_queryset(self):\n        \"\"\"\n        Optionally restricts the returned purchases to a given user,\n        by filtering against a `username` query parameter in the URL.\n        \"\"\"\n        queryset = Purchase.objects.all()\n        username = self.request.query_params.get('username')\n        if username is not None:\n            queryset = queryset.filter(purchaser__username=username)\n        return queryset\n```\n\n---\n\n# Общая фильтрация\n\nПомимо возможности переопределения стандартного QuerySet, DRF также включает поддержку общих бэкендов фильтрации, которые позволяют легко строить сложные поисковые запросы и фильтры.\n\nОбщие фильтры также могут быть представлены в виде элементов управления HTML в API просмотра и API администрирования.\n\n![Пример фильтра](https://github.com/encode/django-rest-framework/raw/main/docs/img/filter-controls.png)\n\n## Настройка бэкендов фильтров\n\nБэкенды фильтров по умолчанию могут быть заданы глобально, с помощью параметра `DEFAULT_FILTER_BACKENDS`. Например:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']\n}\n```\n\nВы также можете задать бэкенды фильтров для каждого вида или для каждого набора видов, используя класс `GenericAPIView`, основанный на представлениях.\n\n```python\nimport django_filters.rest_framework\nfrom django.contrib.auth.models import User\nfrom myapp.serializers import UserSerializer\nfrom rest_framework import generics\n\nclass UserListView(generics.ListAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    filter_backends = [django_filters.rest_framework.DjangoFilterBackend]\n```\n\n## Фильтрация и поиск объектов\n\nОбратите внимание, что если для представления настроен бэкэнд фильтрации, то он будет использоваться не только для фильтрации списочных представлений, но и для фильтрации `QuerySet`, используемых для возврата одного объекта.\n\nНапример, если взять предыдущий пример и товар с идентификатором `4675`, то следующий URL будет либо возвращать соответствующий объект, либо возвращать ответ 404, в зависимости от того, были ли выполнены условия фильтрации для данного экземпляра товара:\n\n```bash\nhttp://example.com/api/products/4675/?category=clothing&max_price=10.00\n```\n\n## Переопределение исходного QuerySet\n\nОбратите внимание, что можно использовать и переопределенный `.get_queryset()`, и общую фильтрацию, и все будет работать так, как ожидается. Например, если у `Product` есть отношение \"многие-ко-многим\" с `User`, названное `purchase`, вы можете написать представление следующим образом:\n\n```python\nclass PurchasedProductsList(generics.ListAPIView):\n    \"\"\"\n    Return a list of all the products that the authenticated\n    user has ever purchased, with optional filtering.\n    \"\"\"\n    model = Product\n    serializer_class = ProductSerializer\n    filterset_class = ProductFilter\n\n    def get_queryset(self):\n        user = self.request.user\n        return user.purchase_set.all()\n```\n\n---\n\n# Руководство по API\n\n## DjangoFilterBackend\n\nБиблиотека [`django-filter`](https://django-filter.readthedocs.io/en/latest/index.html) включает класс `DjangoFilterBackend`, который поддерживает высоконастраиваемую фильтрацию полей для DRF.\n\nЧтобы использовать `DjangoFilterBackend`, сначала установите `django-filter`.\n\n```bash\npip install django-filter\n```\n\nЗатем добавьте `'django_filters'` в `INSTALLED_APPS` Django:\n\n```python\nINSTALLED_APPS = [\n    ...\n    'django_filters',\n    ...\n]\n```\n\nТеперь необходимо либо добавить бэкэнд фильтра в настройки:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']\n}\n```\n\nИли добавить бэкэнд фильтрации в отдельный `View` или `ViewSet`.\n\n```python\nfrom django_filters.rest_framework import DjangoFilterBackend\n\nclass UserListView(generics.ListAPIView):\n    ...\n    filter_backends = [DjangoFilterBackend]\n```\n\nЕсли вам нужна простая фильтрация на основе равенства, вы можете установить атрибут `filterset_fields` для представления или набора представлений, перечислив набор полей, по которым вы хотите осуществлять фильтрацию.\n\n```python\nclass ProductList(generics.ListAPIView):\n    queryset = Product.objects.all()\n    serializer_class = ProductSerializer\n    filter_backends = [DjangoFilterBackend]\n    filterset_fields = ['category', 'in_stock']\n```\n\nЭто автоматически создаст класс `FilterSet` для заданных полей и позволит выполнять такие запросы, как:\n\n```bash\nhttp://example.com/api/products?category=clothing&in_stock=True\n```\n\nДля более сложных требований к фильтрации можно указать класс `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).\n\n## SearchFilter\n\nКласс `SearchFilter` поддерживает простой поиск по одному параметру запроса и основан на функциональности [поиска в админ-панели Django](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields).\n\nПри использовании в состав Web-интерфейса API будет входить элемент управления `SearchFilter`:\n\n![Фильтр поиска](https://github.com/encode/django-rest-framework/raw/main/docs/img/search-filter.png)\n\nКласс `SearchFilter` будет применяться только в том случае, если у представления установлен атрибут `search_fields`. Атрибут `search_fields` должен представлять собой список имен полей текстового типа в модели, например `CharField` или `TextField`.\n\n```python\nfrom rest_framework import filters\n\nclass UserListView(generics.ListAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    filter_backends = [filters.SearchFilter]\n    search_fields = ['username', 'email']\n```\n\nЭто позволит клиенту фильтровать элементы списка, задавая такие запросы, как:\n\n```bash\nhttp://example.com/api/users?search=russell\n```\n\nТакже можно выполнить связанный поиск по полю `ForeignKey` или `ManyToManyField` с помощью нотации двойного подчеркивания в API поиска:\n\n```python\nsearch_fields = ['username', 'email', 'profile__profession']\n```\n\nДля полей [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) можно осуществлять фильтрацию по вложенным значениям внутри структуры данных, используя ту же нотацию двойного подчеркивания:\n\n```python\nsearch_fields = ['data__breed', 'data__owner__other_pets__0__name']\n```\n\nПо умолчанию в поиске используются частичные совпадения без учета регистра. Параметр `search` может содержать несколько условий поиска, которые должны быть разделены пробелами и/или запятыми. Если используется несколько условий поиска, то объекты будут возвращены в списке только при совпадении всех указанных условий. Поиск может содержать _цитируемые фразы_ с пробелами, каждая фраза рассматривается как один поисковый термин.\n\nПоведение поиска может быть задано путем добавления префикса к именам полей в `search_fields` одним из следующих символов (что эквивалентно добавлению `__<lookup>` к полю):\n\n| Префикс | Поиск         |                                                                                                                                                                   |\n|---------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `^`     | `istartswith` | Начинается с поиска.                                                                                                                                              |\n| `=`     | `iexact`      | Точные совпадения.                                                                                                                                                |\n| `$`     | `iregex`      | Regex-поиск.                                                                                                                                                      |\n| `@`     | `search`      | Полнотекстовый поиск (в настоящее время поддерживается только бэкенд Django [PostgreSQL](https://docs.djangoproject.com/en/stable/ref/contrib/postgres/search/)). |\n| None    | `icontains`   | Содержит поиск (по умолчанию).                                                                                                                                    |\n\nНапример:\n\n```python\nsearch_fields = ['=username', '=email']\n```\n\nПо умолчанию параметр поиска называется `'search'`, но это можно переопределить с помощью параметра `SEARCH_PARAM` в секции настроек `REST_FRAMEWORK`.\n\nДля динамического изменения полей поиска в зависимости от содержимого запроса можно подклассифицировать `SearchFilter` и переопределить функцию `get_search_fields()`. Например, следующий подкласс будет искать по `title`, только если в запросе присутствует параметр запроса `title_only`:\n\n```python\nfrom rest_framework import filters\n\nclass CustomSearchFilter(filters.SearchFilter):\n    def get_search_fields(self, view, request):\n        if request.query_params.get('title_only'):\n            return ['title']\n        return super().get_search_fields(view, request)\n```\n\nБолее подробную информацию можно найти в [документации Django](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields).\n\n---\n\n## OrderingFilter\n\nКласс `OrderingFilter` поддерживает простое упорядочивание результатов, управляемое параметрами запроса.\n\n![Ordering Filter](https://github.com/encode/django-rest-framework/raw/main/docs/img/ordering-filter.png)\n\nПо умолчанию параметр запроса называется `'ordering'`, но это можно переопределить с помощью параметра `ORDERING_PARAM` в секции настроек `REST_FRAMEWORK`.\n\nНапример, чтобы упорядочить пользователей по имени пользователя:\n\n```bash\nhttp://example.com/api/users?ordering=username\n```\n\nКлиент может задать и обратный порядок, добавив к имени поля префикс '-', например, так:\n\n```bash\nhttp://example.com/api/users?ordering=-username\n```\n\nТакже может быть задано несколько порядков:\n\n```bash\nhttp://example.com/api/users?ordering=account,username\n```\n\n### Указание того, какие поля могут быть использованы для упорядочивания\n\nРекомендуется явно указывать, какие поля API должен разрешать в фильтре упорядочивания. Это можно сделать, установив атрибут `ordering_fields` на представлении, например, так:\n\n```python\nclass UserListView(generics.ListAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    filter_backends = [filters.OrderingFilter]\n    ordering_fields = ['username', 'email']\n```\n\nЭто позволяет предотвратить непредвиденную утечку данных, например, разрешить пользователям заказывать по хэш-полю пароли или другие конфиденциальные данные.\n\nЕсли _не_ указать атрибут `orderering_fields` для представления, то класс фильтра будет по умолчанию позволять пользователю фильтровать по любым читаемым полям на сериализаторе, указанном атрибутом `serializer_class`.\n\nЕсли вы уверены, что используемый представлением `Queryset` не содержит конфиденциальных данных, вы также можете явно указать, что представление должно разрешать упорядочивание по _любому_ полю модели или агрегату `Queryset`, используя специальное значение `'__all__'`.\n\n```python\nclass BookingsListView(generics.ListAPIView):\n    queryset = Booking.objects.all()\n    serializer_class = BookingSerializer\n    filter_backends = [filters.OrderingFilter]\n    ordering_fields = '__all__'\n```\n\n### Указание порядка по умолчанию\n\nЕсли у представления установлен атрибут `ordering`, то он будет использоваться в качестве упорядочивания по умолчанию.\n\nОбычно для этого используется параметр `order_by` в исходном `Queryset`, но использование параметра `ordering` в представлении позволяет указать порядок таким образом, что он может быть автоматически передан в качестве контекста в шаблон рендеринга. Это позволяет автоматически отображать заголовки столбцов по-разному, если они используются для упорядочивания результатов.\n\n```python\nclass UserListView(generics.ListAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    filter_backends = [filters.OrderingFilter]\n    ordering_fields = ['username', 'email']\n    ordering = ['username']\n```\n\nАтрибут `orderering` может быть как строкой, так и списком/кортежем строк.\n\n---\n\n# Пользовательская общая фильтрация\n\nВы также можете предоставить свой собственный бэкэнд фильтрации или написать устанавливаемое приложение для использования другими разработчиками.\n\nДля этого переопределите `BaseFilterBackend` и переопределите метод `.filter_queryset(self, request, queryset, view)`. Метод должен возвращать новый, отфильтрованный `QuerySet`.\n\nПомимо того, что клиенты могут выполнять поиск и фильтрацию, общие бэкенды фильтров могут быть полезны для ограничения того, какие объекты должны быть видны для каждого конкретного запроса или пользователя.\n\n## Пример\n\nНапример, может потребоваться ограничить доступ пользователей только к созданным ими объектам.\n\n```python\nclass IsOwnerFilterBackend(filters.BaseFilterBackend):\n    \"\"\"\n    Filter that only allows users to see their own objects.\n    \"\"\"\n    def filter_queryset(self, request, queryset, view):\n        return queryset.filter(owner=request.user)\n```\n\nМы могли бы добиться такого же поведения, переопределив `get_queryset()` в представлениях, но использование бэкенда фильтров позволяет более просто добавить это ограничение к нескольким представлениям или применить его ко всему API.\n\n## Настройка интерфейса\n\nОбщие фильтры также могут представлять интерфейс в Web-интерфейсе API. Для этого необходимо реализовать метод `to_html()`, который возвращает отрендеренное HTML-представление фильтра. Этот метод должен иметь следующую сигнатуру:\n\n`to_html(self, request, queryset, view)`.\n\nМетод должен возвращать отрендеренную HTML-строку.\n\n# Пакеты сторонних производителей\n\nСледующие пакеты сторонних производителей предоставляют дополнительные реализации фильтров.\n\n## Django-rest-framework-filters\n\n[django-rest-framework-filters](https://github.com/philipn/django-rest-framework-filters) работает совместно с классом `DjangoFilterBackend` и позволяет легко создавать фильтры по отношениям, а также создавать несколько типов фильтров для поиска по заданному полю.\n\n## Djangorestframework-word-filter\n\n[djangorestframework-word-filter](https://github.com/trollknurr/django-rest-framework-word-search-filter) разработан как альтернатива `filters.SearchFilter`, который будет искать полное слово в тексте, либо точное совпадение.\n\n## Django-url-filter\n\n[django-url-filter](https://github.com/miki725/django-url-filter) предоставляет безопасный способ фильтрации данных по удобным для человека URL-адресам. Он работает очень похоже на сериализаторы и поля DRF в том смысле, что они могут быть вложенными, за исключением того, что они называются `filtersets` и `filters`. Это обеспечивает простой способ фильтрации связанных данных. Кроме того, эта библиотека является универсальной, поэтому ее можно использовать для фильтрации других источников данных, а не только Django `QuerySet`.\n\n## drf-url-filters\n\n[drf-url-filter](https://github.com/manjitkumar/drf-url-filters) - это простое Django-приложение для применения фильтров к `Queryset` в `ModelViewSet` чистым, простым и настраиваемым способом. Оно также поддерживает валидацию входящих параметров запроса и их значений. Для валидации входящих параметров запроса используется красивый python-пакет `Voluptuous`. Самое приятное в `Voluptuous` то, что вы можете определить свои собственные валидации в соответствии с требованиями к параметрам запроса.\n"
  },
  {
    "path": "api-guide/format-suffixes.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Cуффиксы формата\n\n> В разделе 6.2.1 не говорится, что согласование содержания должно использоваться постоянно.\n>\n> - Рой Филдинг, [список рассылки REST discuss](http://tech.groups.yahoo.com/group/rest-discuss/message/5857)\n\nОбщим шаблоном для веб-интерфейсов является использование расширений имен файлов в URL-адресах для предоставления конечной точки для данного типа носителя. Например, 'http://example.com/api/users.json' для предоставления представления JSON.\n\nДобавление шаблонов суффиксов формата к каждой отдельной записи в URLconf для вашего API чревато ошибками и не соответствует стандарту DRY, поэтому DRF предоставляет быстрый способ добавления этих шаблонов в URLConf.\n\n## format_suffix_patterns\n\n**Сигнатура**: `format_suffix_patterns(urlpatterns, suffix_required=False, allowed=None)`\n\nВозвращает список шаблонов URL, который включает шаблоны суффиксов формата, добавленные к каждому из предоставленных шаблонов URL.\n\nАргументы:\n\n* **urlpatterns**: Обязательно. Список шаблонов URL.\n* **suffix_required**: Необязательно. Булево значение, указывающее, должны ли суффиксы в URL быть необязательными или обязательными. По умолчанию `False`, что означает, что суффиксы по умолчанию необязательны.\n* **allowed**: Необязательно. Список или кортеж допустимых суффиксов формата. Если не указан, будет использоваться шаблон суффикса формата.\n\nПример:\n\n```python\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom blog import views\n\nurlpatterns = [\n    path('', views.apt_root),\n    path('comments/', views.comment_list),\n    path('comments/<int:pk>/', views.comment_detail)\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns, allowed=['json', 'html'])\n```\n\nПри использовании `format_suffix_patterns`, вы должны убедиться, что добавили именованный аргумент `'format'` в соответствующие представления. Например:\n\n```python\n@api_view(['GET', 'POST'])\ndef comment_list(request, format=None):\n    # do stuff...\n```\n\nИли с помощью представлений, основанных на классах:\n\n```python\nclass CommentList(APIView):\n    def get(self, request, format=None):\n        # do stuff...\n\n    def post(self, request, format=None):\n        # do stuff...\n```\n\nИмя используемого именнованного аргумента можно изменить с помощью параметра `FORMAT_SUFFIX_KWARG`.\n\nТакже обратите внимание, что `format_suffix_patterns` не поддерживает применение к шаблонам URL `include`.\n\n### Использование с `i18n_patterns`.\n\nПри использовании функции `i18n_patterns`, предоставляемой Django, а также `format_suffix_patterns` вы должны убедиться, что функция `i18n_patterns` применяется как конечная, или крайняя функция. Например:\n\n```python\nurlpatterns = [\n    …\n]\n\nurlpatterns = i18n_patterns(\n    format_suffix_patterns(urlpatterns, allowed=['json', 'html'])\n)\n```\n\n---\n\n## Форматы параметров запроса\n\nАльтернативой суффиксам формата является включение запрашиваемого формата в параметр запроса. DRF предоставляет этот параметр по умолчанию, и он используется в Web-интерфейсе API для переключения между различными доступными представлениями.\n\nЧтобы выбрать представление по его краткому формату, используйте параметр запроса `format`. Например: `http://example.com/organizations/?format=csv`.\n\nИмя этого параметра запроса можно изменить с помощью параметра `URL_FORMAT_OVERRIDE`. Установите значение `None`, чтобы отключить это поведение.\n\n---\n\n## Принимать заголовки против суффиксов формата\n\nПохоже, некоторые представители веб-сообщества считают, что расширения имен файлов не являются шаблоном RESTful, и что вместо них всегда следует использовать заголовки `HTTP Accept`.\n\nНа самом деле это заблуждение. Например, возьмем следующую цитату Роя Филдинга, обсуждающего относительные достоинства индикаторов медиатипа параметров запроса по сравнению с индикаторами медиатипа расширений файлов:\n\n> Вот почему я всегда предпочитаю расширения. Ни тот, ни другой выбор не имеют никакого отношения к REST\".\n>\n> - Рой Филдинг, [Список рассылки REST discuss](https://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/14844)\n\nВ цитате не упоминаются заголовки Accept, но она ясно дает понять, что суффиксы формата следует считать приемлемым шаблоном.\n"
  },
  {
    "path": "api-guide/generic-views.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Общие представления\n\n> Общие представления в Django... были разработаны как кратчайший путь к общим шаблонам использования... Они берут определенные общие идиомы и паттерны, встречающиеся в разработке представлений, и абстрагируют их, чтобы вы могли быстро писать общие представления данных без необходимости повторяться.\n>\n> - [Документация Django](https://docs.djangoproject.com/en/stable/ref/class-based-views/#base-vs-generic-views)\n\nОдним из ключевых преимуществ представлений, основанных на классах, является то, что они позволяют составлять фрагменты многократно используемого поведения. DRF использует это преимущество, предоставляя ряд готовых представлений, которые обеспечивают часто используемые шаблоны.\n\nТиповые представления, предоставляемые DRF, позволяют быстро создавать представления API, которые тесно связаны с моделями вашей базы данных.\n\nЕсли типовые представления не удовлетворяют потребностям вашего API, вы можете перейти к использованию обычного класса `APIView` или повторно использовать миксины и базовые классы, используемые типовыми представлениями, для создания собственного набора многократно используемых типовых представлений.\n\n## Примеры\n\nОбычно при использовании общих представлений вы переопределяете представление и устанавливаете несколько атрибутов класса.\n\n```python\nfrom django.contrib.auth.models import User\nfrom myapp.serializers import UserSerializer\nfrom rest_framework import generics\nfrom rest_framework.permissions import IsAdminUser\n\nclass UserList(generics.ListCreateAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    permission_classes = [IsAdminUser]\n```\n\nДля более сложных случаев вы также можете захотеть переопределить различные методы класса представления. Например.\n\n```python\nclass UserList(generics.ListCreateAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    permission_classes = [IsAdminUser]\n\n    def list(self, request):\n        # Note the use of `get_queryset()` instead of `self.queryset`\n        queryset = self.get_queryset()\n        serializer = UserSerializer(queryset, many=True)\n        return Response(serializer.data)\n```\n\nДля очень простых случаев вы можете передать любые атрибуты класса с помощью метода `.as_view()`. Например, ваша URLconf может включать что-то вроде следующей записи:\n\n```python\npath('users/', ListCreateAPIView.as_view(queryset=User.objects.all(), serializer_class=UserSerializer), name='user-list')\n```\n\n---\n\n# API Reference\n\n## GenericAPIView\n\nЭтот класс расширяет класс `APIView` DRF, добавляя часто требуемое поведение для стандартных представлений списка и детализации.\n\nКаждое из конкретных типовых представлений создается путем объединения `GenericAPIView` с одним или несколькими классами-миксинами.\n\n### Атрибуты\n\n**Основные настройки**:\n\nСледующие атрибуты управляют основным поведением представления.\n\n* `queryset` - Набор queryset, который должен использоваться для возврата объектов из этого представления. Как правило, вы должны либо установить этот атрибут, либо переопределить метод `get_queryset()`. Если вы переопределяете метод представления, важно вызвать `get_queryset()`, а не обращаться к этому свойству напрямую, так как `queryset` будет оценен один раз, и эти результаты будут кэшироваться для всех последующих запросов.\n* `serializer_class` - Класс сериализатора, который должен использоваться для проверки и десериализации входных данных, а также для сериализации выходных данных. Как правило, вы должны либо установить этот атрибут, либо переопределить метод `get_serializer_class()`.\n* `lookup_field` - Поле модели, которое должно использоваться для выполнения поиска объектов в отдельных экземплярах модели. По умолчанию используется значение `'pk'`. Обратите внимание, что при использовании API с гиперссылками вам нужно убедиться, что *и* представления API, *и* классы сериализатора устанавливают поля поиска, если вам нужно использовать пользовательское значение.\n* `lookup_url_kwarg` - Именованный аргумент URL, который должен использоваться для поиска объекта. URL conf должен включать именованный аргумент, соответствующий этому значению. Если значение не установлено, по умолчанию используется то же значение, что и `lookup_field`.\n\n**Пагинация**:\n\nСледующие атрибуты используются для управления пагинацией при использовании представлений списка.\n\n* `pagination_class` - Класс пагинации, который должен использоваться при пагинации результатов списка. По умолчанию имеет то же значение, что и параметр `DEFAULT_PAGINATION_CLASS`, который является `'rest_framework.pagination.PageNumberPagination'`. Установка `pagination_class=None` отключит пагинацию в этом представлении.\n\n**Фильтрация**:\n\n* `filter_backends` - Список классов бэкендов фильтра, которые должны использоваться для фильтрации набора запросов. По умолчанию имеет то же значение, что и параметр `DEFAULT_FILTER_BACKENDS`.\n\n### Методы\n\n**Базовые методы**:\n\n#### `get_queryset(self)`.\n\nВозвращает набор запросов, который должен использоваться для представлений списка и который должен использоваться в качестве базы для поиска в детальных представлениях. По умолчанию возвращается кверисет, указанный атрибутом `queryset`.\n\nЭтот метод всегда следует использовать вместо прямого обращения к `self.queryset`, поскольку `self.queryset` оценивается только один раз, и эти результаты кэшируются для всех последующих запросов.\n\nМожет быть переопределена для обеспечения динамического поведения, например, возврата набора запросов, специфичного для пользователя, делающего запрос.\n\nНапример:\n\n```python\ndef get_queryset(self):\n    user = self.request.user\n    return user.accounts.all()\n```\n\n---\n\n**Примечание:** Если класс `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\n---\n\n### Избегание запросов N+1\n\nПри перечислении объектов (например, с помощью `ListAPIView` или `ModelViewSet`) сериализаторы могут вызвать паттерн запросов N+1, если доступ к связанным объектам осуществляется индивидуально для каждого элемента.\n\nЧтобы этого не произошло, оптимизируйте набор запросов в `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), в зависимости от типа отношения.\n\n**Для ForeignKey и OneToOneField**:\n\nИспользуйте `select_related()`, чтобы получить связанные объекты в одном запросе:\n\n```python\ndef get_queryset(self):\n    return Order.objects.select_related(\"customer\", \"billing_address\")\n```\n\n**Для обратных и many-to-many отношений**:\n\nИспользуйте `prefetch_related()`, чтобы эффективно загрузить коллекции связанных объектов:\n\n```python\ndef get_queryset(self):\n    return Book.objects.prefetch_related(\"categories\", \"reviews__user\")\n```\n\n**Для сложных наборов запросов с несколькими типами отношений**:\n\n```python\ndef get_queryset(self):\n    return (\n        Order.objects\n        .select_related(\"customer\")\n        .prefetch_related(\"items__product\")\n    )\n```\n\nЭти оптимизации уменьшают повторный доступ к базе данных и улучшают производительность представлений списка.\n\n---\n\n#### `get_object(self)`.\n\nВозвращает экземпляр объекта, который должен использоваться для детальных представлений. По умолчанию используется параметр `lookup_field` для фильтрации базового набора запросов.\n\nМожет быть переопределена для обеспечения более сложного поведения, например, поиска объектов на основе более чем одного именованного аргумента URL.\n\nНапример:\n\n```python\ndef get_object(self):\n    queryset = self.get_queryset()\n    filter = {}\n    for field in self.multiple_lookup_fields:\n        filter[field] = self.kwargs[field]\n\n    obj = get_object_or_404(queryset, **filter)\n    self.check_object_permissions(self.request, obj)\n    return obj\n```\n\nОбратите внимание, что если ваш API не включает разрешения на уровне объекта, вы можете исключить `self.check_object_permissions`, и просто вернуть объект из поиска `get_object_or_404`.\n\n#### `filter_queryset(self, queryset)`.\n\nПолучив набор запросов, отфильтруйте его с помощью тех бэкендов фильтрации, которые используются, и верните новый набор запросов.\n\nНапример:\n\n```python\ndef filter_queryset(self, queryset):\n    filter_backends = [CategoryFilter]\n\n    if 'geo_route' in self.request.query_params:\n        filter_backends = [GeoRouteFilter, CategoryFilter]\n    elif 'geo_point' in self.request.query_params:\n        filter_backends = [GeoPointFilter, CategoryFilter]\n\n    for backend in list(filter_backends):\n        queryset = backend().filter_queryset(self.request, queryset, view=self)\n\n    return queryset\n```\n\n#### `get_serializer_class(self)`.\n\nВозвращает класс, который должен быть использован для сериализатора. По умолчанию возвращается атрибут `serializer_class`.\n\nМожет быть переопределен для обеспечения динамического поведения, например, использования различных сериализаторов для операций чтения и записи, или предоставления различных сериализаторов различным типам пользователей.\n\nНапример:\n\n```python\ndef get_serializer_class(self):\n    if self.request.user.is_staff:\n        return FullAccountSerializer\n    return BasicAccountSerializer\n```\n\n**Хуки для сохранения и удаления**:\n\nСледующие методы предоставляются mixin-классами и обеспечивают легкое переопределение поведения сохранения или удаления объекта.\n\n* `perform_create(self, serializer)` - Вызывается `CreateModelMixin` при сохранении нового экземпляра объекта.\n* `perform_update(self, serializer)` - Вызывается `UpdateModelMixin` при сохранении существующего экземпляра объекта.\n* `perform_destroy(self, instance)` - Вызывается `DestroyModelMixin` при удалении экземпляра объекта.\n\nЭти крючки особенно полезны для установки атрибутов, которые подразумеваются в запросе, но не являются частью данных запроса. Например, вы можете установить атрибут объекта на основе пользователя запроса или на основе именованного аргумента URL.\n\n```python\ndef perform_create(self, serializer):\n    serializer.save(user=self.request.user)\n```\n\nЭти точки переопределения также особенно полезны для добавления поведения, которое происходит до или после сохранения объекта, например, отправки подтверждения по электронной почте или регистрации обновления.\n\n```python\ndef perform_update(self, serializer):\n    instance = serializer.save()\n    send_email_confirmation(user=self.request.user, modified=instance)\n```\n\nВы также можете использовать эти крючки для обеспечения дополнительной проверки, вызывая `ValidationError()`. Это может быть полезно, если вам нужно применить логику валидации в момент сохранения базы данных. Например:\n\n```python\ndef perform_create(self, serializer):\n    queryset = SignupRequest.objects.filter(user=self.request.user)\n    if queryset.exists():\n        raise ValidationError('You have already signed up')\n    serializer.save(user=self.request.user)\n```\n\n**Другие методы**:\n\nОбычно вам не нужно переопределять следующие методы, хотя вам может понадобиться обращаться к ним, если вы пишете пользовательские представления, используя `GenericAPIView`.\n\n* `get_serializer_context(self)` - Возвращает словарь, содержащий любой дополнительный контекст, который должен быть предоставлен сериализатору. По умолчанию включает ключи `'request'`, `'view'` и `'format'`.\n* `get_serializer(self, instance=None, data=None, many=False, partial=False)` - Возвращает экземпляр сериализатора.\n* `get_paginated_response(self, data)` - Возвращает объект `Response` в стиле paginated.\n* `paginate_queryset(self, queryset)` - Пагинация набора запросов, если требуется, возвращает либо объект страницы, либо `None`, если пагинация не настроена для этого представления.\n* `filter_queryset(self, queryset)` - Получив набор запросов, отфильтровать его с помощью используемых бэкендов фильтрации, возвращая новый набор запросов.\n\n---\n\n# Миксины\n\nMixin-классы предоставляют действия, которые используются для обеспечения базового поведения представления. Обратите внимание, что mixin-классы предоставляют методы действий, а не определяют методы обработчиков, такие как `.get()` и `.post()`, напрямую. Это позволяет более гибко компоновать поведение.\n\nMixin-классы могут быть импортированы из `rest_framework.mixins`.\n\n## ListModelMixin\n\nПредоставляет метод `.list(request, *args, **kwargs)`, который реализует перечисление набора запросов.\n\nЕсли набор запросов заполнен, возвращается ответ `200 OK` с сериализованным представлением набора запросов в качестве тела ответа. По желанию данные ответа могут быть постраничными.\n\n## CreateModelMixin\n\nПредоставляет метод `.create(request, *args, **kwargs)`, который реализует создание и сохранение нового экземпляра модели.\n\nЕсли объект создан, возвращается ответ `201 Created` с сериализованным представлением объекта в качестве тела ответа. Если представление содержит ключ с именем `url`, то заголовок `Location` ответа будет заполнен этим значением.\n\nЕсли данные запроса, предоставленные для создания объекта, были недействительными, будет возвращен ответ `400 Bad Request`, а в теле ответа будет содержаться информация об ошибке.\n\n## RetrieveModelMixin\n\nПредоставляет метод `.retrieve(request, *args, **kwargs)`, который реализует возврат существующего экземпляра модели в ответ.\n\nЕсли объект может быть получен, то возвращается ответ `200 OK` с сериализованным представлением объекта в качестве тела ответа. В противном случае будет возвращен ответ `404 Not Found`.\n\n## UpdateModelMixin\n\nПредоставляет метод `.update(request, *args, **kwargs)`, который реализует обновление и сохранение существующего экземпляра модели.\n\nТакже предоставляет метод `.partial_update(request, *args, **kwargs)`, который похож на метод `update`, за исключением того, что все поля для обновления будут необязательными. Это позволяет поддерживать HTTP-запросы `PATCH`.\n\nЕсли объект обновлен, возвращается ответ `200 OK` с сериализованным представлением объекта в качестве тела ответа.\n\nЕсли данные запроса, предоставленные для обновления объекта, были недействительными, будет возвращен ответ `400 Bad Request`, в теле которого будет содержаться информация об ошибке.\n\n## DestroyModelMixin\n\nПредоставляет метод `.destroy(request, *args, **kwargs)`, который реализует удаление существующего экземпляра модели.\n\nЕсли объект удален, возвращается ответ `204 No Content`, в противном случае возвращается ответ `404 Not Found`.\n\n---\n\n# Классы специфичных представлений\n\nСледующие классы являются специфичными общими представлениями. Если вы используете общие представления, то обычно вы работаете именно на этом уровне, если только вам не нужно сильно измененное поведение.\n\nКлассы представления могут быть импортированы из `rest_framework.generics`.\n\n## CreateAPIView\n\nИспользуется только для **создания** конечных точек.\n\nПредоставляет обработчик метода `post`.\n\nРасширяет: [GenericAPIView](#genericapiview), [CreateModelMixin](#createmodelmixin)\n\n## ListAPIView\n\nИспользуется для конечных точек **только для чтения** для представления **коллекции экземпляров модели**.\n\nПредоставляет обработчик метода `get`.\n\nРасширяет: [GenericAPIView](#genericapiview), [ListModelMixin](#listmodelmixin)\n\n## RetrieveAPIView\n\nИспользуется для конечных точек **только для чтения** для представления **одного экземпляра модели**.\n\nПредоставляет обработчик метода `get`.\n\nРасширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin)\n\n## DestroyAPIView\n\nИспользуется для **только для удаления** конечных точек для **одного экземпляра модели**.\n\nПредоставляет обработчик метода `delete`.\n\nРасширяет: [GenericAPIView](#genericapiview), [DestroyModelMixin](#destroymodelmixin)\n\n## UpdateAPIView\n\nИспользуется для **только для обновления** конечных точек для **одного экземпляра модели**.\n\nПредоставляет обработчики методов `put` и `patch`.\n\nРасширяет: [GenericAPIView](#genericapiview), [UpdateModelMixin](#updatemodelmixin)\n\n## ListCreateAPIView\n\nИспользуется для конечных точек **чтения-записи** для представления **коллекции экземпляров модели**.\n\nПредоставляет обработчики методов `get` и `post`.\n\nРасширяет: [GenericAPIView](#genericapiview), [ListModelMixin](#listmodelmixin), [CreateModelMixin](#createmodelmixin)\n\n## RetrieveUpdateAPIView\n\nИспользуется для **чтения или обновления** конечных точек для представления **одного экземпляра модели**.\n\nПредоставляет обработчики методов `get`, `put` и `patch`.\n\nРасширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin), [UpdateModelMixin](#updatemodelmixin)\n\n## RetrieveDestroyAPIView\n\nИспользуется для конечных точек **чтения или удаления** для представления **одного экземпляра модели**.\n\nПредоставляет обработчики методов `get` и `delete`.\n\nРасширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin), [DestroyModelMixin](#destroymodelmixin)\n\n## RetrieveUpdateDestroyAPIView\n\nИспользуется для конечных точек **чтение-запись-удаление** для представления **одного экземпляра модели**.\n\nПредоставляет обработчики методов `get`, `put`, `patch` и `delete`.\n\nРасширяет: [GenericAPIView](#genericapiview), [RetrieveModelMixin](#retrievemodelmixin), [UpdateModelMixin](#updatemodelmixin), [DestroyModelMixin](#destroymodelmixin)\n\n---\n\n# Настройка общих представлений\n\nЧасто вы хотите использовать существующие типовые представления, но использовать несколько измененное поведение. Если вы столкнулись с повторным использованием некоторого настроенного поведения в нескольких местах, вы можете захотеть отрефакторить это поведение в общий класс, который затем можно просто применить к любому представлению или набору представлений по мере необходимости.\n\n## Создание пользовательских миксинов\n\nНапример, если вам нужно искать объекты на основе нескольких полей в URL conf, вы можете создать mixin-класс, подобный следующему:\n\n```python\nclass MultipleFieldLookupMixin:\n    \"\"\"\n    Apply this mixin to any view or viewset to get multiple field filtering\n    based on a `lookup_fields` attribute, instead of the default single field filtering.\n    \"\"\"\n    def get_object(self):\n        queryset = self.get_queryset()             # Get the base queryset\n        queryset = self.filter_queryset(queryset)  # Apply any filter backends\n        filter = {}\n        for field in self.lookup_fields:\n            if self.kwargs.get(field): # Ignore empty fields.\n                filter[field] = self.kwargs[field]\n        obj = get_object_or_404(queryset, **filter)  # Lookup the object\n        self.check_object_permissions(self.request, obj)\n        return obj\n```\n\nЗатем вы можете просто применить этот миксин к представлению или набору представлений в любое время, когда вам нужно применить пользовательское поведение.\n\n```python\nclass RetrieveUserView(MultipleFieldLookupMixin, generics.RetrieveAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    lookup_fields = ['account', 'username']\n```\n\nИспользование пользовательских миксинов - хороший вариант, если у вас есть пользовательское поведение, которое необходимо использовать.\n\n## Создание пользовательских базовых классов\n\nЕсли вы используете миксин в нескольких представлениях, вы можете пойти дальше и создать свой собственный набор базовых представлений, которые затем можно использовать во всем проекте. Например:\n\n```python\nclass BaseRetrieveView(MultipleFieldLookupMixin,\n                       generics.RetrieveAPIView):\n    pass\n\nclass BaseRetrieveUpdateDestroyView(MultipleFieldLookupMixin,\n                                    generics.RetrieveUpdateDestroyAPIView):\n    pass\n```\n\nИспользование пользовательских базовых классов является хорошим вариантом, если у вас есть пользовательское поведение, которое последовательно должно повторяться в большом количестве представлений в вашем проекте.\n\n---\n\n# PUT как создание\n\nДо версии 3.0 миксины DRF рассматривали `PUT` как операцию обновления или создания, в зависимости от того, существовал ли уже объект или нет.\n\nРазрешение `PUT` в качестве операций создания является проблематичным, поскольку оно обязательно раскрывает информацию о существовании или несуществовании объектов. Также не очевидно, что прозрачное разрешение повторного создания ранее удаленных экземпляров обязательно является лучшим поведением по умолчанию, чем простое возвращение ответов `404`.\n\nОба стиля \"`PUT` как 404\" и \"`PUT` как создание\" могут быть действительны в различных обстоятельствах, но начиная с версии 3.0 мы теперь используем поведение 404 по умолчанию, поскольку оно проще и очевиднее.\n\n---\n\n# Пакеты сторонних производителей\n\nСледующие пакеты сторонних производителей предоставляют дополнительные реализации общих представлений.\n\n## Django Rest Multiple Models\n\n[Django Rest Multiple Models](https://github.com/MattBroach/DjangoRestMultipleModels) предоставляет общее представление (и миксин) для отправки нескольких сериализованных моделей и/или наборов запросов через один запрос API.\n"
  },
  {
    "path": "api-guide/metadata.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Метаданные\n\n> [Метод `OPTIONS`] позволяет клиенту определить опции и/или требования, связанные с ресурсом, или возможности сервера, не подразумевая действия с ресурсом и не инициируя поиск ресурса.\n>\n> - [RFC7231, раздел 4.3.7.](https://tools.ietf.org/html/rfc7231#section-4.3.7)\n\nDRF включает настраиваемый механизм для определения того, как ваш API должен отвечать на запросы `OPTIONS`. Это позволяет вам возвращать схему API или другую информацию о ресурсе.\n\nВ настоящее время не существует широко принятых соглашений о том, какой именно стиль ответа должен быть возвращен для HTTP `OPTIONS` запросов, поэтому мы предоставляем специальный стиль, который возвращает некоторую полезную информацию.\n\nВот пример ответа, который демонстрирует информацию, возвращаемую по умолчанию.\n\n```http\nHTTP 200 OK\nAllow: GET, POST, HEAD, OPTIONS\nContent-Type: application/json\n\n{\n    \"name\": \"To Do List\",\n    \"description\": \"List existing 'To Do' items, or create a new item.\",\n    \"renders\": [\n        \"application/json\",\n        \"text/html\"\n    ],\n    \"parses\": [\n        \"application/json\",\n        \"application/x-www-form-urlencoded\",\n        \"multipart/form-data\"\n    ],\n    \"actions\": {\n        \"POST\": {\n            \"note\": {\n                \"type\": \"string\",\n                \"required\": false,\n                \"read_only\": false,\n                \"label\": \"title\",\n                \"max_length\": 100\n            }\n        }\n    }\n}\n```\n\n## Установка схемы метаданных\n\nВы можете установить класс метаданных глобально, используя ключ настройки `'DEFAULT_METADATA_CLASS'`:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata'\n}\n```\n\nИли вы можете установить класс метаданных индивидуально для представления:\n\n```python\nclass APIRoot(APIView):\n    metadata_class = APIRootMetadata\n\n    def get(self, request, format=None):\n        return Response({\n            ...\n        })\n```\n\nПакет DRF включает только одну реализацию класса метаданных, названную `SimpleMetadata`. Если вы хотите использовать альтернативный стиль, вам нужно будет реализовать собственный класс метаданных.\n\n## Создание конечных точек схемы\n\nЕсли у вас есть особые требования к созданию конечных точек схемы, доступ к которым осуществляется с помощью обычных запросов `GET`, вы можете рассмотреть возможность повторного использования API метаданных для этого.\n\nНапример, следующий дополнительный маршрут может быть использован в наборе представлений для обеспечения конечной точки схемы со ссылкой.\n\n```python\n@action(methods=['GET'], detail=False)\ndef api_schema(self, request):\n    meta = self.metadata_class()\n    data = meta.determine_metadata(request, self)\n    return Response(data)\n```\n\nЕсть несколько причин, по которым вы можете выбрать такой подход, включая то, что ответы `OPTIONS` [не подлежат кэшированию](https://www.mnot.net/blog/2012/10/29/NO_OPTIONS).\n\n---\n\n# Пользовательские классы метаданных\n\nЕсли вы хотите предоставить собственный класс метаданных, вам следует переопределить `BaseMetadata` и реализовать метод `determine_metadata(self, request, view)`.\n\nПолезные вещи, которые вы, возможно, захотите сделать, могут включать возврат информации о схеме, используя такой формат, как [JSON schema](https://json-schema.org/), или возврат отладочной информации для пользователей-администраторов.\n\n## Пример\n\nСледующий класс может быть использован для ограничения информации, возвращаемой на запросы `OPTIONS`.\n\n```python\nclass MinimalMetadata(BaseMetadata):\n    \"\"\"\n    Don't include field and other information for `OPTIONS` requests.\n    Just return the name and description.\n    \"\"\"\n    def determine_metadata(self, request, view):\n        return {\n            'name': view.get_view_name(),\n            'description': view.get_view_description()\n        }\n```\n\nЗатем настройте свои параметры для использования этого пользовательского класса:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_METADATA_CLASS': 'myproject.apps.core.MinimalMetadata'\n}\n```\n\n# Пакеты сторонних производителей\n\nСледующие пакеты сторонних производителей предоставляют дополнительные реализации метаданных.\n\n## DRF-schema-adapter\n\n[drf-schema-adapter](https://github.com/drf-forms/drf-schema-adapter) - это набор инструментов, облегчающих предоставление информации о схемах фронтенд-фреймворкам и библиотекам. Он предоставляет миксин метаданных, а также 2 класса метаданных и несколько адаптеров, подходящих для генерации [json-schema](https://json-schema.org/), а также информации о схемах, читаемой различными библиотеками.\n\nВы также можете написать свой собственный адаптер для работы с вашим конкретным фронтендом. Если вы захотите это сделать, он также предоставляет экспортер, который может экспортировать информацию о схеме в json-файлы.\n"
  },
  {
    "path": "api-guide/pagination.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- pagination.py\n\n---\n\n# Пагинация\n\n> Django предоставляет несколько классов, которые помогают вам управлять постраничными данными - то есть данными, разделенными на несколько страниц, со ссылками \"Предыдущая/Следующая\".\n>\n> &mdash; [Django documentation](https://docs.djangoproject.com/en/stable/topics/pagination/)\n\nDRF включает поддержку настраиваемых стилей пагинации. Это позволяет изменять, как большие наборы результатов разбиваются на отдельные страницы данных.\n\nAPI пагинации может поддерживать любую из этих функций:\n\n* Ссылки пагинации, которые предоставляются как часть содержимого ответа.\n* Ссылки пагинации, включенные в заголовки ответа, такие как `Content-Range` или `Link`.\n\nВ настоящее время все встроенные стили используют ссылки, включенные как часть содержимого ответа. Этот стиль более доступен при использовании API с возможностью просмотра.\n\nПагинация выполняется автоматически, только если вы используете общие представления или наборы представлений. Если вы используете обычное `APIView`, вам нужно будет самостоятельно обратиться к API пагинации, чтобы убедиться, что вы возвращаете ответ с пагинацией. Пример смотрите в исходном коде классов `mixins.ListModelMixin` и `generics.GenericAPIView`.\n\nПагинацию можно отключить, установив для класса пагинации значение `None`.\n\n## Установка стиля пагинации\n\nСтиль пагинации можно задать глобально, используя ключи настройки `DEFAULT_PAGINATION_CLASS` и `PAGE_SIZE`. Например, чтобы использовать встроенную пагинацию с ограничением/смещением, вы должны сделать следующее:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',\n    'PAGE_SIZE': 100\n}\n```\n\nОбратите внимание, что необходимо задать как класс пагинации, так и размер страницы, которая будет использоваться. По умолчанию и `DEFAULT_PAGINATION_CLASS`, и `PAGE_SIZE` имеют значение `None`.\n\nВы также можете установить класс пагинации для отдельного представления с помощью атрибута `pagination_class`. Обычно вы хотите использовать один и тот же стиль пагинации во всем API, хотя вы можете захотеть варьировать отдельные аспекты пагинации, такие как размер страницы по умолчанию или максимальный размер страницы, на основе каждого представления.\n\n## Изменение стиля пагинации\n\nЕсли вы хотите изменить определенные аспекты стиля пагинации, вам нужно переопределить один из классов пагинации и установить атрибуты, которые вы хотите изменить.\n\n```python\nclass LargeResultsSetPagination(PageNumberPagination):\n    page_size = 1000\n    page_size_query_param = 'page_size'\n    max_page_size = 10000\n\nclass StandardResultsSetPagination(PageNumberPagination):\n    page_size = 100\n    page_size_query_param = 'page_size'\n    max_page_size = 1000\n```\n\nЗатем вы можете применить ваш новый стиль к представлению с помощью атрибута `pagination_class`:\n\n```python\nclass BillingRecordsView(generics.ListAPIView):\n    queryset = Billing.objects.all()\n    serializer_class = BillingRecordsSerializer\n    pagination_class = LargeResultsSetPagination\n```\n\nИли примените стиль глобально, используя ключ настройки `DEFAULT_PAGINATION_CLASS`. Например:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'\n}\n```\n\n---\n\n# Описание API\n\n## PageNumberPagination\n\nЭтот стиль пагинации принимает номер страницы с одним номером в параметрах запроса.\n\n**Запрос**:\n\n```http\nGET https://api.example.org/accounts/?page=4\n```\n\n**Ответ**:\n\n```http\nHTTP 200 OK\n{\n    \"count\": 1023,\n    \"next\": \"https://api.example.org/accounts/?page=5\",\n    \"previous\": \"https://api.example.org/accounts/?page=3\",\n    \"results\": [\n       …\n    ]\n}\n```\n\n#### Настройка\n\nЧтобы включить стиль `PageNumberPagination` глобально, используйте следующую конфигурацию и установите `PAGE_SIZE` по желанию:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n    'PAGE_SIZE': 100\n}\n```\n\nВ подклассах `GenericAPIView` вы также можете установить атрибут `pagination_class` для выбора `PageNumberPagination` на основе каждого вида.\n\n#### Конфигурация\n\nКласс `PageNumberPagination` включает ряд атрибутов, которые могут быть переопределены для изменения стиля пагинации.\n\nЧтобы установить эти атрибуты, необходимо переопределить класс `PageNumberPagination`, а затем включить свой собственный класс пагинации, как указано выше.\n\n* `django_paginator_class` - Класс Django Paginator, который будет использоваться. По умолчанию это `django.core.paginator.Paginator`, что должно быть хорошо для большинства случаев использования.\n* `page_size` - Числовое значение, указывающее размер страницы. Если установлено, оно отменяет настройку `PAGE_SIZE`. По умолчанию имеет то же значение, что и ключ настройки `PAGE_SIZE`.\n* `page_query_param` - Строковое значение, указывающее имя параметра запроса, который будет использоваться для управления пагинацией.\n* `page_size_query_param` - Если установлено, это строковое значение, указывающее имя параметра запроса, который позволяет клиенту устанавливать размер страницы на основе каждого запроса. По умолчанию `None`, что означает, что клиент не может контролировать размер запрашиваемой страницы.\n* `max_page_size` - Если установлено, это числовое значение, указывающее на максимально допустимый размер запрашиваемой страницы. Этот атрибут действителен, только если `page_size_query_param` также установлен.\n* `last_page_strings` - Список или кортеж строковых значений, указывающих на значения, которые могут быть использованы с `page_query_param` для запроса последней страницы в наборе. По умолчанию `('last',)`. Например, используйте `?page=last` для запроса сразу последней страницы.\n* `template` - Имя шаблона для использования при отображении элементов управления пагинацией в Web-интерфейсе API. Может быть переопределено для изменения стиля рендеринга или установлено в `None` для полного отключения HTML элементов управления пагинацией. По умолчанию используется `\"rest_framework/pagination/numbers.html\"`.\n\n---\n\n## LimitOffsetPagination\n\nЭтот стиль пагинации повторяет синтаксис, используемый при поиске нескольких записей в базе данных. Клиент включает в себя как `'limit'`, так и параметр запроса `'offset'`. Лимит указывает на максимальное количество возвращаемых элементов и эквивалентен `page_size` в других стилях. Смещение указывает начальную позицию запроса по отношению к полному набору непагинированных элементов.\n\n**Запрос**:\n\n```http\nGET https://api.example.org/accounts/?limit=100&offset=400\n```\n\n**Ответ**:\n\n```http\nHTTP 200 OK\n{\n    \"count\": 1023,\n    \"next\": \"https://api.example.org/accounts/?limit=100&offset=500\",\n    \"previous\": \"https://api.example.org/accounts/?limit=100&offset=300\",\n    \"results\": [\n       …\n    ]\n}\n```\n\n#### Подключение\n\nЧтобы включить стиль `LimitOffsetPagination` глобально, используйте следующую конфигурацию:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'\n}\n```\n\nПо желанию вы также можете задать ключ `PAGE_SIZE`. Если параметр `PAGE_SIZE` также используется, то параметр запроса `limit` будет необязательным и может быть опущен клиентом.\n\nВ подклассах `GenericAPIView` вы также можете установить атрибут `pagination_class` для выбора `LimitOffsetPagination` для каждого представления.\n\n#### Настройка\n\nКласс `LimitOffsetPagination` включает ряд атрибутов, которые могут быть переопределены для изменения стиля пагинации.\n\nЧтобы установить эти атрибуты, вы должны переопределить класс `LimitOffsetPagination`, а затем включить свой собственный класс пагинации, как указано выше.\n\n* `default_limit` - Числовое значение, указывающее предел, который следует использовать, если он не указан клиентом в параметре запроса. По умолчанию имеет то же значение, что и ключ настройки `PAGE_SIZE`.\n* `limit_query_param` - Строковое значение, указывающее имя параметра запроса \"limit\". По умолчанию имеет значение `'limit'`.\n* `offset_query_param` - Строковое значение, указывающее имя параметра запроса \"offset\". По умолчанию имеет значение `'offset'`.\n* `max_limit` - Если установлено, то это числовое значение, указывающее на максимально допустимый лимит, который может быть запрошен клиентом. По умолчанию `None`.\n* `template` - Имя шаблона, который будет использоваться при отображении элементов управления пагинацией в Web-интерфейсе API. Может быть переопределено для изменения стиля рендеринга или установлено в `None` для полного отключения HTML элементов управления пагинацией. По умолчанию используется `\"rest_framework/pagination/numbers.html\"`.\n\n---\n\n## CursorPagination\n\nПагинация на основе курсора представляет непрозрачный индикатор `cursor`, который клиент может использовать для просмотра набора результатов. Этот стиль пагинации представляет только элементы управления перемоткой вперед и назад и не позволяет клиенту переходить к произвольным позициям.\n\nПагинация на основе курсора требует наличия уникального, неизменного порядка следования элементов в наборе результатов. Обычно таким упорядочиванием может быть временная метка создания записей, так как она представляет собой последовательный порядок для постраничного просмотра.\n\nПагинация на основе курсора является более сложной, чем другие схемы. Она также требует, чтобы набор результатов представлял фиксированный порядок, и не позволяет клиенту произвольно индексировать набор результатов. Однако она обеспечивает следующие преимущества:\n\n* Обеспечивает последовательное представление пагинации. При правильном использовании `CursorPagination` гарантирует, что клиент никогда не увидит один и тот же элемент дважды при листании записей, даже если новые элементы вставляются другими клиентами во время процесса пагинации.\n* Поддержка использования с очень большими наборами данных. При работе с очень большими наборами данных пагинация с использованием стилей пагинации на основе смещения может стать неэффективной или непригодной для использования. Вместо этого схемы пагинации на основе курсора имеют свойства фиксированного времени и не замедляются при увеличении размера набора данных.\n\n#### Подробности и ограничения\n\nПравильное использование пагинации на основе курсора требует некоторого внимания к деталям. Вам нужно подумать о том, в каком порядке вы хотите применять схему. По умолчанию используется порядок по `\"-created\"`. Это предполагает, что **в экземплярах модели должно быть поле временной метки \"created\"**, и будет представлено постраничное представление в стиле \"временной шкалы\", где первыми будут самые последние добавленные элементы.\n\nВы можете изменить порядок, переопределив атрибут `'ordering'` класса пагинации, или используя класс фильтра `OrderingFilter` вместе с `CursorPagination`. При использовании `OrderingFilter` следует тщательно продумать ограничение полей, по которым пользователь может делать заказ.\n\nПравильное использование пагинации курсора должно иметь поле упорядочивания, которое удовлетворяет следующим требованиям:\n\n* Должно быть неизменным значением, таким как временная метка, slug или другое поле, которое устанавливается только один раз, при создании.\n* Должно быть уникальным или почти уникальным. Хорошим примером являются временные метки с точностью до миллисекунды. Эта реализация пагинации курсора использует интеллектуальный стиль \"позиция плюс смещение\", что позволяет ей правильно поддерживать не строго уникальные значения в качестве упорядочивания.\n* Должно быть не нулевым значением, которое можно принудительно преобразовать в строку.\n* Не должно быть плавающей точкой. Ошибки точности легко приводят к неправильным результатам.\n    **Совет**: используйте вместо этого десятичные числа. (Если у вас уже есть поле с плавающей запятой и вам нужно сделать постраничную запись по нему, можно воспользоваться командой [пример подкласса `CursorPagination`, который использует десятичные числа для ограничения точности, доступен здесь] (https://gist.github.com/keturn/8bc88525a183fd41c73ffb729b8865be#file-fpcursorpagination-py).)\n* Поле должно иметь индекс базы данных.\n\nИспользование поля упорядочивания, которое не удовлетворяет этим ограничениям, как правило, будет работать, но вы потеряете некоторые преимущества пагинации курсора.\n\nДля получения более подробной технической информации о реализации, которую мы используем для пагинации курсоров, в статье [\"Building cursors for the Disqus API\"](https://cra.mr/2011/03/08/building-cursors-for-the-disqus-api) блога дается хороший обзор основного подхода.\n\n#### Подключение\n\nЧтобы включить стиль `CursorPagination` глобально, используйте следующую конфигурацию, изменяя `PAGE_SIZE` по желанию:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',\n    'PAGE_SIZE': 100\n}\n```\n\nВ подклассах `GenericAPIView` вы также можете установить атрибут `pagination_class` для выбора `CursorPagination` на основе каждого вида.\n\n#### Настройка\n\nКласс `CursorPagination` включает ряд атрибутов, которые могут быть переопределены для изменения стиля пагинации.\n\nЧтобы установить эти атрибуты, необходимо переопределить класс `CursorPagination`, а затем включить свой собственный класс пагинации, как указано выше.\n\n* `page_size` - числовое значение, указывающее размер страницы. Если установлено, оно отменяет настройку `PAGE_SIZE`. По умолчанию имеет то же значение, что и ключ настройки `PAGE_SIZE`.\n* `cursor_query_param` - Строковое значение, указывающее имя параметра запроса \"cursor\". По умолчанию `'cursor'`.\n* `orderering` - Это должна быть строка или список строк, указывающих на поле, к которому будет применяться пагинация на основе курсора. Например: `ordering = 'slug'`. По умолчанию используется `-created`. Это значение также может быть переопределено с помощью `OrderingFilter` в представлении.\n* `template` - Имя шаблона, который будет использоваться при отображении элементов управления пагинацией в API просмотра. Может быть переопределено для изменения стиля рендеринга или установлено в `None` для полного отключения HTML элементов управления пагинацией. По умолчанию используется `\"rest_framework/pagination/previous_and_next.html\"`.\n\n---\n\n# Пользовательские стили пагинации\n\nЧтобы создать собственный класс сериализатора пагинации, необходимо унаследовать подкласс `pagination.BasePagination`, переопределить методы `paginate_queryset(self, queryset, request, view=None)` и `get_paginated_response(self, data)`:\n\n* Метод `paginate_queryset` передается начальному кверисету и должен возвращать итерируемый объект. Этот объект содержит только данные запрашиваемой страницы.\n* Метод `get_paginated_response` передается сериализованным данным страницы и должен возвращать экземпляр `Response`.\n\nОбратите внимание, что метод `paginate_queryset` может установить состояние экземпляра пагинации, которое впоследствии может быть использовано методом `get_paginated_response`.\n\n## Пример\n\nПредположим, мы хотим заменить стандартный стиль вывода пагинации на модифицированный формат, который включает следующую и предыдущую ссылки во вложенном ключе `'links'`. Мы можем указать пользовательский класс пагинации следующим образом:\n\n```python\nclass CustomPagination(pagination.PageNumberPagination):\n    def get_paginated_response(self, data):\n        return Response({\n            'links': {\n                'next': self.get_next_link(),\n                'previous': self.get_previous_link()\n            },\n            'count': self.page.paginator.count,\n            'results': data\n        })\n```\n\nЗатем нам нужно будет установить пользовательский класс в нашей конфигурации:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.CustomPagination',\n    'PAGE_SIZE': 100\n}\n```\n\nОбратите внимание, что если вам важно, как порядок ключей отображается в ответах в Web-интерфейсе API, вы можете использовать `OrderedDict` при построении тела постраничных ответов, но это необязательно.\n\n## Использование вашего пользовательского класса пагинации\n\nЧтобы ваш пользовательский класс пагинации использовался по умолчанию, используйте параметр `DEFAULT_PAGINATION_CLASS`:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.LinkHeaderPagination',\n    'PAGE_SIZE': 100\n}\n```\n\nОтветы API для конечных точек списка теперь будут включать заголовок `Link`, вместо того чтобы, например, включать ссылки пагинации как часть тела ответа:\n\n![](https://github.com/encode/django-rest-framework/raw/main/docs/img/link-header-pagination.png)\n\n*Настраиваемый стиль пагинации с использованием заголовка `Link`.*\n\n---\n\n# Элементы управления пагинацией HTML\n\nПо умолчанию использование классов пагинации приводит к отображению элементов управления пагинацией HTML в Web-интерфейсе API. Существует два встроенных стиля отображения. Классы `PageNumberPagination` и `LimitOffsetPagination` отображают список номеров страниц с предыдущим и следующим элементами управления. Класс `CursorPagination` отображает более простой стиль, в котором отображаются только предыдущий и следующий элементы управления.\n\n## Настройка элементов управления\n\nВы можете переопределить шаблоны, которые отображают элементы управления пагинацией HTML. Есть два встроенных стиля:\n\n* `'rest_framework/pagination/numbers.html'`\n* `'rest_framework/pagination/previous_and_next.html'`\n\nПредоставление шаблона с любым из этих путей в глобальном каталоге шаблонов переопределит рендеринг по умолчанию для соответствующих классов пагинации.\n\nВ качестве альтернативы вы можете полностью отключить элементы управления HTML-пагинацией, создав подкласс одного из существующих классов и установив `template = None` в качестве атрибута класса. Затем вам нужно будет настроить ключ параметров `DEFAULT_PAGINATION_CLASS`, чтобы использовать ваш пользовательский класс в качестве стиля пагинации по умолчанию.\n\n#### Низкоуровневый API\n\nНизкоуровневый API для определения того, должен ли класс пагинации отображать элементы управления или нет, раскрывается как атрибут `display_page_controls` на экземпляре пагинации. Пользовательские классы пагинации должны быть установлены в `True` в методе `paginate_queryset`, если они требуют отображения элементов управления пагинацией HTML.\n\nМетоды `.to_html()` и `.get_html_context()` также могут быть переопределены в пользовательском классе пагинации для дальнейшей настройки отображения элементов управления.\n\n---\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## DRF-extensions\n\nПакет [`DRF-extensions`](https://chibisov.github.io/drf-extensions/docs/) включает класс-миксин [`PaginateByMaxMixin`](https://chibisov.github.io/drf-extensions/docs/#paginatebymaxmixin), который позволяет вашим клиентам API указывать `?page_size=max` для получения максимально допустимого размера страницы.\n\n## drf-proxy-pagination\n\nПакет [`drf-proxy-pagination`](https://github.com/tuffnatty/drf-proxy-pagination) включает класс `ProxyPagination`, который позволяет выбирать класс пагинации с помощью параметра запроса.\n\n## link-header-pagination\n\nПакет [`django-rest-framework-link-header-pagination`](https://github.com/tbeadle/django-rest-framework-link-header-pagination) включает класс `LinkHeaderPagination`, который обеспечивает пагинацию через HTTP-заголовок `Link`, как описано в [документации GitHub REST API](https://docs.github.com/en/rest/guides/traversing-with-pagination).\n"
  },
  {
    "path": "api-guide/parsers.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- parsers.py\n\n---\n\n# Парсеры\n\n> Взаимодействующие с машинами веб-сервисы обычно используют более структурированные форматы для отправки данных, чем данные формы, поскольку они отправляют более сложные данные, чем простые формы\n>\n> &mdash; Malcom Tredinnick, [Django developers group](https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion)\n\nDRF включает ряд встроенных классов `Parser`, которые позволяют принимать запросы с различными типами носителей. Также есть поддержка определения собственных парсеров, что дает вам гибкость в определении типов медиа, которые принимает ваш API.\n\n## Как определяется синтаксический анализатор\n\nНабор допустимых парсеров для представления всегда определяется как список классов. Когда происходит обращение к `request.data`, DRF изучает заголовок `Content-Type` входящего запроса и определяет, какой парсер использовать для разбора содержимого запроса.\n\n---\n\n**Примечание**: При разработке клиентских приложений всегда помните о том, что при отправке данных в HTTP-запросе нужно обязательно устанавливать заголовок `Content-Type`.\n\nЕсли вы не зададите тип содержимого, большинство клиентов по умолчанию будут использовать `'application/x-www-form-urlencoded'`, что может оказаться не тем, чего вы хотите.\n\nВ качестве примера, если вы отправляете закодированные данные `json` с помощью jQuery с методом [.ajax()](https://api.jquery.com/jQuery.ajax/), вы должны обязательно включить параметр `contentType: 'application/json'`.\n\n---\n\n## Настройка парсеров\n\nНабор парсеров по умолчанию можно задать глобально, используя параметр `DEFAULT_PARSER_CLASSES`. Например, следующие настройки разрешают только запросы с содержимым `JSON`, вместо стандартного JSON или данных формы.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PARSER_CLASSES': [\n        'rest_framework.parsers.JSONParser',\n    ]\n}\n```\n\nВы также можете установить парсеры, используемые для отдельного представления или набора представлений, используя представления на основе класса `APIView`.\n\n```python\nfrom rest_framework.parsers import JSONParser\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n    \"\"\"\n    A view that can accept POST requests with JSON content.\n    \"\"\"\n    parser_classes = [JSONParser]\n\n    def post(self, request, format=None):\n        return Response({'received data': request.data})\n```\n\nИли, если вы используете декоратор `@api_view` с представлениями, основанными на функциях.\n\n```python\nfrom rest_framework.decorators import api_view\nfrom rest_framework.decorators import parser_classes\nfrom rest_framework.parsers import JSONParser\n\n@api_view(['POST'])\n@parser_classes([JSONParser])\ndef example_view(request, format=None):\n    \"\"\"\n    A view that can accept POST requests with JSON content.\n    \"\"\"\n    return Response({'received data': request.data})\n```\n\n---\n\n# API Reference\n\n## JSONParser\n\nРазбирает `JSON` содержимое запроса. `request.data` будет заполнен словарем данных.\n\n**.media_type**: `application/json`.\n\n## FormParser\n\nРазбирает содержимое HTML-формы. `request.data` будет заполнен `QueryDict` данных.\n\nДля полной поддержки данных HTML-формы обычно требуется использовать `FormParser` и `MultiPartParser` вместе.\n\n**.media_type**: `application/x-www-form-urlencoded`.\n\n## MultiPartParser\n\nРазбирает содержимое многокомпонентной HTML-формы, которая поддерживает загрузку файлов. `request.data` и `request.FILES` будут заполнены `QueryDict` и `MultiValueDict` соответственно.\n\nДля полной поддержки данных HTML-формы обычно требуется использовать `FormParser` и `MultiPartParser` вместе.\n\n**.media_type**: `multipart/form-data`.\n\n## FileUploadParser\n\nРазбирает необработанное содержимое загружаемого файла. Свойство `request.data` будет представлять собой словарь с единственным ключом `'file'`, содержащим загруженный файл.\n\nЕсли представление, используемое с `FileUploadParser`, вызывается с именованным аргументом URL `filename`, то этот аргумент будет использоваться в качестве имени файла.\n\nЕсли он вызывается без именованного аргумента URL `filename`, то клиент должен установить имя файла в HTTP-заголовке `Content-Disposition`. Например, `Content-Disposition: attachment; filename=upload.jpg`.\n\n**.media_type**: `*/*`\n\n##### Примечания:\n\n* `FileUploadParser` предназначен для использования с собственными клиентами, которые могут загружать файл как запрос необработанных данных. Для веб-загрузки или для собственных клиентов с поддержкой многочастной загрузки вместо него следует использовать `MultiPartParser`.\n* Поскольку `media_type` этого парсера соответствует любому типу содержимого, `FileUploadParser` обычно должен быть единственным парсером, установленным в представлении API.\n* `FileUploadParser` учитывает стандартную настройку Django `FILE_UPLOAD_HANDLERS` и атрибут `request.upload_handlers`. Более подробную информацию смотрите в [документации Django](https://docs.djangoproject.com/en/stable/topics/http/file-uploads/#upload-handlers).\n\n##### Базовый пример использования:\n\n```python\n# views.py\nclass FileUploadView(views.APIView):\n    parser_classes = [FileUploadParser]\n\n    def put(self, request, filename, format=None):\n        file_obj = request.data['file']\n        # ...\n        # do some stuff with uploaded file\n        # ...\n        return Response(status=204)\n\n# urls.py\nurlpatterns = [\n    # ...\n    re_path(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view())\n]\n```\n\n---\n\n# Пользовательские синтаксические анализаторы\n\nДля реализации пользовательского парсера необходимо переопределить `BaseParser`, установить свойство `.media_type` и реализовать метод `.parse(self, stream, media_type, parser_context)`.\n\nМетод должен возвращать данные, которые будут использоваться для заполнения свойства `request.data`.\n\nАргументами, передаваемыми в `.parse()`, являются:\n\n### stream\n\nПотокоподобный объект, представляющий тело запроса.\n\n### media_type\n\nНеобзательно. Если указано, это тип носителя содержимого входящего запроса.\n\nВ зависимости от заголовка `Content-Type:` запроса, он может быть более конкретным, чем атрибут `media_type` рендерера, и может включать параметры типа медиа. Например, `\"text/plain; charset=utf-8\"`.\n\n### parser_context\n\nНеобзательно. Если этот аргумент указан, то он будет представлять собой словарь, содержащий любой дополнительный контекст, который может потребоваться для разбора содержимого запроса.\n\nПо умолчанию сюда входят следующие ключи: `view`, `request`, `args`, `kwargs`.\n\n## Пример\n\nНиже приведен пример анализатора обычного текста, который заполнит свойство `request.data` строкой, представляющей тело запроса.\n\n```python\nclass PlainTextParser(BaseParser):\n    \"\"\"\n    Plain text parser.\n    \"\"\"\n    media_type = 'text/plain'\n\n    def parse(self, stream, media_type=None, parser_context=None):\n        \"\"\"\n        Simply return a string representing the body of the request.\n        \"\"\"\n        return stream.read()\n```\n\n---\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## YAML\n\n[REST framework YAML](https://jpadilla.github.io/django-rest-framework-yaml/) обеспечивает поддержку разбора и рендеринга [YAML](http://www.yaml.org/). Ранее он был включен непосредственно в пакет DRF, а теперь поддерживается как сторонний пакет.\n\n#### Установка и настройка\n\nУстановите с помощью pip.\n\n```bash\n$ pip install djangorestframework-yaml\n```\n\nИзмените настройки DRF.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PARSER_CLASSES': [\n        'rest_framework_yaml.parsers.YAMLParser',\n    ],\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework_yaml.renderers.YAMLRenderer',\n    ],\n}\n```\n\n## XML\n\n[REST Framework XML](https://jpadilla.github.io/django-rest-framework-xml/) предоставляет простой неформальный формат XML. Ранее он был включен непосредственно в пакет DRF, а теперь поддерживается как сторонний пакет.\n\n#### Установка и настройка\n\nУстановите с помощью pip.\n\n```bash\n$ pip install djangorestframework-xml\n```\n\nИзмените настройки DRF.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PARSER_CLASSES': [\n        'rest_framework_xml.parsers.XMLParser',\n    ],\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework_xml.renderers.XMLRenderer',\n    ],\n}\n```\n\n## MessagePack\n\n[MessagePack](https://github.com/juanriaza/django-rest-framework-msgpack) - это быстрый и эффективный формат двоичной сериализации. [Juan Riaza](https://github.com/juanriaza) поддерживает пакет [djangorestframework-msgpack](https://github.com/juanriaza/django-rest-framework-msgpack), который обеспечивает поддержку рендеринга и парсера MessagePack для DRF.\n\n## CamelCase JSON\n\n[djangorestframework-camel-case](https://github.com/vbabiy/djangorestframework-camel-case) предоставляет рендереры и парсеры JSON в верблюжьем регистре для DRF. Это позволяет сериализаторам использовать имена полей в стиле Python с подчеркиванием, но отображать их в API как имена полей в верблюжьем регистре в стиле Javascript. Поддерживается [Виталием Бабием](https://github.com/vbabiy).\n"
  },
  {
    "path": "api-guide/permissions.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- permissions.py\n\n---\n\n# Разрешения\n\n> Аутентификация или идентификация сами по себе обычно недостаточны для получения доступа к информации или коду. Для этого субъект, запрашивающий доступ, должен иметь авторизацию.\n>\n> &mdash; [Apple Developer Documentation](https://developer.apple.com/library/mac/#documentation/security/Conceptual/AuthenticationAndAuthorizationGuide/Authorization/Authorization.html)\n\nВместе с [authentication](authentication.md) и [throttling](throttling.md) разрешения определяют, следует ли предоставить или отказать в доступе запросу.\n\nПроверка разрешений всегда выполняется в самом начале представления, до того, как будет разрешено выполнение любого другого кода. Проверки разрешений обычно используют информацию об аутентификации в свойствах `request.user` и `request.auth`, чтобы определить, должен ли входящий запрос быть разрешен.\n\nРазрешения используются для предоставления или запрета доступа различных классов пользователей к различным частям API.\n\nСамый простой стиль разрешения - разрешить доступ любому аутентифицированному пользователю и запретить доступ любому неаутентифицированному пользователю. Это соответствует классу `IsAuthenticated` в DRF.\n\nНесколько менее строгий стиль разрешения - разрешить полный доступ для аутентифицированных пользователей, но разрешить доступ только для чтения для неаутентифицированных пользователей. Это соответствует классу `IsAuthenticatedOrReadOnly` в DRF.\n\n## Как определяются разрешения\n\nРазрешения в DRF всегда определяются как список классов разрешений.\n\nПеред запуском основной части представления проверяется каждое разрешение в списке. Если проверка какого-либо разрешения не удалась, будет вызвано исключение `exceptions.PermissionDenied` или `exceptions.NotAuthenticated`, и основное тело представления не будет запущено.\n\nЕсли проверка разрешения не сработала, будет возвращен ответ \"403 Forbidden\" или \"401 Unauthorized\", в соответствии со следующими правилами:\n\n* Запрос был успешно аутентифицирован, но в разрешении было отказано. *&mdash; Будет возвращен ответ HTTP 403 Forbidden.*\n* Запрос не был успешно аутентифицирован, и класс аутентификации с наивысшим приоритетом *не использует* заголовки `WWW-Authenticate`. *&mdash; Будет возвращен ответ HTTP 403 Forbidden.*\n* Запрос не был успешно аутентифицирован, и класс аутентификации с наивысшим приоритетом *использует* заголовки `WWW-Authenticate`. *&mdash; Будет возвращен ответ HTTP 401 Unauthorized с соответствующим заголовком `WWW-Authenticate`.\n\n## Разрешения на уровне объекта\n\nРазрешения DRF также поддерживают разрешение на уровне объекта. Разрешения на уровне объекта используются для определения того, разрешено ли пользователю действовать с определенным объектом, который обычно является экземпляром модели.\n\nРазрешения на уровне объекта запускаются общими представлениями DRF при вызове `.get_object()`. Как и в случае с разрешениями на уровне представления, исключение `exceptions.PermissionDenied` будет поднято, если пользователю не разрешено действовать с данным объектом.\n\nЕсли вы пишете собственные представления и хотите обеспечить разрешения на уровне объекта, или если вы переопределите метод `get_object` в общем представлении, то вам нужно будет явно вызвать метод `.check_object_permissions(request, obj)` в представлении в тот момент, когда вы извлекли объект.\n\nЭто либо вызовет исключение `PermissionDenied` или `NotAuthenticated`, либо просто вернет, если представление имеет соответствующие разрешения.\n\nНапример:\n\n```python\ndef get_object(self):\n    obj = get_object_or_404(self.get_queryset(), pk=self.kwargs[\"pk\"])\n    self.check_object_permissions(self.request, obj)\n    return obj\n```\n\n---\n\n**Примечание**: За исключением `DjangoObjectPermissions`, предоставленные классы разрешений в `rest_framework.permissions` **не** реализуют методы, необходимые для проверки разрешений объектов.\n\nЕсли вы хотите использовать предоставленные классы разрешений для проверки разрешений объектов, **вы должны** подклассифицировать их и реализовать метод `has_object_permission()`, описанный в разделе [*Пользовательские разрешения*](#пользовательские-разрешения) (ниже).\n\n---\n\n#### Ограничения разрешений на уровне объекта\n\nПо причинам производительности общие представления не будут автоматически применять разрешения на уровне объекта к каждому экземпляру в наборе запросов при возврате списка объектов.\n\nЧасто при использовании разрешений на уровне объектов вы также хотите [фильтровать QuerySet](filtering.md) соответствующим образом, чтобы убедиться, что пользователи имеют возможность просматривать только те экземпляры, которые им разрешено просматривать.\n\nПоскольку метод `get_object()` не вызывается, разрешения объектного уровня из метода `has_object_permission()` **не применяются** при создании объектов. Чтобы ограничить создание объектов, вам необходимо реализовать проверку разрешений либо в классе Serializer, либо переопределить метод `perform_create()` вашего класса ViewSet.\n\n## Установка политики разрешений\n\nПолитика разрешений по умолчанию может быть установлена глобально с помощью параметра `DEFAULT_PERMISSION_CLASSES`. Например.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PERMISSION_CLASSES': [\n        'rest_framework.permissions.IsAuthenticated',\n    ]\n}\n```\n\nЕсли этот параметр не указан, то по умолчанию он разрешает неограниченный доступ:\n\n```python\n'DEFAULT_PERMISSION_CLASSES': [\n   'rest_framework.permissions.AllowAny',\n]\n```\n\nВы также можете установить политику аутентификации на основе каждого представления или каждого набора представлений, используя представления на основе класса `APIView`.\n\n```python\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n    permission_classes = [IsAuthenticated]\n\n    def get(self, request, format=None):\n        content = {\n            'status': 'request was permitted'\n        }\n        return Response(content)\n```\n\nИли, если вы используете декоратор `@api_view` с представлениями, основанными на функциях.\n\n```python\nfrom rest_framework.decorators import api_view, permission_classes\nfrom rest_framework.permissions import IsAuthenticated\nfrom rest_framework.response import Response\n\n@api_view(['GET'])\n@permission_classes([IsAuthenticated])\ndef example_view(request, format=None):\n    content = {\n        'status': 'request was permitted'\n    }\n    return Response(content)\n```\n\n---\n\n**Примечание:** когда вы устанавливаете новые классы разрешений с помощью атрибута class или декораторов, вы говорите представлению игнорировать список по умолчанию, установленный в файле `settings.py`.\n\n---\n\nПри условии наследования от `rest_framework.permissions.BasePermission`, разрешения могут быть составлены с использованием стандартных побитовых операторов Python. Например, `IsAuthenticatedOrReadOnly` может быть записано:\n\n```python\nfrom rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass ReadOnly(BasePermission):\n    def has_permission(self, request, view):\n        return request.method in SAFE_METHODS\n\nclass ExampleView(APIView):\n    permission_classes = [IsAuthenticated | ReadOnly]\n\n    def get(self, request, format=None):\n        content = {\n            'status': 'request was permitted'\n        }\n        return Response(content)\n```\n\n\n---\n\n**Примечание:** он поддерживает & (и), | (или) и ~ (не).\n\n---\n\n# API Reference\n\n## AllowAny\n\nКласс разрешения `AllowAny` разрешает неограниченный доступ, **независимо от того, был ли запрос аутентифицирован или неаутентифицирован**.\n\nЭто разрешение не является строго обязательным, поскольку вы можете достичь того же результата, используя пустой список или кортеж для установки разрешений, но вы можете посчитать полезным указать этот класс, поскольку он делает намерение явным.\n\n## IsAuthenticated\n\nКласс разрешения `IsAuthenticated` будет запрещать разрешение любому пользователю, не прошедшему аутентификацию, и разрешать в противном случае.\n\nЭто разрешение подходит, если вы хотите, чтобы ваш API был доступен только зарегистрированным пользователям.\n\n## IsAdminUser\n\nКласс разрешения `IsAdminUser` запрещает разрешение любому пользователю, если только `user.is_staff` не является `True`, в этом случае разрешение будет разрешено.\n\nЭто разрешение подходит, если вы хотите, чтобы ваш API был доступен только подгруппе доверенных администраторов.\n\n## IsAuthenticatedOrReadOnly\n\nПараметр `IsAuthenticatedOrReadOnly` позволит аутентифицированным пользователям выполнять любые запросы. Запросы для неаутентифицированных пользователей будут разрешены, только если метод запроса является одним из \"безопасных\" методов: `GET`, `HEAD` или `OPTIONS`.\n\nЭто разрешение подходит, если вы хотите, чтобы ваш API разрешал разрешения на чтение анонимным пользователям и разрешал разрешения на запись только аутентифицированным пользователям.\n\n## DjangoModelPermissions\n\nЭтот класс разрешений связан со стандартными разрешениями Django `django.contrib.auth` [model permissions](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions). Это разрешение должно применяться только к представлениям, имеющим свойство `.queryset` или метод `get_queryset()`. Авторизация будет предоставлена только в том случае, если пользователь *аутентифицирован* и имеет *соответствующие разрешения модели*. Соответствующая модель определяется путем проверки `get_queryset().model` или `queryset.model`.\n\n* `POST`-запросы требуют от пользователя разрешения `add` на модель.\n* `PUT`-запросы и `PATCH`-запросы требуют от пользователя разрешения `change` модель.\n* `DELETE`-запросы требуют от пользователя разрешения `delete` на модель.\n\nПоведение по умолчанию также может быть переопределено для поддержки пользовательских разрешений модели. Например, вы можете включить разрешение модели `view` для запросов `GET`.\n\nЧтобы использовать пользовательские разрешения модели, переопределите `DjangoModelPermissions` и установите свойство `.perms_map`. Подробности см. в исходном коде.\n\n## DjangoModelPermissionsOrAnonReadOnly\n\nАналогичен `DjangoModelPermissions`, но также позволяет неаутентифицированным пользователям иметь доступ к API только для чтения.\n\n## DjangoObjectPermissions\n\nЭтот класс разрешений связан со стандартным [object permissions framework](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions) Django, который позволяет устанавливать разрешения на модели на уровне объектов. Чтобы использовать этот класс разрешений, вам также необходимо добавить бэкенд разрешений, который поддерживает разрешения на уровне объектов, например [django-guardian](https://github.com/lukaszb/django-guardian).\n\nКак и `DjangoModelPermissions`, это разрешение должно применяться только к представлениям, имеющим свойство `.queryset` или метод `.get_queryset()`. Разрешение будет предоставлено только в том случае, если пользователь *аутентифицирован* и имеет *соответствующие разрешения на объект* и *соответствующие разрешения на модель*.\n\n* `POST`-запросы требуют, чтобы пользователь имел разрешение `add` на экземпляр модели.\n* `PUT`-запросы и `PATCH`-запросы требуют от пользователя разрешения `изменить` на экземпляре модели.\n* `DELETE`-запросы требуют от пользователя разрешения `delete` на экземпляр модели.\n\nОбратите внимание, что `DjangoObjectPermissions` **не** требует пакета `django-guardian`, и должен одинаково хорошо поддерживать другие бэкенды объектного уровня.\n\nКак и в случае с `DjangoModelPermissions`, вы можете использовать пользовательские разрешения модели, переопределив `DjangoObjectPermissions` и установив свойство `.perms_map`. Подробности смотрите в исходном коде.\n\n---\n\n**Примечание**: Если вам нужны разрешения `view` на уровне объектов для запросов `GET`, `HEAD` и `OPTIONS` и вы используете django-guardian для бэкенда разрешений на уровне объектов, вам стоит рассмотреть возможность использования класса `DjangoObjectPermissionsFilter`, предоставляемого [пакетом `djangorestframework-guardian`](https://github.com/rpkilby/django-rest-framework-guardian). Он гарантирует, что конечные точки списка возвращают только те результаты, включающие объекты, для которых у пользователя есть соответствующие разрешения на просмотр.\n\n---\n\n# Пользовательские разрешения\n\nЧтобы реализовать пользовательское разрешение, переопределите `BasePermission` и реализуйте один или оба из следующих методов:\n\n* `.has_permission(self, request, view)`\n* `.has_object_permission(self, request, view, obj)`\n\nМетоды должны возвращать `True`, если запрос должен получить доступ, и `False` в противном случае.\n\nЕсли вам нужно проверить, является ли запрос операцией чтения или записи, вы должны проверить метод запроса по константе `SAFE_METHODS`, которая представляет собой кортеж, содержащий `'GET'`, `'OPTIONS'` и `'HEAD'`. Например:\n\n```python\nif request.method in permissions.SAFE_METHODS:\n    # Check permissions for read-only request\nelse:\n    # Check permissions for write request\n```\n\n---\n\n**Примечание**: Метод `has_object_permission` на уровне экземпляра будет вызван только в том случае, если проверки `has_permission` на уровне представления уже прошли. Также обратите внимание, что для того, чтобы проверки на уровне экземпляра были выполнены, код представления должен явно вызвать `.check_object_permissions(request, obj)`. Если вы используете общие представления, то это будет сделано за вас по умолчанию. (Представления, основанные на функциях, должны будут проверять разрешения объектов явно, выдавая при неудаче сообщение `PermissionDenied`).\n\n---\n\nПользовательские разрешения вызовут исключение `PermissionDenied`, если тест не пройдет. Чтобы изменить сообщение об ошибке, связанное с исключением, реализуйте атрибут `message` непосредственно для вашего пользовательского разрешения. В противном случае будет использоваться атрибут `default_detail` из `PermissionDenied`. Аналогично, чтобы изменить идентификатор кода, связанный с исключением, реализуйте атрибут `code` непосредственно для вашего пользовательского разрешения - иначе будет использоваться атрибут `default_code` из `PermissionDenied`.\n\n```python\nfrom rest_framework import permissions\n\nclass CustomerAccessPermission(permissions.BasePermission):\n    message = 'Adding customers not allowed.'\n\n    def has_permission(self, request, view):\n         ...\n```\n\n## Примеры\n\nНиже приведен пример класса разрешения, который проверяет IP-адрес входящего запроса по списку блокировки и отклоняет запрос, если IP-адрес был заблокирован.\n\n```python\nfrom rest_framework import permissions\n\nclass BlocklistPermission(permissions.BasePermission):\n    \"\"\"\n    Global permission check for blocked IPs.\n    \"\"\"\n\n    def has_permission(self, request, view):\n        ip_addr = request.META['REMOTE_ADDR']\n        blocked = Blocklist.objects.filter(ip_addr=ip_addr).exists()\n        return not blocked\n```\n\nПомимо глобальных разрешений, которые выполняются для всех входящих запросов, вы также можете создавать разрешения на уровне объекта, которые выполняются только для операций, затрагивающих конкретный экземпляр объекта. Например:\n\n```python\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n    \"\"\"\n    Object-level permission to only allow owners of an object to edit it.\n    Assumes the model instance has an `owner` attribute.\n    \"\"\"\n\n    def has_object_permission(self, request, view, obj):\n        # Read permissions are allowed to any request,\n        # so we'll always allow GET, HEAD or OPTIONS requests.\n        if request.method in permissions.SAFE_METHODS:\n            return True\n\n        # Instance must have an attribute named `owner`.\n        return obj.owner == request.user\n```\n\nОбратите внимание, что общие представления будут проверять соответствующие разрешения на уровне объекта, но если вы пишете свои собственные пользовательские представления, вам нужно убедиться, что вы сами проверяете разрешения на уровне объекта. Вы можете сделать это, вызвав `self.check_object_permissions(request, obj)` из представления, когда у вас есть экземпляр объекта. Этот вызов вызовет соответствующее исключение `APIException`, если проверка разрешений на уровне объекта завершится неудачей, а в противном случае просто вернется.\n\nТакже обратите внимание, что общие представления будут проверять разрешения на уровне объекта только для представлений, которые получают один экземпляр модели. Если вам требуется фильтрация представлений списка на уровне объектов, вам нужно будет фильтровать набор запросов отдельно. Более подробную информацию смотрите в документации [filtering documentation](filtering.md).\n\n# Обзор методов ограничения доступа\n\nDRF предлагает три различных метода настройки ограничений доступа в каждом конкретном случае. Они применяются в разных сценариях и имеют различные эффекты и ограничения.\n\n* `queryset`/`get_queryset()`: Ограничивает общую видимость существующих объектов из базы данных. Кверисет ограничивает, какие объекты будут отображаться в списке и какие объекты могут быть изменены или удалены. Метод `get_queryset()` может применять различные кверисеты в зависимости от текущего действия.\n* `permission_classes`/`get_permissions()`: Общая проверка разрешений на основе текущего действия, запроса и целевого объекта. Разрешения на уровне объекта могут быть применены только к действиям получения, изменения и удаления. Проверки разрешений для `list` и `create` будут применены ко всему типу объекта. (В случае списка: с учетом ограничений в наборе запросов).\n* `serializer_class`/`get_serializer()`: Ограничения на уровне экземпляра, которые применяются ко всем объектам на входе и выходе. Сериализатор может иметь доступ к контексту запроса. Метод `get_serializer()` может применять различные сериализаторы в зависимости от текущего действия.\n\nВ следующей таблице перечислены методы ограничения доступа и уровень контроля, который они обеспечивают, над какими действиями.\n\n|                                       | `queryset` | `permission_classes` | `serializer_class` |\n| ------------------------------------- | ---------- | -------------------- | ------------------ |\n| Действие: список                      | глобальный | глобальный           | уровень объектна\\* |\n| Действие: создать                     | нет        | глобальный           | уровень объектна   |\n| Действие: извлечь                     | глобальный | уровень объектна     | уровень объектна   |\n| Действие: обновить                    | глобальный | уровень объектна     | уровень объектна   |\n| Действие: partial_update              | global     | уровень объектна     | уровень объектна   |\n| Действие: уничтожить                  | глобальный | уровень объектна     | нет                |\n| Может ссылаться на действие в решении | нет\\*\\*    | да                   | нет\\*\\*            |\n| Может ссылаться на запрос в решении   | нет\\*\\*    | да                   | да                 |\n\n\\* Класс Serializer не должен поднимать PermissionDenied в действии со списком, иначе весь список не будет возвращен.\n\n\\*\\* Методы `get_*()` имеют доступ к текущему представлению и могут возвращать различные экземпляры Serializer или QuerySet в зависимости от запроса или действия.\n\n---\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## Django REST - Access Policy\n\nПакет [Django REST - Access Policy](https://github.com/rsinger86/drf-access-policy) предоставляет способ определения сложных правил доступа в декларативных классах политик, которые прикрепляются к наборам представлений или представлениям на основе функций. Политики определяются в JSON в формате, аналогичном политикам AWS Identity & Access Management.\n\n## Composed Permissions\n\nПакет [Composed Permissions](https://github.com/niwibe/djangorestframework-composed-permissions) предоставляет простой способ определения сложных и многомерных (с логическими операторами) объектов разрешений, используя небольшие и многократно используемые компоненты.\n\n## REST Condition\n\nПакет [REST Condition](https://github.com/caxap/rest_condition) - это еще одно расширение для построения сложных разрешений простым и удобным способом. Расширение позволяет комбинировать разрешения с логическими операторами.\n\n## DRY Rest Permissions\n\nПакет [DRY Rest Permissions](https://github.com/FJNR-inc/dry-rest-permissions) предоставляет возможность определять различные разрешения для отдельных действий по умолчанию и пользовательских действий. Этот пакет предназначен для приложений с разрешениями, которые являются производными от отношений, определенных в модели данных приложения. Он также поддерживает проверку разрешений, возвращаемую клиентскому приложению через сериализатор API. Кроме того, он поддерживает добавление разрешений к действиям списка по умолчанию и пользовательским действиям списка для ограничения данных, которые они извлекают для каждого пользователя.\n\n## Django Rest Framework Roles\n\nПакет [Django Rest Framework Roles](https://github.com/computer-lab/django-rest-framework-roles) облегчает параметризацию вашего API для нескольких типов пользователей.\n\n## Rest Framework Roles\n\n[Rest Framework Roles](https://github.com/Pithikos/rest-framework-roles) позволяет очень просто защитить представления на основе ролей. Самое главное - позволяет вам отделить логику доступности от моделей и представлений чистым человекочитаемым способом.\n\n## Django REST Framework API Key\n\nПакет [Django REST Framework API Key](https://florimondmanca.github.io/djangorestframework-api-key/) предоставляет классы разрешений, модели и помощники для добавления авторизации по API ключу в ваш API. Его можно использовать для авторизации внутренних или сторонних бэкендов и сервисов (т.е. *машин*), которые не имеют учетной записи пользователя. API ключи хранятся в безопасном месте с использованием инфраструктуры хэширования паролей Django, и их можно просматривать, редактировать и отзывать в любое время в админке Django.\n\n## Django Rest Framework Role Filters\n\nПакет [Django Rest Framework Role Filters](https://github.com/allisson/django-rest-framework-role-filters) обеспечивает простую фильтрацию по нескольким типам ролей.\n\n## Django Rest Framework PSQ\n\nПакет [Django Rest Framework PSQ](https://github.com/drf-psq/drf-psq) - это расширение, которое предоставляет поддержку для использования основанных на действиях **permission_classes**, **serializer_class** и **queryset**, зависящих от правил, основанных на разрешениях.\n\n## Axioms DRF PY\n\nПакет [Axioms DRF PY][https://github.com/abhishektiwari/axioms-drf-py] — это расширение, которое обеспечивает поддержку аутентификации и тонкой авторизации на основе заявлений (**области действия**, **роли**, **группы**, **разрешения** и т. д., включая проверки на уровне объектов) с использованием токенов JWT, выдаваемых сервером авторизации OAuth2/OIDC, включая AWS Cognito, Auth0, Okta, Microsoft Entra и т. д.\n"
  },
  {
    "path": "api-guide/relations.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Отношения сериализаторов\n\n> Структуры данных, а не алгоритмы, занимают центральное место в программировании.\n>\n> - [Роб Пайк](http://users.ece.utexas.edu/~adnan/pike.html)\n\nРеляционные поля используются для представления отношений между моделями. Они могут применяться к отношениям `ForeignKey`, `ManyToManyField` и `OneToOneField`, а также к обратным отношениям и пользовательским отношениям, таким как `GenericForeignKey`.\n\n---\n\n**Примечание:** Реляционные поля объявляются в `relations.py`, но по соглашению вы должны импортировать их из модуля `serializers`, используя `from rest_framework import serializers` и ссылаться на поля как `serializers.<FieldName>`.\n\n---\n\n**Примечание:** DRF не пытается автоматически оптимизировать передаваемые сериализаторам наборы запросов в терминах `select_related` и `prefetch_related`, поскольку это было бы слишком сложной магией. Сериализатор с полем, охватывающим отношение orm через атрибут source, может потребовать дополнительного обращения к базе данных для получения связанных объектов из базы данных. В обязанности программиста входит оптимизация запросов, чтобы избежать дополнительных обращений к базе данных, которые могут возникнуть при использовании такого сериализатора.\n\nНапример, следующий сериализатор будет приводить к попаданию в базу данных каждый раз при оценке поля `tracks`, если оно не префетчено:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.SlugRelatedField(\n        many=True,\n        read_only=True,\n        slug_field='title'\n    )\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n\n# For each album object, tracks should be fetched from database\nqs = Album.objects.all()\nprint(AlbumSerializer(qs, many=True).data)\n```\n\nЕсли `AlbumSerializer` используется для сериализации довольно большого набора запросов с `many=True`, то это может стать серьезной проблемой производительности. Оптимизация кверисета, передаваемого `AlbumSerializer` с:\n\n```python\nqs = Album.objects.prefetch_related('tracks')\n# No additional database hits required\nprint(AlbumSerializer(qs, many=True).data)\n```\n\nрешит эту проблему.\n\n---\n\n#### Инспектирование отношений.\n\nПри использовании класса `ModelSerializer`, поля и отношения сериализатора будут автоматически сгенерированы для вас. Проверка этих автоматически сгенерированных полей может быть полезным инструментом для определения того, как настроить стиль отношений.\n\nДля этого откройте оболочку Django, используя `python manage.py shell`, затем импортируйте класс сериализатора, инстанцируйте его и выведите представление объекта...\n\n```python\n>>> from myapp.serializers import AccountSerializer\n>>> serializer = AccountSerializer()\n>>> print(repr(serializer))\nAccountSerializer():\n    id = IntegerField(label='ID', read_only=True)\n    name = CharField(allow_blank=True, max_length=100, required=False)\n    owner = PrimaryKeyRelatedField(queryset=User.objects.all())\n```\n\n# API Reference\n\nДля того чтобы объяснить различные типы реляционных полей, мы будем использовать пару простых моделей для наших примеров. Нашими моделями будут музыкальные альбомы и треки, перечисленные в каждом альбоме.\n\n```python\nclass Album(models.Model):\n    album_name = models.CharField(max_length=100)\n    artist = models.CharField(max_length=100)\n\nclass Track(models.Model):\n    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)\n    order = models.IntegerField()\n    title = models.CharField(max_length=100)\n    duration = models.IntegerField()\n\n    class Meta:\n        unique_together = ['album', 'order']\n        ordering = ['order']\n\n    def __str__(self):\n        return '%d: %s' % (self.order, self.title)\n```\n\n## StringRelatedField\n\n`StringRelatedField` может использоваться для представления цели отношения с помощью своего метода `__str__`.\n\nНапример, следующий сериализатор:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.StringRelatedField(many=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nСериализуется в следующее представление:\n\n```python\n{\n    'album_name': 'Things We Lost In The Fire',\n    'artist': 'Low',\n    'tracks': [\n        '1: Sunflower',\n        '2: Whitetail',\n        '3: Dinosaur Act',\n        ...\n    ]\n}\n```\n\nЭто поле доступно только для чтения.\n\n**Аргументы**:\n\n* `many` - Если применяется к отношениям типа \"ко многим\", следует установить этот аргумент в `True`.\n\n## PrimaryKeyRelatedField\n\n`PrimaryKeyRelatedField` может использоваться для представления цели отношения с помощью его первичного ключа.\n\nНапример, следующий сериализатор:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nСериализуется в представление, подобное этому:\n\n```python\n{\n    'album_name': 'Undun',\n    'artist': 'The Roots',\n    'tracks': [\n        89,\n        90,\n        91,\n        ...\n    ]\n}\n```\n\nПо умолчанию это поле предназначено для чтения-записи, хотя вы можете изменить это поведение с помощью флага `read_only`.\n\n**Аргументы**:\n\n* `queryset` - Набор запросов, используемый для поиска экземпляра модели при проверке ввода поля. Отношения должны либо явно задать queryset, либо установить `read_only=True`.\n* `many` - Если применяется к отношениям типа \"ко многим\", вы должны установить этот аргумент в `True`.\n* `allow_null` - Если установить значение `True`, поле будет принимать значения `None` или пустую строку для нулевых отношений. По умолчанию `False`.\n* `pk_field` - Устанавливается в поле для управления сериализацией/десериализацией значения первичного ключа. Например, `pk_field=UUIDField(format='hex')` будет сериализовать первичный ключ UUID в его компактное шестнадцатеричное представление.\n\n## HyperlinkedRelatedField\n\n`HyperlinkedRelatedField` может использоваться для представления цели отношения с помощью гиперссылки.\n\nНапример, следующий сериализатор:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.HyperlinkedRelatedField(\n        many=True,\n        read_only=True,\n        view_name='track-detail'\n    )\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nСериализуется в представление, подобное этому:\n\n```python\n{\n    'album_name': 'Graceland',\n    'artist': 'Paul Simon',\n    'tracks': [\n        'http://www.example.com/api/tracks/45/',\n        'http://www.example.com/api/tracks/46/',\n        'http://www.example.com/api/tracks/47/',\n        ...\n    ]\n}\n```\n\nПо умолчанию это поле предназначено для чтения-записи, хотя вы можете изменить это поведение с помощью флага `read_only`.\n\n---\n\n**Примечание**: Это поле предназначено для объектов, сопоставленных с URL, который принимает один именованный аргумент URL, заданный с помощью аргументов `lookup_field` и `lookup_url_kwarg`.\n\nЭто подходит для URL, которые содержат один первичный ключ или аргумент slug как часть URL.\n\nЕсли вам требуется более сложное представление гиперссылок, вам необходимо настроить поле, как описано ниже в разделе [пользовательские гиперссылочные поля](#пользовательские-поля-с-гиперссылками).\n\n---\n\n**Аргументы**:\n\n* `view_name` - Имя представления, которое должно использоваться в качестве цели отношения. Если вы используете [стандартные классы маршрутизаторов](https://www.django-rest-framework.org/api-guide/routers#defaultrouter), это будет строка с форматом `<имя модели>-detail`. **необходимо**.\n* `queryset` - Набор запросов, используемый для поиска экземпляра модели при проверке ввода поля. Отношения должны либо явно задать queryset, либо установить `read_only=True`.\n* `many` - Если применяется к отношениям типа \"ко многим\", вы должны установить этот аргумент в `True`.\n* `allow_null` - Если установить значение `True`, поле будет принимать значения `None` или пустую строку для нулевых отношений. По умолчанию `False`.\n* `lookup_field` - Поле цели, которое должно быть использовано для поиска. Должно соответствовать именованному аргументу URL в ссылающемся представлении. По умолчанию `'pk'`.\n* `lookup_url_kwarg` - Имя именованного аргумента, определенного в URL conf, который соответствует полю поиска. По умолчанию используется то же значение, что и `lookup_field`.\n* `format` - Если используются суффиксы формата, то поля с гиперссылками будут использовать тот же суффикс формата для цели, если это не отменено с помощью аргумента `format`.\n\n## SlugRelatedField\n\n`SlugRelatedField` может использоваться для представления цели отношения с помощью поля цели.\n\nНапример, следующий сериализатор:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = serializers.SlugRelatedField(\n        many=True,\n        read_only=True,\n        slug_field='title'\n     )\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nСериализуется в представление, подобное этому:\n\n```python\n{\n    'album_name': 'Dear John',\n    'artist': 'Loney Dear',\n    'tracks': [\n        'Airport Surroundings',\n        'Everything Turns to You',\n        'I Was Only Going Out',\n        ...\n    ]\n}\n```\n\nПо умолчанию это поле предназначено для чтения-записи, хотя вы можете изменить это поведение с помощью флага `read_only`.\n\nПри использовании `SlugRelatedField` в качестве поля для чтения-записи вы обычно хотите убедиться, что поле slug соответствует полю модели с `unique=True`.\n\n**Аргументы**:\n\n* `slug_field` - Поле цели, которое должно быть использовано для ее представления. Это должно быть поле, которое однозначно идентифицирует любой данный экземпляр. Например, `username`. **обязательно**\n* `queryset` - Набор запросов, используемый для поиска экземпляра модели при проверке ввода поля. Отношения должны либо явно задать queryset, либо установить `read_only=True`.\n* `many` - Если применяется к отношениям типа \"ко многим\", вы должны установить этот аргумент в `True`.\n* `allow_null` - Если установить значение `True`, поле будет принимать значения `None` или пустую строку для нулевых отношений. По умолчанию установлено значение `False`.\n\n## HyperlinkedIdentityField\n\nЭто поле может применяться как отношение идентичности, например, поле `'url'` в `HyperlinkedModelSerializer`. Оно также может быть использовано для атрибута объекта. Например, следующий сериализатор:\n\n```python\nclass AlbumSerializer(serializers.HyperlinkedModelSerializer):\n    track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'track_listing']\n```\n\nСериализуется в представление, подобное этому:\n\n```python\n{\n    'album_name': 'The Eraser',\n    'artist': 'Thom Yorke',\n    'track_listing': 'http://www.example.com/api/track_list/12/',\n}\n```\n\nЭто поле всегда доступно только для чтения.\n\n**Аргументы**:\n\n* `view_name` - Имя представления, которое должно использоваться в качестве цели отношения. Если вы используете [стандартные классы маршрутизаторов](https://www.django-rest-framework.org/api-guide/routers#defaultrouter), это будет строка с форматом `<имя_модели>-detail`. **необходимо**.\n* `lookup_field` - Поле цели, которое должно быть использовано для поиска. Должно соответствовать именованному аргументу URL в ссылающемся представлении. По умолчанию `'pk'`.\n* `lookup_url_kwarg` - Имя именованного слова, определенного в URL conf, который соответствует полю поиска. По умолчанию используется то же значение, что и `lookup_field`.\n* `format` - Если используются суффиксы формата, поля с гиперссылками будут использовать тот же суффикс формата для цели, если это не будет отменено с помощью аргумента `format`.\n\n---\n\n# Вложенные отношения\n\nВ отличие от ранее рассмотренных *ссылок* на другую сущность, ссылающаяся сущность может быть встроена или *вложена* в представление объекта, который на нее ссылается. Такие вложенные отношения могут быть выражены с помощью сериализаторов в качестве полей.\n\nЕсли поле используется для представления отношения \"ко многим\", необходимо добавить флаг `many=True` к полю сериализатора.\n\n## Пример\n\nНапример, следующий сериализатор:\n\n```python\nclass TrackSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Track\n        fields = ['order', 'title', 'duration']\n\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = TrackSerializer(many=True, read_only=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nСериализуется во вложенное представление следующим образом:\n\n```python\n>>> album = Album.objects.create(album_name=\"The Gray Album\", artist='Danger Mouse')\n>>> Track.objects.create(album=album, order=1, title='Public Service Announcement', duration=245)\n<Track: Track object>\n>>> Track.objects.create(album=album, order=2, title='What More Can I Say', duration=264)\n<Track: Track object>\n>>> Track.objects.create(album=album, order=3, title='Encore', duration=159)\n<Track: Track object>\n>>> serializer = AlbumSerializer(instance=album)\n>>> serializer.data\n{\n    'album_name': 'The Gray Album',\n    'artist': 'Danger Mouse',\n    'tracks': [\n        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},\n        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},\n        {'order': 3, 'title': 'Encore', 'duration': 159},\n        ...\n    ],\n}\n```\n\n## Записываемые вложенные сериализаторы\n\nПо умолчанию вложенные сериализаторы доступны только для чтения. Если вы хотите поддерживать операции записи во вложенное поле сериализатора, вам необходимо создать методы `create()` и/или `update()`, чтобы явно указать, как должны сохраняться дочерние отношения:\n\n```python\nclass TrackSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Track\n        fields = ['order', 'title', 'duration']\n\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = TrackSerializer(many=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n\n    def create(self, validated_data):\n        tracks_data = validated_data.pop('tracks')\n        album = Album.objects.create(**validated_data)\n        for track_data in tracks_data:\n            Track.objects.create(album=album, **track_data)\n        return album\n\n>>> data = {\n    'album_name': 'The Gray Album',\n    'artist': 'Danger Mouse',\n    'tracks': [\n        {'order': 1, 'title': 'Public Service Announcement', 'duration': 245},\n        {'order': 2, 'title': 'What More Can I Say', 'duration': 264},\n        {'order': 3, 'title': 'Encore', 'duration': 159},\n    ],\n}\n>>> serializer = AlbumSerializer(data=data)\n>>> serializer.is_valid()\nTrue\n>>> serializer.save()\n<Album: Album object>\n```\n\n---\n\n# Пользовательские реляционные поля\n\nВ редких случаях, когда ни один из существующих реляционных стилей не подходит для нужного вам представления, вы можете реализовать полностью пользовательское реляционное поле, которое описывает, как именно должно быть сгенерировано выходное представление из экземпляра модели.\n\nДля реализации пользовательского реляционного поля необходимо переопределить `RelatedField` и реализовать метод `.to_representation(self, value)`. Этот метод принимает цель поля в качестве аргумента `value` и должен возвращать представление, которое должно использоваться для сериализации цели. Аргумент `value` обычно представляет собой экземпляр модели.\n\nЕсли вы хотите реализовать реляционное поле для чтения и записи, вы должны также реализовать метод [`.to_internal_value(self, data)`](https://www.django-rest-framework.org/api-guide/serializers/#to_internal_valueself-data).\n\nЧтобы обеспечить динамический набор запросов, основанный на `context`, вы также можете переопределить `.get_queryset(self)` вместо указания `.queryset` в классе или при инициализации поля.\n\n## Пример\n\nНапример, мы можем определить реляционное поле для сериализации трека в пользовательское строковое представление, используя его порядок, название и продолжительность:\n\n```python\nimport time\n\nclass TrackListingField(serializers.RelatedField):\n    def to_representation(self, value):\n        duration = time.strftime('%M:%S', time.gmtime(value.duration))\n        return 'Track %d: %s (%s)' % (value.order, value.name, duration)\n\nclass AlbumSerializer(serializers.ModelSerializer):\n    tracks = TrackListingField(many=True)\n\n    class Meta:\n        model = Album\n        fields = ['album_name', 'artist', 'tracks']\n```\n\nЭто пользовательское поле затем сериализуется в следующее представление:\n\n```python\n{\n    'album_name': 'Sometimes I Wish We Were an Eagle',\n    'artist': 'Bill Callahan',\n    'tracks': [\n        'Track 1: Jim Cain (04:39)',\n        'Track 2: Eid Ma Clack Shaw (04:19)',\n        'Track 3: The Wind and the Dove (04:34)',\n        ...\n    ]\n}\n```\n\n---\n\n# Пользовательские поля с гиперссылками\n\nВ некоторых случаях вам может понадобиться настроить поведение поля с гиперссылкой, чтобы представить URL-адреса, для которых требуется более одного поля поиска.\n\nВы можете добиться этого, переопределив `HyperlinkedRelatedField`. Есть два метода, которые могут быть переопределены:\n\n**get_url(self, obj, view_name, request, format)**.\n\nМетод `get_url` используется для сопоставления экземпляра объекта с его URL-представлением.\n\nМожет вызвать ошибку `NoReverseMatch`, если атрибуты `view_name` и `lookup_field` не настроены на правильное соответствие URL conf.\n\n**get_object(self, view_name, view_args, view_kwargs)**.\n\nЕсли вы хотите поддерживать записываемое поле с гиперссылками, вам также потребуется переопределить `get_object`, чтобы сопоставить входящие URL обратно с объектом, который они представляют. Для полей с гиперссылками, доступных только для чтения, нет необходимости переопределять этот метод.\n\nВозвращаемое значение этого метода - объект, соответствующий аргументам URL conf.\n\nМожет вызвать исключение `ObjectDoesNotExist`.\n\n## Пример\n\nДопустим, у нас есть URL для объекта customer, который принимает два аргумента в виде ключевых слов, как показано ниже:\n\n```python\n/api/<organization_slug>/customers/<customer_pk>/\n```\n\nЭто не может быть представлено с помощью реализации по умолчанию, которая принимает только одно поле поиска.\n\nВ этом случае нам нужно переопределить `HyperlinkedRelatedField`, чтобы получить желаемое поведение:\n\n```python\nfrom rest_framework import serializers\nfrom rest_framework.reverse import reverse\n\nclass CustomerHyperlink(serializers.HyperlinkedRelatedField):\n    # We define these as class attributes, so we don't need to pass them as arguments.\n    view_name = 'customer-detail'\n    queryset = Customer.objects.all()\n\n    def get_url(self, obj, view_name, request, format):\n        url_kwargs = {\n            'organization_slug': obj.organization.slug,\n            'customer_pk': obj.pk\n        }\n        return reverse(view_name, kwargs=url_kwargs, request=request, format=format)\n\n    def get_object(self, view_name, view_args, view_kwargs):\n        lookup_kwargs = {\n           'organization__slug': view_kwargs['organization_slug'],\n           'pk': view_kwargs['customer_pk']\n        }\n        return self.get_queryset().get(**lookup_kwargs)\n```\n\nОбратите внимание, что если вы хотите использовать этот стиль вместе с общими представлениями, то вам также необходимо переопределить `.get_object` в представлении, чтобы получить правильное поведение поиска.\n\nОбычно мы рекомендуем использовать плоский стиль для представления API, когда это возможно, но вложенный стиль URL также может быть разумным при умеренном использовании.\n\n---\n\n# Дальнейшие примечания\n\n## Аргумент `queryset`.\n\nАргумент `queryset` требуется только для *записываемого* поля отношения, в этом случае он используется для выполнения поиска экземпляра модели, который отображает примитивный пользовательский ввод в экземпляр модели.\n\nВ версии 2.x класс сериализатора мог *иногда* автоматически определять аргумент `queryset`, *если* использовался класс `ModelSerializer`.\n\nТеперь это поведение заменено на *всегда* использование явного аргумента `queryset` для записываемых реляционных полей.\n\nЭто уменьшает количество скрытой \"магии\", которую обеспечивает `ModelSerializer`, делает поведение поля более понятным и гарантирует, что можно легко переходить от использования ярлыка `ModelSerializer` к использованию полностью явных классов `Serializer`.\n\n## Настройка отображения HTML\n\nВстроенный метод `__str__` модели будет использоваться для создания строковых представлений объектов, используемых для заполнения свойства `choices`. Эти варианты используются для заполнения HTML-вводов выбора в Web-интерфейсе API.\n\nЧтобы обеспечить настраиваемое представление для таких входов, переопределите `display_value()` подкласса `RelatedField`. Этот метод получит объект модели и должен вернуть строку, подходящую для его представления. Например:\n\n```python\nclass TrackPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):\n    def display_value(self, instance):\n        return 'Track: %s' % (instance.title)\n```\n\n## Выберите отсечение полей\n\nПри отображении в Web-интерфейсе API реляционные поля по умолчанию будут отображать не более 1000 элементов для выбора. Если элементов больше, то будет отображаться отключенная опция `'More than 1000 items...'`.\n\nЭто поведение предназначено для того, чтобы предотвратить невозможность отрисовки шаблона за приемлемое время из-за отображения очень большого количества связей.\n\nЕсть два именованных аргумента, которые можно использовать для управления этим поведением:\n\n* `html_cutoff` - Если установлено, это будет максимальное количество вариантов выбора, которое будет отображаться в выпадающем списке HTML select. Установите значение `None`, чтобы отключить любое ограничение. По умолчанию `1000`.\n* `html_cutoff_text` - При установке этого параметра будет отображаться текстовый индикатор, если максимальное количество элементов было отсечено в выпадающем списке HTML select. По умолчанию `'More than {count} items...'`.\n\nВы также можете управлять ими глобально, используя настройки `HTML_SELECT_CUTOFF` и `HTML_SELECT_CUTOFF_TEXT`.\n\nВ случаях, когда отсечение вводится принудительно, вы можете использовать обычное поле ввода в HTML-форме. Вы можете сделать это, используя именованный аргумент `style`. Например:\n\n```python\nassigned_to = serializers.SlugRelatedField(\n   queryset=User.objects.all(),\n   slug_field='username',\n   style={'base_template': 'input.html'}\n)\n```\n\n## Обратные отношения\n\nОбратите внимание, что обратные отношения не включаются автоматически классами `ModelSerializer` и `HyperlinkedModelSerializer`. Чтобы включить обратное отношение, вы должны явно добавить его в список полей. Например:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    class Meta:\n        fields = ['tracks', ...]\n```\n\nОбычно вам нужно убедиться, что вы установили соответствующий аргумент `related_name` для отношения, который вы можете использовать в качестве имени поля. Например:\n\n```python\nclass Track(models.Model):\n    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)\n    ...\n```\n\nЕсли вы не задали связанное имя для обратного отношения, вам придется использовать автоматически сгенерированное связанное имя в аргументе `fields`. Например:\n\n```python\nclass AlbumSerializer(serializers.ModelSerializer):\n    class Meta:\n        fields = ['track_set', ...]\n```\n\nБолее подробную информацию смотрите в документации Django по [обратным отношениям](https://docs.djangoproject.com/en/stable/topics/db/queries/#following-relationships-backward).\n\n## Общие отношения\n\nЕсли вы хотите сериализовать общий внешний ключ, вам необходимо определить пользовательское поле, чтобы явно определить, как вы хотите сериализовать цели отношения.\n\nНапример, дана следующая модель для тега, которая имеет общие отношения с другими произвольными моделями:\n\n```python\nclass TaggedItem(models.Model):\n    \"\"\"\n    Tags arbitrary model instances using a generic relation.\n\n    See: https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/\n    \"\"\"\n    tag_name = models.SlugField()\n    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)\n    object_id = models.PositiveIntegerField()\n    tagged_object = GenericForeignKey('content_type', 'object_id')\n\n    def __str__(self):\n        return self.tag_name\n```\n\nИ следующие две модели, которые могут иметь связанные теги:\n\n```python\nclass Bookmark(models.Model):\n    \"\"\"\n    A bookmark consists of a URL, and 0 or more descriptive tags.\n    \"\"\"\n    url = models.URLField()\n    tags = GenericRelation(TaggedItem)\n\n\nclass Note(models.Model):\n    \"\"\"\n    A note consists of some text, and 0 or more descriptive tags.\n    \"\"\"\n    text = models.CharField(max_length=1000)\n    tags = GenericRelation(TaggedItem)\n```\n\nМы можем определить пользовательское поле, которое будет использоваться для сериализации помеченных экземпляров, используя тип каждого экземпляра для определения того, как он должен быть сериализован:\n\n```python\nclass TaggedObjectRelatedField(serializers.RelatedField):\n    \"\"\"\n    A custom field to use for the `tagged_object` generic relationship.\n    \"\"\"\n\n    def to_representation(self, value):\n        \"\"\"\n        Serialize tagged objects to a simple textual representation.\n        \"\"\"\n        if isinstance(value, Bookmark):\n            return 'Bookmark: ' + value.url\n        elif isinstance(value, Note):\n            return 'Note: ' + value.text\n        raise Exception('Unexpected type of tagged object')\n```\n\nЕсли вам нужно, чтобы цель отношения имела вложенное представление, вы можете использовать необходимые сериализаторы внутри метода `.to_representation()`:\n\n```python\ndef to_representation(self, value):\n        \"\"\"\n        Serialize bookmark instances using a bookmark serializer,\n        and note instances using a note serializer.\n        \"\"\"\n        if isinstance(value, Bookmark):\n            serializer = BookmarkSerializer(value)\n        elif isinstance(value, Note):\n            serializer = NoteSerializer(value)\n        else:\n            raise Exception('Unexpected type of tagged object')\n\n        return serializer.data\n```\n\nОбратите внимание, что обратные родовые ключи, выраженные с помощью поля `GenericRelation`, могут быть сериализованы с использованием обычных типов реляционных полей, поскольку тип цели в отношениях всегда известен.\n\nДля получения дополнительной информации смотрите [документацию Django по общим отношениям](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#id1).\n\n## ManyToManyFields со сквозной моделью\n\nПо умолчанию реляционные поля, нацеленные на `ManyToManyField` с указанной моделью `through`, устанавливаются только для чтения.\n\nЕсли вы явно указываете реляционное поле, указывающее на `ManyToManyField` со сквозной моделью, обязательно установите `read_only` в `True`.\n\nЕсли вы хотите представить [дополнительные поля в сквозной модели](https://docs.djangoproject.com/en/stable/topics/db/models/#intermediary-manytomany), то вы можете сериализовать сквозную модель как [вложенный объект](https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects).\n\n---\n\n# Сторонние пакеты\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## DRF nested routers\n\nПакет [drf-nested-routers](https://github.com/alanjds/drf-nested-routers) предоставляет маршрутизаторы и поля отношений для работы с вложенными ресурсами.\n\n## Rest Framework Generic Relations\n\nБиблиотека [rest-framework-generic-relations](https://github.com/Ian-Foote/rest-framework-generic-relations) обеспечивает сериализацию чтения/записи для общих внешних ключей.\n\nБиблиотека [rest-framework-gm2m-relations][https://github.com/mojtabaakbari221b/rest-framework-gm2m-relations] обеспечивает сериализацию чтения/записи для [django-gm2m][https://github.com/tkhyn/django-gm2m].\n"
  },
  {
    "path": "api-guide/renderers.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Рендереры\n\n> Прежде чем экземпляр `TemplateResponse` будет возвращен клиенту, он должен быть отрендерен. Процесс рендеринга принимает промежуточное представление шаблона и контекста и превращает его в конечный поток байтов, который может быть передан клиенту.\n>\n> - [Django documentation](https://docs.djangoproject.com/en/stable/ref/template-response/#the-rendering-process)\n\nDRF включает ряд встроенных классов Renderer, которые позволяют возвращать ответы с различными типами медиа. Также имеется поддержка определения собственных пользовательских рендереров, что дает вам гибкость в разработке собственных типов медиа.\n\n## Как определяется рендерер\n\nНабор допустимых рендерингов для представления всегда определяется как список классов. При входе в представление DRF будет выполнять согласование содержимого входящего запроса и определять наиболее подходящий рендерер для удовлетворения запроса.\n\nОсновной процесс согласования содержимого включает в себя изучение заголовка `Accept` запроса, чтобы определить, какие типы медиа ожидаются в ответе. По желанию, суффиксы формата в URL могут быть использованы для явного запроса определенного представления. Например, URL `http://example.com/api/users_count.json` может быть конечной точкой, которая всегда возвращает данные в формате JSON.\n\nДля получения дополнительной информации смотрите документацию по [согласованию контента](content-negotiation.md).\n\n## Установка рендереров\n\nНабор рендеров по умолчанию можно задать глобально, используя параметр `DEFAULT_RENDERER_CLASSES`. Например, следующие настройки будут использовать `JSON` в качестве основного типа медиа, а также включать API самоописания.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework.renderers.JSONRenderer',\n        'rest_framework.renderers.BrowsableAPIRenderer',\n    ]\n}\n```\n\nВы также можете установить рендереры, используемые для отдельного представления или набора представлений, используя представления на основе класса `APIView`.\n\n```python\nfrom django.contrib.auth.models import User\nfrom rest_framework.renderers import JSONRenderer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\nclass UserCountView(APIView):\n    \"\"\"\n    A view that returns the count of active users in JSON.\n    \"\"\"\n    renderer_classes = [JSONRenderer]\n\n    def get(self, request, format=None):\n        user_count = User.objects.filter(active=True).count()\n        content = {'user_count': user_count}\n        return Response(content)\n```\n\nИли, если вы используете декоратор `@api_view` с представлениями, основанными на функциях.\n\n```python\n@api_view(['GET'])\n@renderer_classes([JSONRenderer])\ndef user_count_view(request, format=None):\n    \"\"\"\n    A view that returns the count of active users in JSON.\n    \"\"\"\n    user_count = User.objects.filter(active=True).count()\n    content = {'user_count': user_count}\n    return Response(content)\n```\n\n## Упорядочивание классов рендеринга\n\nВажно при определении классов рендеринга для вашего API подумать о том, какой приоритет вы хотите присвоить каждому типу медиа. Если клиент недоопределяет представления, которые он может принимать, например, посылает заголовок `Accept: */*` заголовок, или вообще не включает заголовок `Accept`, то DRF выберет первый рендерер в списке для использования в ответе.\n\nНапример, если ваш API обслуживает JSON-ответы и HTML-просмотр, вы можете сделать `JSONRenderer` рендерером по умолчанию, чтобы отправлять `JSON` ответы клиентам, которые не указывают заголовок `Accept`.\n\nЕсли ваш API включает представления, которые могут обслуживать как обычные веб-страницы, так и ответы API в зависимости от запроса, то вы можете сделать `TemplateHTMLRenderer` рендерером по умолчанию, чтобы хорошо работать со старыми браузерами, которые отправляют [broken accept headers](http://www.gethifi.com/blog/browser-rest-http-accept-headers).\n\n---\n\n# API Reference\n\n## JSONRenderer\n\nПереводит данные запроса в `JSON`, используя кодировку `utf-8`.\n\nОбратите внимание, что стиль по умолчанию включает символы юникода и отображает ответ, используя компактный стиль без лишних пробелов:\n\n```python\n{\"unicode black star\":\"★\",\"value\":999}\n```\n\nКлиент может дополнительно включить параметр медиатипа `'indent'`, в этом случае возвращаемый `JSON` будет иметь отступ. Например, `Accept: application/json; indent=4`.\n\n```python\n{\n    \"unicode black star\": \"★\",\n    \"value\": 999\n}\n```\n\nСтиль кодировки JSON по умолчанию может быть изменен с помощью ключей настроек `UNICODE_JSON` и `COMPACT_JSON`.\n\n**.media_type**: `application/json`.\n\n**.format**: `'json'`.\n\n**.charset**: `None`.\n\n## TemplateHTMLRenderer\n\nРендерит данные в HTML, используя стандартный шаблонный рендеринг Django. В отличие от других рендерингов, данные, передаваемые в `Response`, не нужно сериализовать. Также, в отличие от других рендереров, вы можете включить аргумент `template_name` при создании `Response`.\n\nTemplateHTMLRenderer создаст `RequestContext`, используя `response.data` в качестве диктанта контекста, и определит имя шаблона, который будет использоваться для рендеринга контекста.\n\n---\n\n**Примечание:** При использовании с представлением, которое использует сериализатор, `Response`, отправленный для рендеринга, может не быть словарем и должен быть обернут в `dict` перед возвратом, чтобы `TemplateHTMLRenderer` смог его отрендерить. Например:\n\n```python\nresponse.data = {'results': response.data}\n```\n\n---\n\nИмя шаблона определяется (в порядке предпочтения):\n\n1. Явный аргумент `template_name`, передаваемый в ответ.\n2. Явный атрибут `.template_name`, установленный для этого класса.\n3. Результат вызова `view.get_template_names()`.\n\nПример представления, использующего `TemplateHTMLRenderer`:\n\n```python\nclass UserDetail(generics.RetrieveAPIView):\n    \"\"\"\n    A view that returns a templated HTML representation of a given user.\n    \"\"\"\n    queryset = User.objects.all()\n    renderer_classes = [TemplateHTMLRenderer]\n\n    def get(self, request, *args, **kwargs):\n        self.object = self.get_object()\n        return Response({'user': self.object}, template_name='user_detail.html')\n```\n\nВы можете использовать `TemplateHTMLRenderer` либо для возврата обычных HTML-страниц с помощью DRF, либо для возврата HTML и API ответов с одной конечной точки.\n\nЕсли вы создаете сайты, использующие `TemplateHTMLRenderer` наряду с другими классами рендереров, вам следует рассмотреть возможность включения `TemplateHTMLRenderer` в качестве первого класса в список `renderer_classes`, чтобы он был приоритетным даже для браузеров, которые посылают плохо сформированные заголовки `ACCEPT:`.\n\nДополнительные примеры использования `TemplateHTMLRenderer` смотрите в [*HTML & Forms* Topic Page](../topics/html-and-forms.md).\n\n**.media_type**: `text/html`.\n\n**.format**: `'html'`.\n\n**.charset**: `utf-8`\n\nСм. также: `StaticHTMLRenderer`\n\n## StaticHTMLRenderer\n\nПростой рендерер, который просто возвращает предварительно отрендеренный HTML. В отличие от других рендереров, данные, передаваемые в объект ответа, должны быть строкой, представляющей возвращаемое содержимое.\n\nПример представления, использующего `StaticHTMLRenderer`:\n\n```python\n@api_view(['GET'])\n@renderer_classes([StaticHTMLRenderer])\ndef simple_html_view(request):\n    data = '<html><body><h1>Hello, world</h1></body></html>'\n    return Response(data)\n```\n\nВы можете использовать `StaticHTMLRenderer` либо для возврата обычных HTML-страниц с помощью DRF, либо для возврата HTML- и API-ответов с одной конечной точки.\n\n**.media_type**: `text/html`.\n\n**.format**: `'html'`.\n\n**.charset**: `utf-8`\n\nСм. также: `TemplateHTMLRenderer`.\n\n## BrowsableAPIRenderer\n\nРендерит данные в HTML для Browsable API:\n\n![The BrowsableAPIRenderer](https://github.com/encode/django-rest-framework/raw/main/docs/img/quickstart.png)\n\nЭтот рендерер определяет, какой другой рендерер имел бы наивысший приоритет, и использует его для отображения ответа в стиле API на HTML-странице.\n\n**.media_type**: `text/html`.\n\n**.format**: `'api'`.\n\n**.charset**: `utf-8`\n\n**.template**: `'rest_framework/api.html'`.\n\n#### Настройка BrowsableAPIRenderer\n\nПо умолчанию содержимое ответа будет отображаться рендерером с наивысшим приоритетом, кроме `BrowsableAPIRenderer`. Если вам нужно настроить это поведение, например, использовать HTML в качестве формата возврата по умолчанию, но использовать JSON в Web-интерфейсе API, вы можете сделать это, переопределив метод `get_default_renderer()`. Например:\n\n```python\nclass CustomBrowsableAPIRenderer(BrowsableAPIRenderer):\n    def get_default_renderer(self, view):\n        return JSONRenderer()\n```\n\n## AdminRenderer\n\nРендерит данные в HTML для отображения в стиле администратора:\n\n![Вид AdminRender](https://github.com/encode/django-rest-framework/raw/main/docs/img/quickstart.png)\n\nЭтот рендерер подходит для веб-интерфейсов в стиле CRUD, которые также должны представлять удобный интерфейс для управления данными.\n\nОбратите внимание, что представления, которые имеют вложенные или списковые сериализаторы для своего ввода, не будут хорошо работать с `AdminRenderer`, так как HTML-формы не могут должным образом поддерживать их.\n\n---\n\n**Примечание**: `AdminRenderer` способен включать ссылки на детальные страницы только в том случае, если в данных присутствует правильно настроенный атрибут `URL_FIELD_NAME` (по умолчанию `url`). Для `HyperlinkedModelSerializer` так и будет, но для классов `ModelSerializer` или простого `Serializer` вам нужно будет убедиться, что поле включено явно.\n\nНапример, здесь мы используем метод модели `get_absolute_url`:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    url = serializers.CharField(source='get_absolute_url', read_only=True)\n\n    class Meta:\n        model = Account\n```\n\n---\n\n**.media_type**: `text/html`.\n\n**.format**: `'admin'`.\n\n**.charset**: `utf-8`\n\n**.template**: `'rest_framework/admin.html'`.\n\n## HTMLFormRenderer\n\nПереводит данные, возвращаемые сериализатором, в форму HTML. Вывод этого рендерера не включает заключающие теги `<form>`, скрытый CSRF-вход или какие-либо кнопки отправки.\n\nЭтот рендерер не предназначен для прямого использования, но может быть использован в шаблонах путем передачи экземпляра сериализатора в тег шаблона `render_form`.\n\n```html\n{% load rest_framework %}\n\n<form action=\"/submit-report/\" method=\"post\">\n    {% csrf_token %}\n    {% render_form serializer %}\n    <input type=\"submit\" value=\"Save\" />\n</form>\n```\n\nДля получения дополнительной информации смотрите документацию [HTML & Forms](../topics/html-and-forms.md).\n\n**.media_type**: `text/html`.\n\n**.format**: `'format'`.\n\n**.charset**: `utf-8`\n\n**.template**: `'rest_framework/horizontal/form.html'`.\n\n## MultiPartRenderer\n\nЭтот рендерер используется для рендеринга данных многочастной формы HTML. **Он не подходит для рендеринга ответов**, а используется для создания тестовых запросов, используя [тестовый клиент и фабрику тестовых запросов](testing.md) DRF.\n\n**.media_type**: `multipart/form-data; border=BoUnDaRyStRiNg`.\n\n**.format**: `'multipart'`.\n\n**.charset**: `utf-8`\n\n---\n\n# Пользовательские рендеры\n\nДля реализации пользовательского рендерера необходимо отнаследоваться от `BaseRenderer`, установить свойства `.media_type` и `.format` и реализовать метод `.render(self, data, accepted_media_type=None, renderer_context=None)`.\n\nМетод должен возвращать строку байт, которая будет использоваться в качестве тела ответа HTTP.\n\nАргументы, передаваемые методу `.render()`, следующие:\n\n### `data`\n\nДанные запроса, заданные инстанцией `Response()`.\n\n### `accepted_media_type=None`\n\nДополнительно. Если указано, то это принятый тип носителя, определенный на этапе согласования содержимого.\n\nВ зависимости от заголовка `Accept:` клиента, он может быть более конкретным, чем атрибут `media_type` рендерера, и может включать параметры типа медиа. Например, `'application/json; nested=true'`.\n\n### `renderer_context=None`\n\nОпционально. Если предоставляется, то это словарь контекстной информации, предоставляемой представлением.\n\nПо умолчанию сюда входят следующие ключи: `view`, `request`, `response`, `args`, `kwargs`.\n\n## Пример\n\nНиже приведен пример рендеринга обычного текста, который вернет ответ с параметром `data` в качестве содержимого ответа.\n\n```python\nfrom django.utils.encoding import smart_str\nfrom rest_framework import renderers\n\n\nclass PlainTextRenderer(renderers.BaseRenderer):\n    media_type = 'text/plain'\n    format = 'txt'\n\n    def render(self, data, accepted_media_type=None, renderer_context=None):\n        return smart_str(data, encoding=self.charset)\n```\n\n## Установка набора символов\n\nПо умолчанию предполагается, что классы рендеринга используют кодировку `UTF-8`. Чтобы использовать другую кодировку, установите атрибут `charset` для рендерера.\n\n```python\nclass PlainTextRenderer(renderers.BaseRenderer):\n    media_type = 'text/plain'\n    format = 'txt'\n    charset = 'iso-8859-1'\n\n    def render(self, data, accepted_media_type=None, renderer_context=None):\n        return data.encode(self.charset)\n```\n\nОбратите внимание, что если класс рендерера возвращает строку unicode, то содержимое ответа будет преобразовано в строку байт классом `Response`, при этом атрибут `charset`, установленный на рендерере, будет использоваться для определения кодировки.\n\nЕсли рендерер возвращает строку байт, представляющую необработанное двоичное содержимое, вам следует установить значение charset равное `None`, что обеспечит отсутствие в заголовке `Content-Type` ответа значения `charset`.\n\nВ некоторых случаях вы также можете установить атрибут `render_style` на `'binary'`. Это также гарантирует, что Web-интерфейс API не будет пытаться отобразить двоичное содержимое в виде строки.\n\n```python\nclass JPEGRenderer(renderers.BaseRenderer):\n    media_type = 'image/jpeg'\n    format = 'jpg'\n    charset = None\n    render_style = 'binary'\n\n    def render(self, data, accepted_media_type=None, renderer_context=None):\n        return data\n```\n\n---\n\n# Расширенное использование рендеринга\n\nВы можете делать довольно гибкие вещи, используя рендереры DRF. Некоторые примеры...\n\n* Предоставление плоских или вложенных представлений из одной и той же конечной точки, в зависимости от запрашиваемого типа носителя.\n* Предоставлять как обычные веб-страницы HTML, так и ответы API на основе JSON с одной и той же конечной точки.\n* Указывать несколько типов представления HTML для использования клиентами API.\n* Недоопределять медиатип рендерера, например, используя `media_type = 'image/*'`, и использовать заголовок `Accept` для изменения кодировки ответа.\n\n## Различное поведение в зависимости от типа носителя\n\nВ некоторых случаях вы можете захотеть, чтобы ваше представление использовало различные стили сериализации в зависимости от принятого типа носителя. Если вам нужно сделать это, вы можете обратиться к `request.accepted_renderer`, чтобы определить согласованный рендерер, который будет использоваться для ответа.\n\nНапример:\n\n```python\n@api_view(['GET'])\n@renderer_classes([TemplateHTMLRenderer, JSONRenderer])\ndef list_users(request):\n    \"\"\"\n    A view that can return JSON or HTML representations\n    of the users in the system.\n    \"\"\"\n    queryset = Users.objects.filter(active=True)\n\n    if request.accepted_renderer.format == 'html':\n        # TemplateHTMLRenderer takes a context dict,\n        # and additionally requires a 'template_name'.\n        # It does not require serialization.\n        data = {'users': queryset}\n        return Response(data, template_name='list_users.html')\n\n    # JSONRenderer requires serialized data as normal.\n    serializer = UserSerializer(instance=queryset)\n    data = serializer.data\n    return Response(data)\n```\n\n## Недоопределение типа носителя.\n\nВ некоторых случаях вы можете захотеть, чтобы рендерер обслуживал различные типы медиа. В этом случае вы можете не указывать типы медиа, на которые он должен реагировать, используя значение `media_type`, такое как `image/*`, или `*/*`.\n\nЕсли вы недоопределили медиатип рендерера, вы должны убедиться, что указали медиатип явно, когда возвращаете ответ, используя атрибут `content_type`. Например:\n\n```python\nreturn Response(data, content_type='image/png')\n```\n\n## Проектирование типов носителей\n\nДля целей многих Web API может быть достаточно простых ответов `JSON` с гиперссылками на отношения. Если вы хотите полностью внедрить RESTful дизайн и [HATEOAS](http://timelessrepo.com/haters-gonna-hateoas), вам необходимо более детально продумать дизайн и использование типов медиа.\n\nПо словам [Роя Филдинга](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven), \"REST API должен потратить почти все свои усилия по описанию на определение типа(ов) медиа, используемых для представления ресурсов и управления состоянием приложения, или на определение расширенных имен отношений и/или гипертекстовой разметки для существующих стандартных типов медиа\".\n\nХорошими примерами пользовательских типов медиа являются использование GitHub пользовательского типа медиа [application/vnd.github+json](https://developer.github.com/v3/media/) и одобренная IANA гипермедиа на основе JSON [application/vnd.collection+json](http://www.amundsen.com/media-types/collection/) Майка Амундсена.\n\n## HTML представления ошибок\n\nОбычно рендерер ведет себя одинаково независимо от того, имеет ли он дело с обычным ответом или с ответом, вызванным возникшим исключением, например, исключением `Http404` или `PermissionDenied`, или подклассом `APIException`.\n\nЕсли вы используете `TemplateHTMLRenderer` или `StaticHTMLRenderer` и при этом возникает исключение, поведение немного отличается и является зеркальным отражением [Django's default handling of error views](https://docs.djangoproject.com/en/stable/topics/http/views/#customizing-error-views).\n\nИсключения, возникающие и обрабатываемые средством рендеринга HTML, будут пытаться отобразить с помощью одного из следующих методов, в порядке старшинства.\n\n* Загрузите и отобразите шаблон с именем `{status_code}.html`.\n* Загрузите и отобразите шаблон с именем `api_exception.html`.\n* Вывести код статуса HTTP и текст, например, \"404 Not Found\".\n\nШаблоны будут отображаться с `RequestContext`, который включает ключи `status_code` и `details`.\n\n---\n\n**Примечание**: Если `DEBUG=True`, то вместо отображения кода статуса HTTP и текста будет отображаться стандартная страница ошибки трассировки Django.\n\n---\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## YAML\n\n[REST framework YAML](https://jpadilla.github.io/django-rest-framework-yaml/) обеспечивает поддержку разбора и рендеринга [YAML](http://www.yaml.org/). Ранее он был включен непосредственно в пакет DRF, но теперь поддерживается как сторонний пакет.\n\n#### Установка и настройка\n\nУстановите с помощью pip.\n\n```bash\n$ pip install djangorestframework-yaml\n```\n\nИзмените настройки DRF.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PARSER_CLASSES': [\n        'rest_framework_yaml.parsers.YAMLParser',\n    ],\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework_yaml.renderers.YAMLRenderer',\n    ],\n}\n```\n\n## XML\n\n[REST Framework XML](https://jpadilla.github.io/django-rest-framework-xml/) предоставляет простой неформальный формат XML. Ранее он был включен непосредственно в пакет DRF, но теперь поддерживается как сторонний пакет.\n\n#### Установка и настройка\n\nУстановите с помощью pip.\n\n```bash\n$ pip install djangorestframework-xml\n```\n\nИзмените настройки DRF.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PARSER_CLASSES': [\n        'rest_framework_xml.parsers.XMLParser',\n    ],\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework_xml.renderers.XMLRenderer',\n    ],\n}\n```\n\n## JSONP\n\n[REST framework JSONP](https://jpadilla.github.io/django-rest-framework-jsonp/) обеспечивает поддержку рендеринга JSONP. Ранее он был включен непосредственно в пакет DRF, но теперь поддерживается как сторонний пакет.\n\n---\n\n**Предупреждение**: Если вам требуются междоменные AJAX-запросы, вам следует использовать более современный подход [CORS](https://www.w3.org/TR/cors/) в качестве альтернативы `JSONP`. Более подробную информацию смотрите в документации [CORS](https://www.django-rest-framework.org/topics/ajax-csrf-cors/).\n\nПодход `jsonp` по сути является хаком для браузера и подходит [только для глобально читаемых конечных точек API](https://stackoverflow.com/questions/613962/is-jsonp-safe-to-use), где запросы `GET` являются неаутентифицированными и не требуют никаких разрешений пользователя.\n\n---\n\n#### Установка и настройка\n\nУстановите с помощью pip.\n\n```bash\n$ pip install djangorestframework-jsonp\n```\n\nИзмените настройки DRF.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework_jsonp.renderers.JSONPRenderer',\n    ],\n}\n```\n\n## MessagePack\n\n[MessagePack](https://msgpack.org/) - это быстрый и эффективный формат двоичной сериализации. [Juan Riaza](https://github.com/juanriaza) поддерживает пакет [djangorestframework-msgpack](https://github.com/juanriaza/django-rest-framework-msgpack), который обеспечивает поддержку рендеринга и парсера MessagePack для DRF.\n\n## Microsoft Excel: XLSX (Двоичные конечные точки электронных таблиц)\n\nXLSX - это самый популярный в мире формат двоичных электронных таблиц. [Тим Аллен](https://github.com/flipperpa) из [The Wharton School](https://github.com/wharton) поддерживает [drf-excel](https://github.com/wharton/drf-excel), который отображает конечную точку в виде электронной таблицы XLSX с помощью OpenPyXL и позволяет клиенту загрузить ее. Электронные таблицы могут быть стилизованы для каждого вида.\n\n#### Установка и настройка\n\nУстановите с помощью pip.\n\n```bash\n$ pip install drf-excel\n```\n\nИзмените настройки DRF.\n\n```python\nREST_FRAMEWORK = {\n    ...\n\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework.renderers.JSONRenderer',\n        'rest_framework.renderers.BrowsableAPIRenderer',\n        'drf_excel.renderers.XLSXRenderer',\n    ],\n}\n```\n\nЧтобы избежать потоковой передачи файла без имени (при этом браузер часто принимает по умолчанию имя файла \"download\" без расширения), нам необходимо использовать миксин для переопределения заголовка `Content-Disposition`. Если имя файла не указано, то по умолчанию будет задано `export.xlsx`. Например:\n\n```python\nfrom rest_framework.viewsets import ReadOnlyModelViewSet\nfrom drf_excel.mixins import XLSXFileMixin\nfrom drf_excel.renderers import XLSXRenderer\n\nfrom .models import MyExampleModel\nfrom .serializers import MyExampleSerializer\n\nclass MyExampleViewSet(XLSXFileMixin, ReadOnlyModelViewSet):\n    queryset = MyExampleModel.objects.all()\n    serializer_class = MyExampleSerializer\n    renderer_classes = [XLSXRenderer]\n    filename = 'my_export.xlsx'\n```\n\n## CSV\n\nЗначения, разделенные запятыми, - это формат табличных данных в виде обычного текста, который легко импортируется в приложения электронных таблиц. [Mjumbe Poe](https://github.com/mjumbewu) поддерживает пакет [djangorestframework-csv](https://github.com/mjumbewu/django-rest-framework-csv), который обеспечивает поддержку CSV-рендеринга для DRF.\n\n## UltraJSON\n\n[UltraJSON](https://github.com/esnme/ultrajson) - это оптимизированный кодировщик JSON на языке C, который может значительно ускорить рендеринг JSON. [Adam Mertz](https://github.com/Amertz08) поддерживает [drf_ujson2](https://github.com/Amertz08/drf_ujson2), форк ныне не поддерживаемого [drf-ujson-renderer](https://github.com/gizmag/drf-ujson-renderer), который реализует рендеринг JSON с использованием пакета UJSON.\n\n## CamelCase JSON\n\n[djangorestframework-camel-case](https://github.com/vbabiy/djangorestframework-camel-case) предоставляет рендереры и парсеры JSON в верблюжьем регистре для DRF. Это позволяет сериализаторам использовать имена полей в стиле Python с подчеркиванием, но отображать их в API как имена полей в верблюжьем регистре в стиле Javascript. Поддерживается [Виталием Бабием](https://github.com/vbabiy).\n\n## Pandas (CSV, Excel, PNG)\n\n[Django REST Pandas](https://github.com/wq/django-rest-pandas) предоставляет сериализатор и рендереры, которые поддерживают дополнительную обработку и вывод данных через [Pandas](https://pandas.pydata.org/) DataFrame API. Django REST Pandas включает рендереры для файлов CSV в стиле Pandas, рабочих книг Excel (как `.xls`, так и `.xlsx`) и ряда [других форматов](https://github.com/wq/django-rest-pandas#supported-formats). Он поддерживается [S. Andrew Sheppard](https://github.com/sheppard) в рамках проекта [wq Project](https://github.com/wq).\n\n## LaTeX\n\n[Rest Framework Latex](https://github.com/mypebble/rest-framework-latex) предоставляет рендерер, который выводит PDF-файлы с использованием Lualatex. Он поддерживается [Pebble (S/F Software)](https://github.com/mypebble).\n"
  },
  {
    "path": "api-guide/requests.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Запросы\n\n> Если вы занимаетесь веб-сервисами на основе REST... вам следует игнорировать request.POST.\n>\n> - Malcom Tredinnick, [Django developers group](https://groups.google.com/d/topic/django-developers/dxI4qVzrBY4/discussion)\n\nКласс `Request` DRF расширяет стандартный `HttpRequest`, добавляя поддержку гибкого разбора запросов DRF и аутентификации запросов.\n\n---\n\n# Разбор запроса\n\nОбъекты Request DRF обеспечивают гибкий разбор запросов, что позволяет обрабатывать запросы с данными JSON или другими типами медиа таким же образом, как вы обычно обрабатываете данные формы.\n\n## .data\n\n`request.data` возвращает разобранное содержимое тела запроса. Это похоже на стандартные атрибуты `request.POST` и `request.FILES`, за исключением того, что:\n\n* Он включает в себя все разобранное содержимое, включая *файловые и нефайловые* входы.\n* Поддерживается разбор содержимого методов HTTP, отличных от `POST`, что означает, что вы можете получить доступ к содержимому запросов `PUT` и `PATCH`.\n* Поддерживается гибкий разбор запросов DRF, а не только поддержка данных формы. Например, вы можете обрабатывать входящие [JSON-данные](parsers.md#jsonparser) аналогично тому, как вы обрабатываете входящие [данные формы](parsers.md#formparser).\n\nБолее подробную информацию можно найти в [документации по парсерам](parsers.md).\n\n## .query_params\n\n`request.query_params` - это более правильно названный синоним `request.GET`.\n\nДля ясности внутри вашего кода мы рекомендуем использовать `request.query_params` вместо стандартного для Django `request.GET`. Это поможет сохранить вашу кодовую базу более корректной и очевидной - любой тип HTTP метода может включать параметры запроса, а не только `GET` запросы.\n\n## .parsers\n\nКласс `APIView` или декоратор `@api_view` обеспечат автоматическую установку этого свойства в список экземпляров `Parser` на основе `parser_classes`, установленных в представлении, или на основе настройки `DEFAULT_PARSER_CLASSES`.\n\nОбычно вам не требуется доступ к этой собственности.\n\n---\n\n**Примечание:** Если клиент посылает недостоверное содержимое, то при доступе к `request.data` может возникнуть ошибка `ParseError`. По умолчанию класс `APIView` или декоратор `@api_view` DRF поймает ошибку и вернет ответ `400 Bad Request`.\n\nЕсли клиент посылает запрос с типом содержимого, который не может быть разобран, то возникает исключение `UnsupportedMediaType`, которое по умолчанию будет поймано и вернет ответ `415 Unsupported Media Type`.\n\n---\n\n# Переговоры по содержанию\n\nЗапрос раскрывает некоторые свойства, которые позволяют определить результат этапа согласования содержимого. Это позволяет вам реализовать такое поведение, как выбор различных схем сериализации для различных типов носителей.\n\n## .accepted_renderer\n\nЭкземпляр рендерера, который был выбран на этапе согласования содержимого.\n\n## .accepted_media_type\n\nСтрока, представляющая тип носителя, который был принят на этапе согласования содержимого.\n\n---\n\n# Аутентификация\n\nDRF обеспечивает гибкую аутентификацию по каждому запросу, что дает вам возможность:\n\n* Используйте различные политики аутентификации для разных частей вашего API.\n* Поддерживайте использование нескольких политик аутентификации.\n* Предоставлять информацию о пользователе и маркере, связанную с входящим запросом.\n\n## .user\n\n`request.user` обычно возвращает экземпляр `django.contrib.auth.models.User`, хотя поведение зависит от используемой политики аутентификации.\n\nЕсли запрос не аутентифицирован, значением по умолчанию для `request.user` будет экземпляр `django.contrib.auth.models.AnonymousUser`.\n\nБолее подробную информацию можно найти в [документации по аутентификации](authentication.md).\n\n## .auth\n\n`request.auth` возвращает любой дополнительный контекст аутентификации. Точное поведение `request.auth` зависит от используемой политики аутентификации, но обычно это может быть экземпляр токена, по которому был аутентифицирован запрос.\n\nЕсли запрос не аутентифицирован, или если отсутствует дополнительный контекст, значение по умолчанию `request.auth` равно `None`.\n\nБолее подробную информацию можно найти в [документации по аутентификации](authentication.md).\n\n## .authenticators\n\nКласс `APIView` или декоратор `@api_view` обеспечат автоматическую установку этого свойства в список экземпляров `Authentication` на основе `authentication_classes`, установленных для представления, или на основе параметра `DEFAULT_AUTHENTICATORS`.\n\nОбычно вам не требуется доступ к этой собственности.\n\n---\n\n**Примечание:** При вызове свойств `.user` или `.auth` может возникнуть ошибка `WrappedAttributeError`. Эти ошибки исходят от аутентификатора как стандартные `AttributeError`, однако необходимо, чтобы они были повторно вызваны как исключение другого типа, чтобы предотвратить их подавление внешним доступом к свойству. Python не распознает, что `AttributeError` исходит от аутентификатора, и вместо этого будет считать, что объект запроса не имеет свойства `.user` или `.auth`. Аутентификатор необходимо будет исправить.\n\n---\n\n# Улучшения в браузере\n\nDRF поддерживает несколько улучшений для браузеров, таких как браузерные формы `PUT`, `PATCH` и `DELETE`.\n\n## .method\n\n`request.method` возвращает **упрощенное** строковое представление HTTP-метода запроса.\n\nФормы `PUT`, `PATCH` и `DELETE`, основанные на браузере, поддерживаются прозрачно.\n\nДля получения дополнительной информации см. документацию [browser enhancements documentation](../topics/browser-enhancements.md).\n\n## .content_type\n\n`request.content_type`, возвращает строковый объект, представляющий тип медиа тела HTTP запроса, или пустую строку, если тип медиа не был предоставлен.\n\nОбычно вам не нужно напрямую обращаться к типу содержимого запроса, поскольку вы обычно полагаетесь на стандартное поведение DRF при разборе запроса.\n\nЕсли вам необходимо получить доступ к типу содержимого запроса, вам следует использовать свойство `.content_type`, а не `request.META.get('HTTP_CONTENT_TYPE')`, так как оно обеспечивает прозрачную поддержку неформенного содержимого в браузере.\n\nДля получения дополнительной информации см. документацию [browser enhancements documentation](../topics/browser-enhancements.md).\n\n## .stream\n\n`request.stream` возвращает поток, представляющий содержимое тела запроса.\n\nКак правило, вам не понадобится прямой доступ к содержимому запроса, поскольку вы обычно полагаетесь на стандартное поведение DRF при разборе запроса.\n\n---\n\n# Стандартные атрибуты HttpRequest\n\nПоскольку `Request` DRF расширяет `HttpRequest` фреймворка Django, все остальные стандартные атрибуты и методы также доступны. Например, словари `request.META` и `request.session` доступны как обычно.\n\nОбратите внимание, что по причинам реализации класс `Request` не наследуется от класса `HttpRequest`, а вместо этого расширяет его, используя композицию.\n"
  },
  {
    "path": "api-guide/responses.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Ответы\n\n> В отличие от базовых объектов HttpResponse, объекты TemplateResponse сохраняют детали контекста, который был предоставлен представлением для вычисления ответа. Окончательный результат ответа не вычисляется до тех пор, пока он не понадобится, позже в процессе ответа.\n>\n> - [Django documentation](https://docs.djangoproject.com/en/stable/ref/template-response/)\n\nDRF поддерживает согласование содержимого HTTP, предоставляя класс `Response`, который позволяет возвращать содержимое, которое может быть преобразовано в несколько типов содержимого, в зависимости от запроса клиента.\n\nКласс `Response` является подклассом Django `SimpleTemplateResponse`. Объекты `Response` инициализируются данными, которые должны состоять из собственных примитивов Python. Затем DRF использует стандартное согласование содержимого HTTP для определения того, как он должен отображать конечное содержимое ответа.\n\nНет необходимости использовать класс `Response`, при необходимости вы можете возвращать обычные объекты `HttpResponse` или `StreamingHttpResponse` из ваших представлений. Использование класса `Response` просто предоставляет более удобный интерфейс для возврата ответов Web API, согласованных по содержанию, которые могут быть преобразованы в различные форматы.\n\nЕсли вы по каким-то причинам не хотите сильно кастомизировать DRF, вы всегда должны использовать класс `APIView` или функцию `@api_view` для представлений, которые возвращают объекты `Response`. Это гарантирует, что представление сможет выполнить согласование содержимого и выбрать подходящий рендерер для ответа, прежде чем он будет возвращен из представления.\n\n---\n\n# Создание ответов\n\n## Response()\n\n**Сигнатура:** `Response(data, status=None, template_name=None, headers=None, content_type=None)`.\n\nВ отличие от обычных объектов `HttpResponse`, вы не создаете объекты `Response` со сформированным содержимым. Вместо этого вы передаете несформированные данные, которые могут состоять из любых примитивов Python.\n\nРендереры, используемые классом `Response`, не могут нативно обрабатывать сложные типы данных, такие как экземпляры моделей Django, поэтому вам необходимо сериализовать данные в примитивные типы данных перед созданием объекта `Response`.\n\nВы можете использовать классы DRF `Serializer` для выполнения этой сериализации данных или использовать свою собственную сериализацию.\n\nАргументы:\n\n* `data`: Сериализованные данные для ответа.\n* `status`: Код статуса для ответа. По умолчанию 200. См. также [коды статуса](status-codes.md).\n* `template_name`: Имя шаблона, который будет использоваться, если выбран `HTMLRenderer`.\n* `headers`: Словарь заголовков HTTP для использования в ответе.\n* `content_type`: Тип содержимого ответа. Как правило, он устанавливается автоматически рендерером в результате согласования содержимого, но в некоторых случаях может потребоваться явное указание типа содержимого.\n\n---\n\n# Атрибуты\n\n## .data\n\nНесформированные, сериализованные данные ответа.\n\n## .status_code\n\nЦифровой код состояния ответа HTTP.\n\n## .content\n\nСформированное содержимое ответа. Метод `.render()` должен быть вызван, прежде чем можно будет получить доступ к `.content`.\n\n## .template_name\n\nФайл шаблона `template_name`, если он задан. Требуется, только если `HTMLRenderer` или другой пользовательский рендерер шаблона является применимым рендерером для ответа.\n\n## .accepted_renderer\n\nЭкземпляр рендерера, который будет использоваться для формирования ответа.\n\nУстанавливается автоматически `APIView` или `@api_view` непосредственно перед возвратом ответа из представления.\n\n## .accepted_media_type\n\nТип документа, который был выбран на этапе согласования контента.\n\nУстанавливается автоматически `APIView` или `@api_view` непосредственно перед возвратом ответа из представления.\n\n## .renderer_context\n\nСловарь дополнительной контекстной информации, которая будет передана в метод `.render()` рендерера.\n\nУстанавливается автоматически `APIView` или `@api_view` непосредственно перед возвратом ответа из представления.\n\n---\n\n# Стандартные атрибуты HttpResponse\n\nКласс `Response` расширяет `SimpleTemplateResponse`, и все обычные атрибуты и методы также доступны для ответа. Например, вы можете установить заголовки для ответа стандартным способом:\n\n```python\nresponse = Response()\nresponse['Cache-Control'] = 'no-cache'\n```\n\n## .render()\n\n**Сигнатура:** `.render()`.\n\nКак и любой другой `TemplateResponse`, этот метод вызывается для преобразования сериализованных данных ответа в конечное содержимое ответа. Когда вызывается `.render()`, содержимое ответа будет установлено в результат вызова метода `.render(data, accepted_media_type, renderer_context)` на экземпляре `accepted_renderer`.\n\nОбычно вам не нужно вызывать `.render()` самостоятельно, так как это обрабатывается стандартным циклом ответа Django.\n"
  },
  {
    "path": "api-guide/reverse.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- reverse.py\n\n---\n\n# Возвращение URL-адресов\n\n> Центральной особенностью, отличающей архитектурный стиль REST от других сетевых стилей, является его акцент на едином интерфейсе между компонентами.\n>\n> &mdash; Рой Филдинг, [Архитектурные стили и проектирование сетевых архитектур программного обеспечения](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_1_5)\n\nКак правило, лучше возвращать абсолютные URI из ваших Web API, например `http://example.com/foobar`, а не относительные URI, например `/foobar`.\n\nПреимуществами этого являются:\n\n* Это более четко выражено.\n* Это оставляет меньше работы для ваших клиентов API.\n* Нет двусмысленности относительно значения строки, когда она встречается в таких форматах, как JSON, которые не имеют собственного типа URI.\n* Это упрощает такие вещи, как разметка HTML-представлений с гиперссылками.\n\nФреймворк REST предоставляет две служебные функции для упрощения возврата абсолютных URI из вашего Web API.\n\nИспользовать их не обязательно, но если вы их используете, то самоописывающийся API сможет автоматически делать для вас гиперссылки на свой вывод, что значительно упростит просмотр API.\n\n## reverse\n\n**Сигнатура:** `reverse(viewname, *args, **kwargs)`.\n\nИмеет такое же поведение, как [`django.urls.reverse`](https://docs.djangoproject.com/en/stable/ref/urlresolvers/#reverse), за исключением того, что возвращает полностью определенный URL, используя запрос для определения хоста и порта.\n\nВы должны **включить запрос в качестве именованного аргумента** в функцию, например:\n\n```python\nfrom rest_framework.reverse import reverse\nfrom rest_framework.views import APIView\nfrom django.utils.timezone import now\n\nclass APIRootView(APIView):\n    def get(self, request):\n        year = now().year\n        data = {\n            ...\n            'year-summary-url': reverse('year-summary', args=[year], request=request)\n        }\n        return Response(data)\n```\n\n## reverse_lazy\n\n**Сигнатура:** `reverse_lazy(viewname, *args, **kwargs)`.\n\nИмеет такое же поведение, как [`django.urls.reverse_lazy`](https://docs.djangoproject.com/en/stable/ref/urlresolvers/#reverse-lazy), за исключением того, что возвращает полностью определенный URL, используя запрос для определения хоста и порта.\n\nКак и в случае с функцией `reverse`, вы должны **включить запрос в качестве именованного аргумента** в функцию, например:\n\n```python\napi_root = reverse_lazy('api-root', request=request)\n```\n"
  },
  {
    "path": "api-guide/routers.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- routers.py\n\n---\n\n# Маршрутизаторы\n\n> Маршрутизация ресурсов позволяет быстро объявить все общие маршруты для данного ресурсного контроллера. Вместо объявления отдельных маршрутов для вашего индекса... ресурсный маршрут объявляет их в одной строке кода.\n>\n> &mdash; [Ruby on Rails Documentation](https://guides.rubyonrails.org/routing.html)\n\nНекоторые веб-фреймворки, такие как Rails, предоставляют функциональность для автоматического определения того, как URL-адреса приложения должны быть сопоставлены с логикой, которая занимается обработкой входящих запросов.\n\nDRF добавляет в Django поддержку автоматической маршрутизации URL и предоставляет вам простой, быстрый и последовательный способ подключения логики представления к набору URL.\n\n## Использование\n\nВот пример простого URL conf, который использует `SimpleRouter`.\n\n```python\nfrom rest_framework import routers\n\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\nurlpatterns = router.urls\n```\n\nУ метода `register()` есть два обязательных аргумента:\n\n* `prefix` - Префикс URL, который будет использоваться для этого набора маршрутов.\n* `viewset` - Класс набора представлений.\n\nПо желанию вы можете указать дополнительный аргумент:\n\n* `basename` - Основа, которую следует использовать для создаваемых имен URL. Если значение не задано, то базовое имя будет автоматически генерироваться на основе атрибута `queryset` набора представлений, если он есть. Обратите внимание, что если набор представлений не включает атрибут `queryset`, то вы должны установить `basename` при регистрации набора представлений.\n\nВ приведенном выше примере будут сгенерированы следующие шаблоны URL:\n\n* Шаблон URL: `^users/$` Имя: `'user-list'`\n* Шаблон URL: `^users/{pk}/$` Имя: `'user-detail'`\n* Шаблон URL: `^accounts/$` Имя: `'account-list'`\n* Шаблон URL: `^accounts/{pk}/$` Имя: `'account-detail'`\n\n---\n\n**Примечание**: Аргумент `basename` используется для указания начальной части шаблона имени представления. В приведенном выше примере это часть `user` или `account`.\n\nОбычно вам не нужно указывать аргумент `basename`, но если у вас есть набор представлений, в котором вы определили пользовательский метод `get_queryset`, то набор представлений может не иметь атрибута `.queryset`. Если вы попытаетесь зарегистрировать этот набор представлений, вы увидите ошибку, подобную этой:\n\n```text\n'basename' argument not specified, and could not automatically determine the name from the viewset, as it does not have a '.queryset' attribute.\n```\n\nЭто означает, что вам нужно будет явно задать аргумент `basename` при регистрации набора представлений, поскольку он не может быть автоматически определен из имени модели.\n\n---\n\n### Использование `include` с маршрутизаторами\n\nАтрибут `.urls` экземпляра маршрутизатора - это просто стандартный список шаблонов URL. Существует несколько различных стилей для включения этих URL.\n\nНапример, вы можете добавить `router.urls` к списку существующих представлений...\n\n```python\nrouter = routers.SimpleRouter()\nrouter.register(r'users', UserViewSet)\nrouter.register(r'accounts', AccountViewSet)\n\nurlpatterns = [\n    path('forgot-password/', ForgotPasswordFormView.as_view()),\n]\n\nurlpatterns += router.urls\n```\n\nВ качестве альтернативы вы можете использовать функцию Django `include`, например, так...\n\n```python\nurlpatterns = [\n    path('forgot-password', ForgotPasswordFormView.as_view()),\n    path('', include(router.urls)),\n]\n```\n\nВы можете использовать `include` с пространством имен приложения:\n\n```python\nurlpatterns = [\n    path('forgot-password/', ForgotPasswordFormView.as_view()),\n    path('api/', include((router.urls, 'app_name'))),\n]\n```\n\nИли как пространство имен приложения и экземпляра:\n\n```python\nurlpatterns = [\n    path('forgot-password/', ForgotPasswordFormView.as_view()),\n    path('api/', include((router.urls, 'app_name'), namespace='instance_name')),\n]\n```\n\nБолее подробную информацию смотрите в документации Django [URL namespaces docs](https://docs.djangoproject.com/en/stable/topics/http/urls/#url-namespaces) и в [`include` API reference](https://docs.djangoproject.com/en/stable/ref/urls/#include).\n\n---\n\n**Примечание**: При использовании пространства имен с гиперссылками в сериализаторах вам также необходимо убедиться, что любые параметры `view_name` в сериализаторах правильно отражают пространство имен. В примерах выше вам нужно будет включить параметр типа `view_name='app_name:user-detail'` для полей сериализатора, гиперссылка на представление подробных данных пользователя.\n\nДля автоматического создания `view_name` используется шаблон типа `%(имя_модели)-detail`. Если только имена ваших моделей не противоречат друг другу, вам, возможно, будет лучше **не** расставлять имена в представлениях DRF при использовании сериализаторов с гиперссылками.\n\n---\n\n### Маршрутизация для дополнительных действий\n\nНабор представлений может [пометить дополнительные действия для маршрутизации](viewsets.md#добавление-дополнительных-действий-в-маршрутизацию), украсив метод декоратором `@action`. Эти дополнительные действия будут включены в сгенерированные маршруты. Например, дан метод `set_password` для класса `UserViewSet`:\n\n```python\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n    ...\n\n    @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf])\n    def set_password(self, request, pk=None):\n        ...\n```\n\nБудет создан следующий маршрут:\n\n* Шаблон URL: `^users/{pk}/set_password/$`\n* Имя URL: `'user-set-password'`.\n\nПо умолчанию шаблон URL основан на имени метода, а имя URL представляет собой комбинацию `ViewSet.basename` и имени метода через дефис. Если вы не хотите использовать значения по умолчанию, вы можете указать аргументы `url_path` и `url_name` в декораторе `@action`.\n\nНапример, если вы хотите изменить URL для нашего пользовательского действия на `^users/{pk}/change-password/$`, вы можете написать:\n\n```python\nfrom myapp.permissions import IsAdminOrIsSelf\nfrom rest_framework.decorators import action\n\nclass UserViewSet(ModelViewSet):\n    ...\n\n    @action(methods=['post'], detail=True, permission_classes=[IsAdminOrIsSelf],\n            url_path='change-password', url_name='change_password')\n    def set_password(self, request, pk=None):\n        ...\n```\n\nПриведенный выше пример теперь будет генерировать следующий шаблон URL:\n\n* URL путь: `^users/{pk}/change-password/$`\n* Имя URL: `'user-change_password'`.\n\n### Использование Django `path()` с маршрутизаторами\n\nПо умолчанию URL, создаваемые маршрутизаторами, используют регулярные выражения. Это поведение можно изменить, установив аргумент `use_regex_path` в `False` при инстанцировании маршрутизатора, в этом случае будут использоваться [преобразователи путей](https://docs.djangoproject.com/en/stable/topics/http/urls/#path-converters). Например:\n\n```python\nrouter = SimpleRouter(use_regex_path=False)\n```\n\nМаршрутизатор будет соответствовать значениям поиска, содержащим любые символы, кроме косой черты и точки. Чтобы получить более строгий (или более мягкий) шаблон поиска, установите атрибут `lookup_value_regex` в наборе представлений или `lookup_value_converter` при использовании конвертеров путей. Например, вы можете ограничить поиск допустимыми UUID:\n\n```python\nclass MyModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n    lookup_field = 'my_model_id'\n    lookup_value_regex = '[0-9a-f]{32}'\n\nclass MyPathModelViewSet(mixins.RetrieveModelMixin, viewsets.GenericViewSet):\n    lookup_field = 'my_model_uuid'\n    lookup_value_converter = 'uuid'\n```\n\nОбратите внимание, что преобразователи путей будут использоваться для всех URL, зарегистрированных в маршрутизаторе, включая действия набора представлений.\n\n# Руководство по API\n\n## SimpleRouter\n\nЭтот маршрутизатор включает маршруты для стандартного набора действий `list`, `create`, `retrieve`, `update`, `partial_update` и `destroy`. Набор представлений также может отметить дополнительные методы для маршрутизации, используя декоратор `@action`.\n\n| URL Style                     | HTTP Method                                | Action                                     | URL Name              |\n| ----------------------------- | ------------------------------------------ | ------------------------------------------ | --------------------- |\n| {prefix}/                     | GET                                        | list                                       | {basename}-list       |\n|                               | POST                                       | create                                     |                       |\n| {prefix}/{url_path}/          | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=False)`| {basename}-{url_name} |\n| {prefix}/{lookup}/            | GET                                        | retrieve                                   | {basename}-detail     |\n|                               | PUT                                        | update                                     |                       |\n|                               | PATCH                                      | partial_update                             |                       |\n|                               | DELETE                                     | destroy                                    |                       |\n| {prefix}/{lookup}/{url_path}/ | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=True)` | {basename}-{url_name} |\n\nПо умолчанию URL, создаваемые `SimpleRouter`, дополняются косой чертой. Это поведение можно изменить, установив аргумент `trailing_slash` в `False` при инстанцировании маршрутизатора. Например:\n\n```python\nrouter = SimpleRouter(trailing_slash=False)\n```\n\n## DefaultRouter\n\nЭтот маршрутизатор похож на `SimpleRouter`, но дополнительно включает корневое представление API по умолчанию, которое возвращает ответ, содержащий гиперссылки на все представления списка. Он также генерирует маршруты для необязательных суффиксов формата `.json`.\n\n| URL Style                              | HTTP Method                                | Action                                               | URL Name              |\n| -------------------------------------- | ------------------------------------------ | ---------------------------------------------------- | --------------------- |\n| [.format]                              | GET                                        | автоматически сгенерированное корневое представление | api-root              |\n| {prefix}/[.format]                     | GET                                        | list                                                 | {basename}-list       |\n|                                        | POST                                       | create                                               |                       |\n| {prefix}/{url_path}/[.format]          | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=False)`          | {basename}-{url_name} |\n| {prefix}/{lookup}/[.format]            | GET                                        | retrieve                                             | {basename}-detail     |\n|                                        | PUT                                        | update                                               |                       |\n|                                        | PATCH                                      | partial_update                                       |                       |\n|                                        | DELETE                                     | destroy                                              |                       |\n| {prefix}/{lookup}/{url_path}/[.format] | GET, или как указано в аргументе `methods` | метод с декоратором `@action(detail=True)`           | {basename}-{url_name} |\n\nКак и в `SimpleRouter`, косые черты в маршрутах URL могут быть удалены путем установки аргумента `trailing_slash` в `False` при инстанцировании маршрутизатора.\n\n```python\nrouter = DefaultRouter(trailing_slash=False)\n```\n\n# Пользовательские маршрутизаторы\n\nРеализация пользовательского маршрутизатора - это не то, что вам нужно делать очень часто, но это может быть полезно, если у вас есть особые требования к структуре URL для вашего API. Это позволит вам инкапсулировать структуру URL в многократно используемый способ, который гарантирует, что вам не придется писать шаблоны URL в явном виде для каждого нового представления.\n\nСамый простой способ реализации пользовательского маршрутизатора - это подкласс одного из существующих классов маршрутизаторов. Атрибут `.routes` используется для шаблонизации шаблонов URL, которые будут сопоставлены с каждым набором представлений. Атрибут `.routes` представляет собой список кортежей с именем `Route`.\n\nАргументами кортежа с именем `Route` являются:\n\n**url**: Строка, представляющая URL, который должен быть маршрутизирован. Может включать следующие строки формата:\n\n* `{prefix}` - Префикс URL, который будет использоваться для этого набора маршрутов.\n* `{lookup}` - Поле поиска, используемое для сопоставления с одним экземпляром.\n* `{trailing_slash}` - Либо '/', либо пустая строка, в зависимости от аргумента `trailing_slash`.\n\n**mapping**: Сопоставление имен методов HTTP с методами представления\n\n**name**: Имя URL, используемое в вызовах `reverse`. Может включать следующую строку формата:\n\n* `{basename}` - Основа, которую следует использовать для создаваемых имен URL.\n\n**initkwargs**: Словарь дополнительных аргументов, которые должны быть переданы при инстанцировании представления. Обратите внимание, что аргументы `detail`, `basename` и `suffix` зарезервированы для интроспекции набора представлений и также используются API просмотра для генерации имени представления и ссылок на хлебные крошки.\n\n## Настройка динамических маршрутов\n\nВы также можете настроить способ маршрутизации декоратора `@action`. Включите кортеж с именем `DynamicRoute` в список `.routes`, установив аргумент `detail` в соответствии с требованиями для маршрутов на основе списка и на основе деталей. В дополнение к `detail`, аргументами `DynamicRoute` являются:\n\n**url**: Строка, представляющая URL, который должен быть маршрутизирован. Может включать те же строки формата, что и `Route`, и дополнительно принимает строку формата `{url_path}`.\n\n**name**: Имя URL, используемое в вызовах `reverse`. Может включать следующие строки формата:\n\n* `{basename}` - Основа, которую следует использовать для создаваемых имен URL.\n* `{url_name}` - `имя URL`, предоставляемое `@action`.\n\n**initkwargs**: Словарь любых дополнительных аргументов, которые должны быть переданы при инстанцировании представления.\n\n## Пример\n\nСледующий пример маршрутизирует только действия `list` и `retrieve` и не использует соглашение о косой черте.\n\n```python\nfrom rest_framework.routers import Route, DynamicRoute, SimpleRouter\n\nclass CustomReadOnlyRouter(SimpleRouter):\n    \"\"\"\n    A router for read-only APIs, which doesn't use trailing slashes.\n    \"\"\"\n    routes = [\n        Route(\n            url=r'^{prefix}$',\n            mapping={'get': 'list'},\n            name='{basename}-list',\n            detail=False,\n            initkwargs={'suffix': 'List'}\n        ),\n        Route(\n            url=r'^{prefix}/{lookup}$',\n            mapping={'get': 'retrieve'},\n            name='{basename}-detail',\n            detail=True,\n            initkwargs={'suffix': 'Detail'}\n        ),\n        DynamicRoute(\n            url=r'^{prefix}/{lookup}/{url_path}$',\n            name='{basename}-{url_name}',\n            detail=True,\n            initkwargs={}\n        )\n    ]\n```\n\nДавайте посмотрим на маршруты, которые наш `CustomReadOnlyRouter` будет генерировать для простого набора представлений.\n\n`views.py`:\n\n```python\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n    \"\"\"\n    A viewset that provides the standard actions\n    \"\"\"\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n    lookup_field = 'username'\n\n    @action(detail=True)\n    def group_names(self, request, pk=None):\n        \"\"\"\n        Returns a list of all the group names that the given\n        user belongs to.\n        \"\"\"\n        user = self.get_object()\n        groups = user.groups.all()\n        return Response([group.name for group in groups])\n```\n\n`urls.py`:\n\n```python\nrouter = CustomReadOnlyRouter()\nrouter.register('users', UserViewSet)\nurlpatterns = router.urls\n```\n\nБудут созданы следующие отображения...\n\n| URL                           | HTTP Method | Action      | URL Name         |\n| ----------------------------- | ----------- | ----------- | ---------------- |\n| /users                        | GET         | list        | user-list        |\n| /users/{username}             | GET         | retrieve    | user-detail      |\n| /users/{username}/group_names | GET         | group_names | user-group-names |\n\nДругой пример установки атрибута `.routes` приведен в исходном коде класса `SimpleRouter`.\n\n## Расширенные пользовательские маршрутизаторы\n\nЕсли вы хотите обеспечить полностью пользовательское поведение, вы можете переопределить `BaseRouter` и переопределить метод `get_urls(self)`. Метод должен проверить зарегистрированные наборы представлений и вернуть список шаблонов URL. Зарегистрированные кортежи префикса, набора представлений и базового имени можно проверить, обратившись к атрибуту `self.registry`.\n\nВы также можете переопределить метод `get_default_basename(self, viewset)` или всегда явно задавать аргумент `basename` при регистрации ваших наборов представлений в маршрутизаторе.\n\n# Сторонние пакеты\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## DRF Nested Routers\n\nПакет [drf-nested-routers](https://github.com/alanjds/drf-nested-routers) предоставляет маршрутизаторы и поля отношений для работы с вложенными ресурсами.\n\n## ModelRouter (wq.db.rest)\n\nПакет [wq.db](https://wq.io/wq.db) предоставляет расширенный класс [ModelRouter](https://wq.io/docs/router) (и экземпляр синглтона), который расширяет `DefaultRouter` с API `register_model()`. Подобно Django's `admin.site.register`, единственным необходимым аргументом для `rest.router.register_model` является класс модели. Разумные значения по умолчанию для префикса url, сериализатора и набора представлений будут определяться из модели и глобальной конфигурации.\n\n```python\nfrom wq.db import rest\nfrom myapp.models import MyModel\n\nrest.router.register_model(MyModel)\n```\n\n## DRF-extensions\n\nПакет [`DRF-extensions`](https://chibisov.github.io/drf-extensions/docs/) предоставляет [маршрутизаторы](https://chibisov.github.io/drf-extensions/docs/#routers) для создания [вложенных наборов представлений](https://chibisov.github.io/drf-extensions/docs/#nested-routes), [контроллеров уровня коллекции](https://chibisov.github.io/drf-extensions/docs/#collection-level-controllers) с [настраиваемыми именами конечных точек](https://chibisov.github.io/drf-extensions/docs/#controller-endpoint-name).\n"
  },
  {
    "path": "api-guide/schemas.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Schemas\n\n> Машиночитаемая \\[схема\\] описывает, какие ресурсы доступны через API, каковы их URL, как они представлены и какие операции они поддерживают.\n>\n> - Heroku, [JSON Schema for the Heroku Platform API](https://www.heroku.com/blog/json_schema_for_heroku_platform_api/)\n\n---\n\n**Уведомление о сокращении:**\n\nВстроенная в DRF поддержка генерации схем OpenAPI **утрачена** в пользу сторонних пакетов, которые могут предоставить эту функциональность вместо нее. Встроенная поддержка будет перенесена в отдельный пакет, а затем в последующих релизах будет удалена.\n\nВ качестве полноценной замены мы рекомендуем пакет [drf-spectacular](https://drf-spectacular.readthedocs.io/en/latest/readme.html). Он обладает широкой поддержкой генерации схем OpenAPI 3 из API DRF, причем доступны как автоматические, так и настраиваемые опции. За дополнительной информацией обращайтесь к [Documenting your API](../topics/documenting-your-api.md#drf-spectacular).\n\n---\n\nСхемы API - это полезный инструмент, который позволяет использовать их в различных случаях, включая создание справочной документации или создание динамических клиентских библиотек, которые могут взаимодействовать с вашим API.\n\nDRF обеспечивает поддержку автоматической генерации схем [OpenAPI](https://github.com/OAI/OpenAPI-Specification).\n\n## Обзор\n\nГенерация схемы состоит из нескольких движущихся частей. Стоит сделать обзор:\n\n* `SchemaGenerator` - это класс верхнего уровня, который отвечает за поиск шаблонов URL, нахождение подклассов `APIView`, запрос их представления схемы и компиляцию конечного объекта схемы.\n* `AutoSchema` инкапсулирует все детали, необходимые для интроспекции схемы для каждого представления. Прикрепляется к каждому представлению через атрибут `schema`. Вы подклассифицируете `AutoSchema` для того, чтобы настроить свою схему.\n* Команда управления `generateschema` позволяет вам генерировать статическую схему в автономном режиме.\n* Альтернативно, вы можете направить `SchemaView` для динамической генерации и обслуживания вашей схемы.\n* `settings.DEFAULT_SCHEMA_CLASS` позволяет вам указать подкласс `AutoSchema`, который будет использоваться по умолчанию в вашем проекте.\n\nВ следующих разделах рассказывается подробнее.\n\n## Генерация схемы OpenAPI\n\n### Установите зависимости\n\n```bash\npip install pyyaml uritemplate inflection\n```\n\n* `pyyaml` используется для генерации схемы в формат OpenAPI на основе YAML.\n* `uritemplate` используется для получения параметров в пути.\n* `inflection` для более подходящего множественного числа операций в конечных точках списка.\n\n\n### Генерация статической схемы с помощью команды управления `generateschema`.\n\nЕсли ваша схема статична, вы можете использовать команду управления `generateschema`:\n\n```bash\n./manage.py generateschema --file openapi-schema.yml\n```\n\nПосле создания схемы таким образом вы можете аннотировать ее любой дополнительной информацией, которая не может быть автоматически выведена генератором схемы.\n\nВы можете зарегистрировать схему API в системе контроля версий и обновлять ее с каждым новым релизом, или использовать схему API из статического медиа вашего сайта.\n\n### Генерация динамической схемы с помощью `SchemaView`.\n\nЕсли вам нужна динамическая схема, например, потому что выбор внешнего ключа зависит от значений базы данных, вы можете создать `SchemaView`, который будет генерировать и обслуживать вашу схему по требованию.\n\nДля маршрутизации `SchemaView` используйте помощник `get_schema_view()`.\n\nВ `urls.py`:\n\n```python\nfrom rest_framework.schemas import get_schema_view\n\nurlpatterns = [\n    # ...\n    # Use the `get_schema_view()` helper to add a `SchemaView` to project URLs.\n    #   * `title` and `description` parameters are passed to `SchemaGenerator`.\n    #   * Provide view name for use with `reverse()`.\n    path(\n        \"openapi\",\n        get_schema_view(\n            title=\"Your Project\", description=\"API for all things …\", version=\"1.0.0\"\n        ),\n        name=\"openapi-schema\",\n    ),\n    # ...\n]\n```\n\n#### `get_schema_view()`.\n\nПомощник `get_schema_view()` принимает следующие именованные аргументы:\n\n* `title`: Может использоваться для предоставления описательного заголовка для определения схемы.\n* `description`: Более длинный описательный текст.\n* `version`: Версия API.\n* `url`: Может использоваться для передачи канонического базового URL для схемы.\n    ```python\n    schema_view = get_schema_view(\n        title='API мониторинга серверов',\n        url='https://www.example.org/api/'\n    )\n    ```\n* `urlconf`: Строка, представляющая путь импорта к URL conf, для которого вы хотите\n   сгенерировать схему API. По умолчанию это значение соответствует значению параметра Django\n   `ROOT_URLCONF`.\n    ```python\n    schema_view = get_schema_view(\n        title='API мониторинга серверов',\n        url='https://www.example.org/api/',\n        urlconf='myproject.urls'\n    )\n    ```\n* `patterns`: Список шаблонов url для ограничения интроспекции схемы. Если вам хотите, чтобы в схеме отображались только урлы `myproject.api`:\n    ```python\n    schema_url_patterns = [\n        path('api/', include('myproject.api.urls')),\n    ]\n\n    schema_view = get_schema_view(\n        title='API мониторинга серверов',\n        url='https://www.example.org/api/',\n        patterns=schema_url_patterns,\n    )\n    ```\n* `public`: Может использоваться для указания того, должна ли схема обходить разрешения представления. По умолчанию False\n* `generator_class`: Может использоваться для указания подкласса `SchemaGenerator`, который будет передаваться в `SchemaView`.\n* `authentication_classes`: Может использоваться для указания списка классов аутентификации классов, которые будут применяться к конечной точке схемы. По умолчанию `settings.DEFAULT_AUTHENTICATION_CLASSES`.\n* `permission_classes`: Может использоваться для указания списка классов разрешений. которые будут применяться к конечной точке схемы. По умолчанию `settings.DEFAULT_PERMISSION_CLASSES`.\n* `renderer_classes`: Может использоваться для передачи набора классов рендереров, которые могут использоваться для рендеринга корневой конечной точки API.\n\n## SchemaGenerator\n\n**Настройка на уровне схемы**\n\n```python\nfrom rest_framework.schemas.openapi import SchemaGenerator\n```\n\n`SchemaGenerator` - это класс, который просматривает список шаблонов URL, запрашивает схему для каждого представления и собирает результирующую схему OpenAPI.\n\nОбычно вам не нужно самостоятельно создавать `SchemaGenerator`, но вы можете сделать это следующим образом:\n\n```python\ngenerator = SchemaGenerator(title='Stock Prices API')\n```\n\nАргументы:\n\n* `title` **обязательно**: Название API.\n* `description`: Более длинный описательный текст.\n* `version`: Версия API. По умолчанию `0.1.0`.\n* `url`: Корневой URL схемы API. Этот параметр не требуется, если схема не включена в префикс path.\n* `patterns`: Список URL-адресов для проверки при генерации схемы. По умолчанию используется URL conf проекта.\n* `urlconf`: Имя модуля URL conf для использования при генерации схемы. По умолчанию `settings.ROOT_URLCONF`.\n\nЧтобы настроить схему верхнего уровня, подкласс `rest_framework.schemas.openapi.SchemaGenerator` и предоставьте свой подкласс в качестве аргумента команде `generateschema` или вспомогательной функции `get_schema_view()`.\n\n### get_schema(self, request=None, public=False)\n\nВозвращает словарь, представляющий схему OpenAPI:\n\n```python\ngenerator = SchemaGenerator(title='Stock Prices API')\nschema = generator.get_schema()\n```\n\nАргумент `request` является необязательным и может быть использован, если вы хотите применить разрешения для каждого пользователя к результирующей генерации схемы.\n\nНапример, вы можете добавить условия обслуживания в [объект верхнего уровня `info`](https://swagger.io/specification/#infoObject):\n\n```python\nclass TOSSchemaGenerator(SchemaGenerator):\n    def get_schema(self, *args, **kwargs):\n        schema = super().get_schema(*args, **kwargs)\n        schema[\"info\"][\"termsOfService\"] = \"https://example.com/tos.html\"\n        return schema\n```\n\n## AutoSchema\n\n**Настройка для каждого представления**\n\n```python\nfrom rest_framework.schemas.openapi import AutoSchema\n```\n\nПо умолчанию интроспекция представления выполняется экземпляром `AutoSchema`, доступным через атрибут `schema` на `APIView`.\n\n```python\nauto_schema = some_view.schema\n```\n\n`AutoSchema` предоставляет элементы OpenAPI, необходимые для каждого представления, метода запроса и пути:\n\n* Список [компонентов OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#componentsObject). В терминах DRF это отображения сериализаторов, которые описывают тела запроса и ответа.\n* Соответствующий [объект операции OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#operationObject), описывающий конечную точку, включая путь и параметры запроса для пагинации, фильтрации и так далее.\n\n```python\ncomponents = auto_schema.get_components(...)\noperation = auto_schema.get_operation(...)\n```\n\nПри компиляции схемы `SchemaGenerator` вызывает `get_components()` и `get_operation()` для каждого представления, разрешенного метода и пути.\n\n---\n\n**Примечание**: Автоматическая интроспекция компонентов и многих параметров операций опирается на соответствующие атрибуты и методы `GenericAPIView`: `get_serializer()`, `pagination_class`, `filter_backends` и т.д. По этой причине для базовых подклассов `APIView` интроспекция по умолчанию ограничивается именованными параметрами пути URL.\n\n---\n\n`AutoSchema` инкапсулирует интроспекцию представления, необходимую для генерации схемы. Благодаря этому вся логика генерации схемы хранится в одном месте, а не распределяется по уже существующим API представления, сериализатора и полей.\n\nСледуя этому шаблону, старайтесь не допускать утечки логики схемы в ваши собственные представления, сериализаторы или поля при настройке генерации схемы. У вас может возникнуть соблазн сделать что-то вроде этого:\n\n```python\nclass CustomSchema(AutoSchema):\n    \"\"\"\n    AutoSchema subclass using schema_extra_info on the view.\n    \"\"\"\n\n    ...\n\n\nclass CustomView(APIView):\n    schema = CustomSchema()\n    schema_extra_info = ...  # some extra info\n```\n\nЗдесь подкласс `AutoSchema` ищет `schema_extra_info` в представлении. Это *OK* (на самом деле это не вредит), но это означает, что в итоге вы получите логику схемы, разбросанную по разным местам.\n\nВместо этого попробуйте подкласс `AutoSchema`, чтобы `extra_info` не просачивалась в представление:\n\n```python\nclass BaseSchema(AutoSchema):\n    \"\"\"\n    AutoSchema subclass that knows how to use extra_info.\n    \"\"\"\n\n    ...\n\n\nclass CustomSchema(BaseSchema):\n    extra_info = ...  # some extra info\n\n\nclass CustomView(APIView):\n    schema = CustomSchema()\n```\n\nЭтот стиль немного более многословен, но сохраняет инкапсуляцию кода, связанного со схемой. Он более *целостный* в *парламенте*. Это сделает остальной код вашего API более аккуратным.\n\nЕсли опция применяется ко многим классам представлений, вместо того, чтобы создавать отдельный подкласс для каждого представления, вам может показаться более удобным разрешить указывать опцию как `__init__()` именованный аргумент для вашего базового подкласса `AutoSchema`:\n\n```python\nclass CustomSchema(BaseSchema):\n    def __init__(self, **kwargs):\n        # store extra_info for later\n        self.extra_info = kwargs.pop(\"extra_info\")\n        super().__init__(**kwargs)\n\n\nclass CustomView(APIView):\n    schema = CustomSchema(extra_info=...)  # some extra info\n```\n\nЭто избавит вас от необходимости создавать собственный подкласс для каждого вида для часто используемой опции.\n\nНе все методы `AutoSchema` раскрывают соответствующие ключи `__init__()`, но для наиболее часто используемых опций они есть.\n\n### Методы `AutoSchema`\n\n#### `get_components()`.\n\nГенерирует компоненты OpenAPI, описывающие тела запросов и ответов, получая их свойства от сериализатора.\n\nВозвращает словарь, отображающий имя компонента на сгенерированное представление. По умолчанию он содержит только одну пару, но вы можете переопределить `get_components()`, чтобы вернуть несколько пар, если ваше представление использует несколько сериализаторов.\n\n#### `get_component_name()`.\n\nВычисляет имя компонента из сериализатора.\n\nВы можете увидеть предупреждения, если в вашем API есть дублирующиеся имена компонентов. В этом случае вы можете переопределить `get_component_name()` или передать `component_name` `__init__()` именованный аргумент (см. ниже), чтобы обеспечить разные имена.\n\n#### `get_reference()`.\n\nВозвращает ссылку на компонент сериализатора. Это может быть полезно, если вы переопределите `get_schema()`.\n\n#### `map_serializer()`.\n\nСопоставляет сериализаторы с их представлениями OpenAPI.\n\nБольшинство сериализаторов должны соответствовать стандартному типу OpenAPI `object`, но вы можете переопределить `map_serializer()`, чтобы настроить это или другие поля на уровне сериализатора.\n\n#### `map_field()`.\n\nСопоставляет отдельные поля сериализатора с их схемным представлением. Базовая реализация будет работать с полями по умолчанию, которые предоставляет DRF.\n\nДля экземпляров `SerializerMethodField`, для которых схема неизвестна, или подклассов пользовательских полей следует переопределить `map_field()`, чтобы сгенерировать правильную схему:\n\n```python\nclass CustomSchema(AutoSchema):\n    \"\"\"Extension of ``AutoSchema`` to add support for custom field schemas.\"\"\"\n\n    def map_field(self, field):\n        # Handle SerializerMethodFields or custom fields here...\n        # ...\n        return super().map_field(field)\n```\n\nАвторы сторонних пакетов должны стремиться предоставить подкласс `AutoSchema` и миксин, переопределяющий `map_field()`, чтобы пользователи могли легко генерировать схемы для своих пользовательских полей.\n\n#### `get_tags()`.\n\nOpenAPI группирует операции по тегам. По умолчанию теги берутся из первого сегмента пути маршрутизируемого URL. Например, URL типа `/users/{id}/` будет генерировать тег `users`.\n\nВы можете передать именованный аргумент в `__init__()`, чтобы вручную указать теги (см. ниже), или переопределить `get_tags()`, чтобы обеспечить пользовательскую логику.\n\n#### `get_operation()`.\n\nВозвращает [объект операции OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#operationObject), описывающий конечную точку, включая путь и параметры запроса для пагинации, фильтрации и так далее.\n\nВместе с `get_components()` это основная точка входа в интроспекцию представления.\n\n#### `get_operation_id()`.\n\nДля каждой операции должен быть уникальный [`operationid`](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#fixed-fields-17). По умолчанию `operationId` выводится из имени модели, имени сериализатора или имени представления. `operationId` выглядит как \"listItems\", \"retrieveItem\", \"updateItem\" и т.д. По соглашению `operationId` используется camelCase.\n\n#### `get_operation_id_base()`.\n\nЕсли у вас есть несколько представлений с одинаковым именем модели, вы можете увидеть дублирующиеся идентификаторы операций.\n\nЧтобы обойти это, вы можете переопределить `get_operation_id_base()`, чтобы предоставить другую базу для именной части ID.\n\n#### `get_serializer()`.\n\nЕсли представление реализовало `get_serializer()`, возвращает результат.\n\n#### `get_request_serializer()`.\n\nПо умолчанию возвращает `get_serializer()`, но может быть переопределен для различения объектов запроса и ответа.\n\n#### `get_response_serializer()`.\n\nПо умолчанию возвращает `get_serializer()`, но может быть переопределен для различения объектов запроса и ответа.\n\n### Именованные аргументы `AutoSchema.__init__()`\n\n`AutoSchema` предоставляет ряд именованных аргументов `__init__()`, которые могут быть использованы для общей настройки, если сгенерированные по умолчанию значения не подходят.\n\nДоступные именованные аргументы следующие:\n\n* `tags`: Укажите список тегов.\n* `component_name`: Укажите имя компонента.\n* `operation_id_base`: Укажите часть имени ресурса в идентификаторах операций.\n\nВы передаете именованные аргументы при объявлении экземпляра `AutoSchema` в вашем представлении:\n\n```python\nclass PetDetailView(generics.RetrieveUpdateDestroyAPIView):\n    schema = AutoSchema(\n        tags=['Pets'],\n        component_name='Pet',\n        operation_id_base='Pet',\n    )\n    ...\n```\n\nПредполагая модель `Pet` и сериализатор `PetSerializer`, именованные аргументы в этом примере, вероятно, не нужны. Однако, часто вам придется передавать именованные аргументы, если у вас есть несколько представлений, нацеленных на одну и ту же модель, или несколько представлений с одинаковыми именами сериализаторов.\n\nЕсли ваши представления имеют связанные настройки, которые часто требуются, вы можете создать базовый подкласс `AutoSchema` для вашего проекта, который принимает дополнительные `__init__()` именованные аргументы, чтобы избежать подклассификации `AutoSchema` для каждого представления.\n"
  },
  {
    "path": "api-guide/serializers.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n- serializers.py\n\n---\n\n# Сериализаторы\n\n> Расширение полезности сериализаторов - это то, чем мы хотели бы заняться. Однако это не тривиальная проблема, и она потребует серьезной работы над дизайном.\n>\n> &mdash; Russell Keith-Magee, [Django users group](https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion)\n\nСериализаторы позволяют преобразовывать сложные данные, такие как наборы запросов и экземпляры моделей, в собственные типы данных Python, которые затем могут быть легко преобразованы в `JSON`, `XML` или другие типы содержимого. Сериализаторы также обеспечивают десериализацию, позволяя преобразовывать разобранные данные обратно в сложные типы после предварительной проверки входящих данных.\n\nСериализаторы в DRF работают очень похоже на классы Django `Form` и `ModelForm`. Мы предоставляем класс `Serializer`, который дает вам мощный, универсальный способ управления выводом ваших ответов, а также класс `ModelSerializer`, который предоставляет полезный ярлык для создания сериализаторов, работающих с экземплярами моделей и наборами запросов.\n\n## Объявление сериализаторов\n\nДавайте начнем с создания простого объекта, который мы можем использовать для примера:\n\n```python\nfrom datetime import datetime\n\nclass Comment:\n    def __init__(self, email, content, created=None):\n        self.email = email\n        self.content = content\n        self.created = created or datetime.now()\n\ncomment = Comment(email='leila@example.com', content='foo bar')\n```\n\nМы объявим сериализатор, который мы можем использовать для сериализации и десериализации данных, соответствующих объектам `Comment`.\n\nОбъявление сериализатора очень похоже на объявление формы:\n\n```python\nfrom rest_framework import serializers\n\nclass CommentSerializer(serializers.Serializer):\n    email = serializers.EmailField()\n    content = serializers.CharField(max_length=200)\n    created = serializers.DateTimeField()\n```\n\n## Сериализация объектов\n\nТеперь мы можем использовать `CommentSerializer` для сериализации комментария или списка комментариев. Опять же, использование класса `Serializer` очень похоже на использование класса `Form`.\n\n```python\nserializer = CommentSerializer(comment)\nserializer.data\n# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}\n```\n\nНа данном этапе мы перевели экземпляр модели в собственные типы данных Python. Чтобы завершить процесс сериализации, мы преобразуем данные в `json`.\n\n```python\nfrom rest_framework.renderers import JSONRenderer\n\njson = JSONRenderer().render(serializer.data)\njson\n# b'{\"email\":\"leila@example.com\",\"content\":\"foo bar\",\"created\":\"2016-01-27T15:17:10.375877\"}'\n```\n\n## Десериализация объектов\n\nДесериализация аналогична. Сначала мы разбираем поток на собственные типы данных Python...\n\n```python\nimport io\nfrom rest_framework.parsers import JSONParser\n\nstream = io.BytesIO(json)\ndata = JSONParser().parse(stream)\n```\n\nзатем мы восстанавливаем эти родные типы данных в словарь проверенных данных.\n\n```python\nserializer = CommentSerializer(data=data)\nserializer.is_valid()\n# True\nserializer.validated_data\n# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}\n```\n\n## Сохранение экземпляров\n\nЕсли мы хотим иметь возможность возвращать полные экземпляры объектов на основе проверенных данных, нам необходимо реализовать один или оба метода `.create()` и `.update()`. Например:\n\n```python\nclass CommentSerializer(serializers.Serializer):\n    email = serializers.EmailField()\n    content = serializers.CharField(max_length=200)\n    created = serializers.DateTimeField()\n\n    def create(self, validated_data):\n        return Comment(**validated_data)\n\n    def update(self, instance, validated_data):\n        instance.email = validated_data.get('email', instance.email)\n        instance.content = validated_data.get('content', instance.content)\n        instance.created = validated_data.get('created', instance.created)\n        return instance\n```\n\nЕсли ваши экземпляры объектов соответствуют моделям Django, вы также захотите убедиться, что эти методы сохраняют объект в базе данных. Например, если `Comment` является моделью Django, методы могут выглядеть следующим образом:\n\n```python\ndef create(self, validated_data):\n        return Comment.objects.create(**validated_data)\n\n    def update(self, instance, validated_data):\n        instance.email = validated_data.get('email', instance.email)\n        instance.content = validated_data.get('content', instance.content)\n        instance.created = validated_data.get('created', instance.created)\n        instance.save()\n        return instance\n```\n\nТеперь при десериализации данных мы можем вызвать `.save()`, чтобы вернуть экземпляр объекта, основанный на проверенных данных.\n\n```python\ncomment = serializer.save()\n```\n\nВызов `.save()` либо создаст новый экземпляр, либо обновит существующий, в зависимости от того, был ли передан существующий экземпляр при инстанцировании класса сериализатора:\n\n```python\n# .save() will create a new instance.\nserializer = CommentSerializer(data=data)\n\n# .save() will update the existing `comment` instance.\nserializer = CommentSerializer(comment, data=data)\n```\n\nМетоды `.create()` и `.update()` являются необязательными. Вы можете реализовать либо ни один из них, либо один, либо оба, в зависимости от условий использования вашего класса сериализатора.\n\n#### Передача дополнительных атрибутов в `.save()`.\n\nИногда вы хотите, чтобы код представления мог вводить дополнительные данные в момент сохранения экземпляра. Эти дополнительные данные могут включать информацию о текущем пользователе, текущем времени или что-нибудь еще, что не является частью данных запроса.\n\nВы можете сделать это, включив дополнительные именованные аргументы при вызове `.save()`. Например:\n\n```python\nserializer.save(owner=request.user)\n```\n\nЛюбые дополнительные именованные аргументы будут включены в аргумент `validated_data` при вызове `.create()` или `.update()`.\n\n#### Переопределение `.save()` напрямую.\n\nВ некоторых случаях имена методов `.create()` и `.update()` могут не иметь смысла. Например, в контактной форме мы можем не создавать новые экземпляры, а отправлять электронное письмо или другое сообщение.\n\nВ этих случаях вы можете вместо этого переопределить `.save()` напрямую, как более читабельный и осмысленный.\n\nНапример:\n\n```python\nclass ContactForm(serializers.Serializer):\n    email = serializers.EmailField()\n    message = serializers.CharField()\n\n    def save(self):\n        email = self.validated_data['email']\n        message = self.validated_data['message']\n        send_email(from=email, message=message)\n```\n\nОбратите внимание, что в приведенном выше случае нам приходится напрямую обращаться к свойству сериализатора `.validated_data`.\n\n## Валидация\n\nПри десериализации данных всегда нужно вызывать `is_valid()` прежде, чем пытаться получить доступ к проверенным данным или сохранить экземпляр объекта. Если возникнут ошибки валидации, свойство `.errors` будет содержать словарь, представляющий сообщения об ошибках. Например:\n\n```python\nserializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})\nserializer.is_valid()\n# False\nserializer.errors\n# {'email': ['Enter a valid email address.'], 'created': ['This field is required.']}\n```\n\nКаждый ключ в словаре будет именем поля, а значения будут списками строк любых сообщений об ошибках, соответствующих этому полю. Также может присутствовать ключ `non_field_errors`, в котором будут перечислены все общие ошибки валидации. Имя ключа `non_field_errors` можно настроить с помощью параметра DRF `NON_FIELD_ERRORS_KEY`.\n\nПри десериализации списка элементов ошибки будут возвращены в виде списка словарей, представляющих каждый из десериализованных элементов.\n\n#### Возбуждение исключения при недопустимых данных\n\nМетод `.is_valid()` принимает необязательный флаг `raise_exception`, который заставит его поднять исключение `serializers.ValidationError`, если есть ошибки валидации.\n\nЭти исключения автоматически обрабатываются обработчиком исключений по умолчанию, который предоставляет DRF, и по умолчанию будут возвращать ответы `HTTP 400 Bad Request`.\n\n```python\n# Return a 400 response if the data was invalid.\nserializer.is_valid(raise_exception=True)\n```\n\n#### Валидация на уровне поля\n\nВы можете задать пользовательскую валидацию на уровне полей, добавив методы `.validate_<имя_поля>` в подкласс `Serializer`. Они аналогичны методам `.clean_<имя_поля>` в формах Django.\n\nЭти методы принимают единственный аргумент, который является значением поля, требующего проверки.\n\nВаши методы `validate_<имя_поля>` должны возвращать проверенное значение или вызывать `serializers.ValidationError`. Например:\n\n```python\nfrom rest_framework import serializers\n\nclass BlogPostSerializer(serializers.Serializer):\n    title = serializers.CharField(max_length=100)\n    content = serializers.CharField()\n\n    def validate_title(self, value):\n        \"\"\"\n        Check that the blog post is about Django.\n        \"\"\"\n        if 'django' not in value.lower():\n            raise serializers.ValidationError(\"Blog post is not about Django\")\n        return value\n```\n\n---\n\n**Примечание:** Если ваше `<имя_поля>` объявлено в вашем сериализаторе с параметром `required=False`, то этот шаг валидации не будет выполняться, если поле не включено.\n\n---\n\n#### Валидация на уровне объекта\n\nЧтобы выполнить любую другую проверку, требующую доступа к нескольким полям, добавьте метод `.validate()` к вашему подклассу `Serializer`. Этот метод принимает единственный аргумент, который является словарем значений полей. При необходимости он должен вызывать `serializers.ValidationError, или просто возвращать проверенные значения. Например:\n\n```python\nfrom rest_framework import serializers\n\nclass EventSerializer(serializers.Serializer):\n    description = serializers.CharField(max_length=100)\n    start = serializers.DateTimeField()\n    finish = serializers.DateTimeField()\n\n    def validate(self, data):\n        \"\"\"\n        Check that start is before finish.\n        \"\"\"\n        if data['start'] > data['finish']:\n            raise serializers.ValidationError(\"finish must occur after start\")\n        return data\n```\n\n#### Валидаторы\n\nОтдельные поля сериализатора могут включать валидаторы, например, путем объявления их в экземпляре поля:\n\n```python\ndef multiple_of_ten(value):\n    if value % 10 != 0:\n        raise serializers.ValidationError('Not a multiple of ten')\n\nclass GameRecord(serializers.Serializer):\n    score = serializers.IntegerField(validators=[multiple_of_ten])\n    ...\n```\n\nКлассы сериализаторов могут также включать многократно используемые валидаторы, которые применяются к полному набору данных поля. Эти валидаторы включаются путем объявления их во внутреннем классе `Meta`, например, так:\n\n```python\nclass EventSerializer(serializers.Serializer):\n    name = serializers.CharField()\n    room_number = serializers.ChoiceField(choices=[101, 102, 103, 201])\n    date = serializers.DateField()\n\n    class Meta:\n        # Each room only has one event per day.\n        validators = [\n            UniqueTogetherValidator(\n                queryset=Event.objects.all(),\n                fields=['room_number', 'date']\n            )\n        ]\n```\n\nДля получения дополнительной информации см. документацию [validators](validators.md).\n\n## Доступ к исходным данным и экземпляру\n\nПри передаче исходного объекта или набора запросов экземпляру сериализатора, объект будет доступен как `.instance`. Если начальный объект не передан, то атрибут `.instance` будет иметь значение `None`.\n\nПри передаче данных экземпляру сериализатора, немодифицированные данные будут доступны как `.initial_data`. Если именованный аргумент `data` не передан, то атрибут `.initial_data` не будет существовать.\n\n## Частичные обновления\n\nПо умолчанию сериализаторам должны передаваться значения для всех обязательных полей, иначе они будут выдавать ошибки валидации. Вы можете использовать аргумент `partial`, чтобы разрешить частичное обновление.\n\n```python\n# Update `comment` with partial data\nserializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)\n```\n\n## Работа с вложенными объектами\n\nПредыдущие примеры хорошо подходят для работы с объектами, которые имеют только простые типы данных, но иногда нам также необходимо иметь возможность представлять более сложные объекты, где некоторые атрибуты объекта могут не быть простыми типами данных, такими как строки, даты или целые числа.\n\nКласс `Serializer` сам является типом `Field` и может быть использован для представления отношений, в которых один тип объекта вложен в другой.\n\n```python\nclass UserSerializer(serializers.Serializer):\n    email = serializers.EmailField()\n    username = serializers.CharField(max_length=100)\n\nclass CommentSerializer(serializers.Serializer):\n    user = UserSerializer()\n    content = serializers.CharField(max_length=200)\n    created = serializers.DateTimeField()\n```\n\nЕсли вложенное представление может опционально принимать значение `None`, вы должны передать флаг `required=False` вложенному сериализатору.\n\n```python\nclass CommentSerializer(serializers.Serializer):\n    user = UserSerializer(required=False)  # May be an anonymous user.\n    content = serializers.CharField(max_length=200)\n    created = serializers.DateTimeField()\n```\n\nАналогично, если вложенное представление должно быть списком элементов, вы должны передать флаг `many=True` в сериализатор вложенных элементов.\n\n```python\nclass CommentSerializer(serializers.Serializer):\n    user = UserSerializer(required=False)\n    edits = EditItemSerializer(many=True)  # A nested list of 'edit' items.\n    content = serializers.CharField(max_length=200)\n    created = serializers.DateTimeField()\n```\n\n## Записываемые вложенные представления\n\nПри работе с вложенными представлениями, поддерживающими десериализацию данных, любые ошибки с вложенными объектами будут вложены под именем поля вложенного объекта.\n\n```python\nserializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})\nserializer.is_valid()\n# False\nserializer.errors\n# {'user': {'email': ['Enter a valid email address.']}, 'created': ['This field is required.']}\n```\n\nАналогично, свойство `.validated_data` будет включать в себя вложенные структуры данных.\n\n#### Написание методов `.create()` для вложенных представлений\n\nЕсли вы поддерживаете записываемые вложенные представления, вам нужно написать методы `.create()` или `.update()`, которые обрабатывают сохранение нескольких объектов.\n\nВ следующем примере показано, как можно создать пользователя с вложенным объектом профиля.\n\n```python\nclass UserSerializer(serializers.ModelSerializer):\n    profile = ProfileSerializer()\n\n    class Meta:\n        model = User\n        fields = ['username', 'email', 'profile']\n\n    def create(self, validated_data):\n        profile_data = validated_data.pop('profile')\n        user = User.objects.create(**validated_data)\n        Profile.objects.create(user=user, **profile_data)\n        return user\n```\n\n#### Написание методов `.update()` для вложенных представлений\n\nДля обновлений вам необходимо тщательно продумать, как обрабатывать обновления отношений. Например, если данные для отношения `None`, или не предоставлены, что из перечисленного ниже должно произойти?\n\n* Установите для отношения значение `NULL` в базе данных.\n* Удалите связанный экземпляр.\n* Игнорировать данные и оставить экземпляр как есть.\n* Вызвать ошибку валидации.\n\nВот пример метода `.update()` для нашего предыдущего класса `UserSerializer`.\n\n```python\ndef update(self, instance, validated_data):\n        profile_data = validated_data.pop('profile')\n        # Unless the application properly enforces that this field is\n        # always set, the following could raise a `DoesNotExist`, which\n        # would need to be handled.\n        profile = instance.profile\n\n        instance.username = validated_data.get('username', instance.username)\n        instance.email = validated_data.get('email', instance.email)\n        instance.save()\n\n        profile.is_premium_member = profile_data.get(\n            'is_premium_member',\n            profile.is_premium_member\n        )\n        profile.has_support_contract = profile_data.get(\n            'has_support_contract',\n            profile.has_support_contract\n         )\n        profile.save()\n\n        return instance\n```\n\nПоскольку поведение вложенных созданий и обновлений может быть неоднозначным и может требовать сложных зависимостей между связанными моделями, DRF 3 требует, чтобы вы всегда писали эти методы явно. Методы `ModelSerializer` `.create()` и `.update()` по умолчанию не включают поддержку записываемых вложенных представлений.\n\nОднако существуют сторонние пакеты, такие как [DRF Writable Nested](serializers.md#drf-writable-nested), которые поддерживают автоматические записываемые вложенные представления.\n\n#### Обработка сохранения связанных экземпляров в классах менеджера моделей\n\nАльтернативой сохранению нескольких связанных экземпляров в сериализаторе является написание пользовательских классов менеджера модели, которые занимаются созданием нужных экземпляров.\n\nНапример, предположим, мы хотим, чтобы экземпляры `User` и `Profile` всегда создавались вместе как пара. Мы можем написать пользовательский класс менеджера, который будет выглядеть примерно так:\n\n```python\nclass UserManager(models.Manager):\n    ...\n\n    def create(self, username, email, is_premium_member=False, has_support_contract=False):\n        user = User(username=username, email=email)\n        user.save()\n        profile = Profile(\n            user=user,\n            is_premium_member=is_premium_member,\n            has_support_contract=has_support_contract\n        )\n        profile.save()\n        return user\n```\n\nЭтот класс менеджера теперь более точно передает, что экземпляры пользователя и профиля всегда создаются одновременно. Наш метод `.create()` в классе сериализатора теперь может быть переписан для использования нового метода менеджера.\n\n```python\ndef create(self, validated_data):\n    return User.objects.create(\n        username=validated_data['username'],\n        email=validated_data['email'],\n        is_premium_member=validated_data['profile']['is_premium_member'],\n        has_support_contract=validated_data['profile']['has_support_contract']\n    )\n```\n\nБолее подробно об этом подходе смотрите документацию Django по [менеджерам моделей](https://docs.djangoproject.com/en/stable/topics/db/managers/), и [этот блогпост об использовании классов моделей и менеджеров](https://www.dabapps.com/blog/django-models-and-encapsulation/).\n\n## Работа с несколькими объектами\n\nКласс `Serializer` также может обрабатывать сериализацию или десериализацию списков объектов.\n\n#### Сериализация нескольких объектов\n\nЧтобы сериализовать кверисет или список объектов вместо одного экземпляра объекта, необходимо передать флаг `many=True` при инстанцировании сериализатора. Затем вы можете передать кверисет или список объектов для сериализации.\n\n```python\nqueryset = Book.objects.all()\nserializer = BookSerializer(queryset, many=True)\nserializer.data\n# [\n#     {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},\n#     {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},\n#     {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}\n# ]\n```\n\n#### Десериализация нескольких объектов\n\nПоведение по умолчанию для десериализации нескольких объектов - это поддержка создания нескольких объектов, но не поддержка обновления нескольких объектов. Для получения дополнительной информации о том, как поддержать или настроить любой из этих случаев, см. документацию по [ListSerializer](#listserializer) ниже.\n\n## Включение дополнительного контекста\n\nБывают случаи, когда вам необходимо предоставить сериализатору дополнительный контекст в дополнение к сериализуемому объекту. Одним из распространенных случаев является использование сериализатора, который включает отношения с гиперссылками, что требует, чтобы сериализатор имел доступ к текущему запросу, чтобы он мог правильно генерировать полностью определенные URL.\n\nВы можете предоставить произвольный дополнительный контекст, передав аргумент `context` при инстанцировании сериализатора. Например:\n\n```python\nserializer = AccountSerializer(account, context={'request': request})\nserializer.data\n# {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}\n```\n\nКонтекстный словарь можно использовать в любой логике поля сериализатора, например, в пользовательском методе `.to_representation()`, обращаясь к атрибуту `self.context`.\n\n---\n\n# ModelSerializer\n\nЧасто вам понадобятся классы сериализаторов, которые близко сопоставляются с определениями моделей Django.\n\nКласс `ModelSerializer` предоставляет ярлык, позволяющий автоматически создать класс `Serializer` с полями, соответствующими полям модели.\n\n**Класс `ModelSerializer` такой же, как и обычный класс `Serializer`, за исключением того, что**:\n\n* Он автоматически сгенерирует для вас набор полей на основе модели.\n* Он автоматически генерирует валидаторы для сериализатора, такие как валидаторы unique_together.\n* Он включает простые реализации по умолчанию `.create()` и `.update()`.\n\nОбъявление `ModelSerializer` выглядит следующим образом:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['id', 'account_name', 'users', 'created']\n```\n\nПо умолчанию все поля модели класса будут отображены на соответствующие поля сериализатора.\n\nЛюбые отношения, такие как внешние ключи в модели, будут отображены на `PrimaryKeyRelatedField`. Обратные отношения не включаются по умолчанию, если они не включены явно, как указано в документации [serializer relations](relations.md).\n\n#### Проверка `ModelSerializer`.\n\nКлассы сериализаторов генерируют полезные строки представления, которые позволяют полностью просмотреть состояние их полей. Это особенно полезно при работе с `ModelSerializers`, когда вы хотите определить, какой набор полей и валидаторов автоматически создается для вас.\n\nДля этого откройте оболочку Django, используя `python manage.py shell`, затем импортируйте класс сериализатора, инстанцируйте его и выведите представление объекта...\n\n```python\n>>> from myapp.serializers import AccountSerializer\n>>> serializer = AccountSerializer()\n>>> print(repr(serializer))\nAccountSerializer():\n    id = IntegerField(label='ID', read_only=True)\n    name = CharField(allow_blank=True, max_length=100, required=False)\n    owner = PrimaryKeyRelatedField(queryset=User.objects.all())\n```\n\n## Указание, какие поля включать\n\nЕсли вы хотите, чтобы в сериализаторе модели использовалось только подмножество полей по умолчанию, вы можете сделать это с помощью опций `fields` или `exclude`, как и в случае с `ModelForm`. Настоятельно рекомендуется явно задавать все поля, которые должны быть сериализованы, с помощью атрибута `fields`. Это уменьшит вероятность непреднамеренного раскрытия данных при изменении ваших моделей.\n\nНапример:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['id', 'account_name', 'users', 'created']\n```\n\nВы также можете установить для атрибута `fields` специальное значение `'__all__'`, чтобы указать, что должны использоваться все поля в модели.\n\nНапример:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        fields = '__all__'\n```\n\nВы можете установить атрибут `exclude` в список полей, которые должны быть исключены из сериализатора.\n\nНапример:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        exclude = ['users']\n```\n\nВ приведенном выше примере, если модель `Account` имеет 3 поля `account_name`, `users` и `created`, это приведет к тому, что поля `account_name` и `created` будут сериализованы.\n\nИмена в атрибутах `fields` и `exclude` обычно отображаются на поля модели в классе модели.\n\nАльтернативные имена в опциях `fields` могут отображаться на свойства или методы, не принимающие аргументов, которые существуют в классе модели.\n\nНачиная с версии 3.3.0, **обязательным** является предоставление одного из атрибутов `fields` или `exclude`.\n\n## Указание вложенной сериализации\n\nПо умолчанию `ModelSerializer` использует первичные ключи для отношений, но вы также можете легко генерировать вложенные представления с помощью опции `depth`:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['id', 'account_name', 'users', 'created']\n        depth = 1\n```\n\nПараметр `depth` должен быть установлен в целочисленное значение, которое указывает глубину отношений, которые должны быть пройдены перед возвратом к плоскому представлению.\n\nЕсли вы хотите настроить способ сериализации, вам нужно будет определить поле самостоятельно.\n\n## Указание полей в явном виде\n\nВы можете добавить дополнительные поля в `ModelSerializer` или переопределить поля по умолчанию, объявив поля в классе, как и в классе `Serializer`.\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    url = serializers.CharField(source='get_absolute_url', read_only=True)\n    groups = serializers.PrimaryKeyRelatedField(many=True)\n\n    class Meta:\n        model = Account\n        fields = ['url', 'groups']\n```\n\nДополнительные поля могут соответствовать любому свойству или вызываемому объекту модели.\n\n## Указание полей, доступных только для чтения\n\nВы можете указать несколько полей как доступные только для чтения. Вместо того чтобы добавлять каждое поле явно с атрибутом `read_only=True`, вы можете использовать сокращенную опцию Meta, `read_only_fields`.\n\nЭтот параметр должен представлять собой список или кортеж имен полей и объявляется следующим образом:\n\n```python\nclass AccountSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['id', 'account_name', 'users', 'created']\n        read_only_fields = ['account_name']\n```\n\nПоля модели, для которых установлено значение `editable=False`, и поля `AutoField` по умолчанию будут установлены в режим только для чтения, и их не нужно добавлять в опцию `read_only_fields`.\n\n---\n\n**Примечание**: Существует особый случай, когда поле, доступное только для чтения, является частью ограничения `unique_together` на уровне модели. В этом случае поле требуется классу сериализатора для проверки ограничения, но оно также не должно быть доступно для редактирования пользователем.\n\nПравильный способ решения этой проблемы — явно указать поле в сериализаторе, предоставив аргументы `read_only=True` и `default=…`.\n\nОдним из примеров этого является отношение только для чтения к текущему аутентифицированному `User`, который является `unique_together` с другим идентификатором. В этом случае вы объявите поле пользователя следующим образом:\n\n```python\nuser = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())\n```\n\nПожалуйста, ознакомьтесь с документацией [Validators Documentation](validators.md) для получения подробной информации о классах [UniqueTogetherValidator](validators.md#uniquetogethervalidator) и [CurrentUserDefault](validators.md#currentuserdefault).\n\n---\n\n## Дополнительные именованные аргументы\n\nТакже есть возможность указать произвольные дополнительные именованные аргументы для полей, используя опцию `extra_kwargs`. Как и в случае с `read_only_fields`, это означает, что вам не нужно явно объявлять поле в сериализаторе.\n\nЭта опция представляет собой словарь, отображающий имена полей на словарь именованных аргументов. Например:\n\n```python\nclass CreateUserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = ['email', 'username', 'password']\n        extra_kwargs = {'password': {'write_only': True}}\n\n    def create(self, validated_data):\n        user = User(\n            email=validated_data['email'],\n            username=validated_data['username']\n        )\n        user.set_password(validated_data['password'])\n        user.save()\n        return user\n```\n\nСледует помнить, что если поле уже было явно объявлено в классе сериализатора, то опция `extra_kwargs` будет проигнорирована.\n\n## Реляционные поля\n\nПри сериализации экземпляров модели существует несколько различных способов представления отношений. Представление по умолчанию для `ModelSerializer` заключается в использовании первичных ключей связанных экземпляров.\n\nАльтернативные представления включают сериализацию с помощью гиперссылок, сериализацию полных вложенных представлений или сериализацию с помощью пользовательского представления.\n\nБолее подробную информацию можно найти в документации [serializer relations](relations.md).\n\n## Настройка сопоставлений полей\n\nКласс ModelSerializer также предоставляет API, который вы можете переопределить, чтобы изменить способ автоматического определения полей сериализатора при инстанцировании сериализатора.\n\nОбычно, если `ModelSerializer` не генерирует нужные вам поля по умолчанию, вы должны либо добавить их в класс явно, либо просто использовать вместо них обычный класс `Serializer`. Однако в некоторых случаях вы можете захотеть создать новый базовый класс, определяющий, как создаются поля сериализатора для любой конкретной модели.\n\n### `serializer_field_mapping`.\n\nОтображение полей модели Django на поля сериализатора DRF. Вы можете переопределить это отображение, чтобы изменить поля сериализатора по умолчанию, которые должны использоваться для каждого поля модели.\n\n### `serializer_related_field`.\n\nЭто свойство должно быть классом поля сериализатора, который по умолчанию используется для реляционных полей.\n\nДля `ModelSerializer` это значение по умолчанию равно `serializers.PrimaryKeyRelatedField`.\n\nДля `HyperlinkedModelSerializer` это значение по умолчанию равно `serializers.HyperlinkedRelatedField`.\n\n### `serializer_url_field`.\n\nКласс поля сериализатора, который должен использоваться для любого поля `url` в сериализаторе.\n\nПо умолчанию `serializers.HyperlinkedIdentityField`.\n\n### `serializer_choice_field`\n\nКласс поля сериализатора, который должен использоваться для любых полей выбора в сериализаторе.\n\nПо умолчанию `serializers.ChoiceField`.\n\n### API field_class и field_kwargs\n\nСледующие методы вызываются для определения класса и именованных аргументов для каждого поля, которое должно быть автоматически включено в сериализатор. Каждый из этих методов должен возвращать кортеж `(field_class, field_kwargs)`.\n\n### `build_standard_field(self, field_name, model_field)`.\n\nВызывается для генерации поля сериализатора, которое сопоставляется со стандартным полем модели.\n\nРеализация по умолчанию возвращает класс сериализатора на основе атрибута `serializer_field_mapping`.\n\n### `build_relational_field(self, field_name, relation_info)`.\n\nВызывается для генерации поля сериализатора, которое сопоставляется с полем реляционной модели.\n\nРеализация по умолчанию возвращает класс сериализатора на основе атрибута `serializer_related_field`.\n\nАргумент `relation_info` представляет собой именованный кортеж, содержащий свойства `model_field`, `related_model`, `to_many` и `has_through_model`.\n\n### `build_nested_field(self, field_name, relation_info, nested_depth)`.\n\nВызывается для генерации поля сериализатора, которое сопоставляется с полем реляционной модели, если установлен параметр `depth`.\n\nРеализация по умолчанию динамически создает вложенный класс сериализатора на основе `ModelSerializer` или `HyperlinkedModelSerializer`.\n\nЗначение `nested_depth` будет равно значению опции `depth`, минус один.\n\nАргумент `relation_info` представляет собой именованный кортеж, содержащий свойства `model_field`, `related_model`, `to_many` и `has_through_model`.\n\n### `build_property_field(self, field_name, model_class)`.\n\nВызывается для генерации поля сериализатора, которое сопоставляется со свойством или методом с нулевым аргументом класса модели.\n\nРеализация по умолчанию возвращает класс `ReadOnlyField`.\n\n### `build_url_field(self, field_name, model_class)`.\n\nВызывается для генерации поля сериализатора для собственного поля сериализатора `url`. Реализация по умолчанию возвращает класс `HyperlinkedIdentityField`.\n\n### `build_unknown_field(self, field_name, model_class)`.\n\nВызывается, когда имя поля не сопоставлено ни с одним полем модели или свойством модели. Реализация по умолчанию вызывает ошибку, хотя подклассы могут настраивать это поведение.\n\n---\n\n# HyperlinkedModelSerializer\n\nКласс `HyperlinkedModelSerializer` похож на класс `ModelSerializer`, за исключением того, что он использует гиперссылки для представления отношений, а не первичные ключи.\n\nПо умолчанию сериализатор будет включать поле `url` вместо поля первичного ключа.\n\nПоле url будет представлено с помощью поля сериализатора `HyperlinkedIdentityField`, а любые отношения в модели будут представлены с помощью поля сериализатора `HyperlinkedRelatedField`.\n\nВы можете явно включить первичный ключ, добавив его, например, в опцию `fields`:\n\n```python\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['url', 'id', 'account_name', 'users', 'created']\n```\n\n## Абсолютные и относительные URL-адреса\n\nПри инстанцировании `HyperlinkedModelSerializer` вы должны включить текущий `request` в контекст сериализатора, например:\n\n```python\nserializer = AccountSerializer(queryset, context={'request': request})\n```\n\nЭто гарантирует, что гиперссылки будут содержать соответствующее имя хоста, чтобы в результирующем представлении использовались полные URL-адреса, например:\n\n```python\nhttp://api.example.com/accounts/1/\n```\n\nВместо относительных URL-адресов, таких как:\n\n```python\n/accounts/1/\n```\n\nЕсли вы *хотите* использовать относительные URL, вы должны явно передать `{'request': None}` в контексте сериализатора.\n\n## Как определяются представления с гиперссылками\n\nНеобходимо определить, какие представления следует использовать для гиперссылок на экземпляры модели.\n\nПо умолчанию ожидается, что гиперссылки будут соответствовать имени представления, которое соответствует стилю `'{имя_модели}-detail'`, и ищет экземпляр по именованному аргументу `pk`.\n\nВы можете переопределить имя представления поля URL и поле поиска, используя один или оба параметра `view_name` и `lookup_field` в параметре `extra_kwargs`, как показано ниже:\n\n```python\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n    class Meta:\n        model = Account\n        fields = ['url', 'account_name', 'users', 'created']\n        extra_kwargs = {\n            'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},\n            'users': {'lookup_field': 'username'}\n        }\n```\n\nВ качестве альтернативы вы можете явно задать поля в сериализаторе. Например:\n\n```python\nclass AccountSerializer(serializers.HyperlinkedModelSerializer):\n    url = serializers.HyperlinkedIdentityField(\n        view_name='accounts',\n        lookup_field='slug'\n    )\n    users = serializers.HyperlinkedRelatedField(\n        view_name='user-detail',\n        lookup_field='username',\n        many=True,\n        read_only=True\n    )\n\n    class Meta:\n        model = Account\n        fields = ['url', 'account_name', 'users', 'created']\n```\n\n---\n\n**Совет**: Правильное согласование гиперссылочных представлений и вашего URL conf иногда может быть немного сложным. Печать `repr` экземпляра `HyperlinkedModelSerializer` - особенно полезный способ проверить, какие именно имена представлений и поля поиска должны отображать отношения.\n\n---\n\n## Изменение имени поля URL\n\nИмя поля URL по умолчанию равно 'url'. Вы можете переопределить его глобально, используя параметр `URL_FIELD_NAME`.\n\n---\n\n# ListSerializer\n\nКласс `ListSerializer` обеспечивает поведение для сериализации и валидации нескольких объектов одновременно. Обычно вам не нужно использовать `ListSerializer` напрямую, а следует просто передать `many=True` при инстанцировании сериализатора.\n\nПри инстанцировании сериализатора и передаче `many=True` будет создан экземпляр `ListSerializer`. Затем класс сериализатора становится дочерним классом родительского `ListSerializer`.\n\nСледующий аргумент также может быть передан полю `ListSerializer` или сериализатору, которому передано `many=True`:\n\n### `allow_empty`\n\nПо умолчанию это `True`, но может быть установлено в `False`, если вы хотите запретить пустые списки в качестве допустимого ввода.\n\n### `max_length`\n\nПо умолчанию это `None`, но может быть установлено в положительное целое число, если вы хотите проверить, что список содержит не более этого количества элементов.\n\n### `min_length`\n\nПо умолчанию это `None`, но может быть установлено в положительное целое число, если вы хотите проверить, что список содержит не менее этого количества элементов.\n\n### Настройка поведения `ListSerializer`.\n\n*Существует* несколько случаев, когда вы захотите настроить поведение `ListSerializer`. Например:\n\n* Вы хотите обеспечить определенную проверку списков, например, проверить, что один элемент не конфликтует с другим элементом списка.\n* Вы хотите настроить поведение создания или обновления нескольких объектов.\n\nДля этих случаев вы можете изменить класс, который используется при передаче `many=True`, используя опцию `list_serializer_class` для класса сериализатора `Meta`.\n\nНапример:\n\n```python\nclass CustomListSerializer(serializers.ListSerializer):\n    ...\n\nclass CustomSerializer(serializers.Serializer):\n    ...\n    class Meta:\n        list_serializer_class = CustomListSerializer\n```\n\n#### Настройка множественного создания\n\nРеализация по умолчанию для создания нескольких объектов заключается в простом вызове `.create()` для каждого элемента списка. Если вы хотите настроить это поведение, вам нужно настроить метод `.create()` класса `ListSerializer`, который используется, когда передается `many=True`.\n\nНапример:\n\n```python\nclass BookListSerializer(serializers.ListSerializer):\n    def create(self, validated_data):\n        books = [Book(**item) for item in validated_data]\n        return Book.objects.bulk_create(books)\n\nclass BookSerializer(serializers.Serializer):\n    ...\n    class Meta:\n        list_serializer_class = BookListSerializer\n```\n\n#### Настройка многократного обновления\n\nПо умолчанию класс `ListSerializer` не поддерживает множественные обновления. Это связано с тем, что поведение, которое следует ожидать для вставок и удалений, неоднозначно.\n\nДля поддержки нескольких обновлений необходимо сделать это явно. При написании кода множественных обновлений обязательно учитывайте следующее:\n\n* Как определить, какой экземпляр должен быть обновлен для каждого элемента в списке данных?\n* Как следует обрабатывать вставки? Являются ли они недействительными, или они создают новые объекты?\n* Как следует обрабатывать удаления? Означают ли они удаление объекта или удаление отношения? Следует ли их молча игнорировать, или они недействительны?\n* Как следует обрабатывать упорядочивание? Влечет ли изменение положения двух объектов изменение состояния или оно игнорируется?\n\nВам нужно будет добавить явное поле `id` в сериализатор экземпляра. По умолчанию неявно генерируемое поле `id` помечено как `read_only`. Это приводит к тому, что оно удаляется при обновлении. Как только вы объявите его явно, оно будет доступно в методе `update` сериализатора списка.\n\nВот пример того, как можно реализовать несколько обновлений:\n\n```python\nclass BookListSerializer(serializers.ListSerializer):\n    def update(self, instance, validated_data):\n        # Maps for id->instance and id->data item.\n        book_mapping = {book.id: book for book in instance}\n        data_mapping = {item['id']: item for item in validated_data}\n\n        # Perform creations and updates.\n        ret = []\n        for book_id, data in data_mapping.items():\n            book = book_mapping.get(book_id, None)\n            if book is None:\n                ret.append(self.child.create(data))\n            else:\n                ret.append(self.child.update(book, data))\n\n        # Perform deletions.\n        for book_id, book in book_mapping.items():\n            if book_id not in data_mapping:\n                book.delete()\n\n        return ret\n\nclass BookSerializer(serializers.Serializer):\n    # We need to identify elements in the list using their primary key,\n    # so use a writable field here, rather than the default which would be read-only.\n    id = serializers.IntegerField()\n    ...\n\n    class Meta:\n        list_serializer_class = BookListSerializer\n```\n\n#### Настройка инициализации ListSerializer\n\nКогда инстанцируется сериализатор с `many=True`, нам необходимо определить, какие аргументы и ключевые слова следует передать в метод `.__init__()` как для дочернего класса `Serializer`, так и для родительского класса `ListSerializer`.\n\nПо умолчанию все аргументы передаются обоим классам, за исключением `validators` и любых пользовательских именованных аргументов, которые, как предполагается, предназначены для дочернего класса сериализатора.\n\nИногда вам может понадобиться явно указать, как дочерний и родительский классы должны быть инстанцированы при передаче `many=True`. Вы можете сделать это с помощью метода класса `many_init`.\n\n```python\n@classmethod\ndef many_init(cls, *args, **kwargs):\n    # Instantiate the child serializer.\n    kwargs['child'] = cls()\n    # Instantiate the parent list serializer.\n    return CustomListSerializer(*args, **kwargs)\n```\n\n---\n\n# BaseSerializer\n\nКласс `BaseSerializer`, который можно использовать для простой поддержки альтернативных стилей сериализации и десериализации.\n\nЭтот класс реализует тот же базовый API, что и класс `Serializer`:\n\n* `.data` - Возвращает исходящее примитивное представление.\n* `.is_valid()` - Десериализует и проверяет входящие данные.\n* `.validated_data` - Возвращает проверенные входящие данные.\n* `.errors` - Возвращает любые ошибки во время валидации.\n* `.save()` - Сохраняет проверенные данные в экземпляре объекта.\n\nЕсть четыре метода, которые могут быть переопределены, в зависимости от того, какую функциональность вы хотите, чтобы поддерживал класс сериализатора:\n\n* `.to_representation()` - Переопределить это для поддержки сериализации, для операций чтения.\n* `.to_internal_value()` - Переопределить это для поддержки десериализации, для операций записи.\n* `.create()` и `.update()` - Переопределите один из этих параметров или оба для поддержки сохранения экземпляров.\n\nПоскольку этот класс предоставляет тот же интерфейс, что и класс `Serializer`, вы можете использовать его с существующими общими представлениями на основе классов точно так же, как и обычный `Serializer` или `ModelSerializer`.\n\nЕдинственное отличие, которое вы заметите при этом - классы `BaseSerializer` не будут генерировать HTML-формы в Web-интерфейсе API. Это происходит потому, что данные, которые они возвращают, не включают всю информацию о полях, которая позволит преобразовать каждое поле в подходящий HTML-ввод.\n\n#### Классы `BaseSerializer` только для чтения.\n\nЧтобы реализовать сериализатор только для чтения, используя класс `BaseSerializer`, нам просто нужно переопределить метод `.to_representation()`. Давайте рассмотрим пример на примере простой модели Django:\n\n```python\nclass HighScore(models.Model):\n    created = models.DateTimeField(auto_now_add=True)\n    player_name = models.CharField(max_length=10)\n    score = models.IntegerField()\n```\n\nОчень просто создать сериализатор только для чтения для преобразования экземпляров `HighScore` в примитивные типы данных.\n\n```python\nclass HighScoreSerializer(serializers.BaseSerializer):\n    def to_representation(self, instance):\n        return {\n            'score': instance.score,\n            'player_name': instance.player_name\n        }\n```\n\nТеперь мы можем использовать этот класс для сериализации отдельных экземпляров `HighScore`:\n\n```python\n@api_view(['GET'])\ndef high_score(request, pk):\n    instance = HighScore.objects.get(pk=pk)\n    serializer = HighScoreSerializer(instance)\n    return Response(serializer.data)\n```\n\nИли используйте его для сериализации нескольких экземпляров:\n\n```python\n@api_view(['GET'])\ndef all_high_scores(request):\n    queryset = HighScore.objects.order_by('-score')\n    serializer = HighScoreSerializer(queryset, many=True)\n    return Response(serializer.data)\n```\n\n#### Классы `BaseSerializer` с функцией чтения-записи\n\nДля создания сериализатора чтения-записи нам сначала нужно реализовать метод `.to_internal_value()`. Этот метод возвращает проверенные значения, которые будут использованы для создания экземпляра объекта, и может вызвать `serializers.ValidationError`, если предоставленные данные имеют неправильный формат.\n\nКак только вы реализуете `.to_internal_value()`, базовый API валидации будет доступен в сериализаторе, и вы сможете использовать `.is_valid()`, `.validated_data` и `.errors`.\n\nЕсли вы хотите также поддерживать `.save()`, вам необходимо также реализовать один или оба метода `.create()` и `.update()`.\n\nВот полный пример нашего предыдущего `HighScoreSerializer`, который был обновлен для поддержки операций чтения и записи.\n\n```python\nclass HighScoreSerializer(serializers.BaseSerializer):\n    def to_internal_value(self, data):\n        score = data.get('score')\n        player_name = data.get('player_name')\n\n        # Perform the data validation.\n        if not score:\n            raise serializers.ValidationError({\n                'score': 'This field is required.'\n            })\n        if not player_name:\n            raise serializers.ValidationError({\n                'player_name': 'This field is required.'\n            })\n        if len(player_name) > 10:\n            raise serializers.ValidationError({\n                'player_name': 'May not be more than 10 characters.'\n            })\n\n        # Return the validated values. This will be available as\n        # the `.validated_data` property.\n        return {\n            'score': int(score),\n            'player_name': player_name\n        }\n\n    def to_representation(self, instance):\n        return {\n            'score': instance.score,\n            'player_name': instance.player_name\n        }\n\n    def create(self, validated_data):\n        return HighScore.objects.create(**validated_data)\n```\n\n#### Создание новых базовых классов\n\nКласс `BaseSerializer` также полезен, если вы хотите реализовать новые общие классы сериализаторов для работы с определенными стилями сериализации или для интеграции с альтернативными бэкендами хранения данных.\n\nСледующий класс является примером общего сериализатора, который может обрабатывать принудительное преобразование произвольных сложных объектов в примитивные представления.\n\n```python\nclass ObjectSerializer(serializers.BaseSerializer):\n    \"\"\"\n    A read-only serializer that coerces arbitrary complex objects\n    into primitive representations.\n    \"\"\"\n    def to_representation(self, instance):\n        output = {}\n        for attribute_name in dir(instance):\n            attribute = getattr(instance, attribute_name)\n            if attribute_name.startswith('_'):\n                # Ignore private attributes.\n                pass\n            elif hasattr(attribute, '__call__'):\n                # Ignore methods and other callables.\n                pass\n            elif isinstance(attribute, (str, int, bool, float, type(None))):\n                # Primitive types can be passed through unmodified.\n                output[attribute_name] = attribute\n            elif isinstance(attribute, list):\n                # Recursively deal with items in lists.\n                output[attribute_name] = [\n                    self.to_representation(item) for item in attribute\n                ]\n            elif isinstance(attribute, dict):\n                # Recursively deal with items in dictionaries.\n                output[attribute_name] = {\n                    str(key): self.to_representation(value)\n                    for key, value in attribute.items()\n                }\n            else:\n                # Force anything else to its string representation.\n                output[attribute_name] = str(attribute)\n        return output\n```\n\n---\n\n# Расширенное использование сериализатора\n\n## Переопределение поведения сериализации и десериализации\n\nЕсли вам нужно изменить поведение сериализации или десериализации класса сериализатора, вы можете сделать это, переопределив методы `.to_representation()` или `.to_internal_value()`.\n\nНекоторые причины, по которым это может быть полезно, включают...\n\n* Добавление нового поведения для новых базовых классов сериализаторов.\n* Небольшое изменение поведения для существующего класса.\n* Улучшение производительности сериализации для часто используемой конечной точки API, которая возвращает много данных.\n\nПодписи для этих методов следующие:\n\n#### `to_representation(self, instance)`.\n\nПринимает экземпляр объекта, который требует сериализации, и должен вернуть примитивное представление. Обычно это означает возврат структуры встроенных в Python типов данных. Точные типы, которые могут быть обработаны, зависят от классов рендеринга, которые вы настроили для своего API.\n\nМожет быть переопределена для изменения стиля представления. Например:\n\n```python\ndef to_representation(self, instance):\n    \"\"\"Convert `username` to lowercase.\"\"\"\n    ret = super().to_representation(instance)\n    ret['username'] = ret['username'].lower()\n    return ret\n```\n\n#### `to_internal_value(self, data)`.\n\nПринимает невалидированные входящие данные в качестве входных и должен вернуть валидированные данные, которые будут доступны как `serializer.validated_data`. Возвращаемое значение также будет передано в методы `.create()` или `.update()`, если для класса сериализатора будет вызван `.save()`.\n\nЕсли какая-либо из валидаций не прошла, то метод должен вызвать `serializers.ValidationError(errors)`. Аргумент `errors` должен представлять собой словарь, отображающий имена полей (или `settings.NON_FIELD_ERRORS_KEY`) на список сообщений об ошибках. Если вам не нужно изменять поведение десериализации и вместо этого вы хотите обеспечить проверку на уровне объекта, рекомендуется переопределить метод [`.validate()`](#валидация-на-уровне-объекта).\n\nАргумент `data`, передаваемый этому методу, обычно является значением `request.data`, поэтому тип данных, который он предоставляет, будет зависеть от классов парсера, которые вы настроили для своего API.\n\n## Наследование сериализатора\n\nПодобно формам Django, вы можете расширять и повторно использовать сериализаторы с помощью наследования. Это позволяет вам объявить общий набор полей или методов в родительском классе, который затем может быть использован в нескольких сериализаторах. Например,\n\n```python\nclass MyBaseSerializer(Serializer):\n    my_field = serializers.CharField()\n\n    def validate_my_field(self, value):\n        ...\n\nclass MySerializer(MyBaseSerializer):\n    ...\n```\n\nКак и классы `Model` и `ModelForm` в Django, внутренний класс `Meta` в сериализаторах не наследуется неявно от внутренних классов `Meta` своих родителей. Если вы хотите, чтобы класс `Meta` наследовался от родительского класса, вы должны сделать это явно. Например:\n\n```python\nclass AccountSerializer(MyBaseSerializer):\n    class Meta(MyBaseSerializer.Meta):\n        model = Account\n```\n\nОбычно мы рекомендуем *не* использовать наследование для внутренних классов Meta, а вместо этого объявлять все опции явно.\n\nКроме того, следующие предостережения относятся к наследованию сериализаторов:\n\n* Применяются обычные правила разрешения имен Python. Если у вас есть несколько базовых классов, которые объявляют внутренний класс `Meta`, будет использоваться только первый класс. Это означает дочерний `Meta`, если он существует, иначе `Meta` первого родителя и т.д.\n* Можно декларативно удалить `Field`, унаследованный от родительского класса, указав значение `None` в подклассе.\n\n```python\nclass MyBaseSerializer(ModelSerializer):\n    my_field = serializers.CharField()\n\nclass MySerializer(MyBaseSerializer):\n    my_field = None\n```\n\nОднако вы можете использовать эту технику только для отказа от поля, определенного декларативно родительским классом; это не помешает `ModelSerializer` сгенерировать поле по умолчанию. Чтобы отказаться от полей по умолчанию, смотрите [Указание, какие поля включать](#указание-какие-поля-включать).\n\n## Динамическое изменение полей\n\nПосле инициализации сериализатора, к словарю полей, установленных в сериализаторе, можно получить доступ с помощью атрибута `.fields`. Доступ и изменение этого атрибута позволяет динамически модифицировать сериализатор.\n\nИзменение аргумента `fields` напрямую позволяет вам делать такие интересные вещи, как изменение аргументов полей сериализатора во время выполнения, а не в момент объявления сериализатора.\n\n### Пример\n\nНапример, если вы хотите иметь возможность установить, какие поля должны использоваться сериализатором в момент его инициализации, вы можете создать класс сериализатора следующим образом:\n\n```python\nclass DynamicFieldsModelSerializer(serializers.ModelSerializer):\n    \"\"\"\n    A ModelSerializer that takes an additional `fields` argument that\n    controls which fields should be displayed.\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        # Don't pass the 'fields' arg up to the superclass\n        fields = kwargs.pop('fields', None)\n\n        # Instantiate the superclass normally\n        super().__init__(*args, **kwargs)\n\n        if fields is not None:\n            # Drop any fields that are not specified in the `fields` argument.\n            allowed = set(fields)\n            existing = set(self.fields)\n            for field_name in existing - allowed:\n                self.fields.pop(field_name)\n```\n\nЭто позволит вам сделать следующее:\n\n```python\n>>> class UserSerializer(DynamicFieldsModelSerializer):\n>>>     class Meta:\n>>>         model = User\n>>>         fields = ['id', 'username', 'email']\n>>>\n>>> print(UserSerializer(user))\n{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}\n>>>\n>>> print(UserSerializer(user, fields=('id', 'email')))\n{'id': 2, 'email': 'jon@example.com'}\n```\n\n## Настройка полей по умолчанию\n\nREST framework 2 предоставил API, позволяющий разработчикам переопределять, как класс `ModelSerializer` будет автоматически генерировать набор полей по умолчанию.\n\nЭтот API включал методы `.get_field()`, `.get_pk_field()` и другие.\n\nПоскольку сериализаторы были кардинально переработаны в версии 3.0, этот API больше не существует. Вы все еще можете изменять создаваемые поля, но вам придется обратиться к исходному коду, и имейте в виду, что если изменения, которые вы делаете, направлены против частных частей API, то они могут быть изменены.\n\n---\n\n# Пакеты сторонних производителей\n\nТакже доступны следующие пакеты сторонних производителей.\n\n## Django REST marshmallow\n\nПакет [django-rest-marshmallow](https://marshmallow-code.github.io/django-rest-marshmallow/) предоставляет альтернативную реализацию сериализаторов, используя библиотеку python [marshmallow](https://marshmallow.readthedocs.io/en/latest/). Он предоставляет тот же API, что и сериализаторы DRF, и может быть использован в качестве замены в некоторых случаях.\n\n## Serpy\n\nПакет [serpy](https://github.com/clarkduvall/serpy) - это альтернативная реализация сериализаторов, созданная для скорости. [Serpy](https://github.com/clarkduvall/serpy) сериализует сложные типы данных в простые нативные типы. Родные типы могут быть легко преобразованы в JSON или любой другой необходимый формат.\n\n## MongoengineModelSerializer\n\nПакет [django-rest-framework-mongoengine](https://github.com/umutbozkurt/django-rest-framework-mongoengine) предоставляет класс сериализатора `MongoEngineModelSerializer`, который поддерживает использование MongoDB в качестве уровня хранения данных для DRF.\n\n## GeoFeatureModelSerializer\n\nПакет [django-rest-framework-gis](https://github.com/djangonauts/django-rest-framework-gis) предоставляет класс сериализатора `GeoFeatureModelSerializer`, который поддерживает GeoJSON как для операций чтения, так и для записи.\n\n## HStoreSerializer\n\nПакет [django-rest-framework-hstore](https://github.com/djangonauts/django-rest-framework-hstore) предоставляет `HStoreSerializer` для поддержки поля модели [django-hstore](https://github.com/djangonauts/django-hstore) `DictionaryField` и его функции `chema-mode`.\n\n## Dynamic REST\n\nПакет [dynamic-rest](https://github.com/AltSchool/dynamic-rest) расширяет интерфейсы ModelSerializer и ModelViewSet, добавляя параметры запроса API для фильтрации, сортировки, включения/исключения всех полей и отношений, определенных вашими сериализаторами.\n\n## Dynamic Fields Mixin\n\nПакет [drf-dynamic-fields](https://github.com/dbrgn/drf-dynamic-fields) предоставляет миксин для динамического ограничения полей для сериализатора подмножеством, заданным параметром URL.\n\n## DRF FlexFields\n\nПакет [drf-flex-fields](https://github.com/rsinger86/drf-flex-fields) расширяет ModelSerializer и ModelViewSet для обеспечения широко используемой функциональности для динамической установки полей и расширения примитивных полей во вложенные модели, как из параметров URL, так и из определений класса вашего сериализатора.\n\n## Serializer Extensions\n\nПакет [django-rest-framework-serializer-extensions](https://github.com/evenicoulddoit/django-rest-framework-serializer-extensions) предоставляет набор инструментов для DRY up ваших сериализаторов, позволяя определять поля на основе каждого представления/запроса. Поля могут быть внесены в белый или черный список, а дочерние сериализаторы могут быть расширены по желанию.\n\n## HTML JSON Forms\n\nПакет [html-json-forms](https://github.com/wq/html-json-forms) предоставляет алгоритм и сериализатор для обработки `<form>` в соответствии с (неактивной) [спецификацией HTML JSON Form](https://www.w3.org/TR/html-json-forms/). Сериализатор облегчает обработку произвольно вложенных структур JSON в HTML. Например, `<input name=\"items[0][id]\" value=\"5\">` будет интерпретирован как `{\"items\": [{\"id\": \"5\"}]}`.\n\n## DRF-Base64\n\n[DRF-Base64](https://bitbucket.org/levit_scs/drf_base64) предоставляет набор сериализаторов полей и моделей, который обрабатывает загрузку файлов в base64-кодировке.\n\n## QueryFields\n\n[djangorestframework-queryfields](https://djangorestframework-queryfields.readthedocs.io/) позволяет клиентам API указать, какие поля будут отправлены в ответе с помощью параметров запроса включения/исключения.\n\n## DRF Writable Nested\n\nПакет [drf-writable-nested](https://github.com/beda-software/drf-writable-nested) предоставляет записываемый сериализатор вложенных моделей, который позволяет создавать/обновлять модели с вложенными связанными данными.\n\n## DRF Encrypt Content\n\nПакет [drf-encrypt-content](https://github.com/oguzhancelikarslan/drf-encrypt-content) помогает вам шифровать данные, сериализованные через `ModelSerializer`. Он также содержит некоторые вспомогательные функции. Это поможет вам зашифровать ваши данные.\n\n## Shapeless Serializers\n\nПакет [drf-shapeless-serializers](https://github.com/khaledsukkar2/drf-shapeless-serializers) предоставляет возможности динамической настройки сериализатора, позволяя выбирать поля во время выполнения, переименовывать их, изменять атрибуты и настраивать вложенные отношения без создания нескольких классов сериализатора. Это помогает устранить шаблонные коды сериализатора и обеспечивает гибкие ответы API.\n"
  },
  {
    "path": "api-guide/settings.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Настройки\n\n> Пространства имен - это отличная идея - давайте делать их больше!\n>\n> - [The Zen of Python](https://www.python.org/dev/peps/pep-0020/)\n\nКонфигурация для DRF находится в едином пространстве имен в настройках Django под названием `REST_FRAMEWORK`.\n\nНапример, файл `settings.py` вашего проекта может содержать что-то вроде этого:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_RENDERER_CLASSES': [\n        'rest_framework.renderers.JSONRenderer',\n    ],\n    'DEFAULT_PARSER_CLASSES': [\n        'rest_framework.parsers.JSONParser',\n    ]\n}\n```\n\n## Доступ к настройкам\n\nЕсли вам необходимо получить доступ к значениям настроек API DRF в вашем проекте, вам следует использовать объект `api_settings`. Например.\n\n```python\nfrom rest_framework.settings import api_settings\n\nprint(api_settings.DEFAULT_AUTHENTICATION_CLASSES)\n```\n\nОбъект `api_settings` будет проверять наличие любых пользовательских настроек и в противном случае возвращаться к значениям по умолчанию. Любая настройка, использующая строковые пути импорта для ссылки на класс, будет автоматически импортировать и возвращать класс, на который ссылается, вместо строкового литерала.\n\n---\n\n# API Reference\n\n## Настройки политики API\n\n*Следующие настройки управляют основными политиками API и применяются к каждому представлению `APIView` на основе класса или `@api_view` на основе функции.*\n\n#### DEFAULT_RENDERER_CLASSES\n\nСписок или кортеж классов рендереров, определяющий набор рендереров по умолчанию, которые могут быть использованы при возврате объекта `Response`.\n\nПо умолчанию:\n\n```python\n[\n    'rest_framework.renderers.JSONRenderer',\n    'rest_framework.renderers.BrowsableAPIRenderer',\n]\n```\n\n#### DEFAULT_PARSER_CLASSES\n\nСписок или кортеж классов парсеров, определяющий набор парсеров по умолчанию, используемых при обращении к свойству `request.data`.\n\nПо умолчанию:\n\n```python\n[\n    'rest_framework.parsers.JSONParser',\n    'rest_framework.parsers.FormParser',\n    'rest_framework.parsers.MultiPartParser'\n]\n```\n\n#### DEFAULT_AUTHENTICATION_CLASSES\n\nСписок или кортеж классов аутентификации, определяющий набор аутентификаторов по умолчанию, используемых при обращении к свойствам `request.user` или `request.auth`.\n\nПо умолчанию:\n\n```python\n[\n    'rest_framework.authentication.SessionAuthentication',\n    'rest_framework.authentication.BasicAuthentication'\n]\n```\n\n#### DEFAULT_PERMISSION_CLASSES\n\nСписок или кортеж классов разрешений, который определяет набор разрешений по умолчанию, проверяемых при запуске представления. Разрешение должно быть предоставлено каждым классом в списке.\n\nПо умолчанию:\n\n```python\n[\n    'rest_framework.permissions.AllowAny',\n]\n```\n\n#### DEFAULT_THROTTLE_CLASSES\n\nСписок или кортеж классов дросселей, который определяет набор дросселей по умолчанию, проверяемых при запуске представления.\n\nПо умолчанию: `[]`.\n\n#### DEFAULT_CONTENT_NEGOTIATION_CLASS\n\nКласс согласования содержимого, который определяет, как выбирается рендерер для ответа, учитывая входящий запрос.\n\nПо умолчанию: `'rest_framework.negotiation.DefaultContentNegotiation'`.\n\n#### DEFAULT_SCHEMA_CLASS\n\nКласс инспектора представлений, который будет использоваться для генерации схемы.\n\nПо умолчанию: `'rest_framework.schemas.openapi.AutoSchema'`.\n\n---\n\n## Общие настройки представления\n\n*Следующие настройки управляют поведением общих представлений на основе классов.*\n\n#### DEFAULT_FILTER_BACKENDS\n\nСписок классов бэкенда фильтра, которые должны использоваться для общей фильтрации. Если установлено значение `None`, то общая фильтрация отключена.\n\n#### DEFAULT_PAGINATION_CLASS\n\nКласс по умолчанию, используемый для пагинации наборов запросов. Если установлено значение `None`, пагинация по умолчанию отключена. Дополнительное руководство по [установке](pagination.md#установка-стиля-пагинации) и [изменению](pagination.md#изменение-стиля-пагинации) стиля пагинации см. в документации по пагинации.\n\nПо умолчанию: `None`\n\n#### PAGE_SIZE\n\nРазмер страницы по умолчанию, используемый для пагинации. Если установлено значение `None`, то по умолчанию пагинация отключена.\n\nПо умолчанию: `None`\n\n### SEARCH_PARAM\n\nИмя параметра запроса, который может быть использован для указания поискового термина, используемого `SearchFilter`.\n\nПо умолчанию: `search`.\n\n#### ORDERING_PARAM\n\nИмя параметра запроса, который может быть использован для указания упорядочения результатов, возвращаемых `OrderingFilter`.\n\nПо умолчанию: `ordering`.\n\n---\n\n## Настройки версий\n\n#### DEFAULT_VERSION\n\nЗначение, которое должно использоваться для `request.version`, когда информация о версиях отсутствует.\n\nПо умолчанию: `None`\n\n#### ALLOWED_VERSIONS\n\nЕсли задано, это значение ограничивает набор версий, которые могут быть возвращены схемой версий, и вызывает ошибку, если предоставленная версия не входит в этот набор.\n\nПо умолчанию: `None`\n\n#### VERSION_PARAM\n\nСтрока, которая должна использоваться для любых параметров версионирования, например, в типе медиа или параметрах запроса URL.\n\nПо умолчанию: `'version'`.\n\n#### DEFAULT_VERSIONING_CLASS\n\nСхема версионирования, используемая по умолчанию.\n\nПо умолчанию: `None`\n\n---\n\n## Настройки аутентификации\n\n*Следующие настройки управляют поведением неаутентифицированных запросов.*\n\n#### UNAUTHENTICATED_USER\n\nКласс, который должен использоваться для инициализации `request.user` для неаутентифицированных запросов. (Если аутентификация полностью удалена, например, путем удаления `django.contrib.auth` из `INSTALLED_APPS`, установите `UNAUTHENTICATED_USER` в `None`).\n\nПо умолчанию: `django.contrib.auth.models.AnonymousUser`.\n\n#### UNAUTHENTICATED_TOKEN\n\nКласс, который должен использоваться для инициализации `request.auth` для неаутентифицированных запросов.\n\nПо умолчанию: `Нет`\n\n---\n\n## Настройки тестов\n\n*Следующие настройки управляют поведением APIRequestFactory и APIClient.*\n\n#### TEST_REQUEST_DEFAULT_FORMAT\n\nФормат по умолчанию, который следует использовать при составлении тестовых запросов.\n\nОн должен совпадать с форматом одного из классов рендереров в настройке `TEST_REQUEST_RENDERER_CLASSES`.\n\nПо умолчанию: `'multipart'`.\n\n#### TEST_REQUEST_RENDERER_CLASSES\n\nКлассы рендереров, которые поддерживаются при построении тестовых запросов.\n\nФормат любого из этих классов рендереров может быть использован при построении тестового запроса, например: `client.post('/users', {'username': 'jamie'}, format='json')`.\n\nПо умолчанию:\n\n```python\n[\n    'rest_framework.renderers.MultiPartRenderer',\n    'rest_framework.renderers.JSONRenderer'\n]\n```\n\n---\n\n## Элементы управления генерацией схемы\n\n#### SCHEMA_COERCE_PATH_PK\n\nЕсли задано, то при генерации параметра пути к схеме идентификатор `'pk'` в URL conf сопоставляется с реальным именем поля. Обычно это `'id'`. Это дает более подходящее представление, поскольку \"первичный ключ\" - это деталь реализации, тогда как \"идентификатор\" - более общая концепция.\n\nПо умолчанию: `True`\n\n#### SCHEMA_COERCE_METHOD_NAMES\n\nЕсли установлено, это используется для сопоставления внутренних имен методов набора представлений с именами внешних действий, используемых при генерации схемы. Это позволяет нам генерировать имена, более подходящие для внешнего представления, чем те, которые используются внутри кодовой базы.\n\nПо умолчанию: `{'retrieve': 'read', 'destroy': 'delete'}`\n\n---\n\n## Контроль типа содержимого\n\n#### URL_FORMAT_OVERRIDE\n\nИмя параметра URL, который можно использовать для переопределения стандартного поведения заголовка согласования содержимого `Accept`, используя параметр запроса `format=...` в URL запроса.\n\nНапример: `http://example.com/organizations/?format=csv`\n\nЕсли значение этого параметра равно `None`, то переопределение формата URL будет отключено.\n\nПо умолчанию: `'format'`.\n\n#### FORMAT_SUFFIX_KWARG\n\nИмя параметра в URL conf, который может быть использован для обеспечения суффикса формата. Этот параметр применяется при использовании `format_suffix_patterns` для включения суффиксных шаблонов URL.\n\nНапример: `http://example.com/organizations.csv/`\n\nПо умолчанию: `'format'`.\n\n---\n\n## Форматирование даты и времени\n\n*Следующие параметры используются для управления тем, как представления даты и времени могут быть разобраны и отображены.*\n\n#### DATETIME_FORMAT\n\nСтрока формата, которая должна использоваться по умолчанию для вывода полей сериализатора `DateTimeField`. Если `None`, то поля сериализатора `DateTimeField` будут возвращать объекты Python `datetime`, а кодировка времени будет определяться рендерером.\n\nМожет быть любым из `None`, `'iso-8601'` или строкой Python [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes).\n\nПо умолчанию: `'iso-8601'`.\n\n#### DATETIME_INPUT_FORMATS\n\nСписок форматных строк, которые должны использоваться по умолчанию при разборе входных данных для полей сериализатора `DateTimeField`.\n\nМожет быть списком, включающим строку `'iso-8601'` или строки Python [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes).\n\nПо умолчанию: `['iso-8601']`.\n\n#### DATE_FORMAT\n\nСтрока формата, которая должна использоваться по умолчанию для вывода полей сериализатора `DateField`. Если `None`, то поля сериализатора `DateField` будут возвращать объекты Python `date`, а кодировка даты будет определяться рендерером.\n\nМожет быть любым из `None`, `'iso-8601'` или строкой Python [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes).\n\nПо умолчанию: `'iso-8601'`.\n\n#### DATE_INPUT_FORMATS\n\nСписок форматных строк, которые должны использоваться по умолчанию при разборе входных данных для полей сериализатора `DateField`.\n\nМожет быть списком, включающим строку `'iso-8601'` или строки Python [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes).\n\nПо умолчанию: `['iso-8601']`.\n\n#### TIME_FORMAT\n\nСтрока формата, которая должна использоваться по умолчанию для вывода полей сериализатора `TimeField`. Если `None`, то поля сериализатора `TimeField` будут возвращать объекты Python `time`, а кодировка времени будет определяться рендерером.\n\nМожет быть любым из `None`, `'iso-8601'` или строкой Python [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes).\n\nПо умолчанию: `'iso-8601'`.\n\n#### TIME_INPUT_FORMATS\n\nСписок форматных строк, которые должны использоваться по умолчанию при разборе входных данных для полей сериализатора `TimeField`.\n\nМожет быть списком, включающим строку `'iso-8601'` или строки Python [strftime format](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes).\n\nПо умолчанию: `['iso-8601']`.\n\n\n#### DURATION_FORMAT\n\nУказывает формат по умолчанию, который следует использовать для рендеринга вывода полей сериализатора `DurationField`.  Если `None`, то поля сериализатора `DurationField` будут возвращать объекты Python `timedelta`, а кодировка продолжительности будет определяться рендерером.\n\nМожет быть любым из `None`, `'iso-8601'` или `'django'` (формат, принимаемый `django.utils.dateparse.parse_duration`).\n\nПо умолчанию: `'django'`\n\n---\n\n## Кодировки\n\n#### UNICODE_JSON\n\nЕсли установлено значение `True`, ответы JSON будут разрешать использование символов юникода в ответах. Например:\n\n```python\n{\"unicode black star\":\"★\"}\n```\n\nЕсли установлено значение `False`, в ответах JSON будут экранироваться неасксиальные символы, как показано ниже:\n\n```python\n{\"unicode black star\":\"\\u2605\"}\n```\n\nОба стиля соответствуют [RFC 4627](https://www.ietf.org/rfc/rfc4627.txt) и являются синтаксически правильным JSON. Стиль unicode предпочтительнее, так как он более удобен при проверке ответов API.\n\nПо умолчанию: `True`\n\n#### COMPACT_JSON\n\nЕсли установлено значение `True`, ответы JSON будут возвращать компактные представления, без пробелов после символов `':'` и `','`. Например:\n\n```python\n{\"is_admin\":false,\"email\":\"jane@example\"}\n```\n\nЕсли установлено значение `False`, ответы JSON будут возвращать более подробное представление, как показано ниже:\n\n```python\n{\"is_admin\": false, \"email\": \"jane@example\"}\n```\n\nПо умолчанию возвращаются минифицированные ответы, в соответствии с [Heroku's API design guidelines](https://github.com/interagent/http-api-design#keep-json-minified-in-all-responses).\n\nПо умолчанию: `True`\n\n#### STRICT_JSON\n\nЕсли установлено значение `True`, при рендеринге и разборе JSON будет использоваться только синтаксически правильный JSON, создавая исключение для расширенных значений float (`nan`, `inf`, `inf`), принимаемых модулем Python `json`. Это рекомендуемая настройка, так как эти значения обычно не поддерживаются. Например, ни Javascript `JSON.Parse`, ни PostgreSQL тип данных JSON не принимают эти значения.\n\nЕсли установлено значение `False`, рендеринг и парсинг JSON будут разрешительными. Однако эти значения все еще недействительны и должны быть специально обработаны в вашем коде.\n\nПо умолчанию: `True`\n\n#### COERCE_DECIMAL_TO_STRING\n\nПри возврате десятичных объектов в представлениях API, которые не поддерживают собственный десятичный тип, обычно лучше всего возвращать значение в виде строки. Это позволяет избежать потери точности, которая происходит при двоичной реализации с плавающей запятой.\n\nЕсли установлено значение `True`, сериализатор класса `DecimalField` будет возвращать строки вместо объектов `Decimal`. Если установлено значение `False`, сериализаторы будут возвращать объекты `Decimal`, которые кодировщик JSON по умолчанию будет возвращать как float.\n\nПо умолчанию: `True`\n\n#### COERCE_BIGINT_TO_STRING\n\nПри возвращении объектов biginteger в представлениях API, которые не поддерживают числа до 2^64, лучше всего возвращать значение в виде строки. Это позволяет избежать потери точности, которая происходит при реализации biginteger.\n\nПри установке в `True`, сериализатор класса `BigIntegerField` (по умолчанию) будет возвращать строки вместо объектов `BigInteger`. При установке в `False`, сериализаторы будут возвращать объекты `BigInteger`, которые кодировщик JSON по умолчанию будет возвращать в виде чисел.\n\nПо умолчанию: `False`\n\n---\n\n## Названия и описания представлений\n\n*Следующие настройки используются для создания названий и описаний представлений, которые используются в ответах на запросы `OPTIONS` и в API для просмотра.*\n\n#### VIEW_NAME_FUNCTION\n\nСтрока, представляющая функцию, которая должна использоваться при генерации имен представлений.\n\nЭто должна быть функция со следующей сигнатурой:\n\n```python\nview_name(self)\n```\n\n* `self`: Экземпляр представления. Обычно функция name проверяет имя класса при генерации описательного имени, обращаясь к `self.__class__.__name__`.\n\nЕсли экземпляр представления наследует `ViewSet`, он может быть инициализирован с несколькими необязательными аргументами:\n\n* `name`: Имя, явно предоставленное представлению в наборе представлений. Обычно это значение должно использоваться как есть, если оно предоставлено.\n* `suffix`: Текст, используемый для различения отдельных представлений в наборе представлений. Этот аргумент является взаимоисключающим с `name`.\n* `detail`: Булево значение, отличающее индивидуальное представление в наборе представлений как \"список\" или \"подробное представление\".\n\nПо умолчанию: `'rest_framework.views.get_view_name'`.\n\n#### VIEW_DESCRIPTION_FUNCTION\n\nСтрока, представляющая функцию, которая должна использоваться при генерации описаний представлений.\n\nЭтот параметр может быть изменен для поддержки стилей разметки, отличных от стандартного markdown. Например, вы можете использовать его для поддержки разметки `rst` в ваших документах представления, выводимых в Web-интерфейсе API.\n\nЭто должна быть функция со следующей сигнатурой:\n\n```python\nview_description(self, html=False)\n```\n\n* `self`: Экземпляр представления. Обычно функция описания проверяет строку документа класса при генерации описания, обращаясь к `self.__class__.__doc__`.\n* `html`: Булево значение, указывающее, требуется ли вывод HTML. `True` используется в API для просмотра, а `False` - при генерации ответов `OPTIONS`.\n\nЕсли экземпляр представления наследует `ViewSet`, он может быть инициализирован с несколькими необязательными аргументами:\n\n* `description`: Описание, явно предоставленное представлению в наборе представлений. Обычно оно устанавливается дополнительными `действиями` набора представлений и должно использоваться как есть.\n\nПо умолчанию: `'rest_framework.views.get_view_description'`.\n\n## HTML Select Field cutoffs\n\nГлобальные настройки для [выбора отсечений полей для визуализации реляционных полей](relations.md#выберите-отсечение-полей) в Web-интерфейсе API.\n\n#### HTML_SELECT_CUTOFF\n\nГлобальная настройка для значения `html_cutoff`. Должно быть целое число.\n\nПо умолчанию: `1000`\n\n#### HTML_SELECT_CUTOFF_TEXT\n\nСтрока, представляющая глобальную настройку для `html_cutoff_text`.\n\nПо умолчанию: `'More than {count} items...'`.\n\n---\n\n## Разные настройки\n\n#### EXCEPTION_HANDLER\n\nСтрока, представляющая функцию, которая должна быть использована при возврате ответа для любого данного исключения. Если функция возвращает `None`, будет выдана ошибка 500.\n\nЭтот параметр может быть изменен для поддержки ответов на ошибки, отличных от ответов по умолчанию `{\"detail\": \"Сбой...\"}}` ответов. Например, вы можете использовать его для предоставления ответов API типа `{\"errors\": [{\"message\": \"Failure...\", \"code\": \"\"} ...]}`.\n\nЭто должна быть функция со следующей сигнатурой:\n\n```python\nexception_handler(exc, context)\n```\n\n* `exc`: Исключение.\n\nПо умолчанию: `'rest_framework.views.exception_handler'`.\n\n#### NON_FIELD_ERRORS_KEY\n\nСтрока, представляющая ключ, который следует использовать для ошибок сериализатора, которые не относятся к конкретному полю, а являются общими ошибками.\n\nПо умолчанию: `'non_field_errors'`.\n\n#### URL_FIELD_NAME\n\nСтрока, представляющая ключ, который должен использоваться для полей URL, генерируемых `HyperlinkedModelSerializer`.\n\nПо умолчанию: `'url'`\n\n#### NUM_PROXIES\n\nЦелое число, равное 0 или более, которое может использоваться для указания количества прокси-серверов приложений, за которыми работает API. Это позволяет дросселированию более точно определять IP-адреса клиентов. Если установлено значение `None`, то классы дросселирования будут использовать менее строгое сопоставление IP-адресов.\n\nПо умолчанию: `None`\n"
  },
  {
    "path": "api-guide/status-codes.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Коды состояния\n\n> 418 Я чайник - Любая попытка сварить кофе с помощью чайника должна привести к коду ошибки \"418 I'm a teapot\". Результирующее тело сущности МОЖЕТ быть коротким и крепким.\n>\n> - [RFC 2324](https://www.ietf.org/rfc/rfc2324.txt), Hyper Text Coffee Pot Control Protocol\n\nНе рекомендуется использовать в ответах \"голые\" коды состояния. DRF включает набор именованных констант, которые вы можете использовать, чтобы сделать ваш код более очевидным и читаемым.\n\n```python\nfrom rest_framework import status\nfrom rest_framework.response import Response\n\ndef empty_view(self):\n    content = {'please move along': 'nothing to see here'}\n    return Response(content, status=status.HTTP_404_NOT_FOUND)\n```\n\nПолный набор кодов состояния HTTP, включенных в модуль `status`, приведен ниже.\n\nМодуль также включает набор вспомогательных функций для проверки того, находится ли код состояния в заданном диапазоне.\n\n```python\nfrom rest_framework import status\nfrom rest_framework.test import APITestCase\n\nclass ExampleTestCase(APITestCase):\n    def test_url_root(self):\n        url = reverse('index')\n        response = self.client.get(url)\n        self.assertTrue(status.is_success(response.status_code))\n```\n\nБолее подробную информацию о правильном использовании кодов состояния HTTP смотрите в [RFC 2616](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) и [RFC 6585](https://tools.ietf.org/html/rfc6585).\n\n## Информационный - 1xx\n\nЭтот класс кода состояния указывает на предварительный ответ. По умолчанию в DRF не используются коды состояния 1xx.\n\n```python\nHTTP_100_CONTINUE\nHTTP_101_SWITCHING_PROTOCOLS\nHTTP_102_PROCESSING\nHTTP_103_EARLY_HINTS\n```\n\n## Успешно - 2xx\n\nЭтот класс кода состояния указывает на то, что запрос клиента был успешно получен, понят и принят.\n\n```python\nHTTP_200_OK\nHTTP_201_CREATED\nHTTP_202_ACCEPTED\nHTTP_203_NON_AUTHORITATIVE_INFORMATION\nHTTP_204_NO_CONTENT\nHTTP_205_RESET_CONTENT\nHTTP_206_PARTIAL_CONTENT\nHTTP_207_MULTI_STATUS\nHTTP_208_ALREADY_REPORTED\nHTTP_226_IM_USED\n```\n\n## Перенаправление - 3xx\n\nЭтот класс кода состояния указывает на то, что агенту пользователя необходимо предпринять дополнительные действия для выполнения запроса.\n\n```python\nHTTP_300_MULTIPLE_CHOICES\nHTTP_301_MOVED_PERMANENTLY\nHTTP_302_FOUND\nHTTP_303_SEE_OTHER\nHTTP_304_NOT_MODIFIED\nHTTP_305_USE_PROXY\nHTTP_306_RESERVED\nHTTP_307_TEMPORARY_REDIRECT\nHTTP_308_PERMANENT_REDIRECT\n```\n\n## Ошибка клиента - 4xx\n\nКод состояния класса 4xx предназначен для случаев, когда клиент, похоже, ошибся. За исключением ответа на запрос HEAD, сервер ДОЛЖЕН включать объект, содержащий объяснение ситуации с ошибкой, а также то, является ли она временной или постоянной.\n\n```python\nHTTP_400_BAD_REQUEST\nHTTP_401_UNAUTHORIZED\nHTTP_402_PAYMENT_REQUIRED\nHTTP_403_FORBIDDEN\nHTTP_404_NOT_FOUND\nHTTP_405_METHOD_NOT_ALLOWED\nHTTP_406_NOT_ACCEPTABLE\nHTTP_407_PROXY_AUTHENTICATION_REQUIRED\nHTTP_408_REQUEST_TIMEOUT\nHTTP_409_CONFLICT\nHTTP_410_GONE\nHTTP_411_LENGTH_REQUIRED\nHTTP_412_PRECONDITION_FAILED\nHTTP_413_REQUEST_ENTITY_TOO_LARGE\nHTTP_414_REQUEST_URI_TOO_LONG\nHTTP_415_UNSUPPORTED_MEDIA_TYPE\nHTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE\nHTTP_417_EXPECTATION_FAILED\nHTTP_421_MISDIRECTED_REQUEST\nHTTP_422_UNPROCESSABLE_ENTITY\nHTTP_423_LOCKED\nHTTP_424_FAILED_DEPENDENCY\nHTTP_425_TOO_EARLY\nHTTP_426_UPGRADE_REQUIRED\nHTTP_428_PRECONDITION_REQUIRED\nHTTP_429_TOO_MANY_REQUESTS\nHTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE\nHTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS\n```\n\n## Ошибка сервера - 5xx\n\nКоды состояния ответа, начинающиеся с цифры \"5\", указывают на случаи, когда сервер знает, что он ошибся или не в состоянии выполнить запрос. За исключением ответа на запрос HEAD, сервер ДОЛЖЕН включать объект, содержащий объяснение ситуации с ошибкой, а также то, является ли она временной или постоянной.\n\n```python\nHTTP_500_INTERNAL_SERVER_ERROR\nHTTP_501_NOT_IMPLEMENTED\nHTTP_502_BAD_GATEWAY\nHTTP_503_SERVICE_UNAVAILABLE\nHTTP_504_GATEWAY_TIMEOUT\nHTTP_505_HTTP_VERSION_NOT_SUPPORTED\nHTTP_506_VARIANT_ALSO_NEGOTIATES\nHTTP_507_INSUFFICIENT_STORAGE\nHTTP_508_LOOP_DETECTED\nHTTP_509_BANDWIDTH_LIMIT_EXCEEDED\nHTTP_510_NOT_EXTENDED\nHTTP_511_NETWORK_AUTHENTICATION_REQUIRED\n```\n\n## Вспомогательные функции\n\nДля определения категории кода ответа доступны следующие вспомогательные функции.\n\n```python\nis_informational()  # 1xx\nis_success()        # 2xx\nis_redirect()       # 3xx\nis_client_error()   # 4xx\nis_server_error()   # 5xx\n```\n"
  },
  {
    "path": "api-guide/testing.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Тестирование\n\n> Код без тестов сломан по умолчанию.\n>\n> - [Джейкоб Каплан-Мосс](https://jacobian.org/writing/django-apps-with-buildout/#s-create-a-test-wrapper)\n\nDRF включает несколько вспомогательных классов, которые расширяют существующую тестовую структуру Django и улучшают поддержку выполнения API-запросов.\n\n# APIRequestFactory\n\nРасширяет [существующий в Django класс `RequestFactory`](https://docs.djangoproject.com/en/stable/topics/testing/advanced/#django.test.client.RequestFactory).\n\n## Создание тестовых запросов\n\nКласс `APIRequestFactory` поддерживает почти такой же API, как и стандартный класс Django `RequestFactory`. Это означает, что все стандартные методы `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()` и `.options()` доступны.\n\n```python\nfrom rest_framework.test import APIRequestFactory\n\n# Using the standard RequestFactory API to create a form POST request\nfactory = APIRequestFactory()\nrequest = factory.post('/notes/', {'title': 'new idea'})\n\n# Using the standard RequestFactory API to encode JSON data\nrequest = factory.post('/notes/', {'title': 'new idea'}, content_type='application/json')\n```\n\n#### Использование аргумента `format`\n\nМетоды, создающие тело запроса, такие как `post`, `put` и `patch`, включают аргумент `format`, который позволяет легко генерировать запросы с использованием широкого набора форматов запросов.  При использовании этого аргумента фабрика выберет соответствующий рендерер и его сконфигурированный `content_type`.  Например:\n\n```python\n# Create a JSON POST request\nfactory = APIRequestFactory()\nrequest = factory.post('/notes/', {'title': 'new idea'}, format='json')\n```\n\nПо умолчанию доступны форматы `'multipart'` и `'json'`. Для совместимости с существующей в Django `RequestFactory` по умолчанию используется формат `'multipart'`.\n\nЧтобы поддерживать более широкий набор форматов запросов или изменить формат по умолчанию, [см. раздел конфигурации](#Конфигурация).\n\n#### Явное кодирование тела запроса\n\nЕсли вам нужно явно закодировать тело запроса, вы можете сделать это, установив флаг `content_type`. Например:\n\n```python\nrequest = factory.post('/notes/', yaml.dump({'title': 'new idea'}), content_type='application/yaml')\n```\n\n#### PUT и PATCH с данными формы\n\nСтоит отметить одно отличие между `RequestFactory` Django и `APIRequestFactory` DRF в том, что данные многочастной формы будут закодированы для методов, отличных от `.post()`.\n\nНапример, используя `APIRequestFactory`, вы можете сделать запрос формы PUT следующим образом:\n\n```python\nfactory = APIRequestFactory()\nrequest = factory.put('/notes/547/', {'title': 'remember to email dave'})\n```\n\nИспользуя `RequestFactory` от Django, вам придется явно кодировать данные самостоятельно:\n\n```python\nfrom django.test.client import encode_multipart, RequestFactory\n\nfactory = RequestFactory()\ndata = {'title': 'remember to email dave'}\ncontent = encode_multipart('BoUnDaRyStRiNg', data)\ncontent_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg'\nrequest = factory.put('/notes/547/', content, content_type=content_type)\n```\n\n## Принудительная аутентификация\n\nПри тестировании представлений непосредственно с помощью фабрики запросов часто бывает удобно иметь возможность напрямую аутентифицировать запрос, а не создавать правильные учетные данные для аутентификации.\n\nЧтобы принудительно аутентифицировать запрос, используйте метод `force_authenticate()`.\n\n```python\nfrom rest_framework.test import force_authenticate\n\nfactory = APIRequestFactory()\nuser = User.objects.get(username='olivia')\nview = AccountDetail.as_view()\n\n# Make an authenticated request to the view...\nrequest = factory.get('/accounts/django-superstars/')\nforce_authenticate(request, user=user)\nresponse = view(request)\n```\n\nЕсли вы хотите протестировать запрос, связанный с объектом «Request» фреймворка REST, вам необходимо сначала вручную преобразовать его:\n\n```python\nclass DummyView(APIView):\n    ...\n\nfactory = APIRequestFactory()\nrequest = factory.get('/', {'demo': 'test'})\ndrf_request = DummyView().initialize_request(request)\nassert drf_request.query_params == {'demo': ['test']}\n\nrequest = factory.post('/', {'example': 'test'})\ndrf_request = DummyView().initialize_request(request)\nassert drf_request.data.get('example') == 'test'\n\n```\n\nСигнатура для метода - `force_authenticate(request, user=None, token=None)`. При выполнении вызова может быть задан пользователь и токен или оба.\n\nНапример, при принудительной аутентификации с помощью токена вы можете сделать что-то вроде следующего:\n\n```python\nuser = User.objects.get(username='olivia')\nrequest = factory.get('/accounts/django-superstars/')\nforce_authenticate(request, user=user, token=user.auth_token)\n```\n\n---\n\n**Примечание**: `force_authenticate` напрямую устанавливает `request.user` в экземпляр `user` в памяти. Если вы повторно используете один и тот же экземпляр `user` в нескольких тестах, которые обновляют сохраненное состояние `user`, вам может понадобиться вызывать [`refresh_from_db()`](https://docs.djangoproject.com/en/stable/ref/models/instances/#django.db.models.Model.refresh_from_db) между тестами.\n\n---\n\n**Примечание**: При использовании `APIRequestFactory`, возвращаемый объект - это стандартный `HttpRequest` Django, а не объект `Request` DRF, который генерируется только после вызова представления.\n\nЭто означает, что установка атрибутов непосредственно на объект запроса не всегда может иметь ожидаемый эффект. Например, установка `.token` напрямую не будет иметь никакого эффекта, а установка `.user` напрямую будет работать только при использовании сеансовой аутентификации.\n\n```python\n# Request will only authenticate if `SessionAuthentication` is in use.\nrequest = factory.get('/accounts/django-superstars/')\nrequest.user = user\nresponse = view(request)\n```\n\n---\n\n## Принудительная проверка CSRF\n\nПо умолчанию запросы, созданные с помощью `APIRequestFactory`, не будут проходить проверку CSRF при передаче в представление DRF. Если вам необходимо явно включить проверку CSRF, вы можете сделать это, установив флаг `enforce_csrf_checks` при инстанцировании фабрики.\n\n```python\nfactory = APIRequestFactory(enforce_csrf_checks=True)\n```\n\n---\n\n**Примечание**: Стоит отметить, что стандартная фабрика запросов Django `RequestFactory` не должна включать эту опцию, потому что при использовании обычного Django проверка CSRF происходит в промежуточном ПО, которое не запускается при тестировании представлений напрямую. При использовании DRF проверка CSRF происходит внутри представления, поэтому в фабрике запросов необходимо отключить проверку CSRF на уровне представления.\n\n---\n\n# APIClient\n\nРасширяет [существующий в Django класс `Client`](https://docs.djangoproject.com/en/stable/topics/testing/tools/#the-test-client).\n\n## Выполнение запросов\n\nКласс `APIClient` поддерживает тот же интерфейс запросов, что и стандартный класс Django `Client`. Это означает, что стандартные методы `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`, `.head()` и `.options()` доступны. Например:\n\n```python\nfrom rest_framework.test import APIClient\n\nclient = APIClient()\nclient.post('/notes/', {'title': 'new idea'}, format='json')\n```\n\nЧтобы поддерживать более широкий набор форматов запросов или изменить формат по умолчанию, [см. раздел конфигурации](#Конфигурация).\n\n## Аутентификация\n\n#### .login(**kwargs)\n\nМетод `login` функционирует точно так же, как и в обычном классе Django `Client`. Это позволяет вам аутентифицировать запросы к любым представлениям, которые включают `SessionAuthentication`.\n\n```python\n# Make all requests in the context of a logged in session.\nclient = APIClient()\nclient.login(username='lauren', password='secret')\n```\n\nЧтобы выйти из системы, вызовите метод `logout`, как обычно.\n\n```python\n# Log out\nclient.logout()\n```\n\nМетод `login` подходит для тестирования API, использующих сеансовую аутентификацию, например, веб-сайтов, включающих AJAX-взаимодействие с API.\n\n#### .credentials(**kwargs)\n\nМетод `credentials` можно использовать для установки заголовков, которые затем будут включены во все последующие запросы тестового клиента.\n\n```python\nfrom rest_framework.authtoken.models import Token\nfrom rest_framework.test import APIClient\n\n# Include an appropriate `Authorization:` header on all requests.\ntoken = Token.objects.get(user__username='lauren')\nclient = APIClient()\nclient.credentials(HTTP_AUTHORIZATION='Token ' + token.key)\n```\n\nОбратите внимание, что вызов `credentials` во второй раз перезаписывает все существующие учетные данные. Вы можете удалить все существующие учетные данные, вызвав метод без аргументов.\n\n```python\n# Stop including any credentials\nclient.credentials()\n```\n\nМетод `credentials` подходит для тестирования API, требующих заголовков аутентификации, таких как базовая аутентификация, аутентификация OAuth1a и OAuth2, а также простые схемы аутентификации токенов.\n\n#### .force_authenticate(user=None, token=None)\n\nИногда вы можете захотеть полностью обойти аутентификацию и заставить все запросы тестового клиента автоматически рассматриваться как аутентифицированные.\n\nЭто может быть полезным сокращением, если вы тестируете API, но не хотите создавать действительные учетные данные аутентификации для выполнения тестовых запросов.\n\n```python\nuser = User.objects.get(username='lauren')\nclient = APIClient()\nclient.force_authenticate(user=user)\n```\n\nЧтобы не аутентифицировать последующие запросы, вызовите `force_authenticate`, установив для пользователя и/или токена значение `None`.\n\n```python\nclient.force_authenticate(user=None)\n```\n\n## Проверка CSRF\n\nПо умолчанию проверка CSRF не применяется при использовании `APIClient`. Если вам необходимо явно включить проверку CSRF, вы можете сделать это, установив флаг `enforce_csrf_checks` при инстанцировании клиента.\n\n```python\nclient = APIClient(enforce_csrf_checks=True)\n```\n\nОбычно, проверка CSRF будет применяться только к любым аутентифицированным в сеансе представлениям. Это означает, что проверка CSRF будет происходить только в том случае, если клиент вошел в систему, вызвав `login()`.\n\n---\n\n# RequestsClient\n\nDRF также включает клиент для взаимодействия с вашим приложением с помощью популярной библиотеки Python, `requests`. Это может быть полезно, если:\n\n* Вы предполагаете взаимодействовать с API в основном из другого сервиса Python и хотите протестировать сервис на том же уровне, который будет видеть клиент.\n* Вы хотите написать тесты таким образом, чтобы их можно было запускать в среде постановки или в реальном времени. (См. раздел \"Живые тесты\" ниже).\n\nЭто предоставляет точно такой же интерфейс, как если бы вы использовали сессию запросов напрямую.\n\n```python\nfrom rest_framework.test import RequestsClient\n\nclient = RequestsClient()\nresponse = client.get('http://testserver/users/')\nassert response.status_code == 200\n```\n\nОбратите внимание, что клиент запросов требует передачи полностью определенных URL-адресов.\n\n## RequestsClient и работа с базой данных\n\nКласс `RequestsClient` полезен, если вы хотите написать тесты, которые взаимодействуют только с интерфейсом сервиса. Это немного строже, чем использование стандартного тестового клиента Django, поскольку это означает, что все взаимодействия должны осуществляться через API.\n\nЕсли вы используете `RequestsClient`, вам нужно убедиться, что установка тестов и утверждения результатов выполняются как обычные вызовы API, а не взаимодействуют с моделями базы данных напрямую. Например, вместо того чтобы проверять, что `Customer.objects.count() == 3`, вы должны перечислить конечную точку `customers` и убедиться, что она содержит три записи.\n\n## Заголовки и аутентификация\n\nПользовательские заголовки и учетные данные аутентификации могут быть предоставлены так же, как и [при использовании стандартного экземпляра `requests.Session`](https://requests.readthedocs.io/en/latest/user/advanced/#session-objects).\n\n```python\nfrom requests.auth import HTTPBasicAuth\n\nclient.auth = HTTPBasicAuth('user', 'pass')\nclient.headers.update({'x-test': 'true'})\n```\n\n## CSRF\n\nЕсли вы используете `SessionAuthentication`, то вам необходимо включить CSRF-токен для любых запросов `POST`, `PUT`, `PATCH` или `DELETE`.\n\nВы можете сделать это, следуя той же схеме, которую использует клиент на базе JavaScript. Сначала сделайте запрос `GET`, чтобы получить маркер CSRF, а затем цкажите этот токен в следующем запросе.\n\nНапример...\n\n```python\nclient = RequestsClient()\n\n# Obtain a CSRF token.\nresponse = client.get('http://testserver/homepage/')\nassert response.status_code == 200\ncsrftoken = response.cookies['csrftoken']\n\n# Interact with the API.\nresponse = client.post('http://testserver/organizations/', json={\n    'name': 'MegaCorp',\n    'status': 'active'\n}, headers={'X-CSRFToken': csrftoken})\nassert response.status_code == 200\n```\n\n## Живые тесты\n\nПри тщательном использовании и `RequestsClient`, и `CoreAPIClient` дают возможность писать тесты, которые можно запускать как в процессе разработки, так и непосредственно на build сервере или в production среде.\n\nИспользование этого стиля для создания базовых тестов нескольких основных частей функциональности является мощным способом проверки вашего живого сервиса. Это может потребовать некоторого внимания к `setup` и `teardown`, чтобы убедиться, что тесты выполняются таким образом, что они не влияют непосредственно на данные клиентов.\n\n---\n\n# CoreAPIClient\n\n`CoreAPIClient` позволяет вам взаимодействовать с API с помощью клиентской библиотеки Python `coreapi`.\n\n```python\n# Fetch the API schema\nclient = CoreAPIClient()\nschema = client.get('http://testserver/schema/')\n\n# Create a new organization\nparams = {'name': 'MegaCorp', 'status': 'active'}\nclient.action(schema, ['organizations', 'create'], params)\n\n# Ensure that the organization exists in the listing\ndata = client.action(schema, ['organizations', 'list'])\nassert(len(data) == 1)\nassert(data == [{'name': 'MegaCorp', 'status': 'active'}])\n```\n\n## Заголовки и аутентификация\n\nПользовательские заголовки и аутентификация могут использоваться с `CoreAPIClient` так же, как и с `RequestsClient`.\n\n```python\nfrom requests.auth import HTTPBasicAuth\n\nclient = CoreAPIClient()\nclient.session.auth = HTTPBasicAuth('user', 'pass')\nclient.session.headers.update({'x-test': 'true'})\n```\n\n---\n\n# Тесты API\n\nDRF включает следующие классы тестов, которые являются зеркальным отражением существующих [Django's test case classes](https://docs.djangoproject.com/en/stable/topics/testing/tools/#provided-test-case-classes), но используют `APIClient` вместо Django's default `Client`.\n\n* `APISimpleTestCase`.\n* `APITransactionTestCase`\n* `APITestCase`\n* `APILiveServerTestCase`\n\n## Пример\n\nВы можете использовать любой из классов тестов DRF так же, как и обычные классы тестов Django. Атрибутом `self.client` будет экземпляр `APIClient`.\n\n```python\nfrom django.urls import reverse\nfrom rest_framework import status\nfrom rest_framework.test import APITestCase\nfrom myproject.apps.core.models import Account\n\nclass AccountTests(APITestCase):\n    def test_create_account(self):\n        \"\"\"\n        Ensure we can create a new account object.\n        \"\"\"\n        url = reverse('account-list')\n        data = {'name': 'DabApps'}\n        response = self.client.post(url, data, format='json')\n        self.assertEqual(response.status_code, status.HTTP_201_CREATED)\n        self.assertEqual(Account.objects.count(), 1)\n        self.assertEqual(Account.objects.get().name, 'DabApps')\n```\n\n---\n\n# URLPatternsTestCase\n\nDRF также предоставляет класс тестов для изоляции `urlpatterns` на основе каждого класса. Обратите внимание, что он наследуется от Django `SimpleTestCase`, и, скорее всего, его придется смешивать с другим классом тестов.\n\n## Пример\n\n```python\nfrom django.urls import include, path, reverse\nfrom rest_framework import status\nfrom rest_framework.test import APITestCase, URLPatternsTestCase\n\n\nclass AccountTests(APITestCase, URLPatternsTestCase):\n    urlpatterns = [\n        path('api/', include('api.urls')),\n    ]\n\n    def test_create_account(self):\n        \"\"\"\n        Ensure we can create a new account object.\n        \"\"\"\n        url = reverse('account-list')\n        response = self.client.get(url, format='json')\n        self.assertEqual(response.status_code, status.HTTP_200_OK)\n        self.assertEqual(len(response.data), 1)\n```\n\n---\n\n# Тестирование ответов\n\n## Проверка данных ответа\n\nПри проверке достоверности тестовых ответов часто удобнее проверять данные, на основе которых был создан ответ, чем проверять полностью отрисованный ответ.\n\nНапример, проще проверить `response.data`:\n\n```python\nresponse = self.client.get('/users/4/')\nself.assertEqual(response.data, {'id': 4, 'username': 'lauren'})\n```\n\nВместо того чтобы проверять результат разбора `response.content`:\n\n```python\nresponse = self.client.get('/users/4/')\nself.assertEqual(json.loads(response.content), {'id': 4, 'username': 'lauren'})\n```\n\n## Ответы на рендеринг\n\nЕсли вы тестируете представления напрямую, используя `APIRequestFactory`, возвращаемые ответы еще не будут отрендерены, поскольку рендеринг ответов шаблона выполняется внутренним циклом запроса-ответа Django. Чтобы получить доступ к `response.content`, вам сначала нужно отрендерить ответ.\n\n```python\nview = UserDetail.as_view()\nrequest = factory.get('/users/4')\nresponse = view(request, pk='4')\nresponse.render()  # Cannot access `response.content` without this.\nself.assertEqual(response.content, '{\"username\": \"lauren\", \"id\": 4}')\n```\n\n---\n\n# Конфигурация\n\n## Установка формата по умолчанию\n\nФормат по умолчанию, используемый для выполнения тестовых запросов, можно установить с помощью ключа настройки `TEST_REQUEST_DEFAULT_FORMAT`. Например, чтобы всегда использовать JSON для тестовых запросов по умолчанию вместо стандартных multipart form запросов, установите следующее в файле `settings.py`:\n\n```python\nREST_FRAMEWORK = {\n    ...\n    'TEST_REQUEST_DEFAULT_FORMAT': 'json'\n}\n```\n\n## Установка доступных форматов\n\nЕсли вам нужно протестировать запросы, использующие не многочастичные или json-запросы, вы можете сделать это, установив параметр `TEST_REQUEST_RENDERER_CLASSES`.\n\nНапример, чтобы добавить поддержку использования `format='html'` в тестовых запросах, в файле `settings.py` можно сделать что-то вроде этого.\n\n```python\nREST_FRAMEWORK = {\n    ...\n    'TEST_REQUEST_RENDERER_CLASSES': [\n        'rest_framework.renderers.MultiPartRenderer',\n        'rest_framework.renderers.JSONRenderer',\n        'rest_framework.renderers.TemplateHTMLRenderer'\n    ]\n}\n```\n"
  },
  {
    "path": "api-guide/throttling.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Дросселирование\n\n> HTTP/1.1 420 Повышение спокойствия\n>\n> [Twitter API ограничение скорости ответа](https://developer.twitter.com/en/docs/basics/rate-limiting)\n\nДросселирование аналогично [permissions](permissions.md), поскольку оно определяет, должен ли запрос быть авторизован. Дроссели обозначают временное состояние и используются для контроля скорости запросов, которые клиенты могут делать к API.\n\nКак и в случае с разрешениями, можно использовать несколько дросселей. Ваш API может иметь ограничительный дроссель для неаутентифицированных запросов и менее ограничительный дроссель для аутентифицированных запросов.\n\nЕще один сценарий, в котором вам может понадобиться использовать несколько дросселей, - это если вам нужно наложить различные ограничения на разные части API, поскольку некоторые сервисы являются особенно ресурсоемкими.\n\nНесколько дросселей также можно использовать, если вы хотите наложить дросселирование как на скорость разрыва, так и на скорость устойчивого дросселирования. Например, вы можете ограничить пользователя максимум 60 запросами в минуту и 1000 запросами в день.\n\nДроссели не обязательно относятся только к запросам на ограничение скорости. Например, служба хранения данных может также нуждаться в ограничении пропускной способности, а платная служба данных может захотеть ограничить доступ к определенному количеству записей.\n\n**Дросселирование на уровне приложения, которое обеспечивает DRF, не следует рассматривать как меру безопасности или защиту от перебора или атак типа \"отказ в обслуживании\". Намеренно злоумышленники всегда смогут подделать IP-адреса. В дополнение к этому, встроенная реализация дросселирования реализована с использованием кэш-фреймворка Django и использует неатомарные операции для определения скорости запросов, что иногда может привести к некоторой нечеткости.**\n\n**Дросселирование на уровне приложений, предоставляемое DRF, предназначено для реализации таких политик, как различные бизнес-уровни и базовая защита от чрезмерного использования услуг.**\n\n## Как определяется дросселирование\n\nКак и в случае с разрешениями и аутентификацией, дросселирование в DRF всегда определяется как список классов.\n\nПеред запуском основной части представления проверяется каждый дроссель в списке. Если какая-либо проверка дросселя не прошла, будет вызвано исключение `exceptions.Throttled`, и основное тело представления не будет запущено.\n\n## Установка политики дросселирования\n\nПолитика дросселирования по умолчанию может быть установлена глобально, с помощью параметров `DEFAULT_THROTTLE_CLASSES` и `DEFAULT_THROTTLE_RATES`. Например.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_THROTTLE_CLASSES': [\n        'rest_framework.throttling.AnonRateThrottle',\n        'rest_framework.throttling.UserRateThrottle'\n    ],\n    'DEFAULT_THROTTLE_RATES': {\n        'anon': '100/day',\n        'user': '1000/day'\n    }\n}\n```\n\nПоказатели, используемые в `DEFAULT_THROTTLE_RATES`, могут быть заданы за период в секунду, минуту, час или день. Период должен быть указан после разделителя `/`, используя `s`, `m`, `h` или `d`, соответственно. Для большей ясности допускается использование расширенных единиц измерения, таких как `second`, `minute`, `hour`, `day` или даже сокращений `sec`, `min`, `hr`, поскольку только первый символ имеет значение для идентификации показателя.\n\nВы также можете установить политику дросселирования на основе каждого представления или каждого набора представлений, используя представления на основе класса `APIView`.\n\n```python\nfrom rest_framework.response import Response\nfrom rest_framework.throttling import UserRateThrottle\nfrom rest_framework.views import APIView\n\nclass ExampleView(APIView):\n    throttle_classes = [UserRateThrottle]\n\n    def get(self, request, format=None):\n        content = {\n            'status': 'request was permitted'\n        }\n        return Response(content)\n```\n\nЕсли вы используете декоратор `@api_view` с представлениями, основанными на функциях, вы можете использовать следующий декоратор.\n\n```python\n@api_view(['GET'])\n@throttle_classes([UserRateThrottle])\ndef example_view(request, format=None):\n    content = {\n        'status': 'request was permitted'\n    }\n    return Response(content)\n```\n\nТакже можно установить классы дросселей для маршрутов, которые создаются с помощью декоратора `@action`. Установленные таким образом классы дросселирования будут переопределять любые настройки классов на уровне набора представлений.\n\n```python\n@action(detail=True, methods=[\"post\"], throttle_classes=[UserRateThrottle])\ndef example_adhoc_method(request, pk=None):\n    content = {\n        'status': 'request was permitted'\n    }\n    return Response(content)\n```\n\n## Как определяются клиенты\n\nHTTP-заголовок `X-Forwarded-For` и WSGI-переменная `REMOTE_ADDR` используются для уникальной идентификации IP-адресов клиентов для дросселирования. Если заголовок `X-Forwarded-For` присутствует, то он будет использоваться, иначе будет использоваться значение переменной `REMOTE_ADDR` из среды WSGI.\n\nЕсли вам необходимо строго идентифицировать уникальные IP-адреса клиентов, вам нужно сначала настроить количество прокси-серверов приложений, за которыми работает API, установив параметр `NUM_PROXIES`. Это значение должно быть целым числом, равным нулю или больше. Если значение ненулевое, то IP-адрес клиента будет идентифицироваться как последний IP-адрес в заголовке `X-Forwarded-For`, после того как IP-адреса прокси приложений будут исключены. Если установлено в ноль, то значение `REMOTE_ADDR` всегда будет использоваться в качестве идентифицирующего IP-адреса.\n\nВажно понимать, что если вы настроите параметр `NUM_PROXIES`, то все клиенты за уникальным [NAT-ом](https://en.wikipedia.org/wiki/Network_address_translation) шлюзом будут рассматриваться как один клиент.\n\nДополнительную информацию о том, как работает заголовок `X-Forwarded-For` и как определить IP удаленного клиента, можно найти [здесь](http://oxpedia.org/wiki/index.php?title=AppSuite:Grizzly#Multiple_Proxies_in_front_of_the_cluster).\n\n## Настройка кэша\n\nКлассы дросселирования, предоставляемые DRF, используют бэкенд кэша Django. Вы должны убедиться, что установили соответствующие настройки [cache settings](https://docs.djangoproject.com/en/stable/ref/settings/#caches). Значение по умолчанию бэкенда `LocMemCache` должно быть приемлемым для простых настроек. Более подробную информацию можно найти в [документации по кэшу](https://docs.djangoproject.com/en/stable/topics/cache/#setting-up-the-cache) Django.\n\nЕсли вам нужно использовать кэш, отличный от `'default'`, вы можете сделать это, создав пользовательский класс дросселя и установив атрибут `cache`. Например:\n\n```python\nfrom django.core.cache import caches\n\nclass CustomAnonRateThrottle(AnonRateThrottle):\n    cache = caches['alternate']\n```\n\nВам нужно будет не забыть также установить ваш пользовательский класс дросселя в ключе настроек `'DEFAULT_THROTTLE_CLASSES'` или с помощью атрибута представления `throttle_classes`.\n\n## Заметка о параллелизме\n\nВстроенные реализации дросселей открыты для [условий гонки](https://en.wikipedia.org/wiki/Race_condition#Data_race), поэтому при высоком параллелизме они могут пропустить несколько лишних запросов.\n\nЕсли ваш проект полагается на гарантию количества запросов во время одновременных запросов, вам необходимо реализовать свой собственный класс дросселя. Более подробную информацию см. в [issue #5181][https://github.com/encode/django-rest-framework/issues/5181].\n---\n\n# API Reference\n\n## AnonRateThrottle\n\nДроссель `AnonRateThrottle` будет дросселировать только неаутентифицированных пользователей. IP-адрес входящего запроса используется для генерации уникального ключа для дросселирования.\n\nДопустимая скорость запроса определяется по одному из следующих параметров (в порядке предпочтения).\n\n* Свойство `rate` класса, которое может быть предоставлено путем переопределения `AnonRateThrottle` и установки свойства.\n* Настройка `DEFAULT_THROTTLE_RATES['anon']`.\n\n`AnonRateThrottle` подходит, если вы хотите ограничить скорость запросов от неизвестных источников.\n\n## UserRateThrottle\n\nДроссель `UserRateThrottle` будет дросселировать пользователей до заданной скорости запросов через API. Идентификатор пользователя используется для генерации уникального ключа для дросселирования. Неаутентифицированные запросы будут возвращаться к использованию IP-адреса входящего запроса для генерации уникального ключа для дросселирования.\n\nДопустимая скорость запроса определяется по одному из следующих параметров (в порядке предпочтения).\n\n* Свойство `rate` класса, которое может быть предоставлено путем переопределения `UserRateThrottle` и установки свойства.\n* Настройка `DEFAULT_THROTTLE_RATES['user']`.\n\nAPI может иметь несколько `UserRateThrottles` одновременно. Для этого переопределите `UserRateThrottle` и установите уникальный `'scope'` для каждого класса.\n\nНапример, несколько пользовательских дросселей могут быть реализованы с помощью следующих классов...\n\n```python\nclass BurstRateThrottle(UserRateThrottle):\n    scope = 'burst'\n\nclass SustainedRateThrottle(UserRateThrottle):\n    scope = 'sustained'\n```\n\n...и следующие настройки.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_THROTTLE_CLASSES': [\n        'example.throttles.BurstRateThrottle',\n        'example.throttles.SustainedRateThrottle'\n    ],\n    'DEFAULT_THROTTLE_RATES': {\n        'burst': '60/min',\n        'sustained': '1000/day'\n    }\n}\n```\n\n`UserRateThrottle` подходит, если вам нужны простые глобальные ограничения скорости для каждого пользователя.\n\n## ScopedRateThrottle\n\nКласс `ScopedRateThrottle` можно использовать для ограничения доступа к определенным частям API. Этот дроссель будет применяться, только если представление, к которому осуществляется доступ, включает свойство `.throttle_scope`. Уникальный ключ дросселя будет сформирован путем соединения `'scope'` запроса с уникальным идентификатором пользователя или IP-адресом.\n\nДопустимая скорость запроса определяется настройкой `DEFAULT_THROTTLE_RATES`, используя ключ из `'scope'` запроса.\n\nНапример, учитывая следующие представления...\n\n```python\nclass ContactListView(APIView):\n    throttle_scope = 'contacts'\n    ...\n\nclass ContactDetailView(APIView):\n    throttle_scope = 'contacts'\n    ...\n\nclass UploadView(APIView):\n    throttle_scope = 'uploads'\n    ...\n```\n\n...и следующие настройки.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_THROTTLE_CLASSES': [\n        'rest_framework.throttling.ScopedRateThrottle',\n    ],\n    'DEFAULT_THROTTLE_RATES': {\n        'contacts': '1000/day',\n        'uploads': '20/day'\n    }\n}\n```\n\nЗапросы пользователей к `ContactListView` или `ContactDetailView` будут ограничены до 1000 запросов в день. Запросы пользователей к `UploadView` будут ограничены 20 запросами в день.\n\n---\n\n# Пользовательские дроссели\n\nЧтобы создать пользовательский дроссель, переопределите `BaseThrottle` и реализуйте `.allow_request(self, request, view)`. Метод должен возвращать `True`, если запрос должен быть разрешен, и `False` в противном случае.\n\nПо желанию вы также можете переопределить метод `.wait()`. Если он реализован, `.wait()` должен возвращать рекомендуемое количество секунд ожидания перед попыткой следующего запроса или `None`. Метод `.wait()` будет вызван только в том случае, если `.allow_request()` ранее вернул `False`.\n\nЕсли реализован метод `.wait()` и запрос дросселируется, то в ответ будет включен заголовок `Retry-After`.\n\n## Пример\n\nНиже приведен пример дросселирования скорости, которое будет случайным образом дросселировать 1 из каждых 10 запросов.\n\n```python\nimport random\n\nclass RandomRateThrottle(throttling.BaseThrottle):\n    def allow_request(self, request, view):\n        return random.randint(1, 10) != 1\n```\n"
  },
  {
    "path": "api-guide/validators.md",
    "content": "<!-- TRANSLATED by md-translate -->\n---\n\nисточник:\n    - validators.py\n\n---\n\n# Валидаторы\n\n> Валидаторы могут быть полезны для повторного использования логики проверки между различными типами полей.\n>\n> &mdash; [Django documentation](https://docs.djangoproject.com/en/stable/ref/validators/)\n\nВ большинстве случаев, когда вы имеете дело с проверкой в DRF, вы просто полагаетесь на стандартную проверку полей, либо пишете явные методы проверки в сериализаторе или классах полей.\n\nОднако иногда вам захочется поместить логику валидации в компоненты многократного использования, чтобы ее можно было легко повторно использовать в кодовой базе. Этого можно достичь с помощью функций валидатора и классов валидатора.\n\n## Валидация в DRF\n\nВалидация в сериализаторах DRF обрабатывается немного иначе, чем валидация в классе Django `ModelForm`.\n\nПри использовании `ModelForm` валидация выполняется частично на форме, частично на экземпляре модели. При использовании DRF валидация выполняется полностью на классе сериализатора. Это выгодно по следующим причинам:\n\n* Обеспечивается правильное разделение обязанностей, что делает поведение вашего кода более очевидным.\n* Легко переключаться между использованием коротких классов `ModelSerializer` и явными классами `Serializer`. Любое поведение проверки, используемое для `ModelSerializer`, легко повторить.\n* Распечатка `repr` экземпляра сериализатора покажет вам, какие именно правила проверки он применяет. Нет никакого дополнительного скрытого поведения проверки, вызываемого на экземпляре модели.\n\nПри использовании `ModelSerializer` все это обрабатывается автоматически. Если вы хотите перейти к использованию классов `Serializer`, то вам необходимо явно определить правила валидации.\n\n#### Пример\n\nВ качестве примера того, как DRF использует явную валидацию, возьмем простой класс модели, в котором есть поле с ограничением уникальности.\n\n```python\nclass CustomerReportRecord(models.Model):\n    time_raised = models.DateTimeField(default=timezone.now, editable=False)\n    reference = models.CharField(unique=True, max_length=20)\n    description = models.TextField()\n```\n\nВот базовый `ModelSerializer`, который мы можем использовать для создания или обновления экземпляров `CustomerReportRecord`:\n\n```python\nclass CustomerReportSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = CustomerReportRecord\n```\n\nЕсли мы откроем оболочку Django с помощью `manage.py shell`, то теперь мы можем\n\n```python\n>>> from project.example.serializers import CustomerReportSerializer\n>>> serializer = CustomerReportSerializer()\n>>> print(repr(serializer))\nCustomerReportSerializer():\n    id = IntegerField(label='ID', read_only=True)\n    time_raised = DateTimeField(read_only=True)\n    reference = CharField(max_length=20, validators=[UniqueValidator(queryset=CustomerReportRecord.objects.all())])\n    description = CharField(style={'type': 'textarea'})\n```\n\nИнтересным здесь является поле `reference`. Мы видим, что ограничение уникальности явно обеспечивается валидатором на поле сериализатора.\n\nИз-за этого более явного стиля DRF включает несколько классов валидаторов, которые отсутствуют в основном Django. Эти классы подробно описаны ниже. Валидаторы DRF, как и их аналоги в Django, реализуют метод `__eq__`, позволяющий сравнивать экземпляры на равенство.\n\n---\n\n## UniqueValidator\n\nЭтот валидатор может быть использован для обеспечения ограничения `unique=True` на поля модели. Он принимает один обязательный аргумент и необязательный аргумент `messages`:\n\n* `queryset` _обязательно_ - Это набор запросов, в отношении которого должна быть обеспечена уникальность.\n* `message` - Сообщение об ошибке, которое должно быть использовано при неудачной проверке.\n* `lookup` - Поиск, используемый для нахождения существующего экземпляра с проверяемым значением. По умолчанию `'exact'`.\n\nЭтот валидатор должен применяться к _полям сериализатора_, например, так:\n\n```python\nfrom rest_framework.validators import UniqueValidator\n\nslug = SlugField(\n    max_length=100,\n    validators=[UniqueValidator(queryset=BlogPost.objects.all())]\n)\n```\n\n## UniqueTogetherValidator\n\nЭтот валидатор можно использовать для наложения ограничений `unique_together` на экземпляры моделей. Он имеет два обязательных аргумента и один необязательный аргумент `messages`:\n\n* `queryset` _обязательно_ - Это набор запросов, по которому должна быть обеспечена уникальность.\n* `fields` _обязательно_ - Список или кортеж имен полей, которые должны составлять уникальный набор. Они должны существовать как поля в классе сериализатора.\n* `message` - Сообщение об ошибке, которое должно быть использовано при неудачной проверке.\n\nВалидатор должен применяться к _классам сериализатора_, например, так:\n\n```python\nfrom rest_framework.validators import UniqueTogetherValidator\n\nclass ExampleSerializer(serializers.Serializer):\n    # ...\n    class Meta:\n        # ToDo items belong to a parent list, and have an ordering defined\n        # by the 'position' field. No two items in a given list may share\n        # the same position.\n        validators = [\n            UniqueTogetherValidator(\n                queryset=ToDoItem.objects.all(),\n                fields=['list', 'position']\n            )\n        ]\n```\n\n---\n\n**Примечание**: Класс `UniqueTogetherValidator` всегда накладывает неявное ограничение на то, что все поля, к которым он применяется, всегда рассматриваются как обязательные. Поля со значениями `default` являются исключением из этого правила, так как они всегда предоставляют значение, даже если оно опущено при вводе пользователем.\n\n---\n\n## UniqueForDateValidator\n\n## UniqueForMonthValidator\n\n## UniqueForYearValidator\n\nЭти валидаторы могут использоваться для наложения ограничений `unique_for_date`, `unique_for_month` и `unique_for_year` на экземпляры модели. Они принимают следующие аргументы:\n\n* `queryset` _обязательно_ - Это набор запросов, по которым будет проверяться уникальность.\n* `field` _обязательно_ - Имя поля, по которому будет проверяться уникальность в заданном диапазоне дат. Оно должно существовать как поле в классе сериализатора.\n* `date_field` _обязательно_ - Имя поля, которое будет использоваться для определения диапазона дат для ограничения уникальности. Оно должно существовать как поле в классе сериализатора.\n* `message` - Сообщение об ошибке, которое должно быть использовано при неудачной проверке.\n\nВалидатор должен применяться к _классам сериализатора_, например, так:\n\n```python\nfrom rest_framework.validators import UniqueForYearValidator\n\nclass ExampleSerializer(serializers.Serializer):\n    # ...\n    class Meta:\n        # Blog posts should have a slug that is unique for the current year.\n        validators = [\n            UniqueForYearValidator(\n                queryset=BlogPostItem.objects.all(),\n                field='slug',\n                date_field='published'\n            )\n        ]\n```\n\nПоле даты, которое используется для проверки, всегда должно присутствовать в классе сериализатора. Вы не можете просто положиться на класс модели `default=...`, потому что значение, используемое по умолчанию, будет сгенерировано только после выполнения валидации.\n\nЕсть несколько стилей, которые вы можете использовать для этого, в зависимости от того, как вы хотите, чтобы ваш API вел себя. Если вы используете `ModelSerializer`, вы, вероятно, просто будете полагаться на значения по умолчанию, которые генерирует для вас DRF, но если вы используете `Serializer` или просто хотите более явного контроля, используйте один из стилей, продемонстрированных ниже.\n\n#### Использование с записываемым полем даты.\n\nЕсли вы хотите, чтобы поле даты было доступно для записи, единственное, что стоит отметить, это то, что вы должны убедиться, что оно всегда доступно во входных данных, либо задав аргумент `default`, либо установив `required=True`.\n\n```python\npublished = serializers.DateTimeField(required=True)\n```\n\n#### Использование с полем даты, доступным только для чтения.\n\nЕсли вы хотите, чтобы поле даты было видно, но не редактировалось пользователем, то установите `read_only=True` и дополнительно задайте аргумент `default=...`.\n\n```python\npublished = serializers.DateTimeField(read_only=True, default=timezone.now)\n```\n\n#### Использование со скрытым полем даты.\n\nЕсли вы хотите, чтобы поле даты было полностью скрыто от пользователя, используйте `HiddenField`. Этот тип поля не принимает пользовательский ввод, а вместо этого всегда возвращает значение по умолчанию в `validated_data` в сериализаторе.\n\n```python\npublished = serializers.HiddenField(default=timezone.now)\n```\n\n---\n\n**Примечание**: Классы `UniqueFor<Range>Validator` накладывают неявное ограничение на то, что поля, к которым они применяются, всегда рассматриваются как обязательные. Поля со значениями `default` являются исключением из этого правила, так как они всегда предоставляют значение, даже если оно опущено при вводе пользователем.\n\n---\n\n**Примечание:** `HiddenField()` не появляется в сериализаторе `partial=True` (при выполнении запроса `PATCH`).\n\n---\n\n# Расширенные значения полей по умолчанию\n\nВалидаторы, применяемые к нескольким полям в сериализаторе, иногда могут потребовать ввода поля, которое не должно предоставляться клиентом API, но которое _доступно_ в качестве входных данных для валидатора. Для этих целей используйте `HiddenField`. Это поле будет присутствовать в `validated_data`, но _не_ будет использоваться в выходном представлении сериализатора.\n\n---\n\n**Примечание:** Использование поля `read_only=True` исключается из записываемых полей, поэтому оно не будет использовать аргумент `default=...`. Смотрите [3.8 объявление](https://www.django-rest-framework.org/community/3.8-announcement/#altered-the-behavior-of-read_only-plus-default-on-field).\n\n---\n\nDRF включает в себя несколько параметров по умолчанию, которые могут быть полезны в данном контексте.\n\n#### CurrentUserDefault\n\nКласс по умолчанию, который можно использовать для представления текущего пользователя. Чтобы использовать его, `request` должен быть предоставлен как часть контекстного словаря при инстанцировании сериализатора.\n\n```python\nowner = serializers.HiddenField(\n    default=serializers.CurrentUserDefault()\n)\n```\n\n#### CreateOnlyDefault\n\nКласс по умолчанию, который можно использовать _только для установки аргумента по умолчанию во время операций создания_. При обновлении поле опускается.\n\nОн принимает один аргумент, который является значением по умолчанию или вызываемым модулем, который должен использоваться при создании.\n\n```python\ncreated_at = serializers.DateTimeField(\n    default=serializers.CreateOnlyDefault(timezone.now)\n)\n```\n\n---\n\n# Ограничения валидаторов\n\nЕсть несколько неоднозначных случаев, когда вам придется явно обрабатывать валидацию, а не полагаться на классы сериализаторов по умолчанию, которые генерирует `ModelSerializer`.\n\nВ таких случаях вы можете отключить автоматически сгенерированные валидаторы, указав пустой список для атрибута сериализатора `Meta.validators`.\n\n## Необязательные поля\n\nПо умолчанию валидация `unique_together` принудительно требует, чтобы все поля были `required=True`. В некоторых случаях вы можете захотеть явно применить `required=False` к одному из полей, и тогда желаемое поведение валидации будет неоднозначным.\n\nВ этом случае, как правило, необходимо исключить валидатор из класса сериализатора, а всю логику проверки написать явно либо в методе `.validate()`, либо в представлении.\n\nНапример:\n\n```python\nclass BillingRecordSerializer(serializers.ModelSerializer):\n    def validate(self, attrs):\n        # Apply custom validation either here, or in the view.\n\n    class Meta:\n        fields = ['client', 'date', 'amount']\n        extra_kwargs = {'client': {'required': False}}\n        validators = []  # Remove a default \"unique together\" constraint.\n```\n\n## Обновление вложенных сериализаторов\n\nПри применении обновления к существующему экземпляру валидаторы уникальности исключают текущий экземпляр из проверки уникальности. Текущий экземпляр доступен в контексте проверки уникальности, поскольку он существует как атрибут сериализатора, будучи изначально переданным с помощью `instance=...` при инстанцировании сериализатора.\n\nВ случае операций обновления для _вложенных_ сериализаторов нет возможности применить это исключение, поскольку экземпляр недоступен.\n\nОпять же, вероятно, вам захочется явно удалить валидатор из класса сериализатора и написать код для ограничения валидации явно, в методе `.validate()` или в представлении.\n\n## Отладка сложных случаев\n\nЕсли вы не уверены в том, какое именно поведение будет генерировать класс `ModelSerializer`, обычно полезно запустить `manage.py shell` и распечатать экземпляр сериализатора, чтобы вы могли проверить поля и валидаторы, которые он автоматически генерирует для вас.\n\n```python\n>>> serializer = MyComplexModelSerializer()\n>>> print(serializer)\nclass MyComplexModelSerializer:\n    my_fields = ...\n```\n\nТакже имейте в виду, что в сложных случаях часто бывает лучше явно определить свои классы сериализатора, а не полагаться на поведение `ModelSerializer` по умолчанию. Это потребует немного больше кода, но гарантирует, что результирующее поведение будет более прозрачным.\n\n---\n\n# Написание пользовательских валидаторов\n\nВы можете использовать любой из существующих валидаторов Django или написать свой собственный валидатор.\n\n## Валидаторы, основанные на функциях\n\nВалидатором может быть любой вызываемый объект, который при неудаче вызывает ошибку `serializers.ValidationError`.\n\n```python\ndef even_number(value):\n    if value % 2 != 0:\n        raise serializers.ValidationError('This field must be an even number.')\n```\n\n#### Валидация на уровне поля\n\nВы можете задать пользовательскую проверку на уровне полей, добавив методы `.validate_<имя_поля>` в подкласс `Serializer`. Это описано в [документации по сериализаторам](../api-guide/serializers.md#валидация-на-уровне-поля)\n\n## На основе класса\n\nЧтобы написать валидатор на основе класса, используйте метод `__call__`. Валидаторы на основе классов полезны тем, что позволяют параметризировать и повторно использовать поведение.\n\n```python\nclass MultipleOf:\n    def __init__(self, base):\n        self.base = base\n\n    def __call__(self, value):\n        if value % self.base != 0:\n            message = 'This field must be a multiple of %d.' % self.base\n            raise serializers.ValidationError(message)\n```\n\n#### Доступ к контексту\n\nВ некоторых сложных случаях вы можете захотеть, чтобы валидатору передавалось поле сериализатора, с которым он используется, в качестве дополнительного контекста. Вы можете сделать это, установив атрибут `requires_context = True` для класса валидатора. Тогда метод `__call__` будет вызван с полем `serializer_field` или `serializer` в качестве дополнительного аргумента.\n\n```python\nclass MultipleOf:\n    requires_context = True\n\n    def __call__(self, value, serializer_field):\n        ...\n```\n"
  },
  {
    "path": "api-guide/versioning.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Версионирование\n\n> Версионирование интерфейса - это просто \"вежливый\" способ убить развернутых клиентов.\n>\n> - [Рой Филдинг](https://www.slideshare.net/evolve_conference/201308-fielding-evolve/31).\n\nВерсионность API позволяет изменять поведение различных клиентов. DRF предусматривает несколько различных схем версионирования.\n\nВерсионность определяется входящим запросом клиента и может быть основана либо на URL запроса, либо на заголовках запроса.\n\nСуществует несколько правильных подходов к определению версий. [Неверсионированные системы также могут быть уместны](https://www.infoq.com/articles/roy-fielding-on-versioning), особенно если вы разрабатываете очень долгосрочные системы с множеством клиентов вне вашего контроля.\n\n## Версионирование с помощью DRF\n\nЕсли версионность API включена, атрибут `request.version` будет содержать строку, соответствующую версии, запрошенной во входящем клиентском запросе.\n\nПо умолчанию версионность не включена, и `request.version` всегда будет возвращать `None`.\n\n#### Различное поведение в зависимости от версии\n\nКак вы будете изменять поведение API, зависит от вас, но один из примеров, который обычно может понадобиться, - это переход на другой стиль сериализации в новой версии. Например:\n\n```python\ndef get_serializer_class(self):\n    if self.request.version == 'v1':\n        return AccountSerializerVersion1\n    return AccountSerializer\n```\n\n#### Обратные URL для версионных API\n\nФункция `reverse`, включенная в DRF, связана со схемой версионирования. Вам нужно убедиться, что вы включили текущий `request` в качестве именованного аргумента, как, например.\n\n```python\nfrom rest_framework.reverse import reverse\n\nreverse('bookings-list', request=request)\n```\n\nПриведенная выше функция будет применять любые преобразования URL, соответствующие версии запроса. Например:\n\n* Если используется `NamespaceVersioning`, и версия API равна 'v1', то для поиска URL используется `'v1:bookings-list'`, что может привести к URL типа `http://example.org/v1/bookings/`.\n* Если используется `QueryParameterVersioning`, и версия API была '1.0', то возвращаемый URL может быть чем-то вроде `http://example.org/bookings/?version=1.0`.\n\n#### Версифицированные API и сериализаторы с гиперссылками\n\nПри использовании стилей сериализации с гиперссылками вместе со схемой версионирования на основе URL обязательно включайте запрос в качестве контекста для сериализатора.\n\n```python\ndef get(self, request):\n    queryset = Booking.objects.all()\n    serializer = BookingsSerializer(queryset, many=True, context={'request': request})\n    return Response({'all_bookings': serializer.data})\n```\n\nЭто позволит всем возвращаемым URL-адресам включать соответствующие версии.\n\n## Настройка схемы версионирования\n\nСхема версионирования определяется ключом настройки `DEFAULT_VERSIONING_CLASS`.\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'\n}\n```\n\nЕсли оно не задано явно, значение для `DEFAULT_VERSIONING_CLASS` будет равно `None`. В этом случае атрибут `request.version` всегда будет возвращать `None`.\n\nВы также можете установить схему версионирования для отдельного представления. Обычно вам не нужно этого делать, поскольку логичнее иметь единую схему версионирования, используемую глобально. Если это необходимо, используйте атрибут `versioning_class`.\n\n```python\nclass ProfileList(APIView):\n    versioning_class = versioning.QueryParameterVersioning\n```\n\n#### Другие параметры версионирования\n\nСледующие ключи настроек также используются для управления версионированием:\n\n* `DEFAULT_VERSION`. Значение, которое должно использоваться для `request.version`, когда информация о версиях отсутствует. По умолчанию `None`.\n* `ALLOWED_VERSIONS`. Если задано, это значение ограничивает набор версий, которые могут быть возвращены схемой версионирования, и выдает ошибку, если предоставленная версия не входит в этот набор. Обратите внимание, что значение, используемое для параметра `DEFAULT_VERSION`, всегда считается частью набора `ALLOWED_VERSIONS` (если оно не равно `None`). По умолчанию `None`.\n* `VERSION_PARAM`. Строка, которая должна использоваться для любых параметров версионирования, например, в типе медиа или параметрах запроса URL. По умолчанию имеет значение `'version'`.\n\nВы также можете установить свой класс версионности плюс эти три значения на основе каждого представления или каждого набора представлений, определив свою собственную схему версионности и используя переменные класса `default_version`, `allowed_versions` и `version_param`. Например, если вы хотите использовать `URLPathVersioning`:\n\n```python\nfrom rest_framework.versioning import URLPathVersioning\nfrom rest_framework.views import APIView\n\nclass ExampleVersioning(URLPathVersioning):\n    default_version = ...\n    allowed_versions = ...\n    version_param = ...\n\nclass ExampleView(APIVIew):\n    versioning_class = ExampleVersioning\n```\n\n---\n\n# API Reference\n\n## AcceptHeaderVersioning\n\nЭта схема требует от клиента указать версию как часть типа медиа в заголовке `Accept`. Версия включается в качестве параметра медиатипа, дополняющего основной медиатип.\n\nВот пример HTTP-запроса с использованием стиля версионирования заголовка accept.\n\n```http\nGET /bookings/ HTTP/1.1\nHost: example.com\nAccept: application/json; version=1.0\n```\n\nВ приведенном выше примере запроса атрибут `request.version` вернет строку `'1.0'`.\n\nВерсионирование на основе принимаемых заголовков [обычно рассматривается](http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http#i_want_my_api_to_be_versioned) как [лучшая практика](https://github.com/interagent/http-api-design/blob/master/en/foundations/require-versioning-in-the-accepts-header.md), хотя в зависимости от требований клиента могут подойти и другие стили.\n\n#### Использование заголовков accept с медиатипами поставщиков\n\nСтрого говоря, медиатип `json` не указан как [включающий дополнительные параметры](https://tools.ietf.org/html/rfc4627#section-6). Если вы создаете хорошо специфицированный публичный API, вы можете рассмотреть возможность использования [vendor media type](https://en.wikipedia.org/wiki/Internet_media_type#Vendor_tree). Для этого настройте свои рендереры на использование рендерера на основе JSON с пользовательским медиатипом:\n\n```python\nclass BookingsAPIRenderer(JSONRenderer):\n    media_type = 'application/vnd.megacorp.bookings+json'\n```\n\nТеперь ваши клиентские запросы будут выглядеть следующим образом:\n\n```http\nGET /bookings/ HTTP/1.1\nHost: example.com\nAccept: application/vnd.megacorp.bookings+json; version=1.0\n```\n\n## URLPathVersioning\n\nЭта схема требует, чтобы клиент указывал версию как часть пути URL.\n\n```http\nGET /v1/bookings/ HTTP/1.1\nHost: example.com\nAccept: application/json\n```\n\nВаш URL conf должен включать шаблон, соответствующий версии, с именованным аргументом `'version'`, чтобы эта информация была доступна схеме версионирования.\n\n```python\nurlpatterns = [\n    re_path(\n        r'^(?P<version>(v1|v2))/bookings/$',\n        bookings_list,\n        name='bookings-list'\n    ),\n    re_path(\n        r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',\n        bookings_detail,\n        name='bookings-detail'\n    )\n]\n```\n\n## NamespaceVersioning\n\nДля клиента эта схема аналогична `URLPathVersioning`. Единственное различие заключается в том, как она настраивается в вашем приложении Django, поскольку она использует интервалы между именами URL, вместо именованных аргументов URL.\n\n```http\nGET /v1/something/ HTTP/1.1\nHost: example.com\nAccept: application/json\n```\n\nПри такой схеме атрибут `request.version` определяется на основе `namespace`, соответствующего пути входящего запроса.\n\nВ следующем примере мы даем набору представлений два различных возможных префикса URL, каждый из которых относится к разным пространствам имен:\n\n```python\n# bookings/urls.py\nurlpatterns = [\n    re_path(r'^$', bookings_list, name='bookings-list'),\n    re_path(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')\n]\n\n# urls.py\nurlpatterns = [\n    re_path(r'^v1/bookings/', include('bookings.urls', namespace='v1')),\n    re_path(r'^v2/bookings/', include('bookings.urls', namespace='v2'))\n]\n```\n\nИ `URLPathVersioning`, и `NamespaceVersioning` разумны, если вам нужна простая схема версионирования. Подход `URLPathVersioning` может лучше подойти для небольших специальных проектов, а `NamespaceVersioning`, вероятно, проще в управлении для больших проектов.\n\n## HostNameVersioning\n\nСхема версионности имени хоста требует от клиента указать запрашиваемую версию как часть имени хоста в URL.\n\nНапример, ниже приведен HTTP-запрос к URL `http://v1.example.com/bookings/`:\n\n```http\nGET /bookings/ HTTP/1.1\nHost: v1.example.com\nAccept: application/json\n```\n\nПо умолчанию эта реализация ожидает, что имя хоста будет соответствовать этому простому регулярному выражению:\n\n```python\n^([a-zA-Z0-9]+)\\.[a-zA-Z0-9]+\\.[a-zA-Z0-9]+$\n```\n\nОбратите внимание, что первая группа заключена в скобки, что указывает на то, что это совпадающая часть имени хоста.\n\nСхема `HostNameVersioning` может быть неудобна для использования в режиме отладки, так как обычно вы обращаетесь к необработанному IP-адресу, например, `127.0.0.1`. Существуют различные онлайн-уроки о том, как [получить доступ к localhost с пользовательским поддоменом](https://reinteractive.net/posts/199-developing-and-testing-rails-applications-with-subdomains), которые могут оказаться полезными в этом случае.\n\nВерсионность на основе имени хоста может быть особенно полезна, если у вас есть требования направлять входящие запросы на разные серверы в зависимости от версии, поскольку вы можете настроить разные записи DNS для разных версий API.\n\n## QueryParameterVersioning\n\nЭта схема представляет собой простой стиль, который включает версию в качестве параметра запроса в URL. Например:\n\n```http\nGET /something/?version=0.1 HTTP/1.1\nHost: example.com\nAccept: application/json\n```\n\n---\n\n# Пользовательские схемы версионирования\n\nДля реализации пользовательской схемы версионирования, подкласс `BaseVersioning` и переопределите метод `.determine_version`.\n\n## Пример\n\nВ следующем примере используется пользовательский заголовок `X-API-Version` для определения запрашиваемой версии.\n\n```python\nclass XAPIVersionScheme(versioning.BaseVersioning):\n    def determine_version(self, request, *args, **kwargs):\n        return request.META.get('HTTP_X_API_VERSION', None)\n```\n\nЕсли ваша схема версионирования основана на URL запроса, вы также захотите изменить способ определения версионированных URL. Для этого вам следует переопределить метод `.reverse()` в классе. Примеры см. в исходном коде.\n"
  },
  {
    "path": "api-guide/views.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Представления, основанные на классах\n\n> Представления Django, основанные на классах, являются приятным отступлением от представлений старого стиля.\n>\n> - [Reinout van Rees](https://reinout.vanrees.org/weblog/2011/08/24/class-based-views-usage.html)\n\nDRF предоставляет класс `APIView`, который является подклассом класса Django `View`.\n\nКлассы `APIView` отличаются от обычных классов `View` следующим образом:\n\n* Запросы, передаваемые методам обработчика, будут экземплярами `Request` DRF, а не экземплярами `HttpRequest` Django.\n* Методы обработчика могут возвращать `Response` DRF, а не `HttpResponse` Django. Представление будет управлять согласованием содержимого и установкой правильного рендерера в ответе.\n* Любые исключения `APIException` будут перехвачены и переведены в соответствующие ответы.\n* Входящие запросы будут аутентифицированы, и перед отправкой запроса в метод-обработчик будут выполняться соответствующие проверки разрешений и/или дросселирования.\n\nИспользование класса `APIView` практически не отличается от использования обычного класса `View`. Как обычно, входящий запрос передается соответствующему методу-обработчику, такому как `.get()` или `.post()`. Кроме того, для класса может быть установлен ряд атрибутов, которые контролируют различные аспекты политики API.\n\nНапример:\n\n```python\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import authentication, permissions\nfrom django.contrib.auth.models import User\n\nclass ListUsers(APIView):\n    \"\"\"\n    View to list all users in the system.\n\n    * Requires token authentication.\n    * Only admin users are able to access this view.\n    \"\"\"\n    authentication_classes = [authentication.TokenAuthentication]\n    permission_classes = [permissions.IsAdminUser]\n\n    def get(self, request, format=None):\n        \"\"\"\n        Return a list of all users.\n        \"\"\"\n        usernames = [user.username for user in User.objects.all()]\n        return Response(usernames)\n```\n\n---\n\n**Примечание**: Полные методы, атрибуты и отношения между `APIView`, `GenericAPIView`, различными `Mixins` и `Viewsets` DRF могут быть изначально сложными. В дополнение к документации, представленной здесь, ресурс [Classy Django REST Framework](http://www.cdrf.co) предоставляет просматриваемую ссылку с полными методами и атрибутами для каждого из представлений DRF, основанных на классах.\n\n---\n\n## Атрибуты политики API\n\nСледующие атрибуты управляют подключаемыми аспектами представлений API.\n\n### .renderer_classes\n\n### .parser_classes\n\n### .authentication_classes\n\n### .throttle_classes\n\n### .permission_classes\n\n### .content_negotiation_class\n\n## Методы инстанцирования политики API\n\nСледующие методы используются DRF для инстанцирования различных подключаемых политик API. Как правило, вам не нужно переопределять эти методы.\n\n### .get_renderers(self)\n\n### .get_parsers(self)\n\n### .get_authenticators(self)\n\n### .get_throttles(self)\n\n### .get_permissions(self)\n\n### .get_content_negotiator(self)\n\n### .get_exception_handler(self)\n\n## Методы реализации политики API\n\nПеред отправкой в метод обработчика вызываются следующие методы.\n\n### .check_permissions(self, request)\n\n### .check_throttles(self, request)\n\n### .perform_content_negotiation(self, request, force=False)\n\n## Dispatch методы\n\nСледующие методы вызываются непосредственно методом `.dispatch()` представления. Они выполняют любые действия, которые должны произойти до или после вызова методов обработчика, таких как `.get()`, `.post()`, `put()`, `patch()` и `.delete()`.\n\n### .initial(self, request, *args, **kwargs)\n\nВыполняет любые действия, которые должны произойти до вызова метода обработчика. Этот метод используется для обеспечения разрешений и дросселирования, а также для согласования содержимого.\n\nОбычно вам не нужно переопределять этот метод.\n\n### .handle_exception(self, exc)\n\nЛюбое исключение, выброшенное методом обработчика, будет передано в этот метод, который либо возвращает экземпляр `Response`, либо повторно вызывает исключение.\n\nРеализация по умолчанию обрабатывает любой подкласс `rest_framework.exceptions.APIException`, а также исключения Django `Http404` и `PermissionDenied`, и возвращает соответствующий ответ об ошибке.\n\nЕсли вам нужно настроить ответы на ошибки, которые возвращает ваш API, вам следует подклассифицировать этот метод.\n\n### .initialize_request(self, request, *args, **kwargs)\n\nГарантирует, что объект запроса, передаваемый методу обработчика, является экземпляром `Request`, а не обычным Django `HttpRequest`.\n\nОбычно вам не нужно переопределять этот метод.\n\n### .finalize_response(self, request, response, *args, **kwargs)\n\nГарантирует, что любой объект `Response`, возвращенный из метода обработчика, будет преобразован в правильный тип содержимого, как определено в процессе согласования содержимого.\n\nОбычно вам не нужно переопределять этот метод.\n\n---\n\n# Представления на основе функций\n\n> Говорить [что представления, основанные на классах] всегда являются лучшим решением - это ошибка.\n>\n> - [Nick Coghlan](http://www.boredomandlaziness.org/2012/05/djangos-cbvs-are-not-mistake-but.html)\n\nDRF также позволяет работать с обычными представлениями, основанными на функциях. Он предоставляет набор простых декораторов, которые оборачивают ваши представления на основе функций, чтобы они получали экземпляр `Request` (а не обычный Django `HttpRequest`) и позволяли им возвращать `Response` (а не Django `HttpResponse`), а также позволяют вам настраивать, как обрабатывается запрос.\n\n## @api_view()\n\n**Сигнатура:** `@api_view(http_method_names=['GET'])`.\n\nЯдром этой функциональности является декоратор `api_view`, который принимает список методов HTTP, на которые должно отвечать ваше представление. Например, вот как можно написать очень простое представление, которое просто вручную возвращает некоторые данные:\n\n```python\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\n\n@api_view()\ndef hello_world(request):\n    return Response({\"message\": \"Hello, world!\"})\n```\n\nЭто представление будет использовать рендереры по умолчанию, парсеры, классы аутентификации и т.д., указанные в [settings](settings.md).\n\nПо умолчанию принимаются только методы `GET`. Другие методы будут отвечать \"405 Method Not Allowed\". Чтобы изменить это поведение, укажите, какие методы разрешены представлению, например, так:\n\n```python\n@api_view(['GET', 'POST'])\ndef hello_world(request):\n    if request.method == 'POST':\n        return Response({\"message\": \"Got some data!\", \"data\": request.data})\n    return Response({\"message\": \"Hello, world!\"})\n```\n\n## Декораторы политики API\n\nЧтобы переопределить настройки по умолчанию, DRF предоставляет набор дополнительных декораторов, которые можно добавить к вашим представлениям. Они должны быть *после* (ниже) декоратора `@api_view`. Например, чтобы создать представление, которое использует [throttle](throttling.md) для обеспечения того, что оно может быть вызвано только один раз в день определенным пользователем, используйте декоратор `@throttle_classes`, передавая список классов throttle:\n\n```python\nfrom rest_framework.decorators import api_view, throttle_classes\nfrom rest_framework.throttling import UserRateThrottle\n\nclass OncePerDayUserThrottle(UserRateThrottle):\n    rate = '1/day'\n\n@api_view(['GET'])\n@throttle_classes([OncePerDayUserThrottle])\ndef view(request):\n    return Response({\"message\": \"Hello for today! See you tomorrow!\"})\n```\n\nЭти декораторы соответствуют атрибутам, установленным на подклассах `APIView`, описанных выше.\n\nДоступными декораторами являются:\n\n* `@renderer_classes(...)`\n* `@parser_classes(...)`\n* `@authentication_classes(...)`\n* `@throttle_classes(...)`\n* `@permission_classes(...)`\n* `@content_negotiation_class(...)`\n* `@metadata_class(...)`\n* `@versioning_class(...)`\n\nКаждый из этих декораторов эквивалентен установке соответствующих [атрибутов политики API](#атрибуты-политики-api).\n\nВсе декораторы принимают один аргумент. Те, которые заканчиваются на `_class`, ожидают один класс, а те, которые заканчиваются на `_classes`, ожидают список или кортеж классов.\n## Декоратор схемы представления\n\nЧтобы переопределить генерацию схемы по умолчанию для представлений на основе функций, вы можете использовать декоратор `@schema`. Он должен располагаться *после* (ниже) декоратора `@api_view`. Например:\n\n```python\nfrom rest_framework.decorators import api_view, schema\nfrom rest_framework.schemas import AutoSchema\n\nclass CustomAutoSchema(AutoSchema):\n    def get_link(self, path, method, base_url):\n        # override view introspection here...\n\n@api_view(['GET'])\n@schema(CustomAutoSchema())\ndef view(request):\n    return Response({\"message\": \"Hello for today! See you tomorrow!\"})\n```\n\nЭтот декоратор принимает экземпляр `AutoSchema`, экземпляр подкласса `AutoSchema` или экземпляр `ManualSchema`, как описано в документации [Schemas documentation](schemas.md). Вы можете передать `None`, чтобы исключить представление из генерации схемы.\n\n```python\n@api_view(['GET'])\n@schema(None)\ndef view(request):\n    return Response({\"message\": \"Will not appear in schema!\"})\n```\n"
  },
  {
    "path": "api-guide/viewsets.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# ViewSets\n\n> После того, как маршрутизация определила, какой контроллер использовать для запроса, ваш контроллер отвечает за осмысление запроса и создание соответствующего вывода.\n>\n> &mdash; [Ruby on Rails Documentation](https://guides.rubyonrails.org/action_controller_overview.html)\n\nDRF позволяет объединить логику для набора связанных представлений в одном классе, называемом `ViewSet`. В других фреймворках вы также можете встретить концептуально схожие реализации с названиями типа \"Ресурсы\" или \"Контроллеры\".\n\nКласс `ViewSet` - это просто **тип представления на основе класса, который не предоставляет никаких обработчиков методов**, таких как `.get()` или `.post()`, а вместо этого предоставляет такие действия, как `.list()` и `.create()`.\n\nОбработчики методов для `ViewSet` привязываются к соответствующим действиям только в момент финализации представления с помощью метода `.as_view()`.\n\nОбычно вместо того, чтобы явно регистрировать представления в наборе представлений в urlconf, вы регистрируете набор представлений в классе маршрутизатора, который автоматически определяет urlconf для вас.\n\n## Пример\n\nДавайте определим простой набор представлений, который можно использовать для получения списка всех пользователей в системе или конкретного пользователя.\n\n```python\nfrom django.contrib.auth.models import User\nfrom django.shortcuts import get_object_or_404\nfrom myapps.serializers import UserSerializer\nfrom rest_framework import viewsets\nfrom rest_framework.response import Response\n\nclass UserViewSet(viewsets.ViewSet):\n    \"\"\"\n    A simple ViewSet for listing or retrieving users.\n    \"\"\"\n    def list(self, request):\n        queryset = User.objects.all()\n        serializer = UserSerializer(queryset, many=True)\n        return Response(serializer.data)\n\n    def retrieve(self, request, pk=None):\n        queryset = User.objects.all()\n        user = get_object_or_404(queryset, pk=pk)\n        serializer = UserSerializer(user)\n        return Response(serializer.data)\n```\n\nЕсли нужно, мы можем разделить этот набор представлений на два отдельных представления, как показано ниже:\n\n```python\nuser_list = UserViewSet.as_view({'get': 'list'})\nuser_detail = UserViewSet.as_view({'get': 'retrieve'})\n```\n\nОбычно мы не делаем этого, а регистрируем набор представлений в маршрутизаторе и позволяем автоматически генерировать urlconf.\n\n```python\nfrom myapp.views import UserViewSet\nfrom rest_framework.routers import DefaultRouter\n\nrouter = DefaultRouter()\nrouter.register(r'users', UserViewSet, basename='user')\nurlpatterns = router.urls\n```\n\n---\n\n**Предупреждение**: Не используйте `.as_view()` с методами `@action`. Это обходит настройки маршрутизатора и может игнорировать настройки действий, такие как `permission_classes`. Используйте `DefaultRouter` для действий.\n\n---\n\nВместо того чтобы писать свои собственные наборы представлений, вы часто захотите использовать существующие базовые классы, которые предоставляют набор поведения по умолчанию. Например:\n\n```python\nclass UserViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    A viewset for viewing and editing user instances.\n    \"\"\"\n    serializer_class = UserSerializer\n    queryset = User.objects.all()\n```\n\nЕсть два основных преимущества использования класса `ViewSet` вместо класса `View`.\n\n* Повторяющаяся логика может быть объединена в один класс. В приведенном выше примере нам нужно указать `queryset` только один раз, и он будет использоваться в нескольких представлениях.\n* Используя маршрутизаторы, нам больше не нужно самим создавать URL conf.\n\nОба варианта имеют свои преимущества. Использование обычных представлений и URL-конфигураций является более явным и дает вам больше контроля. Наборы представлений полезны, если вы хотите быстро приступить к работе, или если у вас большой API и вы хотите обеспечить последовательную конфигурацию URL во всем.\n\n## Действия ViewSet\n\nМаршрутизаторы по умолчанию, входящие в состав DRF, обеспечивают маршруты для стандартного набора действий в стиле create/retrieve/update/destroy, как показано ниже:\n\n```python\nclass UserViewSet(viewsets.ViewSet):\n    \"\"\"\n    Example empty viewset demonstrating the standard\n    actions that will be handled by a router class.\n\n    If you're using format suffixes, make sure to also include\n    the `format=None` keyword argument for each action.\n    \"\"\"\n\n    def list(self, request):\n        pass\n\n    def create(self, request):\n        pass\n\n    def retrieve(self, request, pk=None):\n        pass\n\n    def update(self, request, pk=None):\n        pass\n\n    def partial_update(self, request, pk=None):\n        pass\n\n    def destroy(self, request, pk=None):\n        pass\n```\n\n## Интроспекция действий ViewSet\n\nВо время диспетчеризации для `ViewSet` доступны следующие атрибуты.\n\n* `basename` - основа, используемая для имен создаваемых URL.\n* `action` - имя текущего действия (например, `list`, `create`).\n* `detail` - булево значение, указывающее, настроено ли текущее действие на просмотр списка или деталей.\n* `suffix` - суффикс отображения для типа набора представлений - зеркально отражает атрибут `detail`.\n* `name` - отображаемое имя набора представлений. Этот аргумент является взаимоисключающим для `suffix`.\n* `description` - отображаемое описание для отдельного вида набора представлений.\n\nВы можете использовать эти атрибуты для настройки поведения в зависимости от текущего действия. Например, вы можете ограничить права на все действия, кроме действия `list`, следующим образом:\n\n```python\ndef get_permissions(self):\n    \"\"\"\n    Instantiates and returns the list of permissions that this view requires.\n    \"\"\"\n    if self.action == 'list':\n        permission_classes = [IsAuthenticated]\n    else:\n        permission_classes = [IsAdminUser]\n    return [permission() for permission in permission_classes]\n```\n\n---\n\n**Примечание**: атрибут `action` недоступен в методах `get_parsers`, `get_authenticators` и `get_content_negotiator`, так как он устанавливается _после_ их вызова в жизненном цикле фреймворка. Если вы переопределите один из этих методов и попытаетесь получить доступ к атрибуту `action` в них, вы получите ошибку `AttributeError`.\n\n---\n\n## Добавление дополнительных действий в маршрутизацию\n\nЕсли у вас есть специальные методы, которые должны быть маршрутизируемыми, вы можете пометить их как таковые с помощью декоратора `@action`. Как и обычные действия, дополнительные действия могут быть предназначены как для одного объекта, так и для целой коллекции. Чтобы указать это, установите аргумент `detail` в `True` или `False`. Маршрутизатор настроит свои шаблоны URL соответствующим образом. Например, `DefaultRouter` настроит подробные действия так, чтобы они содержали `pk` в своих шаблонах URL.\n\nБолее полный пример дополнительных действий:\n\n```python\nfrom django.contrib.auth.models import User\nfrom rest_framework import status, viewsets\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\nfrom myapp.serializers import UserSerializer, PasswordSerializer\n\nclass UserViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    A viewset that provides the standard actions\n    \"\"\"\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n    @action(detail=True, methods=['post'])\n    def set_password(self, request, pk=None):\n        user = self.get_object()\n        serializer = PasswordSerializer(data=request.data)\n        if serializer.is_valid():\n            user.set_password(serializer.validated_data['password'])\n            user.save()\n            return Response({'status': 'password set'})\n        else:\n            return Response(serializer.errors,\n                            status=status.HTTP_400_BAD_REQUEST)\n\n    @action(detail=False)\n    def recent_users(self, request):\n        recent_users = User.objects.all().order_by('-last_login')\n\n        page = self.paginate_queryset(recent_users)\n        if page is not None:\n            serializer = self.get_serializer(page, many=True)\n            return self.get_paginated_response(serializer.data)\n\n        serializer = self.get_serializer(recent_users, many=True)\n        return Response(serializer.data)\n```\n\nДекоратор `action` по умолчанию направляет запросы `GET`, но может принимать и другие HTTP-методы, задав аргумент `methods`. Например:\n\n```python\n    @action(detail=True, methods=['post', 'delete'])\n    def unset_password(self, request, pk=None):\n       ...\n```\n\nАргумент `methods` также поддерживает методы HTTP, определенные как [HTTPMethod](https://docs.python.org/3/library/http.html#http.HTTPMethod). Пример ниже идентичен приведенному выше:\n\n```python\nfrom http import HTTPMethod\n\n    @action(detail=True, methods=[HTTPMethod.POST, HTTPMethod.DELETE])\n    def unset_password(self, request, pk=None):\n        ...\n```\n\nДекоратор позволяет переопределить любую конфигурацию на уровне набора представлений, такую как `permission_classes`, `serializer_class`, `filter_backends`...:\n\n```python\n    @action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])\n    def set_password(self, request, pk=None):\n       ...\n```\n\nДва новых действия будут доступны по адресам `^users/{pk}/set_password/$` и `^users/{pk}/unset_password/$`. Используйте параметры `url_path` и `url_name` для изменения сегмента URL и обратного имени URL действия.\n\nЧтобы просмотреть все дополнительные действия, вызовите метод `.get_extra_actions()`.\n\n### Маршрутизация дополнительных методов HTTP для дополнительных действий\n\nДополнительные действия могут сопоставлять дополнительные HTTP-методы с отдельными методами `ViewSet`. Например, описанные выше методы установки и снятия пароля могут быть объединены в один маршрут. Обратите внимание, что дополнительные сопоставления не принимают аргументов.\n\n```python\n    @action(detail=True, methods=[\"put\"], name=\"Change Password\")\n    def password(self, request, pk=None):\n        \"\"\"Update the user's password.\"\"\"\n        ...\n\n\n    @password.mapping.delete\n    def delete_password(self, request, pk=None):\n        \"\"\"Delete the user's password.\"\"\"\n        ...\n```\n\n## Получение URL-адреса действия\n\nЕсли вам нужно получить URL-адрес действия, используйте метод `.reverse_action()`. Это удобная обертка для `reverse()`, автоматически передающая объект `request` представления и дополняющая `url_name` атрибутом `.basename`.\n\nОбратите внимание, что `basename` предоставляется маршрутизатором во время регистрации `ViewSet`. Если вы не используете маршрутизатор, то вы должны предоставить аргумент `basename` методу `.as_view()`.\n\nИспользуя пример из предыдущего раздела:\n\n```pycon\n>>> view.reverse_action(\"set-password\", args=[\"1\"])\n'http://localhost:8000/api/users/1/set_password'\n```\n\nВ качестве альтернативы можно использовать атрибут `url_name`, установленный декоратором `@action`.\n\n```pycon\n>>> view.reverse_action(view.set_password.url_name, args=[\"1\"])\n'http://localhost:8000/api/users/1/set_password'\n```\n\nАргумент `url_name` для `.reverse_action()` должен совпадать с тем же аргументом декоратора `@action`. Кроме того, этот метод можно использовать для отмены действий по умолчанию, таких как `list` и `create`.\n\n---\n\n# API Reference\n\n## ViewSet\n\nКласс `ViewSet` наследуется от `APIView`. Вы можете использовать любые стандартные атрибуты, такие как `permission_classes`, `authentication_classes`, чтобы управлять политикой API для набора представлений.\n\nКласс `ViewSet` не предоставляет никаких реализаций действий. Чтобы использовать класс `ViewSet`, вам нужно переопределить его и явно определить реализацию действий.\n\n## GenericViewSet\n\nКласс `GenericViewSet` наследуется от `GenericAPIView` и предоставляет стандартный набор методов `get_object`, `get_queryset` и другие базовые поведения общих представлений, но по умолчанию не включает никаких действий.\n\nЧтобы использовать класс `GenericViewSet`, вы должны переопределить его и либо смешать необходимые классы mixin, либо явно определить реализацию действий.\n\n## ModelViewSet\n\nКласс `ModelViewSet` наследуется от `GenericAPIView` и включает в себя реализации различных действий, смешивая поведение различных классов-миксинов.\n\nКласс `ModelViewSet` предоставляет следующие действия: `.list()`, `.retrieve()`, `.create()`, `.update()`, `.partial_update()` и `.destroy()`.\n\n#### Пример\n\nПоскольку `ModelViewSet` расширяет `GenericAPIView`, вам обычно нужно предоставить как минимум атрибуты `queryset` и `serializer_class`. Например:\n\n```python\nclass AccountViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    A simple ViewSet for viewing and editing accounts.\n    \"\"\"\n    queryset = Account.objects.all()\n    serializer_class = AccountSerializer\n    permission_classes = [IsAccountAdminOrReadOnly]\n```\n\nОбратите внимание, что вы можете использовать любой из стандартных атрибутов или переопределений методов, предоставляемых `GenericAPIView`. Например, чтобы использовать `ViewSet`, который динамически определяет набор запросов, с которым он должен работать, вы можете сделать что-то вроде этого:\n\n```python\nclass AccountViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    A simple ViewSet for viewing and editing the accounts\n    associated with the user.\n    \"\"\"\n    serializer_class = AccountSerializer\n    permission_classes = [IsAccountAdminOrReadOnly]\n\n    def get_queryset(self):\n        return self.request.user.accounts.all()\n```\n\nОднако обратите внимание, что, после удаления свойства `queryset` из вашего `ViewSet`, любой связанный с ним [router](routers.md) не сможет автоматически вывести базовое имя вашей Модели, поэтому вам придется указать именованный аргумент `basename` как часть вашей регистрации [router](routers.md).\n\nТакже обратите внимание, что хотя этот класс по умолчанию предоставляет полный набор действий create/list/retrieve/update/destroy, вы можете ограничить доступные операции с помощью стандартных классов разрешений.\n\n## ReadOnlyModelViewSet\n\nКласс `ReadOnlyModelViewSet` также наследуется от `GenericAPIView`. Как и `ModelViewSet`, он также включает реализации различных действий, но в отличие от `ModelViewSet` предоставляет только действия \"только для чтения\", `.list()` и `.retrieve()`.\n\n#### Пример\n\nКак и в случае с `ModelViewSet`, обычно вам  нужно предоставить как минимум атрибуты `queryset` и `serializer_class`. Например:\n\n```python\nclass AccountViewSet(viewsets.ReadOnlyModelViewSet):\n    \"\"\"\n    A simple ViewSet for viewing accounts.\n    \"\"\"\n    queryset = Account.objects.all()\n    serializer_class = AccountSerializer\n```\n\nКак и в случае с `ModelViewSet`, вы можете использовать любые стандартные атрибуты и переопределения методов, доступные для `GenericAPIView`.\n\n# Пользовательские базовые классы ViewSet\n\nВам может понадобиться предоставить пользовательские классы `ViewSet`, которые не имеют полного набора действий `ModelViewSet` или настраивают поведение каким-либо другим способом.\n\n## Пример\n\nЧтобы создать базовый класс набора представлений, обеспечивающий операции `create`, `list` и `retrieve`, наследуйте от `GenericViewSet` и добавьте необходимые действия:\n\n```python\nfrom rest_framework import mixins, viewsets\n\nclass CreateListRetrieveViewSet(mixins.CreateModelMixin,\n                                mixins.ListModelMixin,\n                                mixins.RetrieveModelMixin,\n                                viewsets.GenericViewSet):\n    \"\"\"\n    A viewset that provides `retrieve`, `create`, and `list` actions.\n\n    To use it, override the class and set the `.queryset` and\n    `.serializer_class` attributes.\n    \"\"\"\n    pass\n```\n\nСоздавая собственные базовые классы `ViewSet`, вы можете обеспечить общее поведение, которое может быть повторно использовано в нескольких наборах представлений в вашем API.\n"
  },
  {
    "path": "monitoring_config.toml",
    "content": "[repository]\nrepo_url = 'https://github.com/encode/django-rest-framework.git'\nrepo_branch = 'main'\noriginal_link = 'https://github.com/encode/django-rest-framework/tree/main/docs'\npaths = [\n    ['docs/api-guide/', '.reference/api-guide/'],\n    ['docs/tutorial/', '.reference/tutorial/'],\n    ['docs/coreapi/', '.reference/tutorial/'],\n    ['docs/topics/', '.reference/topics/'],\n    ['docs/index.md', '.reference/README.md'],\n]\ncache_file = '.files_cache.json'\n"
  },
  {
    "path": "quickstart.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Быстрый старт\n\nМы создадим простой API, который позволит администраторам просматривать и редактировать пользователей и группы в системе.\n\n## Настройка проекта\n\nСоздайте новый проект Django под названием `tutorial`, затем создайте новое приложение под названием `quickstart`.\n\n```bash\n# Create the project directory\nmkdir tutorial\ncd tutorial\n\n# Create a virtual environment to isolate our package dependencies locally\npython3 -m venv env\nsource env/bin/activate  # On Windows use `env\\Scripts\\activate`\n\n# Install Django and Django REST framework into the virtual environment\npip install djangorestframework\n\n# Set up a new project with a single application\ndjango-admin startproject tutorial .  # Note the trailing '.' character\ncd tutorial\ndjango-admin startapp quickstart\ncd ..\n```\n\nСхема проекта должна выглядеть следующим образом:\n\n```bash\n$ pwd\n<some path>/tutorial\n$ find .\n.\n./tutorial\n./tutorial/asgi.py\n./tutorial/__init__.py\n./tutorial/quickstart\n./tutorial/quickstart/migrations\n./tutorial/quickstart/migrations/__init__.py\n./tutorial/quickstart/models.py\n./tutorial/quickstart/__init__.py\n./tutorial/quickstart/apps.py\n./tutorial/quickstart/admin.py\n./tutorial/quickstart/tests.py\n./tutorial/quickstart/views.py\n./tutorial/settings.py\n./tutorial/urls.py\n./tutorial/wsgi.py\n./env\n./env/...\n./manage.py\n```\n\nМожет показаться необычным, что приложение создано в каталоге проекта. Использование пространства имен проекта позволяет избежать конфликта имен с внешними модулями (эта тема выходит за рамки данного краткого руководства).\n\nТеперь синхронизируйте базу данных в первый раз:\n\n```bash\npython manage.py migrate\n```\n\nМы также создадим начального пользователя с именем `admin` и паролем. Мы будем аутентифицироваться под этим пользователем позже в нашем примере.\n\n```bash\npython manage.py createsuperuser --username admin --email admin@example.com\n```\n\nПосле того как вы настроили базу данных и создали начального пользователя, откройте каталог приложения и приступайте к работе...\n\n## Сериализаторы\n\nСначала мы определим некоторые сериализаторы. Давайте создадим новый модуль `tutorial/quickstart/serializers.py`, который мы будем использовать для представления данных.\n\n```python\nfrom django.contrib.auth.models import Group, User\nfrom rest_framework import serializers\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n    class Meta:\n        model = User\n        fields = ['url', 'username', 'email', 'groups']\n\n\nclass GroupSerializer(serializers.HyperlinkedModelSerializer):\n    class Meta:\n        model = Group\n        fields = ['url', 'name']\n```\n\nОбратите внимание, что в данном случае мы используем гиперссылки с помощью `HyperlinkedModelSerializer`. Вы также можете использовать первичный ключ и различные другие отношения, но гиперсвязь - это хороший дизайн RESTful.\n\n## Представления\n\nИтак, нам нужно написать несколько представлений. Откройте `tutorial/quickstart/views.py` и начните печатать.\n\n```python\nfrom django.contrib.auth.models import Group, User\nfrom rest_framework import permissions, viewsets\n\nfrom tutorial.quickstart.serializers import GroupSerializer, UserSerializer\n\n\nclass UserViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    API endpoint that allows users to be viewed or edited.\n    \"\"\"\n    queryset = User.objects.all().order_by('-date_joined')\n    serializer_class = UserSerializer\n    permission_classes = [permissions.IsAuthenticated]\n\n\nclass GroupViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    API endpoint that allows groups to be viewed or edited.\n    \"\"\"\n    queryset = Group.objects.all().order_by('name')\n    serializer_class = GroupSerializer\n    permission_classes = [permissions.IsAuthenticated]\n```\n\nВместо того, чтобы писать несколько представлений, мы объединяем все общее поведение в классы под названием `ViewSets`.\n\nПри необходимости мы можем легко разбить их на отдельные представления, но использование наборов представлений позволяет сохранить логику представления хорошо организованной, а также очень лаконичной.\n\n## URL-адреса\n\nИтак, теперь давайте подключим URL-адреса API. Переходим к `tutorial/urls.py`...\n\n```python\nfrom django.urls import include, path\nfrom rest_framework import routers\n\nfrom tutorial.quickstart import views\n\nrouter = routers.DefaultRouter()\nrouter.register(r'users', views.UserViewSet)\nrouter.register(r'groups', views.GroupViewSet)\n\n# Wire up our API using automatic URL routing.\n# Additionally, we include login URLs for the browsable API.\nurlpatterns = [\n    path('', include(router.urls)),\n    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))\n]\n```\n\nПоскольку мы используем наборы представлений, а не представления, мы можем автоматически генерировать URL conf для нашего API, просто зарегистрировав наборы представлений в классе маршрутизатора.\n\nОпять же, если нам нужен больший контроль над URL API, мы можем просто вернуться к использованию обычных представлений на основе классов и явного написания URL conf.\n\nНаконец, мы включаем стандартные представления входа и выхода для использования с Web-интерфейсом API. Это необязательно, но полезно, если ваш API требует аутентификации, а вы хотите использовать Web-интерфейс API.\n\n## Пагинация\n\nПагинация позволяет управлять количеством возвращаемых объектов на странице. Чтобы включить эту функцию, добавьте следующие строки в `tutorial/settings.py`\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n    'PAGE_SIZE': 10\n}\n```\n\n## Настройки\n\nДобавьте `'rest_framework'` в `INSTALLED_APPS`. Модуль настроек будет находиться в файле `tutorial/settings.py`.\n\n```python\nINSTALLED_APPS = [\n    ...\n    'rest_framework',\n]\n```\n\nХорошо, мы закончили.\n\n---\n\n## Тестирование нашего API\n\nТеперь мы готовы протестировать созданный нами API. Давайте запустим сервер из командной строки.\n\n```bash\npython manage.py runserver\n```\n\nТеперь мы можем получить доступ к нашему API как из командной строки, так и с помощью таких инструментов, как `curl`...\n\n```bash\nbash: curl -u admin -H 'Accept: application/json; indent=4' http://127.0.0.1:8000/users/\nEnter host password for user 'admin':\n{\n    \"count\": 1,\n    \"next\": null,\n    \"previous\": null,\n    \"results\": [\n        {\n            \"url\": \"http://127.0.0.1:8000/users/1/\",\n            \"username\": \"admin\",\n            \"email\": \"admin@example.com\",\n            \"groups\": []\n        }\n    ]\n}\n```\n\nИли с помощью [httpie](https://httpie.io/docs#installation), инструмента командной строки...\n\n```bash\nbash: http -a admin http://127.0.0.1:8000/users/\nhttp: password for admin@127.0.0.1:8000::\n$HTTP/1.1 200 OK\n...\n{\n    \"count\": 1,\n    \"next\": null,\n    \"previous\": null,\n    \"results\": [\n        {\n            \"email\": \"admin@example.com\",\n            \"groups\": [],\n            \"url\": \"http://127.0.0.1:8000/users/1/\",\n            \"username\": \"admin\"\n        }\n    ]\n}\n```\n\nИли непосредственно через браузер, перейдя по URL-адресу `http://127.0.0.1:8000/users/`...\n\n![Изображение быстрого запуска](https://github.com/encode/django-rest-framework/raw/main/docs/img/quickstart.png)\n\nЕсли вы работаете через браузер, обязательно войдите в систему, используя элемент управления в правом верхнем углу.\n\nОтлично, это было легко!\n\nЕсли вы хотите получить более глубокое понимание того, как устроен DRF, перейдите к [учебнику](tutorial/1-serialization.md) или начните просматривать [руководство по API](api-guide/requests.md).\n"
  },
  {
    "path": "topics/ajax-csrf-cors.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Работа с AJAX, CSRF и CORS\n\n> \"Внимательно изучите возможные уязвимости CSRF / XSRF на ваших собственных сайтах. Это худший вид уязвимостей - их очень легко использовать злоумышленникам, но не так просто интуитивно понять разработчикам программного обеспечения, по крайней мере, пока вас не укусит одна из них.\"\n>\n> - [Jeff Atwood](https://blog.codinghorror.com/preventing-csrf-and-xsrf-attacks/)\n\n## Клиенты Javascript\n\nЕсли вы создаете JavaScript-клиент для взаимодействия с вашим Web API, вам нужно подумать, может ли клиент использовать ту же политику аутентификации, которая используется остальной частью сайта, а также определить, нужно ли вам использовать CSRF-токены или CORS-заголовки.\n\nAJAX-запросы, выполняемые в том же контексте, что и API, с которым они взаимодействуют, обычно используют `SessionAuthentication`. Это гарантирует, что после того, как пользователь вошел в систему, любые запросы AJAX могут быть аутентифицированы с помощью той же сеансовой аутентификации, которая используется для остальной части сайта.\n\nAJAX-запросы, которые выполняются на сайте, отличном от сайта API, с которым они взаимодействуют, обычно должны использовать схему аутентификации, не основанную на сеансах, например `TokenAuthentication`.\n\n## Защита от CSRF\n\n[Защита от подделки межсайтовых запросов](https://owasp.org/www-community/attacks/csrf) - это механизм защиты от определенного типа атак, которые могут возникнуть, когда пользователь не вышел с веб-сайта и продолжает иметь действующую сессию. В этом случае вредоносный сайт может выполнить действия против целевого сайта в контексте вошедшей сессии.\n\nЧтобы защититься от такого рода атак, вам нужно сделать две вещи:\n\n1. Убедитесь, что \"безопасные\" операции HTTP, такие как `GET`, `HEAD` и `OPTIONS`, не могут быть использованы для изменения состояния на стороне сервера.\n2. Убедитесь, что для любых \"небезопасных\" операций HTTP, таких как `POST`, `PUT`, `PATCH` и `DELETE`, всегда требуется действительный токен CSRF.\n\nЕсли вы используете `SessionAuthentication`, вам необходимо включить действительные CSRF-токены для любых операций `POST`, `PUT`, `PATCH` или `DELETE`.\n\nЧтобы выполнять AJAX-запросы, необходимо включить CSRF-токен в HTTP-заголовок, как [описано в документации Django](https://docs.djangoproject.com/en/stable/howto/csrf/#using-csrf-protection-with-ajax).\n\n## CORS\n\n[Cross-Origin Resource Sharing](https://www.w3.org/TR/cors/) - это механизм, позволяющий клиентам взаимодействовать с API, размещенными на другом домене. CORS работает, требуя от сервера включения определенного набора заголовков, которые позволяют браузеру определить, разрешены ли междоменные запросы и когда они должны быть разрешены.\n\nЛучший способ справиться с CORS в DRF - добавить необходимые заголовки ответа в middleware. Это гарантирует, что CORS поддерживается прозрачно, без необходимости изменять какое-либо поведение в ваших представлениях.\n\n[Adam Johnson](https://github.com/adamchainz) поддерживает пакет [django-cors-headers](https://github.com/adamchainz/django-cors-headers), который, как известно, корректно работает с API DRF.\n"
  },
  {
    "path": "topics/browsable-api.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Browsable API\n\n> Это глубоко ошибочный трюизм... что мы должны культивировать привычку думать о том, что мы делаем. Все обстоит с точностью до наоборот. Цивилизация развивается за счет увеличения числа важных операций, которые мы можем выполнять, не задумываясь о них.\n>\n> &mdash; [Альфред Норт Уайтхед](https://en.wikiquote.org/wiki/Alfred_North_Whitehead), Введение в математику (1911)\n\nAPI может означать интерфейс прикладного _программирования_(Application _Programming_ Interface), но люди тоже должны уметь читать API; кто-то должен заниматься программированием. DRF поддерживает генерацию удобного для человека HTML-вывода для каждого ресурса при запросе формата `HTML`. Эти страницы позволяют легко просматривать ресурсы, а также содержат формы для отправки данных на ресурсы с помощью `POST`, `PUT` и `DELETE`.\n\n## URLs\n\nЕсли вы включите в вывод ресурсов полностью определенные URL, они будут \"урлизованы\" и сделаны кликабельными для удобства просмотра человеком. Для этого в пакет `rest_framework` включен помощник [`reverse`](../api-guide/reverse.md).\n\n## Форматы\n\nПо умолчанию API возвращает формат, указанный в заголовках, который в случае браузера является HTML. Формат может быть указан с помощью `?format=` в запросе, так что вы можете просмотреть необработанный JSON-ответ в браузере, добавив `?format=json` к URL. Существуют полезные расширения для просмотра JSON в [Firefox](https://addons.mozilla.org/en-US/firefox/addon/jsonview/) и [Chrome](https://chrome.google.com/webstore/detail/chklaanhfefbnpoihckbnefhakgolnmc).\n\n## Аутентификация\n\nЧтобы быстро добавить аутентификацию в Web-интерфейсе API, добавьте маршруты с именами `\"login\"` и `\"logout\"` в пространстве имен `\"rest_framework\"`. DRF предоставляет для этого маршруты по умолчанию, которые вы можете добавить в свой urlconf:\n\n```python\nfrom django.urls import include, path\n\nurlpatterns = [\n    # ...\n    path(\"api-auth/\", include(\"rest_framework.urls\", namespace=\"rest_framework\"))\n]\n```\n\n## Настройка\n\nWeb-интерфейс API построен с использованием [Twitter's Bootstrap](https://getbootstrap.com/) (v 3.4.1), что позволяет легко настроить внешний вид и функциональность.\n\nЧтобы настроить стиль по умолчанию, создайте шаблон `rest_framework/api.html`, который расширяет `rest_framework/base.html`. Например:\n\n**templates/rest_framework/api.html**\n\n```html\n{% extends \"rest_framework/base.html\" %}\n\n...  # Override blocks with required customizations\n```\n\n### Переопределение темы по умолчанию\n\nЧтобы заменить тему по умолчанию, добавьте блок `bootstrap_theme` в ваш `api.html` и вставьте `ссылку` на нужный css-файл темы Bootstrap. Это полностью заменит включенную тему.\n\n```html\n{% block bootstrap_theme %}\n    <link rel=\"stylesheet\" href=\"/path/to/my/bootstrap.css\" type=\"text/css\">\n{% endblock %}\n```\n\nПодходящие готовые темы для замены доступны на сайте [Bootswatch](https://bootswatch.com/). Чтобы использовать любую из тем Bootswatch, просто скачайте файл `bootstrap.min.css` этой темы, добавьте его в свой проект и замените тему по умолчанию, как описано выше. Убедитесь, что версия Bootstrap новой темы совпадает с версией темы по умолчанию.\n\nВы также можете изменить вариант навигационной панели, которая по умолчанию имеет значение `navbar-inverse`, с помощью блока `bootstrap_navbar_variant`. Пустой блок `{% block bootstrap_navbar_variant %}{% endblock %}` будет использовать оригинальный стиль навигационной панели Bootstrap.\n\nПолный пример:\n\n```html\n{% extends \"rest_framework/base.html\" %}\n\n{% block bootstrap_theme %}\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootswatch@3.4.1/flatly/bootstrap.min.css\" type=\"text/css\">\n{% endblock %}\n\n{% block bootstrap_navbar_variant %}{% endblock %}\n```\n\nДля более специфических CSS-настроек, чем просто переопределение темы bootstrap по умолчанию, вы можете переопределить блок `style`.\n\n---\n\n![Cerulean theme](https://github.com/encode/django-rest-framework/raw/main/docs/img/cerulean.png)\n\n*Скриншот темы \"Cerulean\"*\n\n---\n\n![Slate theme](https://github.com/encode/django-rest-framework/raw/main/docs/img/slate.png)\n\n*Скриншот темы \"Slate\"*\n\n---\n\n### Пакеты сторонних разработчиков для настройки\n\nВы можете использовать сторонние пакеты для кастомизации, а не делать это самостоятельно. Вот 3 пакета для настройки API:\n\n* [drf-restwind](https://github.com/youzarsiph/drf-restwind) - Современное переосмысление REST-фреймворка Django использует TailwindCSS и DaisyUI для создания гибких и настраиваемых решений пользовательского интерфейса с минимальными усилиями по кодированию.\n* [drf-redesign](https://github.com/youzarsiph/drf-redesign) - Пакет для настройки API с помощью Bootstrap 5. Современный и элегантный дизайн, поддержка темного режима.\n* [drf-material](https://github.com/youzarsiph/drf-material) - Материальный дизайн для Django REST Framework.\n\n---\n\n![API Root](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-rw-api-root.png)\n\n![List View](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-rw-list-view.png)\n\n![Detail View](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-rw-detail-view.png)\n\n*Скриншоты drf-restwind*\n\n---\n\n![API Root](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-r-api-root.png)\n\n![List View](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-r-list-view.png)\n\n![Detail View](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-r-detail-view.png)\n\n*Скриншоты drf-redesign*\n\n---\n\n![API Root](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-m-api-root.png)\n\n![List View](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-m-list-view.png)\n\n![Detail View](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-m-detail-view.png)\n\n*Скриншоты drf-material*\n\n---\n\n### Блоки\n\nВсе блоки, доступные в базовом шаблоне Web-интерфейса API, которые можно использовать в вашем `api.html`.\n\n* `body` - Весь html-тег `<body>`.\n* `bodyclass` - Атрибут класса для тега `<body>`, по умолчанию пустой.\n* `bootstrap_theme` - CSS для темы Bootstrap.\n* `bootstrap_navbar_variant` - CSS-класс для навигационной панели.\n* `branding` - Раздел брендирования navbar, см. [Bootstrap components](https://getbootstrap.com/2.3.2/components.html#navbar).\n* `breadcrumbs` - Ссылки, показывающие вложенность ресурсов, позволяющие пользователю вернуться к ним. Рекомендуется сохранять их, но их можно переопределить с помощью блока breadcrumbs.\n* `script` - Файлы JavaScript для страницы.\n* `style` - Таблицы стилей CSS для страницы.\n* `title` - Заголовок страницы.\n* `userlinks` - Список ссылок справа от заголовка, по умолчанию содержит ссылки на вход/выход. Чтобы добавить ссылки вместо замены, используйте `{{ block.super }}`, чтобы сохранить ссылки аутентификации.\n\n#### Компоненты\n\nДоступны все стандартные [Bootstrap-компоненты](https://getbootstrap.com/2.3.2/components.html).\n\n#### Всплывающие подсказки\n\nWeb-интерфейс API использует компонент Bootstrap tooltips. Любой элемент с классом `js-tooltip` и атрибутом `title` имеет содержание заголовка, который будет отображать всплывающую подсказку при наведении.\n\n### Шаблон для входа в систему\n\nЧтобы добавить брендинг и настроить внешний вид шаблона входа в систему, создайте шаблон `login.html` и добавьте его в свой проект, например: `templates/rest_framework/login.html`. Шаблон должен расширяться от `rest_framework/login_base.html`.\n\nВы можете добавить название своего сайта или брендинг, включив блок брендинга:\n\n```html\n{% extends \"rest_framework/login_base.html\" %}\n\n{% block branding %}\n    <h3 style=\"margin: 0 0 20px;\">My Site Name</h3>\n{% endblock %}\n```\n\nВы также можете настроить стиль, добавив блок `bootstrap_theme` или `style`, аналогичный `api.html`.\n\n### Расширенная настройка\n\n#### Контекст\n\nКонтекст, доступный шаблону:\n\n* `allowed_methods` : Список методов, разрешенных ресурсу\n* `api_settings` : Настройки API\n* `available_formats` : Список форматов, разрешенных ресурсом\n* `breadcrumblist` : Список ссылок, следующих по цепочке вложенных ресурсов\n* `content` : Содержание ответа API\n* `description` : Описание ресурса, сгенерированное из его docstring\n* `name` : Название ресурса\n* `post_form` : Экземпляр формы для использования POST-формой (если разрешено)\n* `put_form` : Экземпляр формы для использования PUT-формой (если разрешено)\n* `display_edit_forms` : Булево значение, указывающее, будут ли отображаться формы POST, PUT и PATCH.\n* `request` : Объект запроса\n* `response` : Объект ответа\n* `version` : Версия Django REST Framework\n* `view` : Представление, обрабатывающее запрос\n* `FORMAT_PARAM` : Представление может принимать переопределение формата\n* `METHOD_PARAM` : Представление может принимать переопределение метода\n\nВы можете переопределить метод `BrowsableAPIRenderer.get_context()`, чтобы настроить контекст, передаваемый в шаблон.\n\n#### Неиспользование файла base.html\n\nДля более продвинутой настройки, например, без основы Bootstrap или более тесной интеграции с остальным сайтом, вы можете просто убрать в `api.html` расширение `base.html`. Тогда содержание и возможности страницы будут зависеть только от вас.\n\n#### Обработка `ChoiceField` с большим количеством элементов.\n\nКогда в отношениях или `ChoiceField` слишком много элементов, рендеринг виджета, содержащего все варианты, может стать очень медленным, что приведет к плохой работе рендеринга Web-интерфейса API.\n\nСамый простой вариант в этом случае - заменить виджет select на стандартный текстовый виджет. Например:\n\n```python\nauthor = serializers.HyperlinkedRelatedField(\n    queryset=User.objects.all(),\n    style={'base_template': 'input.html'}\n)\n```\n\n#### Автозаполнение\n\nАльтернативным, но более сложным вариантом будет замена виджета ввода виджетом автозаполнения, который будет загружать и отображать только подмножество доступных вариантов по мере необходимости. Если вам нужно сделать это, вам придется потрудиться, чтобы создать собственный HTML-шаблон автозаполнения.\n\nСуществует [множество пакетов для виджетов автозаполнения](https://www.djangopackages.com/grids/g/auto-complete/), например [django-autocomplete-light](https://github.com/yourlabs/django-autocomplete-light), к которым вы можете обратиться. Обратите внимание, что вы не сможете просто включить эти компоненты в качестве стандартных виджетов, а должны будете явно написать HTML-шаблон. Это связано с тем, что REST framework 3.0 больше не поддерживает именованный аргумент `widget`, так как теперь он использует шаблонизацию HTML.\n"
  },
  {
    "path": "topics/browser-enhancements.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Улучшения в браузере\n\n> \"Есть два не вызывающих споров варианта использования перегруженного POST. Первое - это *имитация* унифицированного интерфейса HTTP для клиентов, таких как веб-браузеры, которые не поддерживают PUT или DELETE\".\n>\n> - [RESTful Web Services](https://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260), Leonard Richardson & Sam Ruby.\n\nДля того чтобы Web-интерфейс API функционировал, есть несколько усовершенствований для браузеров, которые должны быть предоставлены REST-фреймворком.\n\nНачиная с версии 3.3.0 и далее они включаются с помощью javascript, используя библиотеку [ajax-form](https://github.com/tomchristie/ajax-form).\n\n## PUT, DELETE и т.д. на основе браузера.\n\nБиблиотека [AJAX form library](https://github.com/tomchristie/ajax-form) поддерживает браузерные методы `PUT`, `DELETE` и другие методы на HTML формах.\n\nПосле включения библиотеки используйте атрибут `data-method` на форме, как показано ниже:\n\n```html\n<form action=\"/\" data-method=\"PUT\">\n    <input name='foo'/>\n    ...\n</form>\n```\n\nОбратите внимание, что до версии 3.3.0 эта поддержка осуществлялась на стороне сервера, а не на основе javascript. Стиль перегрузки методов (используемый в [Ruby on Rails](https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-put-or-delete-methods-work)) больше не поддерживается из-за некоторых проблем, возникающих при разборе запросов.\n\n## Отправка контента вне формы с помощью браузера\n\nОтправка через браузер таких типов содержимого, как JSON, поддерживается [библиотекой форм AJAX](https://github.com/tomchristie/ajax-form), используя поля формы с атрибутами `data-override='content-type'` и `data-override='content'`.\n\nНапример:\n\n```html\n<form action=\"/\">\n        <input data-override='content-type' value='application/json' type='hidden'/>\n        <textarea data-override='content'>{}</textarea>\n        <input type=\"submit\"/>\n    </form>\n```\n\nОбратите внимание, что до версии 3.3.0 эта поддержка осуществлялась на стороне сервера, а не на основе javascript.\n\n## Суффиксы формата на основе URL\n\nDRF может принимать параметры URL в стиле `?format=json`, что может быть полезным сокращением для определения типа содержимого, которое должно быть возвращено из представления.\n\nЭто поведение контролируется с помощью параметра `URL_FORMAT_OVERRIDE`.\n\n## Переопределение метода на основе заголовка HTTP\n\nДо версии 3.3.0 поддерживался заголовок расширения `X-HTTP-Method-Override` для переопределения метода запроса. Это поведение больше не используется в ядре, но может быть добавлено при необходимости с помощью middleware.\n\nНапример:\n\n```python\nMETHOD_OVERRIDE_HEADER = 'HTTP_X_HTTP_METHOD_OVERRIDE'\n\nclass MethodOverrideMiddleware:\n\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        if request.method == 'POST' and METHOD_OVERRIDE_HEADER in request.META:\n            request.method = request.META[METHOD_OVERRIDE_HEADER]\n        return self.get_response(request)\n```\n\n## URL based accept headers\n\n## Заголовки accept на основе URL\n\nДо версии 3.3.0 DRF включал встроенную поддержку параметров URL в стиле `?accept=application/json`, что позволяло переопределять заголовок `Accept`.\n\nПосле внедрения API согласования контента это поведение больше не включено в ядро, но может быть добавлено с помощью пользовательского класса согласования контента, если это необходимо.\n\nНапример:\n\n```python\nclass AcceptQueryParamOverride()\n    def get_accept_list(self, request):\n       header = request.META.get('HTTP_ACCEPT', '*/*')\n       header = request.query_params.get('_accept', header)\n       return [token.strip() for token in header.split(',')]\n```\n\n## Разве HTML5 не поддерживает формы PUT и DELETE?\n\nНет. Одно время предполагалось, что HTML5 будет поддерживать формы `PUT` и `DELETE`, но позже это было [исключено из спецификации](https://www.w3.org/TR/html5-diff/#changes-2010-06-24). Остается [продолжающееся обсуждение](http://amundsen.com/examples/put-delete-forms/) добавления поддержки `PUT` и `DELETE`, а также того, как поддерживать типы содержимого, отличные от закодированных в форме данных.\n"
  },
  {
    "path": "topics/documenting-your-api.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Документирование вашего API\n\n> REST API должен тратить почти все свои усилия по описанию на определение типа(ов) носителей, используемых для представления ресурсов и управления состоянием приложения.\n>\n> - Рой Филдинг, [REST API должны быть гипертекстовыми](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)\n\nDRF предоставляет ряд различных вариантов документирования вашего API. Ниже приведен неполный список наиболее популярных из них.\n\n## Пакеты сторонних разработчиков для поддержки OpenAPI\n\n### drf-spectacular\n\n[drf-spectacular](https://github.com/tfranzel/drf-spectacular/) - это библиотека генерации схем [OpenAPI 3](https://openapis.org/) с явным акцентом на расширяемость, настраиваемость и генерацию клиентов. Это рекомендуемый способ генерации и представления схем OpenAPI.\n\nБиблиотека стремится извлечь как можно больше информации о схеме, предоставляя при этом декораторы и расширения для легкой настройки. Имеется явная поддержка [swagger-codegen](https://swagger.io/), [SwaggerUI](https://swagger.io/tools/swagger-ui/) и [Redoc](https://github.com/Rebilly/ReDoc), i18n, версионность, аутентификация, полиморфизм (динамические запросы и ответы), параметры запроса/пути/заголовка, документация и многое другое. Несколько популярных плагинов для DRF также поддерживаются \"из коробки\".\n\n### drf-yasg\n\n[drf-yasg](https://github.com/axnsan12/drf-yasg/) - это инструмент генерации [Swagger / OpenAPI 2](https://swagger.io/), реализованный без использования генерации схем, предоставляемой DRF.\n\nЕго цель - реализовать как можно больше спецификации [OpenAPI 2](https://openapis.org/) - вложенные схемы, именованные модели, тела ответов, валидаторы enum/pattern/min/max, параметры формы и др. - и генерировать документы, пригодные для использования с помощью инструментов генерации кода, таких как `swagger-codegen`.\n\nЭто также воплощается в очень полезном интерактивном средстве просмотра документации в виде `swagger-ui`:\n\n![Скриншот - drf-yasg](https://github.com/encode/django-rest-framework/raw/main/docs/img/drf-yasg.png)\n\n---\n\n## Built-in OpenAPI schema generation (deprecated)\n\n## Встроенная генерация схем OpenAPI (устаревшая)\n\n**Уведомление о сокращении: Встроенная в REST framework поддержка генерации схем OpenAPI устарела в пользу сторонних пакетов, которые могут предоставить эту функциональность вместо нее. В качестве замены мы рекомендуем использовать пакет [drf-spectacular](#drf-spectacular).**.\n\nСуществует ряд пакетов, позволяющих генерировать HTML-страницы документации на основе схем OpenAPI.\n\nДва популярных варианта - [Swagger UI](https://swagger.io/tools/swagger-ui/) и [ReDoc](https://github.com/Rebilly/ReDoc).\n\nОба требуют лишь указания местоположения статического файла схемы или динамической конечной точки `SchemaView`.\n\n### Минимальный пример с Swagger UI\n\nЕсли предположить, что вы последовали примеру из документации по схемам для маршрутизации динамического `SchemaView`, минимальный шаблон Django для использования Swagger UI может быть таким:\n\n```html\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>Swagger</title>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link\n      rel=\"stylesheet\"\n      type=\"text/css\"\n      href=\"//unpkg.com/swagger-ui-dist@3/swagger-ui.css\"\n    />\n  </head>\n  <body>\n    <div id=\"swagger-ui\"></div>\n    <script src=\"//unpkg.com/swagger-ui-dist@3/swagger-ui-bundle.js\"></script>\n    <script>\n      const ui = SwaggerUIBundle({\n        url: \"{% url schema_url %}\",\n        dom_id: \"#swagger-ui\",\n        presets: [\n          SwaggerUIBundle.presets.apis,\n          SwaggerUIBundle.SwaggerUIStandalonePreset,\n        ],\n        layout: \"BaseLayout\",\n        requestInterceptor: (request) => {\n          request.headers[\"X-CSRFToken\"] = \"{{ csrf_token }}\";\n          return request;\n        },\n      });\n    </script>\n  </body>\n</html>\n```\n\nСохраните его в папке templates под именем `swagger-ui.html`. Затем добавьте маршрут `TemplateView` в URL conf вашего проекта:\n\n```python\nfrom django.views.generic import TemplateView\n\nurlpatterns = [\n    # ...\n    # Route TemplateView to serve Swagger UI template.\n    #   * Provide `extra_context` with view name of `SchemaView`.\n    path(\n        \"swagger-ui/\",\n        TemplateView.as_view(\n            template_name=\"swagger-ui.html\",\n            extra_context={\"schema_url\": \"openapi-schema\"},\n        ),\n        name=\"swagger-ui\",\n    ),\n]\n```\n\nРасширенные возможности использования см. в [Swagger UI documentation](https://swagger.io/tools/swagger-ui/).\n\n### Минимальный пример с ReDoc.\n\nЕсли предположить, что вы последовали примеру из документации по схемам для маршрутизации динамического `SchemaView`, минимальный шаблон Django для использования ReDoc может быть таким:\n\n```html\n<!DOCTYPE html>\n<html>\n  <head>\n    <title>ReDoc</title>\n    <!-- needed for adaptive design -->\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <link\n      href=\"https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700\"\n      rel=\"stylesheet\"\n    />\n    <!-- ReDoc doesn't change outer page styles -->\n    <style>\n      body {\n        margin: 0;\n        padding: 0;\n      }\n    </style>\n  </head>\n  <body>\n    <redoc spec-url=\"{% url schema_url %}\"></redoc>\n    <script src=\"https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js\"></script>\n  </body>\n</html>\n```\n\nСохраните его в папке шаблонов под именем `redoc.html`. Затем проложите маршрут `TemplateView` в URL conf вашего проекта:\n\n```python\nfrom django.views.generic import TemplateView\n\nurlpatterns = [\n    # ...\n    # Route TemplateView to serve the ReDoc template.\n    #   * Provide `extra_context` with view name of `SchemaView`.\n    path(\n        \"redoc/\",\n        TemplateView.as_view(\n            template_name=\"redoc.html\", extra_context={\"schema_url\": \"openapi-schema\"}\n        ),\n        name=\"redoc\",\n    ),\n]\n```\n\nРасширенное использование см. в [документации ReDoc](https://github.com/Rebilly/ReDoc).\n\n## Самоописывающиеся API\n\nWeb-интерфейс API, который предоставляет DRF, позволяет вашему API быть полностью самоописывающимся. Документация для каждой конечной точки API может быть предоставлена просто при посещении URL-адреса в браузере.\n\n![Скриншот - API самоописания](https://github.com/encode/django-rest-framework/raw/main/docs/img/self-describing.png)\n\n---\n\n#### Установка заголовка\n\nЗаголовок, который используется в Web-интерфейсе API, генерируется из имени класса представления или имени функции. Любой суффикс `View` или `ViewSet` удаляется, а строка разделяется пробелами по прописным/строчным буквам или подчеркиваниям.\n\nНапример, представление `UserListView`, будет называться `User List`, когда будет представлено в Web-интерфейсе API.\n\nПри работе с наборами представлений к каждому сгенерированному представлению добавляется соответствующий суффикс. Например, набор представлений `UserViewSet` будет генерировать представления с именами `User List` и `User Instance`.\n\n#### Установка описания\n\nОписание в Web-интерфейсе API генерируется из docstring представления или набора представлений.\n\nЕсли установлена библиотека python `Markdown`, то в docstring можно использовать [синтаксис markdown](https://daringfireball.net/projects/markdown/syntax), который будет преобразован в HTML в Web-интерфейсе API. Например:\n\n```python\nclass AccountListView(views.APIView):\n    \"\"\"\n    Returns a list of all **active** accounts in the system.\n\n    For more details on how accounts are activated please [see here][ref].\n\n    [ref]: http://example.com/activating-accounts\n    \"\"\"\n```\n\nОбратите внимание, что при использовании наборов представлений базовая строка документа используется для всех создаваемых представлений. Чтобы предоставить описания для каждого представления, например, для представлений list и retrieve, используйте секции docstring, как описано в [Schemas as documentation](../api-guide/schemas.md).\n\n#### Метод `OPTIONS`.\n\nAPI DRF также поддерживают программно доступные описания, используя HTTP-метод `OPTIONS`. Представление отвечает на запрос `OPTIONS` с метаданными, включающими название, описание и различные типы медиа, которые оно принимает и на которые отвечает.\n\nПри использовании общих представлений, любые запросы `OPTIONS` будут получать ответ с метаданными о любых доступных действиях `POST` или `PUT`, описывая, какие поля находятся в сериализаторе.\n\nВы можете изменить поведение ответа на `OPTIONS` запросы, переопределив метод представления `options` и/или предоставив пользовательский класс Metadata. Например:\n\n```python\ndef options(self, request, *args, **kwargs):\n    \"\"\"\n    Don't include the view description in OPTIONS responses.\n    \"\"\"\n    meta = self.metadata_class()\n    data = meta.determine_metadata(request, self)\n    data.pop('description')\n    return Response(data=data, status=status.HTTP_200_OK)\n```\n\nБолее подробную информацию смотрите в [документации по метаданным](../api-guide/metadata.md).\n\n---\n\n## The hypermedia approach\n\n## Гипермедийный подход\n\nЧтобы быть полностью RESTful, API должен представлять свои доступные действия в виде гипермедийных элементов управления в ответах, которые он отправляет.\n\nПри таком подходе, вместо того, чтобы документировать доступные конечные точки API, описание концентрируется на *типах медиа*, которые используются. Доступные действия, которые могут быть предприняты на любом данном URL, не являются строго фиксированными, но вместо этого становятся доступными благодаря наличию элементов управления ссылками и формами в возвращаемом документе.\n\nЧтобы реализовать гипермедийный API, вам необходимо выбрать подходящий тип медиа для API и реализовать пользовательский рендерер и парсер для этого типа медиа. Раздел документации [REST, Hypermedia & HATEOAS](rest-hypermedia-hateoas.md) содержит указатели на справочную литературу, а также ссылки на различные форматы гипермедиа.\n"
  },
  {
    "path": "topics/html-and-forms.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# HTML и формы\n\nDRF подходит для возврата как ответов в стиле API, так и обычных HTML-страниц. Кроме того, сериализаторы могут использоваться в качестве HTML-форм и отображаться в шаблонах.\n\n## Рендеринг HTML\n\nДля возврата HTML-ответов вам нужно использовать либо `TemplateHTMLRenderer`, либо `StaticHTMLRenderer`.\n\nКласс `TemplateHTMLRenderer` ожидает, что ответ будет содержать словарь данных контекста, и создает HTML-страницу на основе шаблона, который должен быть указан либо в представлении, либо в ответе.\n\nКласс `StaticHTMLRender` ожидает, что ответ будет содержать строку предварительно отрендеренного HTML-содержимого.\n\nПоскольку поведение статических HTML-страниц обычно отличается от поведения ответов API, вам, вероятно, придется писать любые HTML-представления явно, а не полагаться на встроенные типовые представления.\n\nВот пример представления, которое возвращает список экземпляров \"Profile\", отображенный в шаблоне HTML:\n\n**views.py**:\n\n```python\nfrom my_project.example.models import Profile\nfrom rest_framework.renderers import TemplateHTMLRenderer\nfrom rest_framework.response import Response\nfrom rest_framework.views import APIView\n\n\nclass ProfileList(APIView):\n    renderer_classes = [TemplateHTMLRenderer]\n    template_name = 'profile_list.html'\n\n    def get(self, request):\n        queryset = Profile.objects.all()\n        return Response({'profiles': queryset})\n```\n\n**profile_list.html**:\n\n```html\n<html><body>\n<h1>Profiles</h1>\n<ul>\n    {% for profile in profiles %}\n    <li>{{ profile.name }}</li>\n    {% endfor %}\n</ul>\n</body></html>\n```\n\n## Рендеринг форм\n\nСериализаторы можно отображать в виде форм, используя тег шаблона `render_form` и включая экземпляр сериализатора в качестве контекста в шаблон.\n\nСледующее представление демонстрирует пример использования сериализатора в шаблоне для просмотра и обновления экземпляра модели:\n\n**views.py**:\n\n```python\nfrom django.shortcuts import get_object_or_404\nfrom my_project.example.models import Profile\nfrom rest_framework.renderers import TemplateHTMLRenderer\nfrom rest_framework.views import APIView\n\n\nclass ProfileDetail(APIView):\n    renderer_classes = [TemplateHTMLRenderer]\n    template_name = 'profile_detail.html'\n\n    def get(self, request, pk):\n        profile = get_object_or_404(Profile, pk=pk)\n        serializer = ProfileSerializer(profile)\n        return Response({'serializer': serializer, 'profile': profile})\n\n    def post(self, request, pk):\n        profile = get_object_or_404(Profile, pk=pk)\n        serializer = ProfileSerializer(profile, data=request.data)\n        if not serializer.is_valid():\n            return Response({'serializer': serializer, 'profile': profile})\n        serializer.save()\n        return redirect('profile-list')\n```\n\n**profile_detail.html**:\n\n```html\n{% load rest_framework %}\n\n<html><body>\n\n<h1>Profile - {{ profile.name }}</h1>\n\n<form action=\"{% url 'profile-detail' pk=profile.pk %}\" method=\"POST\">\n    {% csrf_token %}\n    {% render_form serializer %}\n    <input type=\"submit\" value=\"Save\">\n</form>\n\n</body></html>\n```\n\n### Использование пакетов шаблонов\n\nТег `render_form` принимает необязательный аргумент `template_pack`, который указывает, какой каталог шаблонов должен использоваться для рендеринга формы и полей формы.\n\nDRF включает три встроенных пакета шаблонов, все они основаны на Bootstrap 3. Встроенные стили: `horizontal`, `vertical` и `inline`. По умолчанию используется стиль `horizontal`. Чтобы использовать любой из этих пакетов шаблонов, вам необходимо также включить CSS Bootstrap 3.\n\nСледующий HTML будет ссылаться на версию CSS Bootstrap 3, размещенную в CDN:\n\n```html\n<head>\n    …\n    <link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css\">\n</head>\n```\n\nСторонние пакеты могут включать альтернативные пакеты шаблонов, в которые входит каталог шаблонов, содержащий необходимые шаблоны форм и полей.\n\nДавайте рассмотрим, как визуализировать каждый из трех доступных пакетов шаблонов. В этих примерах мы будем использовать один класс сериализатора для представления формы \"Вход в систему\".\n\n```python\nclass LoginSerializer(serializers.Serializer):\n    email = serializers.EmailField(\n        max_length=100,\n        style={'placeholder': 'Email', 'autofocus': True}\n    )\n    password = serializers.CharField(\n        max_length=100,\n        style={'input_type': 'password', 'placeholder': 'Password'}\n    )\n    remember_me = serializers.BooleanField()\n```\n\n---\n\n#### `rest_framework/vertical`.\n\nПредставляет ярлыки формы над соответствующими входами элементов управления, используя стандартный макет Bootstrap.\n\n*Это пакет шаблонов по умолчанию.*\n\n```html\n{% load rest_framework %}\n\n...\n\n<form action=\"{% url 'login' %}\" method=\"post\" novalidate>\n    {% csrf_token %}\n    {% render_form serializer template_pack='rest_framework/vertical' %}\n    <button type=\"submit\" class=\"btn btn-default\">Sign in</button>\n</form>\n```\n\n![Пример вертикальной формы](https://github.com/encode/django-rest-framework/raw/main/docs/img/vertical.png)\n\n---\n\n#### `rest_framework/horizontal`.\n\nПредставляет ярлыки и элементы управления рядом друг с другом, используя разделение колонок 2/10.\n\n*Это стиль формы, используемый в Web-интерфейсе API и администраторских рендерах.*\n\n```html\n{% load rest_framework %}\n\n...\n\n<form class=\"form-horizontal\" action=\"{% url 'login' %}\" method=\"post\" novalidate>\n    {% csrf_token %}\n    {% render_form serializer %}\n    <div class=\"form-group\">\n        <div class=\"col-sm-offset-2 col-sm-10\">\n            <button type=\"submit\" class=\"btn btn-default\">Sign in</button>\n        </div>\n    </div>\n</form>\n```\n\n![Пример горизонтальной формы](https://github.com/encode/django-rest-framework/raw/main/docs/img/horizontal.png)\n\n---\n\n#### `rest_framework/inline`.\n\nКомпактный стиль формы, который представляет все элементы управления в линию.\n\n```html\n{% load rest_framework %}\n\n...\n\n<form class=\"form-inline\" action=\"{% url 'login' %}\" method=\"post\" novalidate>\n    {% csrf_token %}\n    {% render_form serializer template_pack='rest_framework/inline' %}\n    <button type=\"submit\" class=\"btn btn-default\">Sign in</button>\n</form>\n```\n\n![Пример инлайн-формы](https://github.com/encode/django-rest-framework/raw/main/docs/img/inline.png)\n\n## Стили полей\n\nПоля сериализатора могут иметь свой стиль рендеринга, настроенный с помощью именованного аргумента `style`. Этот аргумент представляет собой словарь опций, которые управляют используемым шаблоном и макетом.\n\nНаиболее распространенным способом настройки стиля поля является использование именованного аргумента стиля `base_template`, чтобы выбрать, какой шаблон из пакета шаблонов следует использовать.\n\nНапример, чтобы отобразить `CharField` как HTML textarea, а не как HTML input по умолчанию, вы должны использовать что-то вроде этого:\n\n```python\ndetails = serializers.CharField(\n    max_length=1000,\n    style={'base_template': 'textarea.html'}\n)\n```\n\nЕсли вы хотите, чтобы поле отображалось с использованием пользовательского шаблона, который *не является частью включенного пакета шаблонов*, вы можете использовать опцию стиля `template`, чтобы полностью указать имя шаблона:\n\n```python\ndetails = serializers.CharField(\n    max_length=1000,\n    style={'template': 'my-field-templates/custom-input.html'}\n)\n```\n\nШаблоны полей также могут использовать дополнительные свойства стиля, в зависимости от их типа. Например, шаблон `textarea.html` также принимает свойство `rows`, которое можно использовать для изменения размера элемента управления.\n\n```python\ndetails = serializers.CharField(\n    max_length=1000,\n    style={'base_template': 'textarea.html', 'rows': 10}\n)\n```\n\nПолный список опций `base_template` и связанных с ними опций стиля приведен ниже.\n\n| base_template          | Правильные типы полей                                    | Дополнительные параметры стиля                   |\n| ---------------------- | -------------------------------------------------------- | ------------------------------------------------ |\n| input.html             | Любое строковое, числовое или поле даты/времени          | input_type, placeholder, hide_label, autofocus   |\n| textarea.html          | `CharField`                                              | rows, placeholder, hide_label                    |\n| select.html            | `ChoiceField` или типы реляционных полей                 | hide_label                                       |\n| radio.html             | `ChoiceField` или реляционные типы поля                  | inline, hide_label                               |\n| select_multiple.html   | `MultipleChoiceField` или реляционные поля с `many=True` | hide_label                                       |\n| checkbox_multiple.html | `MultipleChoiceField` или реляционные поля с `many=True` | inline, hide_label                               |\n| checkbox.html          | `BooleanField`                                           | hide_label                                       |\n| fieldset.html          | Вложенный сериализатор                                   | hide_label                                       |\n| list_fieldset.html     | `ListField` или вложенный сериализатор с `many=True`     | hide_label                                       |\n"
  },
  {
    "path": "topics/internationalization.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Интернационализация\n\n> Поддержка интернационализации не является необязательной. Она должна быть основной функцией.\n>\n> - [Яннис Лейдель, выступая на Django Under the Hood, 2015](https://youtu.be/Wa0VfS2q94Y).\n\nDRF поставляется с переводимыми сообщениями об ошибках. Вы можете сделать так, чтобы они отображались на вашем языке, используя [стандартные механизмы перевода Django](https://docs.djangoproject.com/en/stable/topics/i18n/translation).\n\nЭто позволит вам:\n\n- Выбрать язык по умолчанию, отличный от английского, используя стандартную настройку Django `LANGUAGE_CODE`.\n\n- Позволить клиентам самим выбирать язык, используя `LocaleMiddleware`, включенный в Django. Типичное использование для клиентов API - включение заголовка запроса `Accept-Language`.\n\n## Включение интернационализированных API\n\nВы можете изменить язык по умолчанию с помощью стандартной настройки Django `LANGUAGE_CODE`:\n\n```python\nLANGUAGE_CODE = \"es-es\"\n```\n\nВы можете включить языковые запросы на каждый запрос, добавив `LocaleMiddleware` в настройку `MIDDLEWARE`:\n\n```python\nMIDDLEWARE = [\n    ...\n    'django.middleware.locale.LocaleMiddleware'\n]\n```\n\nКогда интернационализация по каждому запросу включена, клиентские запросы будут учитывать заголовок `Accept-Language`, где это возможно. Например, давайте сделаем запрос для неподдерживаемого типа медиа:\n\n**Запрос**\n\n```http\nGET /api/users HTTP/1.1\nAccept: application/xml\nAccept-Language: es-es\nHost: example.org\n```\n\n**Ответ**\n\n```http\nHTTP/1.0 406 NOT ACCEPTABLE\n\n{\"detail\": \"No se ha podido satisfacer la solicitud de cabecera de Accept.\"}\n```\n\nDRF включает эти встроенные переводы как для стандартных случаев исключений, так и для ошибок валидации сериализатора.\n\nОбратите внимание, что переводы относятся только к самим строкам ошибок. Формат сообщений об ошибках и ключи имен полей останутся неизменными. Пример тела ответа `400 Bad Request` может выглядеть следующим образом:\n\n```http\n{\"detail\": {\"username\": [\"Esse campo deve ser único.\"]}}\n```\n\nЕсли вы хотите использовать разные строки для таких частей ответа, как `detail` и `non_field_errors`, вы можете изменить это поведение, используя [пользовательский обработчик исключений](../api-guide/exceptions.md#пользовательская-обработка-исключений).\n\n#### Указание набора поддерживаемых языков.\n\nПо умолчанию поддерживаются все доступные языки.\n\nЕсли вы хотите поддерживать только часть доступных языков, используйте стандартную настройку Django `LANGUAGES`:\n\n```python\nLANGUAGES = [\n    ('de', _('German')),\n    ('en', _('English')),\n]\n```\n\n## Добавление новых переводов\n\nПереводы REST Framework управляются на GitHub. Вы можете добавить новые языки перевода или обновить существующие, следуя инструкциям в разделе [Вклад в REST Framework](https://www.django-rest-framework.org/community/contributing.md#development) и отправив запрос на извлечение.\n\nИногда вам может понадобиться добавить строки перевода в ваш проект локально. Это может понадобиться, если:\n\n- Вы хотите использовать REST Framework на языке, который не поддерживается проектом.\n- Ваш проект включает пользовательские сообщения об ошибках, которые не входят в строки перевода DRF по умолчанию.\n\n#### Локальный перевод на новый язык\n\nЭто руководство предполагает, что вы уже знакомы с тем, как перевести приложение Django. Если это не так, начните с чтения [Django's translation docs](https://docs.djangoproject.com/en/stable/topics/i18n/translation).\n\nЕсли вы делаете перевод на новый язык, вам нужно будет перевести существующие сообщения об ошибках DRF:\n\n1. Создайте новую папку, в которой вы хотите хранить ресурсы интернационализации. Добавьте этот путь в настройку [`LOCALE_PATHS`](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-LOCALE_PATHS).\n2. Теперь создайте подпапку для языка, на который вы хотите перевести. Папка должна быть названа с использованием нотации [имя локали](https://docs.djangoproject.com/en/stable/topics/i18n/#term-locale-name). Например: `de`, `pt_BR`, `es_AR`.\n3. Теперь скопируйте файл [base translations file](https://raw.githubusercontent.com/encode/django-rest-framework/main/rest_framework/locale/en_US/LC_MESSAGES/django.po) из исходного кода DRF в папку translations.\n4. Отредактируйте только что скопированный файл `django.po`, переведя все сообщения об ошибках.\n5. Запустите `manage.py compilemessages -l pt_BR`, чтобы сделать переводы доступными для использования Django. Вы должны увидеть сообщение типа `processing file django.po in <...>/locale/pt_BR/LC_MESSAGES`.\n6. Перезапустите ваш сервер разработки, чтобы увидеть, что изменения вступили в силу.\n\nЕсли вы переводите только пользовательские сообщения об ошибках, которые существуют в кодовой базе вашего проекта, вам не нужно копировать исходный файл DRF `django.po` в папку `LOCALE_PATHS`, а можно просто запустить стандартный процесс Django `makemessages`.\n\n## Как определяется язык\n\nЕсли вы хотите разрешить языковые предпочтения для каждого запроса, вам нужно включить `django.middleware.locale.LocaleMiddleware` в настройку `MIDDLEWARE`.\n\nБолее подробную информацию о том, как определяется предпочтение языка, вы можете найти в [документации Django](https://docs.djangoproject.com/en/stable/topics/i18n/translation/#how-django-discovers-language-preference). Для справки, метод следующий:\n\n1. Во-первых, он ищет префикс языка в запрашиваемом URL.\n2. Если это не удается, он ищет ключ `LANGUAGE_SESSION_KEY` в текущей сессии пользователя.\n3. Если это не удается, выполняется поиск cookie.\n4. Если это не удается, просматривается HTTP-заголовок `Accept-Language`.\n5. Если это не удается, используется глобальная настройка `LANGUAGE_CODE`.\n\nДля клиентов API наиболее подходящим из них обычно является использование заголовка `Accept-Language`; сеансы и cookies будут недоступны, если не используется аутентификация сеанса, и вообще лучше предпочесть заголовок `Accept-Language` для клиентов API, а не использовать языковые префиксы URL.\n"
  },
  {
    "path": "topics/rest-hypermedia-hateoas.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# REST, гипермедиа и HATEOAS\n\n> Вы продолжаете использовать это слово \"REST\". Я не думаю, что оно означает то, что вы думаете, что оно означает.\n>\n> - Майк Амундсен, [REST fest 2012 keynote](https://vimeo.com/channels/restfest/49503453).\n\nВо-первых, отказ от ответственности. Название \"Django REST framework\" было принято еще в начале 2011 года и было выбрано просто для того, чтобы разработчики могли легко найти проект. Во всей документации мы стараемся использовать более простую и технически корректную терминологию \"Web API\".\n\nЕсли вы серьезно относитесь к разработке Hypermedia API, вам следует обратиться к ресурсам за пределами этой документации, чтобы помочь в выборе дизайна.\n\nСледующее относится к категории \"обязательного чтения\".\n\n- Диссертация Роя Филдинга - [Архитектурные стили и проектирование сетевых архитектур программного обеспечения](https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm).\n\n- Запись в блоге Роя Филдинга \"[REST API должны быть гипертекстовыми](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)\".\n\n- Leonard Richardson & Mike Amundsen's [RESTful Web APIs](http://restfulwebapis.org/).\n\n- Mike Amundsen's [Building Hypermedia APIs with HTML5 and Node](https://www.amazon.com/Building-Hypermedia-APIs-HTML5-Node/dp/1449306578).\n\n- Steve Klabnik's [Designing Hypermedia APIs](http://designinghypermediaapis.com/).\n\n- [Модель зрелости Ричардсона](https://martinfowler.com/articles/richardsonMaturityModel.html).\n\nFor a more thorough background, check out Klabnik's [Hypermedia API reading list](http://blog.steveklabnik.com/posts/2012-02-27-hypermedia-api-reading-list).\n\nДля получения более подробной информации ознакомьтесь со списком [Hypermedia API reading list](http://blog.steveklabnik.com/posts/2012-02-27-hypermedia-api-reading-list) Клабника.\n\n## Создание гипермедийных API с помощью DRF\n\nDRF - это агностический инструментарий Web API. Он помогает ориентироваться в создании хорошо связанных API и облегчает разработку соответствующих типов носителей, но не обеспечивает строгого соблюдения какого-либо определенного стиля оформления.\n\n## Что предоставляетсобой DRF.\n\nСамо собой разумеется, что DRF позволяет создавать гипермедийные API. Web-интерфейс API, который он предлагает, построен на HTML - гипермедийном языке Интернета.\n\nDRF также включает [serialization](../api-guide/serializers.md) и [parser](../api-guide/parsers.md)/[renderer](../api-guide/renderers.md), которые облегчают создание соответствующих типов медиа, [гиперсвязанные отношения](../api-guide/fields.md) для создания хорошо связанных систем, и отличная поддержка [согласования контента](../api-guide/content-negotiation.md).\n\n## Чего не предоставляет DRF.\n\nЧего DRF не делает, так это не дает вам машиночитаемых гипермедийных форматов, таких как [HAL](http://stateless.co/hal_specification.html), [Collection+JSON](http://www.amundsen.com/media-types/collection/), [JSON API](http://jsonapi.org/) или HTML [microformats](http://microformats.org/wiki/Main_Page) по умолчанию, или возможности автоматически создавать API в стиле HATEOAS, которые включают гипермедийные описания форм и семантически маркированные гиперссылки. Это потребует принятия решений о дизайне API, которые должны оставаться за пределами сферы применения фреймворка.\n"
  },
  {
    "path": "topics/writable-nested-serializers.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Вложенные сериализаторы с возможностью записи\n\n> Для экономии HTTP-запросов может быть удобно отправлять вместе с запросом сопутствующие документы.\n>\n> - [Спецификация JSON API для Ember Data](http://jsonapi.org/format/#url-based-json-api).\n\nХотя плоские структуры данных служат для правильного разграничения отдельных сущностей в вашем сервисе, бывают случаи, когда более целесообразно или удобно использовать вложенные структуры данных.\n\nС вложенными структурами данных достаточно легко работать, если они доступны только для чтения - просто вложите классы сериализатора, и все готово. Однако при использовании вложенных сериализаторов с возможностью записи есть еще несколько тонкостей, связанных с зависимостями между различными экземплярами модели и необходимостью сохранения или удаления нескольких экземпляров одним действием.\n\n## Структуры данных \"один ко многим\"\n\n*Пример вложенного сериализатора **только для чтения**. Здесь нет ничего сложного.*\n\n```python\nclass ToDoItemSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = ToDoItem\n        fields = ['text', 'is_completed']\n\nclass ToDoListSerializer(serializers.ModelSerializer):\n    items = ToDoItemSerializer(many=True, read_only=True)\n\n    class Meta:\n        model = ToDoList\n        fields = ['title', 'items']\n```\n\nНекоторые примеры вывода нашего сериализатора.\n\n```python\n{\n    'title': 'Leaving party preparations',\n    'items': [\n        {'text': 'Compile playlist', 'is_completed': True},\n        {'text': 'Send invites', 'is_completed': False},\n        {'text': 'Clean house', 'is_completed': False}\n    ]\n}\n```\n\nДавайте рассмотрим обновление нашей вложенной структуры данных \"один ко многим\".\n\n### Ошибки валидации\n\n### Добавление и удаление элементов\n\n### Выполнение PATCH-запросов\n"
  },
  {
    "path": "topics.md",
    "content": "# Статьи\n\n* [AJAX, CSRF & CORS](topics/ajax-csrf-cors.md)\n* [The Browsable API](topics/browsable-api.md)\n* [Улучшения в браузере](topics/browser-enhancements.md)\n* [Документирование вашего API](topics/documenting-your-api.md)\n* [HTML и формы](topics/html-and-forms.md)\n* [Интернационализация](topics/internationalization.md)\n* [REST, гипермедиа и HATEOAS](topics/rest-hypermedia-hateoas.md)\n* [Вложенные сериализаторы с возможностью записи](topics/writable-nested-serializers.md)\n"
  },
  {
    "path": "tutorial/1-serialization.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Урок 1: Сериализация\n\n## Введение\n\nВ этом уроке мы рассмотрим создание простого Web API с подсветкой кода для фрагментов кода. Попутно будут представлены различные компоненты, составляющие DRF, и вы получите полное представление о том, как все это сочетается друг с другом.\n\nУчебник довольно подробный, поэтому перед началом работы вам, вероятно, стоит взять печенье и выпить чашку любимого напитка. Если вам нужен лишь краткий обзор, лучше обратиться к документации [quickstart](../quickstart.md).\n\n---\n\n**Примечание**: Код для этого руководства доступен в репозитории [encode/rest-framework-tutorial](https://github.com/encode/rest-framework-tutorial) на GitHub. Не стесняйтесь клонировать репозиторий и посмотреть код в действии.\n\n---\n\n## Настройка новой среды\n\nПрежде чем делать что-либо еще, мы создадим новую виртуальную среду, используя [venv](https://docs.python.org/3/library/venv.html). Это позволит убедиться, что наша конфигурация пакетов будет изолирована от других проектов, над которыми мы работаем.\n\n```bash\npython3 -m venv env\nsource env/bin/activate\n```\n\nТеперь, когда мы находимся в виртуальной среде, мы можем установить наши зависимости.\n\n```bash\npip install django\npip install djangorestframework\npip install pygments  # We'll be using this for the code highlighting\n```\n\n**Примечание:** Чтобы выйти из виртуальной среды в любое время, просто введите `deactivate`. Для получения дополнительной информации смотрите [документацию venv](https://docs.python.org/3/library/venv.html).\n\n## Начало работы\n\nИтак, мы готовы приступить к кодированию. Чтобы начать, давайте создадим новый проект для работы.\n\n```bash\ncd ~\ndjango-admin startproject tutorial\ncd tutorial\n```\n\nПосле этого мы можем создать приложение, которое мы будем использовать для создания простого Web API.\n\n```bash\npython manage.py startapp snippets\n```\n\nНам нужно добавить наше новое приложение `snippets` и приложение `rest_framework` в `INSTALLED_APPS`. Давайте отредактируем файл `tutorial/settings.py`:\n\n```python\nINSTALLED_APPS = [\n    ...\n    'rest_framework',\n    'snippets',\n]\n```\n\nХорошо, мы готовы к работе.\n\n## Создание модели для работы\n\nДля целей этого руководства мы начнем с создания простой модели `Snippet`, которая используется для хранения фрагментов кода. Перейдите к редактированию файла `snippets/models.py`. Примечание: Хорошая практика программирования включает комментарии. Хотя вы найдете их в нашей версии этого учебного кода в репозитории, здесь мы их опустили, чтобы сосредоточиться на самом коде.\n\n```python\nfrom django.db import models\nfrom pygments.lexers import get_all_lexers\nfrom pygments.styles import get_all_styles\n\nLEXERS = [item for item in get_all_lexers() if item[1]]\nLANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])\nSTYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])\n\n\nclass Snippet(models.Model):\n    created = models.DateTimeField(auto_now_add=True)\n    title = models.CharField(max_length=100, blank=True, default='')\n    code = models.TextField()\n    linenos = models.BooleanField(default=False)\n    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)\n    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)\n\n    class Meta:\n        ordering = ['created']\n```\n\nНам также потребуется создать начальную миграцию для нашей модели сниппетов и впервые синхронизировать базу данных.\n\n```bash\npython manage.py makemigrations snippets\npython manage.py migrate snippets\n```\n\n## Создание класса Serializer\n\nПервое, что нам нужно для начала работы над нашим Web API, это обеспечить способ сериализации и десериализации экземпляров сниппетов в такие форматы, как `json`. Мы можем сделать это, объявив сериализаторы, которые работают очень похоже на формы Django. Создайте файл в каталоге `snippets` с именем `serializers.py` и добавьте следующее.\n\n```python\nfrom rest_framework import serializers\nfrom snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES\n\n\nclass SnippetSerializer(serializers.Serializer):\n    id = serializers.IntegerField(read_only=True)\n    title = serializers.CharField(required=False, allow_blank=True, max_length=100)\n    code = serializers.CharField(style={'base_template': 'textarea.html'})\n    linenos = serializers.BooleanField(required=False)\n    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')\n    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')\n\n    def create(self, validated_data):\n        \"\"\"\n        Create and return a new `Snippet` instance, given the validated data.\n        \"\"\"\n        return Snippet.objects.create(**validated_data)\n\n    def update(self, instance, validated_data):\n        \"\"\"\n        Update and return an existing `Snippet` instance, given the validated data.\n        \"\"\"\n        instance.title = validated_data.get('title', instance.title)\n        instance.code = validated_data.get('code', instance.code)\n        instance.linenos = validated_data.get('linenos', instance.linenos)\n        instance.language = validated_data.get('language', instance.language)\n        instance.style = validated_data.get('style', instance.style)\n        instance.save()\n        return instance\n```\n\nПервая часть класса сериализатора определяет поля, которые сериализуются/десериализуются. Методы `create()` и `update()` определяют, как создаются или изменяются полноценные экземпляры при вызове `serializer.save()`.\n\nКласс сериализатора очень похож на класс Django `Form` и включает аналогичные флаги проверки различных полей, такие как `required`, `max_length` и `default`.\n\nФлаги полей также могут управлять тем, как сериализатор должен отображаться в определенных обстоятельствах, например, при рендеринге в HTML. Флаг `{'base_template': 'textarea.html'}` выше эквивалентен использованию `widget=widgets.Textarea` в классе Django `Form`. Это особенно полезно для управления отображением Web-интерфейса API, как мы увидим далее в учебнике.\n\nМы также можем сэкономить время, используя класс `ModelSerializer`, как мы увидим позже, но пока мы сохраним определение нашего сериализатора явным.\n\n## Работа с сериализаторами\n\nПрежде чем двигаться дальше, мы ознакомимся с использованием нашего нового класса Serializer. Давайте зайдем в оболочку Django.\n\n```bash\npython manage.py shell\n```\n\nХорошо, когда мы разобрались с несколькими импортами, давайте создадим пару фрагментов кода для работы.\n\n```pycon\n>>> from snippets.models import Snippet\n>>> from snippets.serializers import SnippetSerializer\n>>> from rest_framework.renderers import JSONRenderer\n>>> from rest_framework.parsers import JSONParser\n\n>>> snippet = Snippet(code='foo = \"bar\"\\n')\n>>> snippet.save()\n\n>>> snippet = Snippet(code='print(\"hello, world\")\\n')\n>>> snippet.save()\n```\n\nТеперь у нас есть несколько экземпляров фрагментов, с которыми можно поиграть. Давайте посмотрим на сериализацию одного из этих экземпляров.\n\n```pycon\n>>> serializer = SnippetSerializer(snippet)\n>>> serializer.data\n{'id': 2, 'title': '', 'code': 'print(\"hello, world\")\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}\n```\n\nНа данном этапе мы перевели экземпляр модели в собственные типы данных Python. Для завершения процесса сериализации мы преобразуем данные в `json`.\n\n```pycon\n>>> content = JSONRenderer().render(serializer.data)\n>>> content\nb'{'id':2,'title':'','code':'print(\\\\\"hello, world\\\\\")\\\\n','linenos':false,'language':'python','style':'friendly'}'\n```\n\nДесериализация аналогична. Сначала мы разбираем поток на собственные типы данных Python...\n\n```pycon\n>>> import io\n>>> stream = io.BytesIO(content)\n>>> data = JSONParser().parse(stream)\n```\n\n...затем мы восстанавливаем эти собственные типы данных в полностью заполненный экземпляр объекта.\n\n```pycon\n>>> serializer = SnippetSerializer(data=data)\n>>> serializer.is_valid()\nTrue\n>>> serializer.validated_data\n{'title': '', 'code': 'print(\"hello, world\")', 'linenos': False, 'language': 'python', 'style': 'friendly'}\n>>> serializer.save()\n<Snippet: Snippet object>\n```\n\nОбратите внимание, насколько API похож на работу с формами. Сходство должно стать еще более очевидным, когда мы начнем писать представления, использующие наш сериализатор.\n\nМы также можем сериализовать наборы запросов вместо экземпляров моделей. Для этого мы просто добавим флаг `many=True` в аргументы сериализатора.\n\n```pycon\n>>> serializer = SnippetSerializer(Snippet.objects.all(), many=True)\n>>> serializer.data\n[{'id': 1, 'title': '', 'code': 'foo = \"bar\"\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 2, 'title': '', 'code': 'print(\"hello, world\")\\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}, {'id': 3, 'title': '', 'code': 'print(\"hello, world\")', 'linenos': False, 'language': 'python', 'style': 'friendly'}]\n```\n\n## Использование сериализаторов моделей\n\nНаш класс `SnippetSerializer` повторяет много информации, которая также содержится в модели `Snippet`. Было бы неплохо, если бы мы могли сделать наш код более лаконичным.\n\nПодобно тому, как Django предоставляет классы `Form` и `ModelForm`, DRF включает классы `Serializer` и `ModelSerializer`.\n\nДавайте рассмотрим рефакторинг нашего сериализатора с помощью класса `ModelSerializer`. Снова откройте файл `snippets/serializers.py` и замените класс `SnippetSerializer` на следующий.\n\n```python\nclass SnippetSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Snippet\n        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']\n```\n\nОдним из приятных свойств сериализаторов является то, что вы можете просмотреть все поля экземпляра сериализатора, распечатав его представление. Откройте оболочку Django с помощью команды `python manage.py shell`, затем попробуйте выполнить следующее:\n\n```pycon\n>>> from snippets.serializers import SnippetSerializer\n>>> serializer = SnippetSerializer()\n>>> print(repr(serializer))\nSnippetSerializer():\n    id = IntegerField(label='ID', read_only=True)\n    title = CharField(allow_blank=True, max_length=100, required=False)\n    code = CharField(style={'base_template': 'textarea.html'})\n    linenos = BooleanField(required=False)\n    language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...\n    style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...\n```\n\nВажно помнить, что классы `ModelSerializer` не делают ничего особенно волшебного, они просто являются синтаксическим сахаром для создания классов сериализаторов:\n\n* Автоматически определяемый набор полей.\n* Простые реализации по умолчанию для методов `create()` и `update()`.\n\n## Написание обычных представлений Django с использованием нашего сериализатора\n\nДавайте посмотрим, как мы можем написать несколько представлений API, используя наш новый класс Serializer. На данный момент мы не будем использовать другие возможности фреймворка REST, мы просто напишем представления как обычные представления Django.\n\nОтредактируйте файл `snippets/views.py` и добавьте следующее.\n\n```python\nfrom django.http import HttpResponse, JsonResponse\nfrom django.views.decorators.csrf import csrf_exempt\nfrom rest_framework.parsers import JSONParser\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n```\n\nКорнем нашего API будет представление, которое поддерживает вывод списка всех существующих сниппетов или создание нового сниппета.\n\n```python\n@csrf_exempt\ndef snippet_list(request):\n    \"\"\"\n    List all code snippets, or create a new snippet.\n    \"\"\"\n    if request.method == 'GET':\n        snippets = Snippet.objects.all()\n        serializer = SnippetSerializer(snippets, many=True)\n        return JsonResponse(serializer.data, safe=False)\n\n    elif request.method == 'POST':\n        data = JSONParser().parse(request)\n        serializer = SnippetSerializer(data=data)\n        if serializer.is_valid():\n            serializer.save()\n            return JsonResponse(serializer.data, status=201)\n        return JsonResponse(serializer.errors, status=400)\n```\n\nОбратите внимание, что, поскольку мы хотим иметь возможность делать POST запросы к этому представлению от клиентов, у которых не будет CSRF токена, мы должны пометить представление как `csrf_exempt`. Это не то, что вы обычно хотите сделать, и представления DRF на самом деле используют более разумное поведение, чем это, но для наших целей сейчас это подойдет.\n\nНам также понадобится представление, которое соответствует отдельному фрагменту и может быть использовано для получения, обновления или удаления фрагмента.\n\n```python\n@csrf_exempt\ndef snippet_detail(request, pk):\n    \"\"\"\n    Retrieve, update or delete a code snippet.\n    \"\"\"\n    try:\n        snippet = Snippet.objects.get(pk=pk)\n    except Snippet.DoesNotExist:\n        return HttpResponse(status=404)\n\n    if request.method == 'GET':\n        serializer = SnippetSerializer(snippet)\n        return JsonResponse(serializer.data)\n\n    elif request.method == 'PUT':\n        data = JSONParser().parse(request)\n        serializer = SnippetSerializer(snippet, data=data)\n        if serializer.is_valid():\n            serializer.save()\n            return JsonResponse(serializer.data)\n        return JsonResponse(serializer.errors, status=400)\n\n    elif request.method == 'DELETE':\n        snippet.delete()\n        return HttpResponse(status=204)\n```\n\nНаконец, нам нужно подключить эти представления. Создайте файл `snippets/urls.py`:\n\n```python\nfrom django.urls import path\nfrom snippets import views\n\nurlpatterns = [\n    path('snippets/', views.snippet_list),\n    path('snippets/<int:pk>/', views.snippet_detail),\n]\n```\n\nНам также нужно настроить корневой urlconf в файле `tutorial/urls.py`, чтобы включить в нее URL нашего приложения-фрагмента.\n\n```python\nfrom django.urls import path, include\n\nurlpatterns = [\n    path('', include('snippets.urls')),\n]\n```\n\nСтоит отметить, что есть несколько крайних случаев, которые мы не обрабатываем должным образом в настоящее время. Если мы отправим неверно сформированный `json`, или если запрос будет сделан с методом, который представление не обрабатывает, то мы получим ответ 500 \"ошибка сервера\". Тем не менее это пока сойдет.\n\n## Тестирование нашей первой попытки создания Web API\n\nТеперь мы можем запустить отладочный сервер, который будет обслуживать наши фрагменты.\n\nВыйти из оболочки...\n\n```pycon\n>>> quit()\n```\n\n...и запустите сервер разработки Django.\n\n```bash\npython manage.py runserver\n\nValidating models...\n\n0 errors found\nDjango version 5.0, using settings 'tutorial.settings'\nStarting Development server at http://127.0.0.1:8000/\nQuit the server with CONTROL-C.\n```\n\nВ другом окне терминала мы можем протестировать сервер.\n\nМы можем протестировать наш API, используя [curl](https://curl.haxx.se/) или [httpie](https://github.com/httpie/httpie#installation). Httpie - это удобный http-клиент, написанный на Python. Давайте установим его.\n\nВы можете установить httpie с помощью pip:\n\n```bash\npip install httpie\n```\n\nНаконец, мы можем получить список всех сниппетов:\n\n```bash\nhttp GET http://127.0.0.1:8000/snippets/ --unsorted\n\nHTTP/1.1 200 OK\n...\n[\n    {\n        \"id\": 1,\n        \"title\": \"\",\n        \"code\": \"foo = \\\"bar\\\"\\n\",\n        \"linenos\": false,\n        \"language\": \"python\",\n        \"style\": \"friendly\"\n    },\n    {\n        \"id\": 2,\n        \"title\": \"\",\n        \"code\": \"print(\\\"hello, world\\\")\\n\",\n        \"linenos\": false,\n        \"language\": \"python\",\n        \"style\": \"friendly\"\n    },\n    {\n        \"id\": 3,\n        \"title\": \"\",\n        \"code\": \"print(\\\"hello, world\\\")\",\n        \"linenos\": false,\n        \"language\": \"python\",\n        \"style\": \"friendly\"\n    }\n]\n```\n\nИли мы можем получить конкретный фрагмент, обратившись к нему по id:\n\n```bash\nhttp GET http://127.0.0.1:8000/snippets/2/ --unsorted\n\nHTTP/1.1 200 OK\n...\n{\n    \"id\": 2,\n    \"title\": \"\",\n    \"code\": \"print(\\\"hello, world\\\")\\n\",\n    \"linenos\": false,\n    \"language\": \"python\",\n    \"style\": \"friendly\"\n}\n```\n\nАналогично, вы можете получить тот же json, посетив эти URL-адреса в веб-браузере.\n\n## Где мы сейчас\n\nПока что у нас все в порядке, у нас есть API сериализации, который очень похож на Django Forms API, и несколько обычных представлений Django.\n\nНаши представления API на данный момент не делают ничего особенного, кроме как обслуживают `json` ответы, и есть несколько крайних случаев обработки ошибок, которые мы хотели бы убрать, но это функционирующий Web API.\n\nВ [части 2 учебника](2-requests-and-responses.md) мы посмотрим, как можно начать улучшать ситуацию.\n"
  },
  {
    "path": "tutorial/2-requests-and-responses.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Учебник 2: Запросы и ответы\n\nНачиная с этого момента мы действительно начнем освещать суть DRF. Давайте представим несколько основных строительных блоков.\n\n## Объекты запроса\n\nDRF вводит объект `Request`, который расширяет обычный `HttpRequest` и обеспечивает более гибкий разбор запроса. Основной функциональностью объекта `Request` является атрибут `request.data`, который аналогичен `request.POST`, но более полезен для работы с Web API.\n\n```python\nrequest.POST  # Only handles form data.  Only works for 'POST' method.\nrequest.data  # Handles arbitrary data.  Works for 'POST', 'PUT' and 'PATCH' methods.\n```\n\n## Объекты ответа\n\nDRF также вводит объект `Response`, который является типом `TemplateResponse`, который принимает неотрендерреное содержимое и использует согласование содержимого для определения правильного типа содержимого для возврата клиенту.\n\n```python\nreturn Response(data)  # Renders to content type as requested by the client.\n```\n\n## Коды состояния\n\nИспользование числовых кодов состояния HTTP в представлениях не всегда удобно для чтения, и легко не заметить, если вы ошиблись с кодом ошибки. DRF предоставляет более явные идентификаторы для каждого кода состояния, такие как `HTTP_400_BAD_REQUEST` в модуле `status`. Хорошая идея - использовать их повсеместно, а не использовать числовые идентификаторы.\n\n## Оборачивание представлений API\n\nDRF предоставляет две обертки, которые можно использовать для написания представлений API.\n\n1. Декоратор `@api_view` для работы с представлениями, основанными на функциях.\n2. Класс `APIView` для работы с представлениями на основе классов.\n\nЭти обертки предоставляют несколько функциональных возможностей, таких как обеспечение получения экземпляров `Request` в вашем представлении и добавление контекста к объектам `Response`, чтобы можно было выполнить согласование содержимого.\n\nОбертки также обеспечивают такое поведение, как возврат ответов `405 Method Not Allowed`, когда это необходимо, и обработку любых исключений `ParseError`, возникающих при доступе к `request.data` с неправильно сформированным вводом.\n\n## Собираем все вместе\n\nИтак, давайте начнем использовать эти новые компоненты, чтобы немного отрефакторить наши представления.\n\n```python\nfrom rest_framework import status\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\n\n\n@api_view(['GET', 'POST'])\ndef snippet_list(request):\n    \"\"\"\n    List all code snippets, or create a new snippet.\n    \"\"\"\n    if request.method == 'GET':\n        snippets = Snippet.objects.all()\n        serializer = SnippetSerializer(snippets, many=True)\n        return Response(serializer.data)\n\n    elif request.method == 'POST':\n        serializer = SnippetSerializer(data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_201_CREATED)\n        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n```\n\nНаше представление экземпляра улучшилось по сравнению с предыдущим примером. Оно немного лаконичнее, и код теперь очень похож на тот, который мы использовали при работе с Forms API. Мы также используем именованные коды состояния, что делает значения ответов более очевидными.\n\nВот представление для отдельного фрагмента в модуле `views.py`.\n\n```python\n@api_view(['GET', 'PUT', 'DELETE'])\ndef snippet_detail(request, pk):\n    \"\"\"\n    Retrieve, update or delete a code snippet.\n    \"\"\"\n    try:\n        snippet = Snippet.objects.get(pk=pk)\n    except Snippet.DoesNotExist:\n        return Response(status=status.HTTP_404_NOT_FOUND)\n\n    if request.method == 'GET':\n        serializer = SnippetSerializer(snippet)\n        return Response(serializer.data)\n\n    elif request.method == 'PUT':\n        serializer = SnippetSerializer(snippet, data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data)\n        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n    elif request.method == 'DELETE':\n        snippet.delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n```\n\nВсе это должно казаться очень знакомым - это не сильно отличается от работы с обычными представлениями Django.\n\nОбратите внимание, что мы больше не привязываем наши запросы или ответы к определенному типу содержимого. `request.data` может обрабатывать входящие запросы `json`, но может обрабатывать и другие форматы. Точно так же мы возвращаем объекты ответа с данными, но позволяем DRF преобразовать ответ в нужный нам тип содержимого.\n\n## Добавление необязательных суффиксов формата к нашим URL-адресам\n\nЧтобы воспользоваться тем, что наши ответы больше не привязаны к одному типу содержимого, давайте добавим поддержку суффиксов формата в наши конечные точки API. Использование суффиксов формата дает нам URL, которые явно ссылаются на определенный формат, и означает, что наш API сможет обрабатывать такие URL, как [<http://example.com/api/items/4.json>](http://example.com/api/items/4.json).\n\nНачните с добавления именованного аргумента `format` к обоим представлениям, как показано ниже.\n\n```python\ndef snippet_list(request, format=None):\n```\n\nи\n\n```python\ndef snippet_detail(request, pk, format=None):\n```\n\nТеперь немного обновите файл `snippets/urls.py`, чтобы добавить набор `format_suffix_patterns` в дополнение к существующим URL.\n\n```python\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n    path('snippets/', views.snippet_list),\n    path('snippets/<int:pk>/', views.snippet_detail),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns)\n```\n\nНам необязательно добавлять эти дополнительные шаблоны url, но это дает нам простой и чистый способ ссылаться на определенный формат.\n\n## Как это выглядит?\n\nПерейдите к тестированию API из командной строки, как мы это делали в [учебнике часть 1](1-serialization.md). Все работает примерно так же, хотя мы получили более удобную обработку ошибок при отправке некорректных запросов.\n\nМы можем получить список всех сниппетов, как и раньше.\n\n```bash\nhttp http://127.0.0.1:8000/snippets/\n\nHTTP/1.1 200 OK\n...\n[\n  {\n    \"id\": 1,\n    \"title\": \"\",\n    \"code\": \"foo = \\\"bar\\\"\\n\",\n    \"linenos\": false,\n    \"language\": \"python\",\n    \"style\": \"friendly\"\n  },\n  {\n    \"id\": 2,\n    \"title\": \"\",\n    \"code\": \"print(\\\"hello, world\\\")\\n\",\n    \"linenos\": false,\n    \"language\": \"python\",\n    \"style\": \"friendly\"\n  }\n]\n```\n\nМы можем контролировать формат ответа, который мы получаем, либо используя заголовок `Accept`:\n\n```bash\nhttp http://127.0.0.1:8000/snippets/ Accept:application/json  # Request JSON\nhttp http://127.0.0.1:8000/snippets/ Accept:text/html         # Request HTML\n```\n\nЛибо путем добавления суффикса формата:\n\n```bash\nhttp http://127.0.0.1:8000/snippets.json  # JSON suffix\nhttp http://127.0.0.1:8000/snippets.api   # Browsable API suffix\n```\n\nАналогично, мы можем контролировать формат отправляемого запроса, используя заголовок `Content-Type`.\n\n```bash\n# POST using form data\nhttp --form POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n  \"id\": 3,\n  \"title\": \"\",\n  \"code\": \"print(123)\",\n  \"linenos\": false,\n  \"language\": \"python\",\n  \"style\": \"friendly\"\n}\n\n# POST using JSON\nhttp --json POST http://127.0.0.1:8000/snippets/ code=\"print(456)\"\n\n{\n    \"id\": 4,\n    \"title\": \"\",\n    \"code\": \"print(456)\",\n    \"linenos\": false,\n    \"language\": \"python\",\n    \"style\": \"friendly\"\n}\n```\n\nЕсли вы добавите переключатель `--debug` к вышеуказанным запросам `http`, вы сможете увидеть тип запроса в заголовках запросов.\n\nТеперь откройте API в веб-браузере, посетив [<http://127.0.0.1:8000/snippets/>](http://127.0.0.1:8000/snippets/).\n\n### Возможность просмотра\n\nПоскольку API выбирает тип содержимого ответа на основе запроса клиента, он по умолчанию возвращает представление ресурса в формате HTML, когда ресурс запрашивается веб-браузером. Это позволяет API возвращать полностью доступное для веб-браузера представление HTML.\n\nНаличие browsable API - это огромный выигрыш в удобстве использования, он значительно упрощает разработку и использование вашего API. Это также значительно снижает барьер для входа в систему для других разработчиков, желающих ознакомиться с вашим API и работать с ним.\n\nДополнительную информацию о функции browsable API и ее настройке см. в теме [browsable api](../topics/browsable-api.md).\n\n## Что дальше?\n\nВ [уроке 3](3-class-based-views.md) мы начнем использовать представления на основе классов и увидим, как общие представления уменьшают количество кода, который нам нужно писать.\n"
  },
  {
    "path": "tutorial/3-class-based-views.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Урок 3: Представления на основе классов\n\nМы также можем писать наши представления API, используя представления на основе классов, а не на основе функций. Как мы увидим, это мощный паттерн, который позволяет нам повторно использовать общую функциональность и помогает нам сохранить наш код [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).\n\n## Переписывание нашего API с использованием представлений на основе классов\n\nМы начнем с того, что перепишем корневое представление как представление на основе классов. Все, что для этого нужно, это немного подправить `views.py`.\n\n```python\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom django.http import Http404\nfrom rest_framework.views import APIView\nfrom rest_framework.response import Response\nfrom rest_framework import status\n\n\nclass SnippetList(APIView):\n    \"\"\"\n    List all snippets, or create a new snippet.\n    \"\"\"\n    def get(self, request, format=None):\n        snippets = Snippet.objects.all()\n        serializer = SnippetSerializer(snippets, many=True)\n        return Response(serializer.data)\n\n    def post(self, request, format=None):\n        serializer = SnippetSerializer(data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data, status=status.HTTP_201_CREATED)\n        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n```\n\nПока все хорошо. Это выглядит довольно похоже на предыдущий случай, но мы получили лучшее разделение между различными HTTP-методами. Нам также потребуется обновить представление экземпляра в `views.py`.\n\n```python\nclass SnippetDetail(APIView):\n    \"\"\"\n    Retrieve, update or delete a snippet instance.\n    \"\"\"\n    def get_object(self, pk):\n        try:\n            return Snippet.objects.get(pk=pk)\n        except Snippet.DoesNotExist:\n            raise Http404\n\n    def get(self, request, pk, format=None):\n        snippet = self.get_object(pk)\n        serializer = SnippetSerializer(snippet)\n        return Response(serializer.data)\n\n    def put(self, request, pk, format=None):\n        snippet = self.get_object(pk)\n        serializer = SnippetSerializer(snippet, data=request.data)\n        if serializer.is_valid():\n            serializer.save()\n            return Response(serializer.data)\n        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)\n\n    def delete(self, request, pk, format=None):\n        snippet = self.get_object(pk)\n        snippet.delete()\n        return Response(status=status.HTTP_204_NO_CONTENT)\n```\n\nВыглядит неплохо. Опять же, сейчас это все еще очень похоже на представление на основе функций.\n\nНам также придется немного отрефакторить наш `snippets/urls.py` теперь, когда мы используем представления на основе классов.\n\n```python\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\nurlpatterns = [\n    path('snippets/', views.SnippetList.as_view()),\n    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),\n]\n\nurlpatterns = format_suffix_patterns(urlpatterns)\n```\n\nХорошо, мы закончили. Если вы запустите сервер разработки, все должно работать как прежде.\n\n## Использование миксинов\n\nОдним из главных преимуществ использования представлений на основе классов является то, что они позволяют нам легко комбинировать многократно используемые фрагменты поведения.\n\nОперации create/retrieve/update/delete, которые мы использовали до сих пор, будут довольно похожими для всех создаваемых нами представлений API, основанных на моделях. Эти части общего поведения реализованы в классах-миксинах DRF.\n\nДавайте рассмотрим, как мы можем компоновать представления с помощью классов миксинов. Вот наш модуль `views.py`.\n\n```python\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import mixins\nfrom rest_framework import generics\n\nclass SnippetList(mixins.ListModelMixin,\n                  mixins.CreateModelMixin,\n                  generics.GenericAPIView):\n    queryset = Snippet.objects.all()\n    serializer_class = SnippetSerializer\n\n    def get(self, request, *args, **kwargs):\n        return self.list(request, *args, **kwargs)\n\n    def post(self, request, *args, **kwargs):\n        return self.create(request, *args, **kwargs)\n```\n\nСейчас мы рассмотрим, что именно здесь происходит. Мы создаем наше представление, используя `GenericAPIView`, и добавляем `ListModelMixin` и `CreateModelMixin`.\n\nБазовый класс обеспечивает основную функциональность, а классы-миксины предоставляют действия `.list()` и `.create()`. Затем мы явно привязываем методы `get` и `post` к соответствующим действиям. Пока все достаточно просто.\n\n```python\nclass SnippetDetail(\n    mixins.RetrieveModelMixin,\n    mixins.UpdateModelMixin,\n    mixins.DestroyModelMixin,\n    generics.GenericAPIView\n):\n    queryset = Snippet.objects.all()\n    serializer_class = SnippetSerializer\n\n    def get(self, request, *args, **kwargs):\n        return self.retrieve(request, *args, **kwargs)\n\n    def put(self, request, *args, **kwargs):\n        return self.update(request, *args, **kwargs)\n\n    def delete(self, request, *args, **kwargs):\n        return self.destroy(request, *args, **kwargs)\n```\n\nДовольно похоже. Мы снова используем класс `GenericAPIView` для обеспечения основной функциональности, и добавляем миксины для обеспечения действий `.retrieve()`, `.update()` и `.destroy()`.\n\n## Использование общих представлений на основе классов\n\nИспользуя классы-миксины, мы переписали представления, чтобы использовать немного меньше кода, чем раньше, но мы можем пойти еще на один шаг дальше. DRF предоставляет набор уже скомбинированных общих представлений, которые мы можем использовать, чтобы еще больше сократить наш модуль `views.py`.\n\n```python\nfrom snippets.models import Snippet\nfrom snippets.serializers import SnippetSerializer\nfrom rest_framework import generics\n\n\nclass SnippetList(generics.ListCreateAPIView):\n    queryset = Snippet.objects.all()\n    serializer_class = SnippetSerializer\n\n\nclass SnippetDetail(generics.RetrieveUpdateDestroyAPIView):\n    queryset = Snippet.objects.all()\n    serializer_class = SnippetSerializer\n```\n\nУх ты, как лаконично. Мы получили огромное количество функционала бесплатно, и наш код выглядит как хороший, чистый, идиоматический Django.\n\nДалее мы перейдем к [уроку 4](4-authentication-and-permissions.md), где мы рассмотрим, как мы можем работать с аутентификацией и разрешениями для нашего API.\n"
  },
  {
    "path": "tutorial/4-authentication-and-permissions.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Урок 4: Аутентификация и разрешения\n\nВ настоящее время наш API не имеет ограничений на то, кто может редактировать или удалять фрагменты кода. Мы хотели бы иметь более продвинутое поведение, чтобы быть уверенными в том, что:\n\n* Фрагменты кода всегда связаны с создателем.\n* Создавать сниппеты могут только авторизованные пользователи.\n* Только создатель сниппета может обновлять или удалять его.\n* Неаутентифицированные запросы должны иметь полный доступ только для чтения.\n\n## Добавление информации в нашу модель\n\nМы собираемся внести несколько изменений в наш класс модели `Snippet`. Во-первых, добавим пару полей. Одно из этих полей будет использоваться для представления пользователя, создавшего фрагмент кода. Другое поле будет использоваться для хранения выделенного HTML-представления кода.\n\nДобавьте следующие два поля к модели `Snippet` в `models.py`.\n\n```python\nowner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)\nhighlighted = models.TextField()\n```\n\nНам также нужно убедиться, что при сохранении модели мы заполним выделенное поле, используя библиотеку подсветки кода `pygments`.\n\nНам понадобятся дополнительные импорты:\n\n```python\nfrom pygments.lexers import get_lexer_by_name\nfrom pygments.formatters.html import HtmlFormatter\nfrom pygments import highlight\n```\n\nИ теперь мы можем добавить метод `.save()` в наш класс модели:\n\n```python\ndef save(self, *args, **kwargs):\n    \"\"\"\n    Use the `pygments` library to create a highlighted HTML\n    representation of the code snippet.\n    \"\"\"\n    lexer = get_lexer_by_name(self.language)\n    linenos = 'table' if self.linenos else False\n    options = {'title': self.title} if self.title else {}\n    formatter = HtmlFormatter(style=self.style, linenos=linenos,\n                              full=True, **options)\n    self.highlighted = highlight(self.code, lexer, formatter)\n    super().save(*args, **kwargs)\n```\n\nКогда все будет готово, нам нужно будет обновить таблицы нашей базы данных. Обычно для этого мы создаем миграцию базы данных, но для целей данного руководства давайте просто удалим базу данных и начнем все сначала.\n\n```bash\nrm -f db.sqlite3\nrm -r snippets/migrations\npython manage.py makemigrations snippets\npython manage.py migrate\n```\n\nВозможно, вы также захотите создать несколько разных пользователей, чтобы использовать их для тестирования API. Быстрее всего это можно сделать с помощью команды `createsuperuser`.\n\n```bash\npython manage.py createsuperuser\n```\n\n## Добавление конечных точек для наших моделей User\n\nТеперь, когда у нас есть несколько пользователей для работы, нам лучше добавить представления этих пользователей в наш API. Создать новый сериализатор очень просто. В `serializers.py` добавьте:\n\n```python\nfrom django.contrib.auth.models import User\n\nclass UserSerializer(serializers.ModelSerializer):\n    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())\n\n    class Meta:\n        model = User\n        fields = ['id', 'username', 'snippets']\n```\n\nПоскольку `'snippets'` является *обратным* отношением к модели `User`, оно не будет включено по умолчанию при использовании класса `ModelSerializer`, поэтому нам нужно добавить явное поле для него.\n\nМы также добавим пару представлений в `views.py`. Мы хотим использовать представления только для чтения для пользовательских представлений, поэтому мы будем использовать представления `ListAPIView` и `RetrieveAPIView`, основанные на общих классах.\n\n```python\nfrom django.contrib.auth.models import User\n\n\nclass UserList(generics.ListAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n\n\nclass UserDetail(generics.RetrieveAPIView):\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n```\n\nНе забудьте также импортировать класс `UserSerializer`.\n\n```python\nfrom snippets.serializers import UserSerializer\n```\n\nНаконец, нам нужно добавить эти представления в API, ссылаясь на них из URL conf. Добавьте следующее к шаблонам в `snippets/urls.py`.\n\n```python\npath('users/', views.UserList.as_view()),\npath('users/<int:pk>/', views.UserDetail.as_view()),\n```\n\n## Связывание сниппетов с пользователями\n\nСейчас, если мы создаем сниппет кода, нет возможности связать пользователя, создавшего сниппет, с экземпляром сниппета. Пользователь не передается как часть сериализованного представления, а является свойством входящего запроса.\n\nМы решаем эту проблему путем переопределения метода `.perform_create()` в наших представлениях фрагментов, что позволяет нам изменять способ сохранения экземпляра и обрабатывать любую информацию, которая подразумевается во входящем запросе или запрашиваемом URL.\n\nВ классе представления `SnippetList` добавьте следующий метод:\n\n```python\ndef perform_create(self, serializer):\n    serializer.save(owner=self.request.user)\n```\n\nТеперь методу `create()` нашего сериализатора будет передано дополнительное поле `'owner'` вместе с провалидированными данными из запроса.\n\n## Обновление нашего сериализатора\n\nТеперь, когда сниппеты связаны с пользователем, который их создал, давайте обновим наш `SnippetSerializer`, чтобы отразить это. Добавьте следующее поле в определение сериализатора в `serializers.py`:\n\n```python\nowner = serializers.ReadOnlyField(source='owner.username')\n```\n\n**Примечание**: Убедитесь, что вы также добавили `'owner'` в список полей во внутреннем классе `Meta`.\n\nВ этом поле происходит нечто весьма интересное. Аргумент `source` управляет тем, какой атрибут используется для заполнения поля, и может указывать на любой атрибут сериализованного экземпляра. Он также может принимать точечную нотацию, показанную выше, в этом случае он будет перебирать заданные атрибуты, подобно тому, как это используется в языке шаблонов Django.\n\nПоле, которое мы добавили, представляет собой нетипизированный класс `ReadOnlyField`, в отличие от других типизированных полей, таких как `CharField`, `BooleanField` и т.д.. Нетипизированное `ReadOnlyField` всегда только для чтения, оно будет использоваться для сериализованных представлений, но не будет использоваться для обновления экземпляров модели при их десериализации. Мы могли бы также использовать здесь `CharField(read_only=True)`.\n\n## Добавление необходимых разрешений к представлениям\n\nТеперь, когда фрагменты кода связаны с пользователями, мы хотим убедиться, что только аутентифицированные пользователи могут создавать, обновлять и удалять фрагменты кода.\n\nDRF включает в себя ряд классов разрешений, которые мы можем использовать для ограничения доступа к определенному представлению. В данном случае нам нужен класс `IsAuthenticatedOrReadOnly`, который обеспечит аутентифицированным запросам доступ на чтение-запись, а неаутентифицированным - только на чтение.\n\nСначала добавьте следующий импорт в модуль views\n\n```python\nfrom rest_framework import permissions\n```\n\nЗатем добавьте следующее свойство к **обоим** классам представления `SnippetList` и `SnippetDetail`.\n\n```python\npermission_classes = [permissions.IsAuthenticatedOrReadOnly]\n```\n\n## Добавление входа в Web-интерфейс API\n\nЕсли вы откроете браузер и перейдете к Web-интерфейсу API, то обнаружите, что больше не можете создавать новые фрагменты кода. Для этого нам нужно иметь возможность войти в систему как пользователь.\n\nМы можем добавить представление входа для использования с Web-интерфейсом API, отредактировав URLconf в нашем файле `urls.py` на уровне проекта.\n\nДобавьте следующий импорт в верхней части файла:\n\n```python\nfrom django.urls import path, include\n```\n\nИ в конце файла добавьте шаблон для включения представлений входа и выхода для Web-интерфейса API.\n\n```python\nurlpatterns += [\n    path('api-auth/', include('rest_framework.urls')),\n]\n```\n\nЧасть шаблона `'api-auth/'` может быть любым URL, который вы хотите использовать.\n\nТеперь, если вы снова откроете браузер и обновите страницу, вы увидите ссылку 'Login' в правом верхнем углу страницы. Если вы войдете в систему как один из пользователей, созданных ранее, вы снова сможете создавать фрагменты кода.\n\nПосле создания нескольких фрагментов кода перейдите к конечной точке `'/users/'` и обратите внимание, что представление включает список идентификаторов фрагментов, связанных с каждым пользователем, в поле 'snippets' каждого пользователя.\n\n## Разрешения на уровне объекта\n\nНам бы хотелось, чтобы все кодовые сниппеты были видны всем, но при этом чтобы только пользователь, создавший кодовый сниппет, мог его обновить или удалить.\n\nДля этого нам понадобится создать пользовательское разрешение.\n\nВ приложении `snippets` создайте новый файл `permissions.py`.\n\n```python\nfrom rest_framework import permissions\n\n\nclass IsOwnerOrReadOnly(permissions.BasePermission):\n    \"\"\"\n    Custom permission to only allow owners of an object to edit it.\n    \"\"\"\n\n    def has_object_permission(self, request, view, obj):\n        # Read permissions are allowed to any request,\n        # so we'll always allow GET, HEAD or OPTIONS requests.\n        if request.method in permissions.SAFE_METHODS:\n            return True\n\n        # Write permissions are only allowed to the owner of the snippet.\n        return obj.owner == request.user\n```\n\nТеперь мы можем добавить это пользовательское разрешение в конечную точку экземпляра сниппета, отредактировав свойство `permission_classes` в классе представления `SnippetDetail`:\n\n```python\npermission_classes = [permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]\n```\n\nНе забудьте также импортировать класс `IsOwnerOrReadOnly`.\n\n```python\nfrom snippets.permissions import IsOwnerOrReadOnly\n```\n\nТеперь, если вы снова откроете браузер, вы обнаружите, что действия 'DELETE' и 'PUT' появляются на конечной точке экземпляра сниппета, только если вы вошли в систему как тот же пользователь, который создал сниппет кода.\n\n## Аутентификация в API\n\nПоскольку теперь у нас есть набор прав доступа к API, нам нужно аутентифицировать наши запросы к нему, если мы хотим редактировать какие-либо сниппеты. Мы не установили никаких [классов аутентификации](../api-guide/authentication.md), поэтому сейчас применяются значения по умолчанию, а именно `SessionAuthentication` и `BasicAuthentication`.\n\nКогда мы взаимодействуем с API через веб-браузер, мы можем войти в систему, и тогда сессия браузера обеспечит необходимую аутентификацию для запросов.\n\nЕсли мы взаимодействуем с API программно, нам необходимо явно предоставлять учетные данные аутентификации при каждом запросе.\n\nЕсли мы попытаемся создать сниппет без аутентификации, мы получим ошибку:\n\n```bash\nhttp POST http://127.0.0.1:8000/snippets/ code=\"print(123)\"\n\n{\n    \"detail\": \"Authentication credentials were not provided.\"\n}\n```\n\nМы можем сделать успешный запрос, включив в него имя пользователя и пароль одного из пользователей, созданных нами ранее.\n\n```bash\nhttp -a admin:password123 POST http://127.0.0.1:8000/snippets/ code=\"print(789)\"\n\n{\n    \"id\": 1,\n    \"owner\": \"admin\",\n    \"title\": \"foo\",\n    \"code\": \"print(789)\",\n    \"linenos\": false,\n    \"language\": \"python\",\n    \"style\": \"friendly\"\n}\n```\n\n## Резюме\n\nТеперь у нас есть довольно тонкий набор разрешений для нашего Web API, а также конечные точки для пользователей системы и для созданных ими фрагментов кода.\n\nВ [части 5](5-relationships-and-hyperlinked-apis.md) учебника мы рассмотрим, как мы можем связать все вместе, создав конечную точку HTML для наших выделенных фрагментов, и улучшить связность нашего API, используя гиперссылки для связей внутри системы.\n"
  },
  {
    "path": "tutorial/5-relationships-and-hyperlinked-apis.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Урок 5: Отношения и API с гиперссылками\n\nВ настоящее время отношения в нашем API представлены с помощью первичных ключей. В этой части учебника мы улучшим связность и наглядность нашего API, используя вместо этого гиперссылки для отношений.\n\n## Создание конечной точки для корня нашего API\n\nСейчас у нас есть конечные точки для \"сниппетов\" и \"пользователей\", но у нас нет единой точки входа в наш API. Чтобы создать ее, мы воспользуемся обычным представлением на основе функций и декоратором `@api_view`, который мы представили ранее. В вашем `snippets/views.py` добавьте:\n\n```python\nfrom rest_framework.decorators import api_view\nfrom rest_framework.response import Response\nfrom rest_framework.reverse import reverse\n\n\n@api_view(['GET'])\ndef api_root(request, format=None):\n    return Response(\n        {\n            'users': reverse('user-list', request=request, format=format),\n            'snippets': reverse('snippet-list', request=request, format=format)\n        }\n    )\n```\n\nЗдесь следует отметить два момента. Во-первых, мы используем функцию DRF `reverse`, чтобы вернуть полностью квалифицированные URL; во-вторых, шаблоны URL идентифицируются удобными именами, которые мы объявим позже в нашем `snippets/urls.py`.\n\n## Создание конечной точки для подсвеченных фрагментов\n\nДругая очевидная вещь, которой все еще не хватает в нашем API pastebin, — это конечные точки подсветки кода.\n\nВ отличие от всех других конечных точек API, мы не хотим использовать JSON, а вместо этого просто представим HTML-представление. Существует два способа рендеринга HTML, предоставляемых DRF: один для работы с HTML, созданным с помощью шаблонов, другой для работы с предварительно созданным HTML. Для этой конечной точки мы хотим использовать второй рендерер.\n\nДругая вещь, которую мы должны учитывать при создании представления подсветки кода, заключается в том, что нет существующего конкретного общего представления, которое мы могли бы использовать. Мы возвращаем не экземпляр объекта, а свойство экземпляра объекта.\n\nВместо того чтобы использовать конкретное общее представление, мы будем использовать базовый класс для представления экземпляров и создадим свой собственный метод `.get()`. В вашем `snippets/views.py` добавьте:\n\n```python\nfrom rest_framework import renderers\n\nclass SnippetHighlight(generics.GenericAPIView):\n    queryset = Snippet.objects.all()\n    renderer_classes = [renderers.StaticHTMLRenderer]\n\n    def get(self, request, *args, **kwargs):\n        snippet = self.get_object()\n        return Response(snippet.highlighted)\n```\n\nКак обычно, нам нужно добавить новые представления, которые мы создали, в наш URLconf. Мы добавим шаблон url для нашего нового корня API в `snippets/urls.py`:\n\n```python\npath('', views.api_root),\n```\n\nА затем добавьте шаблон url для выделения фрагмента:\n\n```python\npath('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view()),\n```\n\n## Гиперссылка на наш API\n\nРабота с отношениями между сущностями - один из самых сложных аспектов разработки Web API. Существует множество различных способов, которые мы можем выбрать для отображения отношений:\n\n* Использование первичных ключей.\n* Использование гиперссылок между сущностями.\n* Использование уникального идентифицирующего поля slug в связанной сущности.\n* Использование стандартного строкового представления связанной сущности.\n* Вложение связанной сущности в родительское представление.\n* Другое пользовательское представление.\n\nDRF поддерживает все эти стили и может применять их к прямым или обратным отношениям, или применять их к пользовательским менеджерам, таким как общие внешние ключи.\n\nВ данном случае мы хотели бы использовать гиперссылки между сущностями. Для этого мы изменим наши сериализаторы, чтобы расширить `HyperlinkedModelSerializer` вместо существующего `ModelSerializer`.\n\n`HyperlinkedModelSerializer` имеет следующие отличия от `ModelSerializer`:\n\n* По умолчанию он не включает поле `id`.\n* Он включает поле `url`, используя `HyperlinkedIdentityField`.\n* Отношения используют `HyperlinkedRelatedField`, вместо `PrimaryKeyRelatedField`.\n\nМы можем легко переписать наши существующие сериализаторы для использования гиперссылок. В вашем `snippets/serializers.py` добавьте:\n\n```python\nclass SnippetSerializer(serializers.HyperlinkedModelSerializer):\n    owner = serializers.ReadOnlyField(source='owner.username')\n    highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')\n\n    class Meta:\n        model = Snippet\n        fields = [\n            'url',\n            'id',\n            'highlight',\n            'owner',\n            'title',\n            'code',\n            'linenos',\n            'language',\n            'style',\n        ]\n\n\nclass UserSerializer(serializers.HyperlinkedModelSerializer):\n    snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)\n\n    class Meta:\n        model = User\n        fields = [\n            'url',\n            'id',\n            'username',\n            'snippets',\n        ]\n```\n\nОбратите внимание, что мы также добавили новое поле `'highlight'`. Это поле того же типа, что и поле `url`, за исключением того, что оно указывает на шаблон url `'snippet-highlight'`, а не на шаблон url `'snippet-detail'`.\n\nПоскольку мы включили URL с суффиксом формата, например, `'.json'`, нам также необходимо указать полю `highlight`, что все гиперссылки с суффиксом формата, которые оно возвращает, должны использовать суффикс `'.html'`.\n\n---\n\n**Обратите внимание:**\n\nКогда вы вручную создаете экземпляры этих сериализаторов внутри своих представлений (например, в `SnippetDetail` или `SnippetList`), вы **обязательно должны** передать `context={'request': request}`, чтобы сериализатор знал, как создавать абсолютные URL-адреса. Например, вместо:\n\n```python\nserializer = SnippetSerializer(snippet)\n```\n\nВы должны написать:\n\n```python\nserializer = SnippetSerializer(snippet, context={'request': request})\n```\n\nЕсли ваше представление является подклассом `GenericAPIView`, вы можете использовать `get_serializer_context()` в качестве вспомогательного метода.\n\n---\n\n## Убедитесь, что наши шаблоны URL названы\n\nЕсли мы собираемся иметь API с гиперссылками, нам нужно убедиться, что мы назвали наши шаблоны URL. Давайте рассмотрим, какие шаблоны URL нам нужно назвать.\n\n* Корень нашего API ссылается на `'user-list'` и `'snippet-list'`.\n* Наш сериализатор сниппетов включает поле, которое ссылается на `'snippet-highlight'`.\n* Наш сериализатор пользователей включает поле, которое ссылается на `'snippet-detail'`.\n* Наши сериализаторы сниппетов и пользователей включают поля `'url'`, которые по умолчанию ссылаются на `'{model_name}-detail'`, что в данном случае будет `'snippet-detail'` и `'user-detail'`.\n\nПосле добавления всех этих имен в нашу URLconf, наш файл `snippets/urls.py` должен выглядеть следующим образом:\n\n```python\nfrom django.urls import path\nfrom rest_framework.urlpatterns import format_suffix_patterns\nfrom snippets import views\n\n# API endpoints\nurlpatterns = format_suffix_patterns([\n    path(\n        '',\n        views.api_root,\n    ),\n    path(\n        'snippets/',\n        views.SnippetList.as_view(),\n        name='snippet-list',\n        ),\n    path(\n        'snippets/<int:pk>/',\n        views.SnippetDetail.as_view(),\n        name='snippet-detail',\n    ),\n    path(\n        'snippets/<int:pk>/highlight/',\n        views.SnippetHighlight.as_view(),\n        name='snippet-highlight',\n    ),\n    path(\n        'users/',\n        views.UserList.as_view(),\n        name='user-list',\n    ),\n    path(\n        'users/<int:pk>/',\n        views.UserDetail.as_view(),\n        name='user-detail',\n    )\n])\n```\n\n## Добавление пагинации\n\nПредставления списка для пользователей и фрагментов кода могут в конечном итоге возвращать довольно много экземпляров, поэтому на самом деле мы хотели бы убедиться, что мы постранично отображаем результаты, и позволить клиенту API пройтись по каждой отдельной странице.\n\nМы можем изменить тип списка по умолчанию на использование пагинации, слегка изменив наш файл `tutorial/settings.py`. Добавьте следующую настройку:\n\n```python\nREST_FRAMEWORK = {\n    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',\n    'PAGE_SIZE': 10\n}\n```\n\nОбратите внимание, что все настройки DRF разнесены по именам в один словарь с именем `REST_FRAMEWORK`, что позволяет отделить их от других настроек проекта.\n\nПри необходимости мы также можем настроить стиль пагинации, но в данном случае мы будем придерживаться стиля по умолчанию.\n\n## Просмотр API\n\nЕсли мы откроем браузер и перейдем к API с возможностью просмотра, вы увидите, что теперь вы можете работать с API, просто переходя по ссылкам.\n\nВы также сможете увидеть ссылки \"highlight\" на экземплярах сниппетов, которые приведут вас к HTML-представлениям выделенного кода.\n\nВ [части 6](6-viewsets-and-routers.md) учебника мы рассмотрим, как мы можем использовать ViewSets и Routers для уменьшения количества кода, необходимого для создания нашего API.\n"
  },
  {
    "path": "tutorial/6-viewsets-and-routers.md",
    "content": "<!-- TRANSLATED by md-translate -->\n# Урок 6: Наборы представлений и маршрутизаторы\n\nDRF включает в себя абстракцию для работы с `ViewSets`, которая позволяет разработчику сосредоточиться на моделировании состояния и взаимодействия API, а построение URL-адресов оставить на автоматическое управление, основанное на общих соглашениях.\n\nКлассы `ViewSet` - это почти то же самое, что и классы `View`, за исключением того, что они предоставляют такие операции, как `retrieve` или `update`, а не обработчики методов, таких как `get` или `put`.\n\nКласс `ViewSet` привязывается к набору обработчиков методов только в последний момент, когда он инстанцируется в набор представлений, обычно с помощью класса `Router`, который обрабатывает все сложности определения URL conf за вас.\n\n## Рефакторинг для использования ViewSets\n\nДавайте возьмем наш текущий набор представлений и переделаем их в наборы представлений.\n\nПрежде всего, давайте переделаем наши классы `UserList` и `UserDetail` в один класс `UserViewSet`. В файле `snippets/views.py` мы можем удалить два класса view и заменить их одним классом ViewSet:\n\n```python\nfrom rest_framework import viewsets\n\n\nclass UserViewSet(viewsets.ReadOnlyModelViewSet):\n    \"\"\"\n    This viewset automatically provides `list` and `retrieve` actions.\n    \"\"\"\n    queryset = User.objects.all()\n    serializer_class = UserSerializer\n```\n\nЗдесь мы использовали класс `ReadOnlyModelViewSet` для автоматического обеспечения операций по умолчанию \"только для чтения\". Мы по-прежнему задаем атрибуты `queryset` и `serializer_class` точно так же, как и при использовании обычных представлений, но нам больше не нужно предоставлять одну и ту же информацию двум отдельным классам.\n\nДалее мы заменим классы представлений `SnippetList`, `SnippetDetail` и `SnippetHighlight`. Мы можем удалить эти три класса и снова заменить их одним.\n\n```python\nfrom rest_framework import permissions\nfrom rest_framework import renderers\nfrom rest_framework.decorators import action\nfrom rest_framework.response import Response\n\n\nclass SnippetViewSet(viewsets.ModelViewSet):\n    \"\"\"\n    This ViewSet automatically provides `list`, `create`, `retrieve`,\n    `update` and `destroy` actions.\n\n    Additionally we also provide an extra `highlight` action.\n    \"\"\"\n    queryset = Snippet.objects.all()\n    serializer_class = SnippetSerializer\n    permission_classes = [permissions.IsAuthenticatedOrReadOnly,\n                          IsOwnerOrReadOnly]\n\n    @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer])\n    def highlight(self, request, *args, **kwargs):\n        snippet = self.get_object()\n        return Response(snippet.highlighted)\n\n    def perform_create(self, serializer):\n        serializer.save(owner=self.request.user)\n```\n\nНа этот раз мы использовали класс `ModelViewSet`, чтобы получить полный набор стандартных операций чтения и записи.\n\nОбратите внимание, что мы также использовали декоратор `@action` для создания пользовательского действия с именем `highlight`. Этот декоратор можно использовать для добавления любых пользовательских конечных точек, которые не вписываются в стандартный стиль `create`/`update`/`delete`.\n\nПользовательские действия, использующие декоратор `@action`, по умолчанию будут отвечать на `GET` запросы. Мы можем использовать аргумент `methods`, если хотим получить действие, отвечающее на `POST` запросы.\n\nURL для пользовательских действий по умолчанию зависит от имени метода. Если вы хотите изменить способ построения URL, вы можете включить `url_path` в качестве именованного аргумента декоратора.\n\n## Привязка наборов представлений к URL-адресам в явном виде\n\nМетоды обработчика привязываются к действиям только после определения URLConf. Чтобы увидеть, что происходит под капотом, давайте сначала явно создадим набор представлений из наших ViewSet.\n\nВ файле `snippets/urls.py` мы связываем наши классы `ViewSet` в набор конкретных представлений.\n\n```python\nfrom rest_framework import renderers\n\nfrom snippets.views import api_root, SnippetViewSet, UserViewSet\n\nsnippet_list = SnippetViewSet.as_view(\n    {'get': 'list', 'post': 'create'}\n)\nsnippet_detail = SnippetViewSet.as_view(\n    {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}\n)\nsnippet_highlight = SnippetViewSet.as_view(\n    {'get': 'highlight'},\n    renderer_classes=[renderers.StaticHTMLRenderer]\n)\nuser_list = UserViewSet.as_view(\n    {'get': 'list'}\n)\nuser_detail = UserViewSet.as_view(\n    {'get': 'retrieve'}\n)\n```\n\nОбратите внимание, как мы создаем несколько представлений из каждого класса `ViewSet`, привязывая HTTP-методы к требуемым действиям для каждого представления.\n\nТеперь, когда мы связали наши ресурсы с конкретными представлениями, мы можем зарегистрировать представления с помощью конфигурации URL, как обычно.\n\n```python\nurlpatterns = format_suffix_patterns([\n    path('', api_root),\n    path('snippets/', snippet_list, name='snippet-list'),\n    path('snippets/<int:pk>/', snippet_detail, name='snippet-detail'),\n    path('snippets/<int:pk>/highlight/', snippet_highlight, name='snippet-highlight'),\n    path('users/', user_list, name='user-list'),\n    path('users/<int:pk>/', user_detail, name='user-detail')\n])\n```\n\n## Использование маршрутизаторов\n\nПоскольку мы используем классы `ViewSet`, а не `View`, нам не нужно самим разрабатывать конфигурацию URL. Соглашения о соединении ресурсов в представления и URL-адреса могут быть обработаны автоматически, с помощью класса `Router`. Все, что нам нужно сделать, это зарегистрировать соответствующие наборы представлений в маршрутизаторе, и пусть он сделает все остальное.\n\nВот наш переделанный файл `snippets/urls.py`.\n\n```python\nfrom django.urls import path, include\nfrom rest_framework.routers import DefaultRouter\n\nfrom snippets import views\n\n# Create a router and register our ViewSets with it.\nrouter = DefaultRouter()\nrouter.register(r'snippets', views.SnippetViewSet, basename='snippet')\nrouter.register(r'users', views.UserViewSet, basename='user')\n\n# The API URLs are now determined automatically by the router.\nurlpatterns = [\n    path('', include(router.urls)),\n]\n```\n\nРегистрация наборов представлений в маршрутизаторе аналогична заданию url-шаблона. Мы указываем два аргумента - префикс URL для представлений и сам набор представлений.\n\nКласс `DefaultRouter`, который мы используем, также автоматически создает для нас корневое представление API, поэтому мы можем удалить функцию `api_root` из нашего модуля `views`.\n\n## Компромиссы между представлениями и наборами представлений\n\nИспользование `ViewSets` может быть действительно полезной абстракцией. Она помогает обеспечить согласованность соглашений URL в вашем API, минимизирует объем кода, который вам нужно написать, и позволяет вам сосредоточиться на взаимодействии и представлениях, которые предоставляет ваш API, а не на специфике URL conf.\n\nНо это не значит, что такой подход всегда правильный. Существует аналогичный набор компромиссов, которые необходимо учитывать при использовании представлений на основе классов вместо представлений на основе функций. Использование наборов представлений менее очевидно, чем создание представлений API по отдельности.\n"
  }
]