diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 95c9f07cb..2e0061f5e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,7 +4,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" cooldown: default-days: 7 commit-message: @@ -21,7 +21,7 @@ updates: - package-ecosystem: "uv" directory: "/" schedule: - interval: "weekly" + interval: "monthly" cooldown: default-days: 7 commit-message: @@ -31,20 +31,3 @@ updates: dependency-type: "development" patterns: - "*" - # pre-commit - - package-ecosystem: "pre-commit" - directory: "/" - schedule: - interval: "weekly" - cooldown: - default-days: 7 - commit-message: - prefix: ⬆ - labels: - - "internal" - - "dependencies" - - "pre-commit" - groups: - pre-commit: - patterns: - - "*" diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index fe2a39c45..030132a17 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -21,7 +21,7 @@ jobs: with: persist-credentials: false # For pull requests it's not necessary to checkout the code but for the main branch it is - - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 id: filter with: filters: | @@ -51,7 +51,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 @@ -90,7 +90,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/bump-pre-commit-hooks.yml b/.github/workflows/bump-pre-commit-hooks.yml new file mode 100644 index 000000000..b64341a45 --- /dev/null +++ b/.github/workflows/bump-pre-commit-hooks.yml @@ -0,0 +1,67 @@ +name: Bump pre-commit hooks + +on: + schedule: + - cron: "0 12 1 * *" + workflow_dispatch: + +permissions: {} + +jobs: + bump-pre-commit-hooks: + if: github.repository_owner == 'fastapi' + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + token: ${{ secrets.FASTAPI_LATEST_CHANGES }} + persist-credentials: true + - name: Set up Python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version-file: ".python-version" + - name: Setup uv + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 + with: + # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. + # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 + version: "0.11.18" + cache-dependency-glob: | + pyproject.toml + uv.lock + - name: Bump pre-commit hooks + run: uv run prek auto-update --freeze --cooldown-days 7 + - name: Create pull request + env: + GH_TOKEN: ${{ secrets.FASTAPI_LATEST_CHANGES }} + BASE_BRANCH: ${{ github.event.repository.default_branch }} + run: | + set -euo pipefail + if git diff --quiet; then + echo "No pre-commit hook updates available" + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + branch="bump-pre-commit-hooks" + git switch -C "$branch" + git add .pre-commit-config.yaml + git commit -m "⬆ Bump pre-commit hooks" + git push --force origin "$branch" + if [ -z "$(gh pr list --head "$branch" --state open --json number --jq '.[].number')" ]; then + gh pr create \ + --base "$BASE_BRANCH" \ + --head "$branch" \ + --title "⬆ Bump pre-commit hooks" \ + --body "Bump pre-commit hook versions via \`prek auto-update --freeze --cooldown-days 7\`." \ + --label internal \ + --label dependencies \ + --label pre-commit + else + echo "PR for \"$branch\" already open; branch updated in place." + fi diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml deleted file mode 100644 index 1d869e7b8..000000000 --- a/.github/workflows/contributors.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: FastAPI People Contributors - -on: - schedule: - - cron: "0 3 1 * *" - workflow_dispatch: - inputs: - debug_enabled: - description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" - required: false - default: "false" - -permissions: {} - -jobs: - job: - if: github.repository_owner == 'fastapi' - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - persist-credentials: true # Required for `git push` in `contributors.py` - - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - with: - python-version-file: ".python-version" - - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - with: - # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. - # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 - version: "0.11.18" - enable-cache: true - cache-dependency-glob: | - pyproject.toml - uv.lock - - name: Install Dependencies - run: uv sync --locked --no-dev --group github-actions - # Allow debugging with tmate - - name: Setup tmate session - uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3.24 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} - with: - limit-access-to-actor: true - env: - GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} # zizmor: ignore[secrets-outside-env] - - name: FastAPI People Contributors - run: uv run ./scripts/contributors.py - env: - GITHUB_TOKEN: ${{ secrets.FASTAPI_PR_TOKEN }} # zizmor: ignore[secrets-outside-env] diff --git a/.github/workflows/create-draft-release.yml b/.github/workflows/create-draft-release.yml index e0af097e2..8f9e6fc73 100644 --- a/.github/workflows/create-draft-release.yml +++ b/.github/workflows/create-draft-release.yml @@ -31,7 +31,7 @@ jobs: with: python-version-file: ".python-version" - name: Install uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index d8353ad55..6e40b5b83 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -30,7 +30,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/label-approved.yml b/.github/workflows/label-approved.yml index 6d4f2ef52..f5f036ec7 100644 --- a/.github/workflows/label-approved.yml +++ b/.github/workflows/label-approved.yml @@ -27,7 +27,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 803160ef5..2cbfe8cca 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 + - uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0 if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }} - run: echo "Done adding labels" # Run this after labeler applied labels diff --git a/.github/workflows/notify-translations.yml b/.github/workflows/notify-translations.yml index 261be1a98..7e72488ef 100644 --- a/.github/workflows/notify-translations.yml +++ b/.github/workflows/notify-translations.yml @@ -39,7 +39,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/people.yml b/.github/workflows/people.yml deleted file mode 100644 index 2e48c9d70..000000000 --- a/.github/workflows/people.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: FastAPI People - -on: - schedule: - - cron: "0 14 1 * *" - workflow_dispatch: - inputs: - debug_enabled: - description: Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate) - required: false - default: "false" - -permissions: {} - -jobs: - job: - if: github.repository_owner == 'fastapi' - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - with: - persist-credentials: true # Required for `git push` in `people.py` - - name: Set up Python - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - with: - python-version-file: ".python-version" - - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 - with: - # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. - # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 - version: "0.11.18" - enable-cache: true - cache-dependency-glob: | - pyproject.toml - uv.lock - - name: Install Dependencies - run: uv sync --locked --no-dev --group github-actions - # Allow debugging with tmate - - name: Setup tmate session - uses: mxschmitt/action-tmate@35b54afac29c97fb54faba5b513f8fbd1882f113 # v3.24 - if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }} - with: - limit-access-to-actor: true - env: - GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }} # zizmor: ignore[secrets-outside-env] - - name: FastAPI People Experts - run: uv run ./scripts/people.py - env: - GITHUB_TOKEN: ${{ secrets.FASTAPI_PEOPLE }} # zizmor: ignore[secrets-outside-env] - SLEEP_INTERVAL: ${{ vars.PEOPLE_SLEEP_INTERVAL }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 963fd68ab..06a706928 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -43,7 +43,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 5b241aa4f..6b84ade70 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -43,7 +43,7 @@ jobs: with: python-version-file: ".python-version" - name: Install uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 447ce8c33..bb601611b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,7 +27,7 @@ jobs: with: python-version-file: ".python-version" - name: Install uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/smokeshow.yml b/.github/workflows/smokeshow.yml index 41804cee9..f7e4323c3 100644 --- a/.github/workflows/smokeshow.yml +++ b/.github/workflows/smokeshow.yml @@ -26,7 +26,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml index a20dcaf05..c880f578e 100644 --- a/.github/workflows/sponsors.yml +++ b/.github/workflows/sponsors.yml @@ -32,7 +32,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 42725b3df..6855f0d48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,7 +30,7 @@ jobs: with: persist-credentials: false # For pull requests it's not necessary to checkout the code but for the main branch it is - - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 id: filter with: filters: | @@ -115,7 +115,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 @@ -179,7 +179,7 @@ jobs: with: python-version: "3.13" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 @@ -213,7 +213,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/topic-repos.yml b/.github/workflows/topic-repos.yml index b0fb40398..762422813 100644 --- a/.github/workflows/topic-repos.yml +++ b/.github/workflows/topic-repos.yml @@ -27,7 +27,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.github/workflows/translate.yml b/.github/workflows/translate.yml index 7f96798da..abe77537b 100644 --- a/.github/workflows/translate.yml +++ b/.github/workflows/translate.yml @@ -58,7 +58,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 @@ -101,7 +101,7 @@ jobs: with: python-version-file: ".python-version" - name: Setup uv - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@d31148d669074a8d0a63714ba94f3201e7020bc3 # v8.3.0 with: # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum. # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eb0762df5..967d5e6d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/crate-ci/typos - rev: 37bb98842b0d8c4ffebdb75301a13db0267cef89 # v1.47.2 + rev: bee27e3a4fd1ea2111cf90ab89cd076c870fce14 # frozen: v1.48.0 hooks: - id: typos args: [--force-exclude] diff --git a/docs/de/docs/tutorial/frontend.md b/docs/de/docs/tutorial/frontend.md index 9cd4644d9..55213e550 100644 --- a/docs/de/docs/tutorial/frontend.md +++ b/docs/de/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ In diesem Beispiel werden Frontend-Pfade unter `/app` bereitgestellt. Alle regulären *Pfadoperationen* in der App haben weiterhin Vorrang, auch in anderen Routern. +## Abhängigkeiten und Middleware { #dependencies-and-middleware } + +Frontend-Responses laufen innerhalb der normalen **FastAPI**-Anwendung, daher gilt HTTP-Middleware für sie. + +Abhängigkeiten aus der App, aus einem `APIRouter` und aus `include_router()` gelten ebenfalls für Frontend-Responses. Das kann nützlich sein, um ein Frontend mit Cookie-Authentifizierung oder Ähnlichem zu schützen. + ## Nur statischer Build-Output { #static-build-output-only } `app.frontend()` liefert Dateien aus, die bereits von Ihrem Frontend-Build generiert wurden. diff --git a/docs/en/data/members.yml b/docs/en/data/members.yml index 7ec16e917..caf5953ff 100644 --- a/docs/en/data/members.yml +++ b/docs/en/data/members.yml @@ -2,21 +2,21 @@ members: - login: tiangolo avatar_url: https://avatars.githubusercontent.com/u/1326112 url: https://github.com/tiangolo -- login: Kludex - avatar_url: https://avatars.githubusercontent.com/u/7353520 - url: https://github.com/Kludex -- login: alejsdev - avatar_url: https://avatars.githubusercontent.com/u/90076947 - url: https://github.com/alejsdev -- login: svlandeg - avatar_url: https://avatars.githubusercontent.com/u/8796347 - url: https://github.com/svlandeg - login: YuriiMotov avatar_url: https://avatars.githubusercontent.com/u/109919500 url: https://github.com/YuriiMotov +- login: svlandeg + avatar_url: https://avatars.githubusercontent.com/u/8796347 + url: https://github.com/svlandeg +- login: alejsdev + avatar_url: https://avatars.githubusercontent.com/u/90076947 + url: https://github.com/alejsdev - login: patrick91 avatar_url: https://avatars.githubusercontent.com/u/667029 url: https://github.com/patrick91 - login: luzzodev avatar_url: https://avatars.githubusercontent.com/u/27291415 url: https://github.com/luzzodev +- login: Kludex + avatar_url: https://avatars.githubusercontent.com/u/7353520 + url: https://github.com/Kludex diff --git a/docs/en/data/skip_users.yml b/docs/en/data/skip_users.yml index cf24003af..26b2247bf 100644 --- a/docs/en/data/skip_users.yml +++ b/docs/en/data/skip_users.yml @@ -1,3 +1,4 @@ +users: - tiangolo - codecov - github-actions diff --git a/docs/en/data/topic_repos.yml b/docs/en/data/topic_repos.yml index ecfad6cb4..eff9f8078 100644 --- a/docs/en/data/topic_repos.yml +++ b/docs/en/data/topic_repos.yml @@ -1,3 +1,4 @@ +repos: - name: headroom html_url: https://github.com/headroomlabs-ai/headroom stars: 55017 diff --git a/docs/en/docs/external-links.md b/docs/en/docs/external-links.md index d614c64eb..7138c3425 100644 --- a/docs/en/docs/external-links.md +++ b/docs/en/docs/external-links.md @@ -23,7 +23,7 @@ But now that FastAPI is the backend framework with the most GitHub stars across Most starred [GitHub repositories with the topic `fastapi`](https://github.com/topics/fastapi): -{% for repo in topic_repos %} +{% for repo in topic_repos.repos %} ★ {{repo.stars}} - {{repo.name}} by @{{repo.owner_login}}. diff --git a/docs/en/docs/fastapi-people.md b/docs/en/docs/fastapi-people.md index e79928fb3..a914385d0 100644 --- a/docs/en/docs/fastapi-people.md +++ b/docs/en/docs/fastapi-people.md @@ -1,7 +1,4 @@ --- -hide: - - navigation - include_yaml: github_sponsors: data/github_sponsors.yml people: data/people.yml @@ -33,22 +30,6 @@ This is me: I'm the creator of **FastAPI**. You can read more about that in [Help FastAPI - Follow the author](help-fastapi.md#follow-the-author). -...But here I want to show you the community. - ---- - -**FastAPI** receives a lot of support from the community. And I want to highlight their contributions. - -These are the people that: - -* [Help others with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github). -* Create or review Pull Requests. -* Help [manage the repository](https://tiangolo.com/open-source/management-tasks/) (team members). - -All these tasks help maintain the repository. - -A round of applause to them. 👏 🙇 - ## Team This is the current list of team members. 😎 @@ -65,113 +46,19 @@ They have different levels of involvement and permissions, they can perform [rep -Although the team members have the permissions to perform privileged tasks, all the help from others maintaining FastAPI is very much appreciated! 🙇‍♂️ - ## FastAPI Experts -These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github). 🙇 - -They have proven to be **FastAPI Experts** by helping many others. ✨ - -/// tip - -You could become an official FastAPI Expert too! - -Just [help others with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github). 🤓 - -/// - -You can see the **FastAPI Experts** for: - -* [Last Month](#fastapi-experts-last-month) 🤓 -* [3 Months](#fastapi-experts-3-months) 😎 -* [6 Months](#fastapi-experts-6-months) 🧐 -* [1 Year](#fastapi-experts-1-year) 🧑‍🔬 -* [**All Time**](#fastapi-experts-all-time) 🧙 - -### FastAPI Experts - Last Month - -These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last month. 🤓 - -
- -{% for user in people.last_month_experts[:10] %} - -{% if user.login not in skip_users %} - -
@{{ user.login }}
Questions replied: {{ user.count }}
- -{% endif %} - -{% endfor %} - -
- -### FastAPI Experts - 3 Months - -These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last 3 months. 😎 - -
- -{% for user in people.three_months_experts[:10] %} - -{% if user.login not in skip_users %} - -
@{{ user.login }}
Questions replied: {{ user.count }}
- -{% endif %} - -{% endfor %} - -
+For a long time, answering questions from the community in GitHub Discussions was done by other community volunteers. -### FastAPI Experts - 6 Months +They proved they are **FastAPI Experts** by helping many others. ✨ -These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last 6 months. 🧐 +Here's the hall of fame of the first FastAPI Experts:
-{% for user in people.six_months_experts[:10] %} +{% for user in people.experts[:30] %} -{% if user.login not in skip_users %} - -
@{{ user.login }}
Questions replied: {{ user.count }}
- -{% endif %} - -{% endfor %} - -
- -### FastAPI Experts - 1 Year - -These are the users that have been [helping others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) during the last year. 🧑‍🔬 - -
- -{% for user in people.one_year_experts[:20] %} - -{% if user.login not in skip_users %} - -
@{{ user.login }}
Questions replied: {{ user.count }}
- -{% endif %} - -{% endfor %} - -
- -### FastAPI Experts - All Time - -Here are the all time **FastAPI Experts**. 🤓🤯 - -These are the users that have [helped others the most with questions in GitHub](help-fastapi.md#help-others-with-questions-in-github) through *all time*. 🧙 - -
- -{% for user in people.experts[:50] %} - -{% if user.login not in skip_users %} +{% if user.login not in skip_users.users %}
@{{ user.login }}
Questions replied: {{ user.count }}
@@ -183,17 +70,19 @@ These are the users that have [helped others the most with questions in GitHub]( ## Top Contributors -Here are the **Top Contributors**. 👷 +Currently, most of the code changes in FastAPI are done by the team. + +But over the years, there have also been many contributions made by others. -These users have created the most Pull Requests that have been *merged*. +They contributed source code, documentation, etc. 📦 -They have contributed source code, documentation, etc. 📦 +Here's the hall of fame of the first **Top Contributors**. 👷
-{% for user in (contributors.values() | list)[:50] %} +{% for user in (contributors.values() | list)[:30] %} -{% if user.login not in skip_users %} +{% if user.login not in skip_users.users %}
@{{ user.login }}
Pull Requests: {{ user.count }}
@@ -207,14 +96,16 @@ There are hundreds of other contributors, you can see them all in the [FastAPI G ## Top Translation Reviewers -These users are the **Top Translation Reviewers**. 🕵️ +Currently, translations are done using AI tools, steered by the FastAPI team and native speakers. -Translation reviewers have the **power to approve translations** of the documentation. Without them, there wouldn't be documentation in several other languages. +At some point, FastAPI had some documentation pages that community members translated into other languages by hand. + +Here's the hall of fame of the first **Top Translation Reviewers**. 🕵️
-{% for user in (translation_reviewers.values() | list)[:50] %} +{% for user in (translation_reviewers.values() | list)[:30] %} -{% if user.login not in skip_users %} +{% if user.login not in skip_users.users %}
@{{ user.login }}
Reviews: {{ user.count }}
@@ -226,9 +117,7 @@ Translation reviewers have the **power to approve translations** of the document ## Sponsors -These are the **Sponsors**. 😎 - -They are supporting my work with **FastAPI** (and others), mainly through [GitHub Sponsors](https://github.com/sponsors/tiangolo). +**Sponsors** support **FastAPI** and friends, mainly through [GitHub Sponsors](https://github.com/sponsors/tiangolo). ✨ {% if sponsors %} @@ -283,12 +172,22 @@ They are supporting my work with **FastAPI** (and others), mainly through [GitHu ## About the data - technical details -The main intention of this page is to highlight the effort of the community to help others. +The main intention of this page has been to highlight the effort of the community to help others, especially efforts that were normally less visible and, in many cases, more arduous, like helping others with questions and reviewing Pull Requests with translations. + +It also highlights contributions from sponsors. + +The data used to be calculated continuously, each month. + +As of July 2026, most of the work has been done by (paid) team members for quite some time. + +GitHub Discussions are answered mostly by team members. + +Most of the code changes are done by team members. -Especially including efforts that are normally less visible, and in many cases more arduous, like helping others with questions and reviewing Pull Requests with translations. +And translations are continuously done for the entire documentation in multiple languages, using AI tools, managed by team members. -The data is calculated each month, you can read the [source code here](https://github.com/fastapi/fastapi/blob/master/scripts/). +Additionally, in recent months, there's been an overwhelming amount of AI spam, mainly to cheat the FastAPI Experts system or to get a PR merged by any means and thereby be considered a contributor. You can read more about the point of view in [Automated Code and AI](https://tiangolo.com/open-source/contributing/#automated-code-and-ai). -Here I'm also highlighting contributions from sponsors. +Because of this, the data for the FastAPI Experts, Top Contributors, and Top Translation Reviewers is no longer continuously updated. -I also reserve the right to update the algorithm, sections, thresholds, etc (just in case 🤷). +This section is currently kept as a tribute to the humans that helped shape what FastAPI is today. 🙌 diff --git a/docs/en/docs/help-fastapi.md b/docs/en/docs/help-fastapi.md index 14bd05646..f995d899f 100644 --- a/docs/en/docs/help-fastapi.md +++ b/docs/en/docs/help-fastapi.md @@ -45,20 +45,6 @@ You can follow [me (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), th * [@tiangolo.com on **Bluesky**](https://bsky.app/profile/tiangolo.com) * [@tiangolo on **LinkedIn**](https://www.linkedin.com/in/tiangolo/). -## Help others with questions in GitHub { #help-others-with-questions-in-github } - -You can try and help others with their questions in [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered). - -In many cases you might already know the answer for those questions. 🤓 - -If you are helping a lot of people with their questions, you will become an official [FastAPI Expert](fastapi-people.md#fastapi-experts). 🎉 - -Just remember, the most important point is: try to be kind. 🤗 - -### How to Help { #how-to-help } - -Follow the [guide on how to help](https://tiangolo.com/open-source/help/#help-others-with-questions-in-github) here. - ## Ask Questions { #ask-questions } You can [create a new question](https://github.com/fastapi/fastapi/discussions/new?category=questions) in the GitHub repository, for example to: @@ -85,3 +71,9 @@ Keep in mind that as chats allow more "free conversation", it's easy to ask ques In GitHub, the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. Conversations in the chat systems are also not as easily searchable as in GitHub, they get lost. + +## Try FastAPI Cloud { #try-fastapi-cloud } + +The main funding for FastAPI and friends comes from [**FastAPI Cloud**](https://fastapicloud.com), a platform to deploy FastAPI applications in a simple and fast way, with a single command, `fastapi deploy`. + +FastAPI Cloud is built by the same team behind FastAPI. You can try it and consider it for your projects. diff --git a/docs/en/docs/management.md b/docs/en/docs/management.md index 740494768..a1a9bff1f 100644 --- a/docs/en/docs/management.md +++ b/docs/en/docs/management.md @@ -13,13 +13,3 @@ I normally give the final review to each PR before merging them. I make the fina There's a team of people that help manage and maintain the project. 😎 Learn more about it in [tiangolo.com - GitHub FastAPI](https://tiangolo.com/github-fastapi/). - -## FastAPI Experts - -The people that help others the most in GitHub Discussions can become [**FastAPI Experts**](./fastapi-people.md#fastapi-experts). - -This is normally the best way to contribute to the project. - -## External Help - -External help is very much appreciated. There are many ways to [help](./help-fastapi.md). ☕️ diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 3cc2548db..212368dad 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -7,14 +7,58 @@ hide: ## Latest Changes +### Docs + +* 📝 Restructure FastAPI People and related pages. PR [#16015](https://github.com/fastapi/fastapi/pull/16015) by [@tiangolo](https://github.com/tiangolo). + +### Internal + +* 🔥 Remove now-obsolete scripts to generate data for FastAPI People. PR [#16016](https://github.com/fastapi/fastapi/pull/16016) by [@tiangolo](https://github.com/tiangolo). + +## 0.139.2 (2026-07-16) + +### Fixes + +* 🐛 Refactor router route building to make it thread-safe, mainly relevant for tests running in parallel threads (uncommon). PR [#16013](https://github.com/fastapi/fastapi/pull/16013) by [@tiangolo](https://github.com/tiangolo). + +## 0.139.1 (2026-07-16) + +### Fixes + +* 🐛 Fix frontend fallback support for doted paths like `/users/john.doe`. PR [#16011](https://github.com/fastapi/fastapi/pull/16011) by [@tiangolo](https://github.com/tiangolo). + +### Docs + +* 📝 Fix topic repository list not being displayed and `skip_users` not being applied. PR [#15995](https://github.com/fastapi/fastapi/pull/15995) by [@YuriiMotov](https://github.com/YuriiMotov). + ### Translations +* 🌐 Update translations for tr (update-outdated). PR [#16005](https://github.com/fastapi/fastapi/pull/16005) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for zh-hant (update-outdated). PR [#15996](https://github.com/fastapi/fastapi/pull/15996) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for fr (update-outdated). PR [#16006](https://github.com/fastapi/fastapi/pull/16006) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for de (update-outdated). PR [#15999](https://github.com/fastapi/fastapi/pull/15999) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for ko (update-outdated). PR [#16004](https://github.com/fastapi/fastapi/pull/16004) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for zh (update-outdated). PR [#16001](https://github.com/fastapi/fastapi/pull/16001) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for uk (update-outdated). PR [#16003](https://github.com/fastapi/fastapi/pull/16003) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for ja (update-outdated). PR [#15998](https://github.com/fastapi/fastapi/pull/15998) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for pt (update-outdated). PR [#16000](https://github.com/fastapi/fastapi/pull/16000) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for es (update-outdated). PR [#15997](https://github.com/fastapi/fastapi/pull/15997) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for ru (update-outdated). PR [#16002](https://github.com/fastapi/fastapi/pull/16002) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for hi (add-missing). PR [#15990](https://github.com/fastapi/fastapi/pull/15990) by [@tiangolo](https://github.com/tiangolo). +* 🌐 Update translations for hi (add-missing). PR [#15925](https://github.com/fastapi/fastapi/pull/15925) by [@tiangolo](https://github.com/tiangolo). * 🌐 Update translations for hi (add-missing). PR [#15797](https://github.com/fastapi/fastapi/pull/15797) by [@tiangolo](https://github.com/tiangolo). * 🌐 Update `llm-prompt.md` for Hindi. PR [#15810](https://github.com/fastapi/fastapi/pull/15810) by [@YuriiMotov](https://github.com/YuriiMotov). * 🌐 Fix language-specific translation prompt for Russian language. PR [#15924](https://github.com/fastapi/fastapi/pull/15924) by [@YuriiMotov](https://github.com/YuriiMotov). ### Internal +* ⬆ Bump the python-packages group across 1 directory with 6 updates. PR [#15981](https://github.com/fastapi/fastapi/pull/15981) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump typing-extensions from 4.15.0 to 4.16.0. PR [#15982](https://github.com/fastapi/fastapi/pull/15982) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump the github-actions group across 1 directory with 4 updates. PR [#15983](https://github.com/fastapi/fastapi/pull/15983) by [@dependabot[bot]](https://github.com/apps/dependabot). +* ⬆ Bump pre-commit hooks. PR [#15985](https://github.com/fastapi/fastapi/pull/15985) by [@tiangolo](https://github.com/tiangolo). +* 👷 Use `FASTAPI_LATEST_CHANGES` token in `bump-pre-commit-hooks` workflow. PR [#15984](https://github.com/fastapi/fastapi/pull/15984) by [@YuriiMotov](https://github.com/YuriiMotov). +* 👷 Add GH workflow to bump pre-commit hook versions. PR [#15873](https://github.com/fastapi/fastapi/pull/15873) by [@YuriiMotov](https://github.com/YuriiMotov). +* 🔧 Set Dependabot schedule interval to "monthly". PR [#15874](https://github.com/fastapi/fastapi/pull/15874) by [@YuriiMotov](https://github.com/YuriiMotov). * ⬆ Bump CodSpeedHQ/action from 4.17.6 to 4.18.1 in the github-actions group. PR [#15950](https://github.com/fastapi/fastapi/pull/15950) by [@dependabot[bot]](https://github.com/apps/dependabot). * ⬆ Bump the python-packages group with 8 updates. PR [#15952](https://github.com/fastapi/fastapi/pull/15952) by [@dependabot[bot]](https://github.com/apps/dependabot). * 🔧 Update sponsors: add TutorCruncher. PR [#15947](https://github.com/fastapi/fastapi/pull/15947) by [@tiangolo](https://github.com/tiangolo). diff --git a/docs/en/docs/tutorial/frontend.md b/docs/en/docs/tutorial/frontend.md index 433cea275..0dacc73ba 100644 --- a/docs/en/docs/tutorial/frontend.md +++ b/docs/en/docs/tutorial/frontend.md @@ -52,7 +52,7 @@ For that, use `fallback="index.html"`: {* ../../docs_src/frontend/tutorial002_py310.py hl[5] *} -**FastAPI** uses this fallback only for `GET` and `HEAD` requests that look like browser navigation. Missing files like JavaScript, CSS, and images still return `404`. +**FastAPI** uses this fallback only for `GET` and `HEAD` requests that explicitly accept HTML with `Accept: text/html` or `Accept: application/xhtml+xml`, as browser navigation requests normally do. Missing files like JavaScript, CSS, and images still return `404`. Requests with other methods, like `POST` or `PUT`, to paths that only match the frontend fallback also return `404`. Regular **FastAPI** *path operations* still have higher priority than frontend routes. diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index 884307dcf..9a2321e04 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -223,9 +223,9 @@ nav: - reference/staticfiles.md - reference/templating.md - reference/testclient.md -- fastapi-people.md - "": - resources/index.md + - fastapi-people.md - help-fastapi.md - contributing.md - translations.md diff --git a/docs/es/docs/tutorial/frontend.md b/docs/es/docs/tutorial/frontend.md index 707772467..3365ae7f1 100644 --- a/docs/es/docs/tutorial/frontend.md +++ b/docs/es/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ En este ejemplo, los paths frontend se sirven bajo `/app`. Cualquier *path operation* regular en la app seguirá teniendo prioridad, incluso en otros routers. +## Dependencias y Middleware { #dependencies-and-middleware } + +Las responses frontend se ejecutan dentro de la aplicación **FastAPI** normal, así que el middleware HTTP se aplica a ellas. + +Las dependencias de la app, de un `APIRouter` y de `include_router()` también se aplican a las responses frontend. Esto puede ser útil para proteger un frontend con autenticación por cookie o similar. + ## Solo salida estática del build { #static-build-output-only } `app.frontend()` sirve archivos ya generados por tu build del frontend. diff --git a/docs/fr/docs/tutorial/frontend.md b/docs/fr/docs/tutorial/frontend.md index 6adb8a241..43a136608 100644 --- a/docs/fr/docs/tutorial/frontend.md +++ b/docs/fr/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ Dans cet exemple, les chemins frontend sont servis sous `/app`. Tous les *chemins d'accès* réguliers dans l'application seront toujours prioritaires, y compris dans d'autres routers. +## Dépendances et middleware { #dependencies-and-middleware } + +Les réponses frontend s'exécutent au sein de l'application **FastAPI** normale, donc le middleware HTTP s'applique à elles. + +Les dépendances de l'application, d'un `APIRouter` et de `include_router()` s'appliquent également aux réponses frontend. Cela peut être utile pour protéger un frontend avec une authentification par cookie ou similaire. + ## Sortie de build statique uniquement { #static-build-output-only } `app.frontend()` sert des fichiers déjà générés par votre build frontend. diff --git a/docs/hi/docs/about/index.md b/docs/hi/docs/about/index.md new file mode 100644 index 000000000..c155450a3 --- /dev/null +++ b/docs/hi/docs/about/index.md @@ -0,0 +1,3 @@ +# परिचय { #about } + +FastAPI, इसके design, प्रेरणा और और भी बहुत कुछ के बारे में। 🤓 diff --git a/docs/hi/docs/advanced/additional-responses.md b/docs/hi/docs/advanced/additional-responses.md new file mode 100644 index 000000000..410157c7a --- /dev/null +++ b/docs/hi/docs/advanced/additional-responses.md @@ -0,0 +1,247 @@ +# OpenAPI में अतिरिक्त Responses { #additional-responses-in-openapi } + +/// warning | चेतावनी + +यह एक काफ़ी advanced विषय है। + +अगर आप **FastAPI** के साथ शुरुआत कर रहे हैं, तो शायद आपको इसकी ज़रूरत न पड़े। + +/// + +आप अतिरिक्त status codes, media types, descriptions आदि के साथ अतिरिक्त responses घोषित कर सकते हैं। + +ये अतिरिक्त responses OpenAPI schema में शामिल किए जाएँगे, इसलिए वे API docs में भी दिखाई देंगे। + +लेकिन उन अतिरिक्त responses के लिए आपको यह सुनिश्चित करना होगा कि आप अपने status code और content के साथ सीधे `JSONResponse` जैसा कोई `Response` return करें। + +## `model` के साथ अतिरिक्त Response { #additional-response-with-model } + +आप अपने *path operation decorators* को `responses` parameter दे सकते हैं। + +यह एक `dict` प्राप्त करता है: keys प्रत्येक response के status codes होते हैं (जैसे `200`), और values अन्य `dict`s होते हैं जिनमें उनमें से प्रत्येक की जानकारी होती है। + +इनमें से प्रत्येक response `dict` में `model` key हो सकती है, जिसमें `response_model` की तरह एक Pydantic model होता है। + +**FastAPI** उस model को लेगा, उसका JSON Schema generate करेगा और उसे OpenAPI में सही जगह शामिल करेगा। + +उदाहरण के लिए, status code `404` और Pydantic model `Message` के साथ एक और response घोषित करने के लिए, आप लिख सकते हैं: + +{* ../../docs_src/additional_responses/tutorial001_py310.py hl[18,22] *} + +/// note | नोट + +ध्यान रखें कि आपको सीधे `JSONResponse` return करना होगा। + +/// + +/// note | नोट + +`model` key OpenAPI का हिस्सा नहीं है। + +**FastAPI** वहाँ से Pydantic model लेगा, JSON Schema generate करेगा, और उसे सही जगह रखेगा। + +सही जगह है: + +* `content` key में, जिसकी value एक और JSON object (`dict`) होती है जिसमें शामिल है: + * media type वाली एक key, जैसे `application/json`, जिसकी value एक और JSON object होती है, जिसमें शामिल है: + * एक key `schema`, जिसकी value model से JSON Schema होती है, यही सही जगह है। + * **FastAPI** इसे सीधे शामिल करने के बजाय आपके OpenAPI में किसी अन्य जगह मौजूद global JSON Schemas का reference यहाँ जोड़ता है। इस तरह, अन्य applications और clients उन JSON Schemas को सीधे उपयोग कर सकते हैं, बेहतर code generation tools प्रदान कर सकते हैं, आदि। + +/// + +इस *path operation* के लिए OpenAPI में generate किए गए responses होंगे: + +```JSON hl_lines="3-12" +{ + "responses": { + "404": { + "description": "Additional Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Message" + } + } + } + }, + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Item" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } +} +``` + +Schemas को OpenAPI schema के अंदर किसी दूसरी जगह reference किया गया है: + +```JSON hl_lines="4-16" +{ + "components": { + "schemas": { + "Message": { + "title": "Message", + "required": [ + "message" + ], + "type": "object", + "properties": { + "message": { + "title": "Message", + "type": "string" + } + } + }, + "Item": { + "title": "Item", + "required": [ + "id", + "value" + ], + "type": "object", + "properties": { + "id": { + "title": "Id", + "type": "string" + }, + "value": { + "title": "Value", + "type": "string" + } + } + }, + "ValidationError": { + "title": "ValidationError", + "required": [ + "loc", + "msg", + "type" + ], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "type": "string" + } + }, + "msg": { + "title": "Message", + "type": "string" + }, + "type": { + "title": "Error Type", + "type": "string" + } + } + }, + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + } + } + } + } + } + } +} +``` + +## मुख्य response के लिए अतिरिक्त media types { #additional-media-types-for-the-main-response } + +आप इसी `responses` parameter का उपयोग करके उसी मुख्य response के लिए अलग-अलग media types जोड़ सकते हैं। + +उदाहरण के लिए, आप `image/png` का एक अतिरिक्त media type जोड़ सकते हैं, यह घोषित करते हुए कि आपका *path operation* एक JSON object (media type `application/json` के साथ) या एक PNG image return कर सकता है: + +{* ../../docs_src/additional_responses/tutorial002_py310.py hl[17:22,26] *} + +/// note | नोट + +ध्यान दें कि आपको image को सीधे `FileResponse` का उपयोग करके return करना होगा। + +/// + +/// note | नोट + +जब तक आप अपने `responses` parameter में स्पष्ट रूप से कोई अलग media type specify नहीं करते, FastAPI मान लेगा कि response का media type मुख्य response class (default `application/json`) जैसा ही है। + +लेकिन अगर आपने custom response class specify की है जिसका media type `None` है, तो FastAPI किसी भी ऐसे अतिरिक्त response के लिए `application/json` का उपयोग करेगा जिसके साथ कोई associated model है। + +/// + +## जानकारी को मिलाना { #combining-information } + +आप कई जगहों से response जानकारी को भी मिला सकते हैं, जिसमें `response_model`, `status_code`, और `responses` parameters शामिल हैं। + +आप default status code `200` (या ज़रूरत पड़ने पर custom code) का उपयोग करके `response_model` घोषित कर सकते हैं, और फिर उसी response के लिए अतिरिक्त जानकारी सीधे OpenAPI schema में `responses` के अंदर घोषित कर सकते हैं। + +**FastAPI** `responses` से अतिरिक्त जानकारी बनाए रखेगा, और उसे आपके model से JSON Schema के साथ मिला देगा। + +उदाहरण के लिए, आप status code `404` वाला एक response घोषित कर सकते हैं जो Pydantic model का उपयोग करता है और जिसमें custom `description` है। + +और status code `200` वाला एक response, जो आपके `response_model` का उपयोग करता है, लेकिन जिसमें custom `example` शामिल है: + +{* ../../docs_src/additional_responses/tutorial003_py310.py hl[20:31] *} + +यह सब मिलाकर आपके OpenAPI में शामिल किया जाएगा, और API docs में दिखाया जाएगा: + + + +## पहले से परिभाषित responses और custom responses को मिलाएँ { #combine-predefined-responses-and-custom-ones } + +आप कुछ पहले से परिभाषित responses रखना चाह सकते हैं जो कई *path operations* पर लागू होते हैं, लेकिन आप उन्हें प्रत्येक *path operation* के लिए ज़रूरी custom responses के साथ मिलाना चाहते हैं। + +ऐसे मामलों के लिए, आप `**dict_to_unpack` के साथ `dict` को "unpacking" करने की Python technique का उपयोग कर सकते हैं: + +```Python +old_dict = { + "old key": "old value", + "second old key": "second old value", +} +new_dict = {**old_dict, "new key": "new value"} +``` + +यहाँ, `new_dict` में `old_dict` के सभी key-value pairs के साथ नया key-value pair भी होगा: + +```Python +{ + "old key": "old value", + "second old key": "second old value", + "new key": "new value", +} +``` + +आप इस technique का उपयोग अपने *path operations* में कुछ पहले से परिभाषित responses को reuse करने और उन्हें अतिरिक्त custom responses के साथ मिलाने के लिए कर सकते हैं। + +उदाहरण के लिए: + +{* ../../docs_src/additional_responses/tutorial004_py310.py hl[11:15,24] *} + +## OpenAPI responses के बारे में अधिक जानकारी { #more-information-about-openapi-responses } + +Responses में आप ठीक-ठीक क्या शामिल कर सकते हैं, यह देखने के लिए आप OpenAPI specification में ये sections देख सकते हैं: + +* [OpenAPI Responses Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#responses-object), इसमें `Response Object` शामिल है। +* [OpenAPI Response Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#response-object), आप इसमें से कुछ भी सीधे अपने `responses` parameter के अंदर प्रत्येक response में शामिल कर सकते हैं। जिसमें `description`, `headers`, `content` (इसी के अंदर आप अलग-अलग media types और JSON Schemas घोषित करते हैं), और `links` शामिल हैं। diff --git a/docs/hi/docs/advanced/additional-status-codes.md b/docs/hi/docs/advanced/additional-status-codes.md new file mode 100644 index 000000000..bec6e4eae --- /dev/null +++ b/docs/hi/docs/advanced/additional-status-codes.md @@ -0,0 +1,41 @@ +# अतिरिक्त Status Codes { #additional-status-codes } + +default रूप से, **FastAPI** responses को `JSONResponse` का उपयोग करके return करेगा, जिसमें आपके *path operation* से return किया गया content उस `JSONResponse` के अंदर रखा जाएगा। + +यह default status code या वह status code उपयोग करेगा जो आपने अपने *path operation* में set किया है। + +## अतिरिक्त status codes { #additional-status-codes_1 } + +अगर आप मुख्य status code के अलावा अतिरिक्त status codes return करना चाहते हैं, तो आप सीधे `Response`, जैसे `JSONResponse`, return करके और अतिरिक्त status code को सीधे set करके ऐसा कर सकते हैं। + +उदाहरण के लिए, मान लीजिए कि आप एक ऐसा *path operation* रखना चाहते हैं जो items को update करने की अनुमति देता है, और सफल होने पर HTTP status code 200 "OK" return करता है। + +लेकिन आप यह भी चाहते हैं कि यह नए items को स्वीकार करे। और जब items पहले मौजूद नहीं थे, तो यह उन्हें बनाता है, और HTTP status code 201 "Created" return करता है। + +ऐसा करने के लिए, `JSONResponse` import करें, और अपना content वहीं सीधे return करें, साथ में अपनी पसंद का `status_code` set करें: + +{* ../../docs_src/additional_status_codes/tutorial001_an_py310.py hl[4,25] *} + +/// warning | चेतावनी + +जब आप सीधे `Response` return करते हैं, जैसे ऊपर के उदाहरण में, तो वह सीधे return किया जाएगा। + +इसे किसी model आदि के साथ serialize नहीं किया जाएगा। + +सुनिश्चित करें कि इसमें वही data है जो आप चाहते हैं, और values valid JSON हैं (अगर आप `JSONResponse` उपयोग कर रहे हैं)। + +/// + +/// note | तकनीकी विवरण + +आप `from starlette.responses import JSONResponse` भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.responses` `fastapi.responses` के रूप में प्रदान करता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। `status` के साथ भी यही है। + +/// + +## OpenAPI और API docs { #openapi-and-api-docs } + +अगर आप अतिरिक्त status codes और responses सीधे return करते हैं, तो वे OpenAPI schema (API docs) में शामिल नहीं होंगे, क्योंकि FastAPI के पास पहले से यह जानने का तरीका नहीं है कि आप क्या return करने वाले हैं। + +लेकिन आप इसे अपने code में document कर सकते हैं, उपयोग करके: [अतिरिक्त Responses](additional-responses.md)। diff --git a/docs/hi/docs/advanced/advanced-dependencies.md b/docs/hi/docs/advanced/advanced-dependencies.md new file mode 100644 index 000000000..4c6648b95 --- /dev/null +++ b/docs/hi/docs/advanced/advanced-dependencies.md @@ -0,0 +1,163 @@ +# Advanced Dependencies { #advanced-dependencies } + +## Parameterized dependencies { #parameterized-dependencies } + +अब तक हमने जो भी dependencies देखी हैं, वे एक निश्चित function या class हैं। + +लेकिन ऐसे मामले हो सकते हैं जहाँ आप dependency पर parameters सेट कर पाना चाहें, बिना कई अलग-अलग functions या classes declare किए। + +कल्पना करें कि हम एक ऐसी dependency रखना चाहते हैं जो जाँचती है कि query parameter `q` में कुछ निश्चित content है या नहीं। + +लेकिन हम उस निश्चित content को parameterize कर पाना चाहते हैं। + +## एक "callable" instance { #a-callable-instance } + +Python में किसी class के instance को "callable" बनाने का एक तरीका है। + +खुद class को नहीं (जो पहले से ही callable होती है), बल्कि उस class के एक instance को। + +ऐसा करने के लिए, हम एक method `__call__` declare करते हैं: + +{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[12] *} + +इस मामले में, यही `__call__` है जिसे **FastAPI** अतिरिक्त parameters और sub-dependencies की जाँच के लिए उपयोग करेगा, और बाद में आपकी *path operation function* में parameter को value पास करने के लिए यही call किया जाएगा। + +## Instance को parameterize करें { #parameterize-the-instance } + +और अब, हम `__init__` का उपयोग करके instance के parameters declare कर सकते हैं जिन्हें हम dependency को "parameterize" करने के लिए उपयोग कर सकते हैं: + +{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[9] *} + +इस मामले में, **FastAPI** कभी भी `__init__` को छुएगा या उसकी परवाह नहीं करेगा, हम इसे सीधे अपने code में उपयोग करेंगे। + +## एक instance बनाएँ { #create-an-instance } + +हम इस class का एक instance इस तरह बना सकते हैं: + +{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[18] *} + +और इस तरह हम अपनी dependency को "parameterize" कर पाते हैं, जिसमें अब `"bar"` उसके अंदर है, attribute `checker.fixed_content` के रूप में। + +## Instance को dependency के रूप में उपयोग करें { #use-the-instance-as-a-dependency } + +फिर, हम `Depends(FixedContentQueryChecker)` के बजाय इस `checker` को `Depends(checker)` में उपयोग कर सकते हैं, क्योंकि dependency class खुद नहीं, बल्कि instance `checker` है। + +और dependency को solve करते समय, **FastAPI** इस `checker` को इस तरह call करेगा: + +```Python +checker(q="somequery") +``` + +...और जो भी यह return करेगा उसे हमारी *path operation function* में dependency की value के रूप में parameter `fixed_content_included` में पास करेगा: + +{* ../../docs_src/dependencies/tutorial011_an_py310.py hl[22] *} + +/// tip | सुझाव + +यह सब थोड़ा बनावटी लग सकता है। और अभी यह बहुत स्पष्ट नहीं हो सकता कि यह कैसे उपयोगी है। + +ये उदाहरण जानबूझकर सरल रखे गए हैं, लेकिन दिखाते हैं कि यह सब कैसे काम करता है। + +Security वाले chapters में utility functions हैं जिन्हें इसी तरीके से implement किया गया है। + +अगर आपने यह सब समझ लिया है, तो आप पहले से जानते हैं कि security के लिए वे utility tools अंदर से कैसे काम करते हैं। + +/// + +## `yield`, `HTTPException`, `except` और Background Tasks वाली Dependencies { #dependencies-with-yield-httpexception-except-and-background-tasks } + +/// warning | चेतावनी + +सबसे अधिक संभावना है कि आपको इन तकनीकी विवरणों की आवश्यकता नहीं है। + +ये विवरण मुख्य रूप से तब उपयोगी हैं जब आपके पास 0.121.0 से पुरानी FastAPI application थी और आपको `yield` वाली dependencies के साथ समस्याएँ आ रही हैं। + +/// + +`yield` वाली dependencies समय के साथ अलग-अलग use cases को संभालने और कुछ समस्याएँ ठीक करने के लिए विकसित हुई हैं, यहाँ बदले हुए व्यवहार का सारांश है। + +### `yield` और `scope` वाली Dependencies { #dependencies-with-yield-and-scope } + +Version 0.121.0 में, FastAPI ने `yield` वाली dependencies के लिए `Depends(scope="function")` का support जोड़ा। + +`Depends(scope="function")` का उपयोग करने पर, `yield` के बाद का exit code *path operation function* के समाप्त होते ही, response client को वापस भेजे जाने से पहले execute होता है। + +और `Depends(scope="request")` (default) का उपयोग करने पर, `yield` के बाद का exit code response भेजे जाने के बाद execute होता है। + +आप इसके बारे में docs में [Dependencies with `yield` - Early exit and `scope`](../tutorial/dependencies/dependencies-with-yield.md#early-exit-and-scope) में अधिक पढ़ सकते हैं। + +### `yield` और `StreamingResponse` वाली Dependencies, तकनीकी विवरण { #dependencies-with-yield-and-streamingresponse-technical-details } + +FastAPI 0.118.0 से पहले, यदि आप `yield` वाली dependency उपयोग करते थे, तो यह *path operation function* के return करने के बाद लेकिन response भेजने से ठीक पहले exit code run करती थी। + +इरादा यह था कि आवश्यक से अधिक समय तक resources को पकड़े रखने से बचा जाए, response के network से गुजरने की प्रतीक्षा करते हुए। + +इस बदलाव का अर्थ यह भी था कि यदि आपने `StreamingResponse` return किया, तो `yield` वाली dependency का exit code पहले ही run हो चुका होता। + +उदाहरण के लिए, यदि आपके पास `yield` वाली dependency में database session था, तो `StreamingResponse` data stream करते समय उस session का उपयोग नहीं कर पाता क्योंकि `yield` के बाद वाले exit code में session पहले ही बंद हो चुका होता। + +यह व्यवहार 0.118.0 में revert कर दिया गया, ताकि `yield` के बाद का exit code response भेजे जाने के बाद execute हो। + +/// note | ध्यान दें + +जैसा कि आप नीचे देखेंगे, यह version 0.106.0 से पहले के व्यवहार से बहुत मिलता-जुलता है, लेकिन कई सुधारों और corner cases के लिए bug fixes के साथ। + +/// + +#### Early Exit Code वाले Use Cases { #use-cases-with-early-exit-code } + +कुछ specific conditions वाले use cases हैं जिन्हें response भेजने से पहले `yield` वाली dependencies का exit code run करने के पुराने व्यवहार से लाभ हो सकता है। + +उदाहरण के लिए, कल्पना करें कि आपके पास ऐसा code है जो `yield` वाली dependency में database session का उपयोग केवल user verify करने के लिए करता है, लेकिन database session फिर *path operation function* में कभी उपयोग नहीं होता, केवल dependency में उपयोग होता है, **और** response भेजे जाने में लंबा समय लेता है, जैसे `StreamingResponse` जो data धीरे-धीरे भेजता है, लेकिन किसी कारण से database का उपयोग नहीं करता। + +इस मामले में, database session तब तक पकड़ा रहेगा जब तक response भेजना समाप्त नहीं हो जाता, लेकिन यदि आप इसका उपयोग नहीं करते हैं, तो इसे पकड़े रखना आवश्यक नहीं होगा। + +यह इस तरह दिख सकता है: + +{* ../../docs_src/dependencies/tutorial013_an_py310.py *} + +Exit code, यानी `Session` का automatic closing, यहाँ: + +{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[19:21] *} + +...response द्वारा slow data भेजना समाप्त करने के बाद run होगा: + +{* ../../docs_src/dependencies/tutorial013_an_py310.py ln[30:38] hl[31:33] *} + +लेकिन क्योंकि `generate_stream()` database session का उपयोग नहीं करता, response भेजते समय session को खुला रखना वास्तव में आवश्यक नहीं है। + +यदि आपके पास SQLModel (या SQLAlchemy) का उपयोग करते हुए यह specific use case है, तो आप session को तब explicit रूप से बंद कर सकते हैं जब आपको इसकी आगे आवश्यकता न हो: + +{* ../../docs_src/dependencies/tutorial014_an_py310.py ln[24:28] hl[28] *} + +इस तरह session database connection release कर देगा, ताकि अन्य requests उसका उपयोग कर सकें। + +यदि आपके पास कोई अलग use case है जिसे `yield` वाली dependency से early exit करने की आवश्यकता है, तो कृपया अपने specific use case और dependencies with `yield` के लिए early closing से आपको क्यों लाभ होगा, इसके साथ एक [GitHub Discussion Question](https://github.com/fastapi/fastapi/discussions/new?category=questions) बनाएँ। + +यदि dependencies with `yield` में early closing के लिए compelling use cases होते हैं, तो मैं early closing में opt in करने का नया तरीका जोड़ने पर विचार करूँगा। + +### `yield` और `except` वाली Dependencies, तकनीकी विवरण { #dependencies-with-yield-and-except-technical-details } + +FastAPI 0.110.0 से पहले, यदि आप `yield` वाली dependency उपयोग करते थे, और फिर उस dependency में `except` के साथ exception capture करते थे, और exception को फिर से raise नहीं करते थे, तो exception automatic रूप से किसी भी exception handlers या internal server error handler को raise/forward कर दिया जाता था। + +यह version 0.110.0 में बदला गया ताकि handler के बिना forwarded exceptions (internal server errors) से होने वाली unhandled memory consumption ठीक की जा सके, और इसे regular Python code के व्यवहार के साथ consistent बनाया जा सके। + +### Background Tasks और `yield` वाली Dependencies, तकनीकी विवरण { #background-tasks-and-dependencies-with-yield-technical-details } + +FastAPI 0.106.0 से पहले, `yield` के बाद exceptions raise करना संभव नहीं था, `yield` वाली dependencies में exit code response भेजे जाने के *बाद* execute होता था, इसलिए [Exception Handlers](../tutorial/handling-errors.md#install-custom-exception-handlers) पहले ही run हो चुके होते। + +इसे मुख्य रूप से इस तरह design किया गया था ताकि dependencies द्वारा "yielded" किए गए उन्हीं objects को background tasks के अंदर उपयोग किया जा सके, क्योंकि exit code background tasks के समाप्त होने के बाद execute होता था। + +यह FastAPI 0.106.0 में बदला गया, इस इरादे से कि response के network से गुजरने की प्रतीक्षा करते समय resources को पकड़े न रखा जाए। + +/// tip | सुझाव + +इसके अतिरिक्त, background task सामान्यतः logic का एक independent set होता है जिसे अलग से संभाला जाना चाहिए, अपने स्वयं के resources के साथ (जैसे उसका अपना database connection)। + +इसलिए, इस तरह आपके पास शायद अधिक साफ़ code होगा। + +/// + +यदि आप इस व्यवहार पर निर्भर थे, तो अब आपको background tasks के लिए resources background task के अंदर ही बनाने चाहिए, और internally केवल ऐसा data उपयोग करना चाहिए जो `yield` वाली dependencies के resources पर निर्भर न हो। + +उदाहरण के लिए, उसी database session का उपयोग करने के बजाय, आप background task के अंदर एक नया database session बनाएँगे, और इस नए session का उपयोग करके database से objects प्राप्त करेंगे। और फिर database से object को background task function में parameter के रूप में पास करने के बजाय, आप उस object की ID पास करेंगे और फिर background task function के अंदर object को फिर से प्राप्त करेंगे। diff --git a/docs/hi/docs/advanced/advanced-python-types.md b/docs/hi/docs/advanced/advanced-python-types.md new file mode 100644 index 000000000..d73a95fd8 --- /dev/null +++ b/docs/hi/docs/advanced/advanced-python-types.md @@ -0,0 +1,61 @@ +# उन्नत Python Types { #advanced-python-types } + +Python types के साथ काम करते समय यहाँ कुछ अतिरिक्त विचार हैं जो उपयोगी हो सकते हैं। + +## `Union` या `Optional` का उपयोग { #using-union-or-optional } + +अगर आपका code किसी कारण से `|` का उपयोग नहीं कर सकता, उदाहरण के लिए अगर यह type annotation में नहीं बल्कि `response_model=` जैसी किसी चीज़ में है, तो vertical bar (`|`) का उपयोग करने के बजाय आप `typing` से `Union` का उपयोग कर सकते हैं। + +उदाहरण के लिए, आप declare कर सकते हैं कि कोई चीज़ `str` या `None` हो सकती है: + +```python +from typing import Union + + +def say_hi(name: Union[str, None]): + print(f"Hi {name}!") +``` + +`typing` में `Optional` के साथ यह declare करने का एक shortcut भी है कि कोई चीज़ `None` हो सकती है। + +मेरे बहुत **subjective** दृष्टिकोण से एक tip यहाँ है: + +* 🚨 `Optional[SomeType]` का उपयोग करने से बचें +* इसके बजाय ✨ **`Union[SomeType, None]` का उपयोग करें** ✨। + +दोनों equivalent हैं और अंदर से वे समान हैं, लेकिन मैं `Optional` के बजाय `Union` की सलाह दूँगा क्योंकि "**optional**" शब्द से ऐसा लग सकता है कि value optional है, जबकि इसका वास्तविक अर्थ है "यह `None` हो सकता है", भले ही यह optional न हो और अभी भी required हो। + +मुझे लगता है कि `Union[SomeType, None]` अपने अर्थ के बारे में अधिक explicit है। + +यह बस शब्दों और नामों की बात है। लेकिन ये शब्द इस बात को प्रभावित कर सकते हैं कि आप और आपके teammates code के बारे में कैसे सोचते हैं। + +एक उदाहरण के रूप में, इस function को लेते हैं: + +```python +from typing import Optional + + +def say_hi(name: Optional[str]): + print(f"Hey {name}!") +``` + +parameter `name` को `Optional[str]` के रूप में define किया गया है, लेकिन यह **optional नहीं है**, आप function को parameter के बिना call नहीं कर सकते: + +```Python +say_hi() # अरे नहीं, यह error throw करता है! 😱 +``` + +`name` parameter **अभी भी required** है (*optional* नहीं) क्योंकि इसमें default value नहीं है। फिर भी, `name` value के रूप में `None` स्वीकार करता है: + +```Python +say_hi(name=None) # यह काम करता है, None valid है 🎉 +``` + +अच्छी खबर यह है कि अधिकतर मामलों में, आप types के unions को define करने के लिए बस `|` का उपयोग कर पाएँगे: + +```python +def say_hi(name: str | None): + print(f"Hey {name}!") +``` + +इसलिए, सामान्यतः आपको `Optional` और `Union` जैसे नामों के बारे में चिंता करने की ज़रूरत नहीं होती। 😎 diff --git a/docs/hi/docs/advanced/async-tests.md b/docs/hi/docs/advanced/async-tests.md new file mode 100644 index 000000000..8981d552b --- /dev/null +++ b/docs/hi/docs/advanced/async-tests.md @@ -0,0 +1,99 @@ +# Async Tests { #async-tests } + +आपने पहले ही देखा है कि दिए गए `TestClient` का उपयोग करके अपनी **FastAPI** applications को कैसे test किया जाता है। अब तक, आपने केवल synchronous tests लिखना देखा है, `async` functions का उपयोग किए बिना। + +अपने tests में asynchronous functions का उपयोग कर पाना उपयोगी हो सकता है, उदाहरण के लिए, जब आप अपने database को asynchronously query कर रहे हों। कल्पना करें कि आप अपनी FastAPI application को requests भेजना test करना चाहते हैं और फिर verify करना चाहते हैं कि आपके backend ने async database library का उपयोग करते हुए database में सही data सफलतापूर्वक लिखा है। + +आइए देखें कि हम इसे कैसे काम करवा सकते हैं। + +## pytest.mark.anyio { #pytest-mark-anyio } + +अगर हम अपने tests में asynchronous functions call करना चाहते हैं, तो हमारे test functions asynchronous होने चाहिए। AnyIO इसके लिए एक अच्छा plugin प्रदान करता है, जो हमें specify करने देता है कि कुछ test functions को asynchronously call किया जाना है। + +## HTTPX { #httpx } + +भले ही आपकी **FastAPI** application `async def` के बजाय सामान्य `def` functions का उपयोग करती हो, यह अंदर से फिर भी एक `async` application होती है। + +`TestClient` अंदर कुछ magic करता है ताकि standard pytest का उपयोग करते हुए आपकी सामान्य `def` test functions में asynchronous FastAPI application को call किया जा सके। लेकिन जब हम इसे asynchronous functions के अंदर उपयोग करते हैं, तो वह magic अब काम नहीं करता। अपने tests को asynchronously चलाने पर, हम अपने test functions के अंदर `TestClient` का उपयोग नहीं कर सकते। + +`TestClient` [HTTPX](https://www.python-httpx.org) पर आधारित है, और सौभाग्य से, हम API को test करने के लिए इसे सीधे उपयोग कर सकते हैं। + +## उदाहरण { #example } + +एक सरल उदाहरण के लिए, आइए [बड़ी Applications](../tutorial/bigger-applications.md) और [Testing](../tutorial/testing.md) में वर्णित file structure जैसी एक structure पर विचार करें: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +│   └── test_main.py +``` + +file `main.py` में यह होगा: + +{* ../../docs_src/async_tests/app_a_py310/main.py *} + +file `test_main.py` में `main.py` के लिए tests होंगे, यह अब कुछ ऐसा दिख सकता है: + +{* ../../docs_src/async_tests/app_a_py310/test_main.py *} + +## इसे चलाएँ { #run-it } + +आप अपने tests को हमेशा की तरह इस तरह चला सकते हैं: + +
+ +```console +$ pytest + +---> 100% +``` + +
+ +## विस्तार से { #in-detail } + +marker `@pytest.mark.anyio` pytest को बताता है कि इस test function को asynchronously call किया जाना चाहिए: + +{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *} + +/// tip | सुझाव + +ध्यान दें कि test function अब पहले की तरह `TestClient` का उपयोग करते समय केवल `def` नहीं, बल्कि `async def` है। + +/// + +फिर हम app के साथ एक `AsyncClient` बना सकते हैं, और `await` का उपयोग करते हुए इसमें async requests भेज सकते हैं। + +{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *} + +यह इसके बराबर है: + +```Python +response = client.get('/') +``` + +...जिसका उपयोग हम `TestClient` के साथ अपनी requests बनाने के लिए करते थे। + +/// tip | सुझाव + +ध्यान दें कि हम नए `AsyncClient` के साथ async/await का उपयोग कर रहे हैं - request asynchronous है। + +/// + +/// warning | चेतावनी + +अगर आपकी application lifespan events पर निर्भर करती है, तो `AsyncClient` इन events को trigger नहीं करेगा। यह सुनिश्चित करने के लिए कि वे trigger हों, [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage) से `LifespanManager` का उपयोग करें। + +/// + +## अन्य asynchronous function calls { #other-asynchronous-function-calls } + +क्योंकि testing function अब asynchronous है, आप अब अपने tests में अपनी FastAPI application को requests भेजने के अलावा अन्य `async` functions को भी call (और `await`) कर सकते हैं, ठीक वैसे ही जैसे आप उन्हें अपने code में कहीं और call करते हैं। + +/// tip | सुझाव + +अगर अपने tests में asynchronous function calls integrate करते समय आपको `RuntimeError: Task attached to a different loop` मिलता है (जैसे [MongoDB's MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop) का उपयोग करते समय), तो याद रखें कि जिन objects को event loop की जरूरत होती है, उन्हें केवल async functions के भीतर ही instantiate करें, जैसे कि `@app.on_event("startup")` callback। + +/// diff --git a/docs/hi/docs/advanced/behind-a-proxy.md b/docs/hi/docs/advanced/behind-a-proxy.md new file mode 100644 index 000000000..11bd984a2 --- /dev/null +++ b/docs/hi/docs/advanced/behind-a-proxy.md @@ -0,0 +1,466 @@ +# Proxy के पीछे { #behind-a-proxy } + +कई स्थितियों में, आप अपने FastAPI app के सामने Traefik या Nginx जैसा **proxy** उपयोग करेंगे। + +ये proxies HTTPS certificates और दूसरी चीज़ें संभाल सकते हैं। + +## Proxy Forwarded Headers { #proxy-forwarded-headers } + +आपकी application के सामने मौजूद **proxy** आम तौर पर requests को आपके **server** तक भेजने से पहले तुरंत कुछ headers सेट करेगा, ताकि server को पता चल सके कि request proxy द्वारा **forwarded** की गई थी, उसे मूल (public) URL पता चल सके, जिसमें domain शामिल हो, कि वह HTTPS उपयोग कर रहा है, आदि। + +**server** program (उदाहरण के लिए **FastAPI CLI** के जरिए **Uvicorn**) इन headers को समझने में सक्षम है, और फिर वह जानकारी आपकी application को पास कर सकता है। + +लेकिन security के लिए, क्योंकि server को यह नहीं पता कि वह किसी trusted proxy के पीछे है, वह उन headers को interpret नहीं करेगा। + +/// note | तकनीकी विवरण + +Proxy headers हैं: + +* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For) +* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto) +* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host) + +/// + +### Proxy Forwarded Headers सक्षम करें { #enable-proxy-forwarded-headers } + +आप FastAPI CLI को *CLI Option* `--forwarded-allow-ips` के साथ शुरू कर सकते हैं और वे IP addresses पास कर सकते हैं जिन पर उन forwarded headers को पढ़ने के लिए भरोसा किया जाना चाहिए। + +अगर आप इसे `--forwarded-allow-ips="*"` पर सेट करते हैं, तो यह सभी incoming IPs पर भरोसा करेगा। + +अगर आपका **server** किसी trusted **proxy** के पीछे है और केवल proxy ही उससे बात करता है, तो इससे वह उस **proxy** का जो भी IP है, उसे accept करेगा। + +
+ +```console +$ fastapi run --forwarded-allow-ips="*" + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### HTTPS के साथ Redirects { #redirects-with-https } + +उदाहरण के लिए, मान लें कि आप एक *path operation* `/items/` define करते हैं: + +{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *} + +अगर client `/items` पर जाने की कोशिश करता है, तो default रूप से, उसे `/items/` पर redirect किया जाएगा। + +लेकिन *CLI Option* `--forwarded-allow-ips` सेट करने से पहले यह `http://localhost:8000/items/` पर redirect कर सकता है। + +लेकिन शायद आपकी application `https://mysuperapp.com` पर hosted है, और redirection `https://mysuperapp.com/items/` पर होना चाहिए। + +अब `--proxy-headers` सेट करने से FastAPI सही location पर redirect कर पाएगा। 😎 + +``` +https://mysuperapp.com/items/ +``` + +/// tip | सुझाव + +अगर आप HTTPS के बारे में और जानना चाहते हैं, तो guide [HTTPS के बारे में](../deployment/https.md) देखें। + +/// + +### Proxy Forwarded Headers कैसे काम करते हैं { #how-proxy-forwarded-headers-work } + +यहाँ client और **application server** के बीच **proxy** द्वारा forwarded headers जोड़ने का एक visual representation है: + +```mermaid +sequenceDiagram + participant Client + participant Proxy as Proxy/Load Balancer + participant Server as FastAPI Server + + Client->>Proxy: HTTPS Request
Host: mysuperapp.com
Path: /items + + Note over Proxy: Proxy adds forwarded headers + + Proxy->>Server: HTTP Request
X-Forwarded-For: [client IP]
X-Forwarded-Proto: https
X-Forwarded-Host: mysuperapp.com
Path: /items + + Note over Server: Server interprets headers
(if --forwarded-allow-ips is set) + + Server->>Proxy: HTTP Response
with correct HTTPS URLs + + Proxy->>Client: HTTPS Response +``` + +**proxy** मूल client request को intercept करता है और request को **application server** तक पास करने से पहले खास *forwarded* headers (`X-Forwarded-*`) जोड़ता है। + +ये headers मूल request के बारे में वह जानकारी सुरक्षित रखते हैं जो अन्यथा खो जाती: + +* **X-Forwarded-For**: मूल client का IP address +* **X-Forwarded-Proto**: मूल protocol (`https`) +* **X-Forwarded-Host**: मूल host (`mysuperapp.com`) + +जब **FastAPI CLI** को `--forwarded-allow-ips` के साथ configured किया जाता है, तो यह इन headers पर भरोसा करता है और उनका उपयोग करता है, उदाहरण के लिए redirects में सही URLs generate करने के लिए। + +## Stripped path prefix वाला Proxy { #proxy-with-a-stripped-path-prefix } + +आपके पास ऐसा proxy हो सकता है जो आपकी application में एक path prefix जोड़ता हो। + +इन मामलों में आप अपनी application configure करने के लिए `root_path` का उपयोग कर सकते हैं। + +`root_path` ASGI specification द्वारा प्रदान किया गया एक mechanism है (जिस पर FastAPI, Starlette के जरिए, बना है)। + +`root_path` का उपयोग इन specific cases को handle करने के लिए किया जाता है। + +और इसका उपयोग sub-applications mount करते समय internally भी किया जाता है। + +इस case में, stripped path prefix वाला proxy होने का मतलब है कि आप अपने code में `/app` पर एक path declare कर सकते हैं, लेकिन फिर आप ऊपर एक layer (proxy) जोड़ते हैं जो आपकी **FastAPI** application को `/api/v1` जैसे path के नीचे रखेगी। + +इस case में, मूल path `/app` वास्तव में `/api/v1/app` पर serve किया जाएगा। + +हालाँकि आपका सारा code यह मानकर लिखा गया है कि सिर्फ `/app` है। + +{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *} + +और proxy app server (शायद FastAPI CLI के जरिए Uvicorn) तक request भेजने से पहले तुरंत **path prefix** को **"strip"** कर देगा, आपकी application को यह भरोसा दिलाते हुए कि वह `/app` पर serve हो रही है, ताकि आपको prefix `/api/v1` शामिल करने के लिए अपना सारा code update न करना पड़े। + +यहाँ तक, सब कुछ सामान्य रूप से काम करेगा। + +लेकिन फिर, जब आप integrated docs UI (frontend) खोलेंगे, तो वह OpenAPI schema को `/api/v1/openapi.json` के बजाय `/openapi.json` पर पाने की अपेक्षा करेगा। + +इसलिए, frontend (जो browser में चलता है) `/openapi.json` तक पहुँचने की कोशिश करेगा और OpenAPI schema प्राप्त नहीं कर पाएगा। + +क्योंकि हमारे app के लिए `/api/v1` का path prefix वाला proxy है, frontend को OpenAPI schema `/api/v1/openapi.json` पर fetch करना होगा। + +```mermaid +graph LR + +browser("Browser") +proxy["Proxy on http://0.0.0.0:9999/api/v1/app"] +server["Server on http://127.0.0.1:8000/app"] + +browser --> proxy +proxy --> server +``` + +/// tip | सुझाव + +IP `0.0.0.0` आम तौर पर यह बताने के लिए उपयोग किया जाता है कि program उस machine/server में उपलब्ध सभी IPs पर listen करता है। + +/// + +Docs UI को OpenAPI schema में यह declare करने की भी ज़रूरत होगी कि यह API `server` `/api/v1` (proxy के पीछे) पर स्थित है। उदाहरण के लिए: + +```JSON hl_lines="4-8" +{ + "openapi": "3.1.0", + // यहाँ और चीज़ें + "servers": [ + { + "url": "/api/v1" + } + ], + "paths": { + // यहाँ और चीज़ें + } +} +``` + +इस उदाहरण में, "Proxy" कुछ **Traefik** जैसा हो सकता है। और server **Uvicorn** के साथ FastAPI CLI जैसा हो सकता है, जो आपकी FastAPI application चला रहा है। + +### `root_path` प्रदान करना { #providing-the-root-path } + +इसे हासिल करने के लिए, आप command line option `--root-path` इस तरह उपयोग कर सकते हैं: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +अगर आप Hypercorn उपयोग करते हैं, तो उसमें भी option `--root-path` है। + +/// note | तकनीकी विवरण + +ASGI specification इस use case के लिए `root_path` define करती है। + +और `--root-path` command line option वही `root_path` प्रदान करता है। + +/// + +### वर्तमान `root_path` जाँचना { #checking-the-current-root-path } + +आप प्रत्येक request के लिए आपकी application द्वारा उपयोग किया गया वर्तमान `root_path` प्राप्त कर सकते हैं, यह `scope` dictionary का हिस्सा है (जो ASGI spec का हिस्सा है)। + +यहाँ हम इसे केवल demonstration purposes के लिए message में शामिल कर रहे हैं। + +{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *} + +फिर, अगर आप Uvicorn को इस तरह शुरू करते हैं: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +Response कुछ ऐसा होगा: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +### FastAPI app में `root_path` सेट करना { #setting-the-root-path-in-the-fastapi-app } + +वैकल्पिक रूप से, अगर आपके पास `--root-path` या equivalent जैसा command line option देने का तरीका नहीं है, तो आप अपनी FastAPI app बनाते समय `root_path` parameter सेट कर सकते हैं: + +{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *} + +`root_path` को `FastAPI` में पास करना, Uvicorn या Hypercorn को `--root-path` command line option पास करने के equivalent होगा। + +### `root_path` के बारे में { #about-root-path } + +ध्यान रखें कि server (Uvicorn) उस `root_path` का उपयोग app को पास करने के अलावा किसी और चीज़ के लिए नहीं करेगा। + +लेकिन अगर आप अपने browser में [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app) पर जाते हैं, तो आपको normal response दिखाई देगा: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +इसलिए, यह `http://127.0.0.1:8000/api/v1/app` पर access किए जाने की अपेक्षा नहीं करेगा। + +Uvicorn अपेक्षा करेगा कि proxy Uvicorn को `http://127.0.0.1:8000/app` पर access करे, और फिर ऊपर extra `/api/v1` prefix जोड़ना proxy की जिम्मेदारी होगी। + +## Stripped path prefix वाले proxies के बारे में { #about-proxies-with-a-stripped-path-prefix } + +ध्यान रखें कि stripped path prefix वाला proxy इसे configure करने के तरीकों में से केवल एक है। + +शायद कई cases में default यह होगा कि proxy के पास stripped path prefix नहीं होगा। + +ऐसे case में (बिना stripped path prefix के), proxy कुछ `https://myawesomeapp.com` जैसा listen करेगा, और फिर अगर browser `https://myawesomeapp.com/api/v1/app` पर जाता है और आपका server (जैसे Uvicorn) `http://127.0.0.1:8000` पर listen करता है, तो proxy (बिना stripped path prefix के) Uvicorn को उसी path पर access करेगा: `http://127.0.0.1:8000/api/v1/app`। + +## Traefik के साथ local testing { #testing-locally-with-traefik } + +आप [Traefik](https://docs.traefik.io/) का उपयोग करके stripped path prefix के साथ experiment आसानी से locally चला सकते हैं। + +[Traefik download करें](https://github.com/containous/traefik/releases), यह एक single binary है, आप compressed file extract कर सकते हैं और इसे सीधे terminal से चला सकते हैं। + +फिर `traefik.toml` नाम की file बनाएँ जिसमें यह हो: + +```TOML hl_lines="3" +[entryPoints] + [entryPoints.http] + address = ":9999" + +[providers] + [providers.file] + filename = "routes.toml" +``` + +यह Traefik को port 9999 पर listen करने और दूसरी file `routes.toml` उपयोग करने के लिए कहता है। + +/// tip | सुझाव + +हम standard HTTP port 80 के बजाय port 9999 उपयोग कर रहे हैं ताकि आपको इसे admin (`sudo`) privileges के साथ न चलाना पड़े। + +/// + +अब वह दूसरी file `routes.toml` बनाएँ: + +```TOML hl_lines="5 12 20" +[http] + [http.middlewares] + + [http.middlewares.api-stripprefix.stripPrefix] + prefixes = ["/api/v1"] + + [http.routers] + + [http.routers.app-http] + entryPoints = ["http"] + service = "app" + rule = "PathPrefix(`/api/v1`)" + middlewares = ["api-stripprefix"] + + [http.services] + + [http.services.app] + [http.services.app.loadBalancer] + [[http.services.app.loadBalancer.servers]] + url = "http://127.0.0.1:8000" +``` + +यह file Traefik को path prefix `/api/v1` उपयोग करने के लिए configure करती है। + +और फिर Traefik अपनी requests को `http://127.0.0.1:8000` पर चल रहे आपके Uvicorn पर redirect करेगा। + +अब Traefik शुरू करें: + +
+ +```console +$ ./traefik --configFile=traefik.toml + +INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml +``` + +
+ +और अब `--root-path` option का उपयोग करके अपना app शुरू करें: + +
+ +```console +$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1 + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +### Responses जाँचें { #check-the-responses } + +अब, अगर आप Uvicorn के port वाले URL पर जाते हैं: [http://127.0.0.1:8000/app](http://127.0.0.1:8000/app), तो आपको normal response दिखाई देगा: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +/// tip | सुझाव + +ध्यान दें कि भले ही आप इसे `http://127.0.0.1:8000/app` पर access कर रहे हैं, यह option `--root-path` से लिया गया `/api/v1` का `root_path` दिखाता है। + +/// + +और अब Traefik के port वाले URL को खोलें, जिसमें path prefix शामिल है: [http://127.0.0.1:9999/api/v1/app](http://127.0.0.1:9999/api/v1/app)। + +हमें वही response मिलता है: + +```JSON +{ + "message": "Hello World", + "root_path": "/api/v1" +} +``` + +लेकिन इस बार proxy द्वारा प्रदान किए गए prefix path वाले URL पर: `/api/v1`। + +बेशक, यहाँ विचार यह है कि हर कोई app को proxy के जरिए access करेगा, इसलिए path prefix `/api/v1` वाला version "correct" है। + +और बिना path prefix वाला version (`http://127.0.0.1:8000/app`), जो सीधे Uvicorn द्वारा प्रदान किया गया है, केवल _proxy_ (Traefik) के access के लिए होगा। + +यह दिखाता है कि Proxy (Traefik) path prefix का उपयोग कैसे करता है और server (Uvicorn) option `--root-path` से `root_path` का उपयोग कैसे करता है। + +### Docs UI जाँचें { #check-the-docs-ui } + +लेकिन यहाँ मज़ेदार हिस्सा है। ✨ + +App को access करने का "official" तरीका उस path prefix वाले proxy के जरिए होगा जिसे हमने define किया है। इसलिए, जैसा कि हम अपेक्षा करेंगे, अगर आप Uvicorn द्वारा सीधे serve किया गया docs UI try करते हैं, URL में path prefix के बिना, तो यह काम नहीं करेगा, क्योंकि यह proxy के जरिए access किए जाने की अपेक्षा करता है। + +आप इसे [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) पर देख सकते हैं: + + + +लेकिन अगर हम port `9999` वाले proxy का उपयोग करके "official" URL पर, `/api/v1/docs` पर docs UI access करते हैं, तो यह सही तरीके से काम करता है! 🎉 + +आप इसे [http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) पर देख सकते हैं: + + + +बिल्कुल जैसा हम चाहते थे। ✔️ + +ऐसा इसलिए है क्योंकि FastAPI इस `root_path` का उपयोग OpenAPI में default `server` बनाने के लिए करता है, जिसमें `root_path` द्वारा दिया गया URL होता है। + +## अतिरिक्त servers { #additional-servers } + +/// warning | चेतावनी + +यह एक अधिक advanced use case है। चाहें तो इसे skip कर सकते हैं। + +/// + +Default रूप से, **FastAPI** OpenAPI schema में `root_path` के URL वाला एक `server` बनाएगा। + +लेकिन आप अन्य alternative `servers` भी प्रदान कर सकते हैं, उदाहरण के लिए अगर आप चाहते हैं कि *वही* docs UI staging और production environment दोनों के साथ interact करे। + +अगर आप `servers` की custom list पास करते हैं और कोई `root_path` है (क्योंकि आपकी API proxy के पीछे रहती है), तो **FastAPI** list की शुरुआत में इस `root_path` के साथ एक "server" insert करेगा। + +उदाहरण के लिए: + +{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *} + +यह इस तरह का OpenAPI schema generate करेगा: + +```JSON hl_lines="5-7" +{ + "openapi": "3.1.0", + // यहाँ और चीज़ें + "servers": [ + { + "url": "/api/v1" + }, + { + "url": "https://stag.example.com", + "description": "Staging environment" + }, + { + "url": "https://prod.example.com", + "description": "Production environment" + } + ], + "paths": { + // यहाँ और चीज़ें + } +} +``` + +/// tip | सुझाव + +ध्यान दें कि `/api/v1` के `url` value वाला auto-generated server `root_path` से लिया गया है। + +/// + +[http://127.0.0.1:9999/api/v1/docs](http://127.0.0.1:9999/api/v1/docs) पर docs UI में यह ऐसा दिखेगा: + + + +/// tip | सुझाव + +Docs UI आपके द्वारा चुने गए server के साथ interact करेगा। + +/// + +/// note | तकनीकी विवरण + +OpenAPI specification में `servers` property optional है। + +अगर आप `servers` parameter specify नहीं करते और `root_path` `/` के बराबर है, तो generated OpenAPI schema में `servers` property default रूप से पूरी तरह omit कर दी जाएगी, जो `/` के `url` value वाले single server के equivalent है। + +/// + +### `root_path` से automatic server disable करें { #disable-automatic-server-from-root-path } + +अगर आप नहीं चाहते कि **FastAPI** `root_path` का उपयोग करके automatic server शामिल करे, तो आप parameter `root_path_in_servers=False` उपयोग कर सकते हैं: + +{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *} + +और फिर यह उसे OpenAPI schema में शामिल नहीं करेगा। + +## Sub-application mount करना { #mounting-a-sub-application } + +अगर आपको `root_path` वाले proxy का उपयोग करते हुए भी sub-application mount करनी है (जैसा कि [Sub Applications - Mounts](sub-applications.md) में बताया गया है), तो आप इसे सामान्य रूप से कर सकते हैं, जैसा कि आप अपेक्षा करेंगे। + +FastAPI internally `root_path` का smart तरीके से उपयोग करेगा, इसलिए यह बस काम करेगा। ✨ diff --git a/docs/hi/docs/advanced/custom-response.md b/docs/hi/docs/advanced/custom-response.md new file mode 100644 index 000000000..8655618f2 --- /dev/null +++ b/docs/hi/docs/advanced/custom-response.md @@ -0,0 +1,273 @@ +# कस्टम Response - HTML, Stream, File, अन्य { #custom-response-html-stream-file-others } + +Default रूप से, **FastAPI** JSON responses लौटाएगा। + +आप [सीधे Response लौटाएँ](response-directly.md) में दिखाए गए अनुसार सीधे `Response` लौटाकर इसे override कर सकते हैं। + +लेकिन अगर आप सीधे `Response` लौटाते हैं (या कोई subclass, जैसे `JSONResponse`), तो data अपने आप convert नहीं होगा (भले ही आप `response_model` declare करें), और documentation अपने आप generate नहीं होगी (उदाहरण के लिए, generated OpenAPI के हिस्से के रूप में HTTP header `Content-Type` में specific "media type" शामिल करना)। + +लेकिन आप *path operation decorator* में `response_class` parameter का उपयोग करके वह `Response` भी declare कर सकते हैं जिसे आप उपयोग करना चाहते हैं (जैसे कोई भी `Response` subclass)। + +आप अपनी *path operation function* से जो contents लौटाते हैं, उन्हें उस `Response` के अंदर रख दिया जाएगा। + +/// note | नोट + +यदि आप बिना media type वाली response class का उपयोग करते हैं, तो FastAPI अपेक्षा करेगा कि आपके response में कोई content न हो, इसलिए यह अपने generated OpenAPI docs में response format को document नहीं करेगा। + +/// + +## JSON Responses { #json-responses } + +Default रूप से FastAPI JSON responses लौटाता है। + +यदि आप [Response Model](../tutorial/response-model.md) declare करते हैं तो FastAPI Pydantic का उपयोग करके data को JSON में serialize करने के लिए उसका उपयोग करेगा। + +यदि आप response model declare नहीं करते हैं, तो FastAPI [JSON Compatible Encoder](../tutorial/encoder.md) में समझाए गए `jsonable_encoder` का उपयोग करेगा और उसे `JSONResponse` में रखेगा। + +यदि आप JSON media type (`application/json`) के साथ `response_class` declare करते हैं, जैसा कि `JSONResponse` के साथ होता है, तो आपके द्वारा लौटाया गया data आपकी *path operation decorator* में declare किए गए किसी भी Pydantic `response_model` के साथ अपने आप convert (और filter) हो जाएगा। लेकिन data Pydantic के साथ JSON bytes में serialize नहीं होगा, इसके बजाय इसे `jsonable_encoder` के साथ convert किया जाएगा और फिर `JSONResponse` class को pass किया जाएगा, जो Python की standard JSON library का उपयोग करके इसे bytes में serialize करेगी। + +### JSON Performance { #json-performance } + +संक्षेप में, यदि आप maximum performance चाहते हैं, तो [Response Model](../tutorial/response-model.md) का उपयोग करें और *path operation decorator* में `response_class` declare न करें। + +{* ../../docs_src/response_model/tutorial001_01_py310.py ln[15:17] hl[16] *} + +## HTML Response { #html-response } + +**FastAPI** से सीधे HTML के साथ response लौटाने के लिए, `HTMLResponse` का उपयोग करें। + +* `HTMLResponse` import करें। +* अपने *path operation decorator* के parameter `response_class` के रूप में `HTMLResponse` pass करें। + +{* ../../docs_src/custom_response/tutorial002_py310.py hl[2,7] *} + +/// note | नोट + +Parameter `response_class` का उपयोग response के "media type" को define करने के लिए भी किया जाएगा। + +इस मामले में, HTTP header `Content-Type` को `text/html` पर set किया जाएगा। + +और इसे OpenAPI में इसी तरह document किया जाएगा। + +/// + +### `Response` लौटाएँ { #return-a-response } + +जैसा कि [सीधे Response लौटाएँ](response-directly.md) में देखा गया है, आप अपनी *path operation* में response को सीधे लौटाकर भी override कर सकते हैं। + +ऊपर वाला वही उदाहरण, जो `HTMLResponse` लौटाता है, इस तरह दिख सकता है: + +{* ../../docs_src/custom_response/tutorial003_py310.py hl[2,7,19] *} + +/// warning | चेतावनी + +आपकी *path operation function* द्वारा सीधे लौटाया गया `Response` OpenAPI में document नहीं होगा (उदाहरण के लिए, `Content-Type` document नहीं होगा) और automatic interactive docs में visible नहीं होगा। + +/// + +/// note | नोट + +बेशक, वास्तविक `Content-Type` header, status code, आदि, आपके द्वारा लौटाए गए `Response` object से आएँगे। + +/// + +### OpenAPI में document करें और `Response` override करें { #document-in-openapi-and-override-response } + +यदि आप function के अंदर से response को override करना चाहते हैं लेकिन साथ ही OpenAPI में "media type" document करना चाहते हैं, तो आप `response_class` parameter का उपयोग कर सकते हैं और `Response` object भी लौटा सकते हैं। + +तब `response_class` का उपयोग केवल OpenAPI *path operation* को document करने के लिए किया जाएगा, लेकिन आपका `Response` जैसा है वैसा ही उपयोग किया जाएगा। + +#### सीधे `HTMLResponse` लौटाएँ { #return-an-htmlresponse-directly } + +उदाहरण के लिए, यह कुछ ऐसा हो सकता है: + +{* ../../docs_src/custom_response/tutorial004_py310.py hl[7,21,23] *} + +इस उदाहरण में, function `generate_html_response()` पहले से ही HTML को `str` में लौटाने के बजाय `Response` generate करके लौटाता है। + +`generate_html_response()` को call करने का result लौटाकर, आप पहले से ही एक `Response` लौटा रहे हैं जो default **FastAPI** behavior को override करेगा। + +लेकिन क्योंकि आपने `response_class` में भी `HTMLResponse` pass किया है, **FastAPI** को पता होगा कि इसे OpenAPI और interactive docs में `text/html` के साथ HTML के रूप में कैसे document करना है: + + + +## उपलब्ध responses { #available-responses } + +यहाँ कुछ उपलब्ध responses दिए गए हैं। + +ध्यान रखें कि आप कुछ और लौटाने के लिए `Response` का उपयोग कर सकते हैं, या custom sub-class भी बना सकते हैं। + +/// note | तकनीकी विवरण + +आप `from starlette.responses import HTMLResponse` भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.responses` `fastapi.responses` के रूप में प्रदान करता है। लेकिन अधिकांश उपलब्ध responses सीधे Starlette से आते हैं। + +/// + +### `Response` { #response } + +मुख्य `Response` class, बाकी सभी responses इससे inherit करते हैं। + +आप इसे सीधे लौटा सकते हैं। + +यह निम्नलिखित parameters accept करता है: + +* `content` - एक `str` या `bytes`। +* `status_code` - एक `int` HTTP status code। +* `headers` - strings का एक `dict`। +* `media_type` - media type बताने वाला एक `str`। उदाहरण के लिए `"text/html"`। + +FastAPI (असल में Starlette) अपने आप एक Content-Length header शामिल करेगा। यह `media_type` के आधार पर Content-Type header भी शामिल करेगा और text types के लिए charset append करेगा। + +{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *} + +### `HTMLResponse` { #htmlresponse } + +कुछ text या bytes लेता है और HTML response लौटाता है, जैसा आपने ऊपर पढ़ा। + +### `PlainTextResponse` { #plaintextresponse } + +कुछ text या bytes लेता है और plain text response लौटाता है। + +{* ../../docs_src/custom_response/tutorial005_py310.py hl[2,7,9] *} + +### `JSONResponse` { #jsonresponse } + +कुछ data लेता है और `application/json` encoded response लौटाता है। + +जैसा आपने ऊपर पढ़ा, यह **FastAPI** में उपयोग किया जाने वाला default response है। + +/// note | तकनीकी विवरण + +लेकिन यदि आप response model या return type declare करते हैं, तो उसका उपयोग सीधे data को JSON में serialize करने के लिए किया जाएगा, और JSON के लिए सही media type वाला response सीधे लौटाया जाएगा, `JSONResponse` class का उपयोग किए बिना। + +यह best performance पाने का ideal तरीका है। + +/// + +### `RedirectResponse` { #redirectresponse } + +HTTP redirect लौटाता है। Default रूप से 307 status code (Temporary Redirect) का उपयोग करता है। + +आप सीधे `RedirectResponse` लौटा सकते हैं: + +{* ../../docs_src/custom_response/tutorial006_py310.py hl[2,9] *} + +--- + +या आप इसे `response_class` parameter में उपयोग कर सकते हैं: + + +{* ../../docs_src/custom_response/tutorial006b_py310.py hl[2,7,9] *} + +यदि आप ऐसा करते हैं, तो आप अपनी *path operation* function से URL सीधे लौटा सकते हैं। + +इस मामले में, उपयोग किया गया `status_code` `RedirectResponse` के लिए default वाला होगा, जो `307` है। + +--- + +आप `status_code` parameter को `response_class` parameter के साथ combine करके भी उपयोग कर सकते हैं: + +{* ../../docs_src/custom_response/tutorial006c_py310.py hl[2,7,9] *} + +### `StreamingResponse` { #streamingresponse } + +एक async generator या सामान्य generator/iterator (`yield` वाली function) लेता है और response body को stream करता है। + +{* ../../docs_src/custom_response/tutorial007_py310.py hl[3,16] *} + +/// note | तकनीकी विवरण + +एक `async` task केवल तब cancel किया जा सकता है जब वह किसी `await` तक पहुँचता है। यदि कोई `await` नहीं है, तो generator (`yield` वाली function) ठीक से cancel नहीं हो सकता और cancellation request किए जाने के बाद भी चलना जारी रख सकता है। + +चूँकि इस छोटे उदाहरण को किसी `await` statement की आवश्यकता नहीं है, हम event loop को cancellation handle करने का अवसर देने के लिए `await anyio.sleep(0)` जोड़ते हैं। + +यह बड़े या infinite streams के साथ और भी अधिक महत्वपूर्ण होगा। + +/// + +/// tip | टिप + +सीधे `StreamingResponse` लौटाने के बजाय, आपको शायद [Stream Data](./stream-data.md) में दिए गए style का पालन करना चाहिए, यह कहीं अधिक सुविधाजनक है और आपके लिए पर्दे के पीछे cancellation handle करता है। + +यदि आप JSON Lines stream कर रहे हैं, तो [Stream JSON Lines](../tutorial/stream-json-lines.md) tutorial का पालन करें। + +/// + +### `FileResponse` { #fileresponse } + +एक file को response के रूप में asynchronously stream करता है। + +Instantiate करने के लिए अन्य response types की तुलना में अलग set of arguments लेता है: + +* `path` - stream की जाने वाली file का file path। +* `headers` - dictionary के रूप में शामिल किए जाने वाले कोई भी custom headers। +* `media_type` - media type बताने वाली string। यदि unset है, तो media type infer करने के लिए filename या path का उपयोग किया जाएगा। +* `filename` - यदि set है, तो इसे response `Content-Disposition` में शामिल किया जाएगा। + +File responses में उपयुक्त `Content-Length`, `Last-Modified` और `ETag` headers शामिल होंगे। + +{* ../../docs_src/custom_response/tutorial009_py310.py hl[2,10] *} + +आप `response_class` parameter का उपयोग भी कर सकते हैं: + +{* ../../docs_src/custom_response/tutorial009b_py310.py hl[2,8,10] *} + +इस मामले में, आप अपनी *path operation* function से file path सीधे लौटा सकते हैं। + +## Custom response class { #custom-response-class } + +आप `Response` से inherit करके और उसका उपयोग करके अपनी खुद की custom response class बना सकते हैं। + +उदाहरण के लिए, मान लें कि आप कुछ settings के साथ [`orjson`](https://github.com/ijl/orjson) का उपयोग करना चाहते हैं। + +मान लें आप चाहते हैं कि यह indented और formatted JSON लौटाए, इसलिए आप orjson option `orjson.OPT_INDENT_2` का उपयोग करना चाहते हैं। + +आप `CustomORJSONResponse` बना सकते हैं। आपको मुख्य रूप से `Response.render(content)` method बनाना है जो content को `bytes` के रूप में लौटाता है: + +{* ../../docs_src/custom_response/tutorial009c_py310.py hl[9:14,17] *} + +अब यह लौटाने के बजाय: + +```json +{"message": "Hello World"} +``` + +...यह response लौटाएगा: + +```json +{ + "message": "Hello World" +} +``` + +बेशक, JSON formatting की तुलना में इसका लाभ उठाने के लिए आपको शायद कहीं बेहतर तरीके मिलेंगे। 😉 + +### `orjson` या Response Model { #orjson-or-response-model } + +यदि आप performance खोज रहे हैं, तो शायद `orjson` response की तुलना में [Response Model](../tutorial/response-model.md) का उपयोग करना आपके लिए बेहतर होगा। + +Response model के साथ, FastAPI data को JSON में serialize करने के लिए Pydantic का उपयोग करेगा, intermediate steps के बिना, जैसे `jsonable_encoder` के साथ convert करना, जो किसी भी अन्य मामले में होता। + +और अंदर से, Pydantic JSON में serialize करने के लिए `orjson` जैसे ही underlying Rust mechanisms का उपयोग करता है, इसलिए response model के साथ आपको पहले से ही best performance मिल जाएगी। + +## Default response class { #default-response-class } + +**FastAPI** class instance या `APIRouter` बनाते समय आप specify कर सकते हैं कि default रूप से कौन-सी response class उपयोग करनी है। + +इसे define करने वाला parameter `default_response_class` है। + +नीचे दिए गए उदाहरण में, **FastAPI** सभी *path operations* में JSON के बजाय default रूप से `HTMLResponse` का उपयोग करेगा। + +{* ../../docs_src/custom_response/tutorial010_py310.py hl[2,4] *} + +/// tip | टिप + +आप पहले की तरह *path operations* में अब भी `response_class` override कर सकते हैं। + +/// + +## अतिरिक्त documentation { #additional-documentation } + +आप `responses` का उपयोग करके OpenAPI में media type और कई अन्य details भी declare कर सकते हैं: [OpenAPI में अतिरिक्त Responses](additional-responses.md)। diff --git a/docs/hi/docs/advanced/dataclasses.md b/docs/hi/docs/advanced/dataclasses.md new file mode 100644 index 000000000..55e487691 --- /dev/null +++ b/docs/hi/docs/advanced/dataclasses.md @@ -0,0 +1,95 @@ +# Dataclasses का उपयोग { #using-dataclasses } + +FastAPI **Pydantic** के ऊपर बनाया गया है, और मैंने आपको दिखाया है कि requests और responses घोषित करने के लिए Pydantic models का उपयोग कैसे करें। + +लेकिन FastAPI उसी तरह [`dataclasses`](https://docs.python.org/3/library/dataclasses.html) का उपयोग भी support करता है: + +{* ../../docs_src/dataclasses_/tutorial001_py310.py hl[1,6:11,18:19] *} + +यह अभी भी **Pydantic** की वजह से support किया जाता है, क्योंकि इसमें [`dataclasses` के लिए internal support](https://docs.pydantic.dev/latest/concepts/dataclasses/#use-of-stdlib-dataclasses-with-basemodel) है। + +इसलिए, ऊपर दिए गए code में भी, जो Pydantic का स्पष्ट रूप से उपयोग नहीं करता, FastAPI उन standard dataclasses को Pydantic की अपनी तरह की dataclasses में बदलने के लिए Pydantic का उपयोग कर रहा है। + +और निश्चित रूप से, यह इन्हें भी support करता है: + +* data validation +* data serialization +* data documentation, आदि। + +यह Pydantic models की तरह ही काम करता है। और अंदर से यह वास्तव में उसी तरह, Pydantic का उपयोग करके हासिल किया जाता है। + +/// note | नोट + +ध्यान रखें कि dataclasses वह सब कुछ नहीं कर सकतीं जो Pydantic models कर सकते हैं। + +इसलिए, आपको अभी भी Pydantic models का उपयोग करना पड़ सकता है। + +लेकिन अगर आपके पास बहुत सारी dataclasses पहले से मौजूद हैं, तो FastAPI का उपयोग करके web API को power देने के लिए उनका उपयोग करने की यह एक अच्छी तरकीब है। 🤓 + +/// + +## `response_model` में Dataclasses { #dataclasses-in-response-model } + +आप `response_model` parameter में भी `dataclasses` का उपयोग कर सकते हैं: + +{* ../../docs_src/dataclasses_/tutorial002_py310.py hl[1,6:12,18] *} + +dataclass अपने आप Pydantic dataclass में बदल जाएगी। + +इस तरह, उसका schema API docs के user interface में दिखाई देगा: + + + +## Nested Data Structures में Dataclasses { #dataclasses-in-nested-data-structures } + +आप nested data structures बनाने के लिए `dataclasses` को अन्य type annotations के साथ भी जोड़ सकते हैं। + +कुछ मामलों में, आपको अभी भी Pydantic के `dataclasses` वाले version का उपयोग करना पड़ सकता है। उदाहरण के लिए, अगर automatically generated API documentation में errors हों। + +उस स्थिति में, आप standard `dataclasses` को बस `pydantic.dataclasses` से बदल सकते हैं, जो एक drop-in replacement है: + +{* ../../docs_src/dataclasses_/tutorial003_py310.py hl[1,4,7:10,13:16,22:24,27] *} + +1. हम अभी भी standard `dataclasses` से `field` import करते हैं। + +2. `pydantic.dataclasses`, `dataclasses` के लिए एक drop-in replacement है। + +3. `Author` dataclass में `Item` dataclasses की एक list शामिल है। + +4. `Author` dataclass को `response_model` parameter के रूप में उपयोग किया गया है। + +5. आप request body के रूप में dataclasses के साथ अन्य standard type annotations का उपयोग कर सकते हैं। + + इस मामले में, यह `Item` dataclasses की एक list है। + +6. यहाँ हम एक dictionary return कर रहे हैं जिसमें `items` है, जो dataclasses की एक list है। + + FastAPI अभी भी data को JSON में serialize करने में सक्षम है। + +7. यहाँ `response_model`, `Author` dataclasses की list के type annotation का उपयोग कर रहा है। + + फिर से, आप `dataclasses` को standard type annotations के साथ जोड़ सकते हैं। + +8. ध्यान दें कि यह *path operation function* `async def` की बजाय सामान्य `def` का उपयोग करता है। + + हमेशा की तरह, FastAPI में आप आवश्यकता के अनुसार `def` और `async def` को जोड़ सकते हैं। + + अगर आपको यह याद दिलाने की आवश्यकता है कि किसे कब उपयोग करना है, तो [`async` और `await`](../async.md#in-a-hurry) के docs में _"जल्दी में हैं?"_ section देखें। + +9. यह *path operation function* dataclasses return नहीं कर रहा है (हालाँकि कर सकता था), बल्कि internal data वाली dictionaries की list return कर रहा है। + + FastAPI response को बदलने के लिए `response_model` parameter (जिसमें dataclasses शामिल हैं) का उपयोग करेगा। + +आप जटिल data structures बनाने के लिए `dataclasses` को कई अलग-अलग combinations में अन्य type annotations के साथ जोड़ सकते हैं। + +अधिक विशिष्ट विवरण देखने के लिए ऊपर दिए गए in-code annotation tips देखें। + +## और जानें { #learn-more } + +आप `dataclasses` को अन्य Pydantic models के साथ भी जोड़ सकते हैं, उनसे inherit कर सकते हैं, उन्हें अपने models में शामिल कर सकते हैं, आदि। + +अधिक जानने के लिए, [dataclasses के बारे में Pydantic docs](https://docs.pydantic.dev/latest/concepts/dataclasses/) देखें। + +## Version { #version } + +यह FastAPI version `0.67.0` से उपलब्ध है। 🔖 diff --git a/docs/hi/docs/advanced/events.md b/docs/hi/docs/advanced/events.md new file mode 100644 index 000000000..05afd4e1c --- /dev/null +++ b/docs/hi/docs/advanced/events.md @@ -0,0 +1,165 @@ +# Lifespan Events { #lifespan-events } + +आप ऐसी logic (code) define कर सकते हैं जिसे application के **starts up** होने से पहले execute किया जाना चाहिए। इसका मतलब है कि यह code application के **requests receive करना शुरू करने से पहले**, **एक बार** execute होगा। + +उसी तरह, आप ऐसी logic (code) define कर सकते हैं जिसे application के **shutting down** होने पर execute किया जाना चाहिए। इस मामले में, यह code संभवतः **कई requests** handle करने के **बाद**, **एक बार** execute होगा। + +क्योंकि यह code application के requests लेना **शुरू** करने से पहले, और requests handle करना **पूरा** करने के तुरंत बाद execute होता है, यह पूरी application **lifespan** को cover करता है (शब्द "lifespan" थोड़ी देर में महत्वपूर्ण होगा 😉)। + +यह उन **resources** को setup करने के लिए बहुत उपयोगी हो सकता है जिनकी आपको पूरी app में जरूरत होती है, और जो requests के बीच **shared** होते हैं, और/या जिन्हें आपको बाद में **clean up** करना होता है। उदाहरण के लिए, database connection pool, या कोई shared machine learning model load करना। + +## Use Case { #use-case } + +आइए एक उदाहरण **use case** से शुरू करते हैं और फिर देखते हैं कि इसे इससे कैसे solve किया जाए। + +मान लीजिए कि आपके पास कुछ **machine learning models** हैं जिन्हें आप requests handle करने के लिए use करना चाहते हैं। 🤖 + +वही models requests के बीच shared हैं, इसलिए, यह हर request के लिए एक model, या हर user के लिए एक model या ऐसा कुछ नहीं है। + +मान लीजिए कि model load करने में **काफी समय लग सकता है**, क्योंकि उसे disk से बहुत सारा **data** read करना होता है। इसलिए आप इसे हर request के लिए नहीं करना चाहते। + +आप इसे module/file के top level पर load कर सकते हैं, लेकिन इसका मतलब यह भी होगा कि अगर आप सिर्फ एक simple automated test run कर रहे हैं, तब भी यह **model load** करेगा, और फिर वह test **slow** होगा क्योंकि code के किसी independent part को run कर पाने से पहले उसे model load होने का इंतजार करना पड़ेगा। + +यही हम solve करेंगे, चलिए model को requests handle होने से पहले load करते हैं, लेकिन केवल application के requests receive करना शुरू करने से ठीक पहले, code load होते समय नहीं। + +## Lifespan { #lifespan } + +आप `FastAPI` app के `lifespan` parameter और एक "context manager" (मैं अभी दिखाऊंगा कि यह क्या है) का use करके यह *startup* और *shutdown* logic define कर सकते हैं। + +आइए एक उदाहरण से शुरू करते हैं और फिर इसे detail में देखते हैं। + +हम `yield` के साथ एक async function `lifespan()` इस तरह create करते हैं: + +{* ../../docs_src/events/tutorial003_py310.py hl[16,19] *} + +यहां हम `yield` से पहले machine learning models वाली dictionary में (fake) model function रखकर model load करने वाली महंगी *startup* operation को simulate कर रहे हैं। यह code application के **requests लेना शुरू करने से पहले**, *startup* के दौरान execute होगा। + +और फिर, `yield` के तुरंत बाद, हम model unload करते हैं। यह code application के **requests handle करना पूरा करने के बाद**, *shutdown* से ठीक पहले execute होगा। उदाहरण के लिए, यह memory या GPU जैसे resources release कर सकता है। + +/// tip | सुझाव + +`shutdown` तब होगा जब आप application को **stop** कर रहे होंगे। + +शायद आपको कोई नया version start करना हो, या आप इसे चलाते-चलाते बस थक गए हों। 🤷 + +/// + +### Lifespan function { #lifespan-function } + +ध्यान देने वाली पहली चीज यह है कि हम `yield` के साथ एक async function define कर रहे हैं। यह `yield` वाली Dependencies से बहुत मिलता-जुलता है। + +{* ../../docs_src/events/tutorial003_py310.py hl[14:19] *} + +function का पहला हिस्सा, `yield` से पहले वाला, application start होने से **पहले** execute होगा। + +और `yield` के बाद वाला हिस्सा application के finish हो जाने के **बाद** execute होगा। + +### Async Context Manager { #async-context-manager } + +अगर आप check करें, तो function को `@asynccontextmanager` से decorate किया गया है। + +यह function को "**async context manager**" नाम की चीज में convert करता है। + +{* ../../docs_src/events/tutorial003_py310.py hl[1,13] *} + +Python में एक **context manager** ऐसी चीज है जिसे आप `with` statement में use कर सकते हैं, उदाहरण के लिए, `open()` को context manager की तरह use किया जा सकता है: + +```Python +with open("file.txt") as file: + file.read() +``` + +Python के नए versions में, एक **async context manager** भी है। आप इसे `async with` के साथ use करेंगे: + +```Python +async with lifespan(app): + await do_stuff() +``` + +जब आप ऊपर की तरह कोई context manager या async context manager create करते हैं, तो यह क्या करता है कि `with` block में enter करने से पहले, यह `yield` से पहले वाला code execute करेगा, और `with` block से exit करने के बाद, यह `yield` के बाद वाला code execute करेगा। + +ऊपर हमारे code example में, हम इसे सीधे use नहीं करते, बल्कि FastAPI को pass करते हैं ताकि वह इसे use कर सके। + +`FastAPI` app का `lifespan` parameter एक **async context manager** लेता है, इसलिए हम अपना नया `lifespan` async context manager उसे pass कर सकते हैं। + +{* ../../docs_src/events/tutorial003_py310.py hl[22] *} + +## Alternative Events (deprecated) { #alternative-events-deprecated } + +/// warning | चेतावनी + +*startup* और *shutdown* को handle करने का recommended तरीका ऊपर बताए गए अनुसार `FastAPI` app के `lifespan` parameter का use करना है। अगर आप `lifespan` parameter provide करते हैं, तो `startup` और `shutdown` event handlers अब call नहीं किए जाएंगे। यह पूरा `lifespan` होगा या पूरे events, दोनों नहीं। + +आप शायद यह हिस्सा skip कर सकते हैं। + +/// + +इस logic को *startup* के दौरान और *shutdown* के दौरान execute करने के लिए define करने का एक alternative तरीका है। + +आप event handlers (functions) define कर सकते हैं जिन्हें application के starts up होने से पहले, या application के shutting down होने पर execute किया जाना चाहिए। + +इन functions को `async def` या normal `def` के साथ declare किया जा सकता है। + +### `startup` event { #startup-event } + +application start होने से पहले run होने वाला function add करने के लिए, इसे event `"startup"` के साथ declare करें: + +{* ../../docs_src/events/tutorial001_py310.py hl[8] *} + +इस मामले में, `startup` event handler function items "database" (बस एक `dict`) को कुछ values के साथ initialize करेगा। + +आप एक से अधिक event handler function add कर सकते हैं। + +और आपकी application requests receive करना तब तक शुरू नहीं करेगी जब तक सभी `startup` event handlers complete नहीं हो जाते। + +### `shutdown` event { #shutdown-event } + +application के shutting down होने पर run होने वाला function add करने के लिए, इसे event `"shutdown"` के साथ declare करें: + +{* ../../docs_src/events/tutorial002_py310.py hl[6] *} + +यहां, `shutdown` event handler function एक text line `"Application shutdown"` को `log.txt` file में write करेगा। + +/// note | नोट + +`open()` function में, `mode="a"` का मतलब "append" होता है, इसलिए, line उस file में जो भी है उसके बाद add की जाएगी, पिछले contents को overwrite किए बिना। + +/// + +/// tip | सुझाव + +ध्यान दें कि इस मामले में हम एक standard Python `open()` function use कर रहे हैं जो एक file के साथ interact करता है। + +इसलिए, इसमें I/O (input/output) शामिल है, जिसके लिए चीजों के disk पर write होने का "waiting" करना पड़ता है। + +लेकिन `open()` `async` और `await` use नहीं करता। + +इसलिए, हम event handler function को `async def` के बजाय standard `def` के साथ declare करते हैं। + +/// + +### `startup` और `shutdown` साथ में { #startup-and-shutdown-together } + +इस बात की काफी संभावना है कि आपके *startup* और *shutdown* की logic connected हो, आप शायद कुछ start करना और फिर उसे finish करना, कोई resource acquire करना और फिर उसे release करना, आदि चाहें। + +इसे अलग-अलग functions में करना, जो logic या variables को साथ में share नहीं करते, अधिक कठिन है क्योंकि आपको values को global variables या इसी तरह की tricks में store करना पड़ेगा। + +इसी वजह से, अब इसके बजाय ऊपर explain किए गए `lifespan` को use करने की recommendation है। + +## Technical Details { #technical-details } + +जिज्ञासु nerds के लिए बस एक technical detail। 🤓 + +अंदर से, ASGI technical specification में, यह [Lifespan Protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html) का हिस्सा है, और यह `startup` और `shutdown` नाम के events define करता है। + +/// note | नोट + +आप Starlette `lifespan` handlers के बारे में [Starlette की Lifespan docs](https://www.starlette.dev/lifespan/) में और पढ़ सकते हैं। + +इसमें यह भी शामिल है कि lifespan state को कैसे handle किया जाए जिसे आपके code के अन्य areas में use किया जा सकता है। + +/// + +## Sub Applications { #sub-applications } + +🚨 ध्यान रखें कि ये lifespan events (startup और shutdown) केवल main application के लिए execute होंगे, [Sub Applications - Mounts](sub-applications.md) के लिए नहीं। diff --git a/docs/hi/docs/advanced/generate-clients.md b/docs/hi/docs/advanced/generate-clients.md new file mode 100644 index 000000000..fa4b8f496 --- /dev/null +++ b/docs/hi/docs/advanced/generate-clients.md @@ -0,0 +1,192 @@ +# SDKs जेनरेट करना { #generating-sdks } + +क्योंकि **FastAPI** **OpenAPI** specification पर आधारित है, इसकी APIs को एक standard format में वर्णित किया जा सकता है जिसे कई tools समझते हैं। + +इससे up-to-date **documentation**, कई भाषाओं में client libraries (**SDKs**), और **testing** या **automation workflows** जेनरेट करना आसान हो जाता है, जो आपके code के साथ sync में रहते हैं। + +इस guide में, आप सीखेंगे कि अपने FastAPI backend के लिए **TypeScript SDK** कैसे जेनरेट करें। + +## Open Source SDK Generators { #open-source-sdk-generators } + +एक versatile विकल्प [OpenAPI Generator](https://openapi-generator.tech/) है, जो **कई programming languages** को support करता है और आपकी OpenAPI specification से SDKs जेनरेट कर सकता है। + +**TypeScript clients** के लिए, [Hey API](https://heyapi.dev/) एक purpose-built solution है, जो TypeScript ecosystem के लिए optimized experience प्रदान करता है। + +आप [OpenAPI.Tools](https://openapi.tools/#sdk) पर और SDK generators खोज सकते हैं। + +/// tip | सुझाव + +FastAPI अपने-आप **OpenAPI 3.1** specifications जेनरेट करता है, इसलिए आपके द्वारा उपयोग किया जाने वाला कोई भी tool इस version को support करना चाहिए। + +/// + +## TypeScript SDK बनाएँ { #create-a-typescript-sdk } + +आइए एक सरल FastAPI application से शुरू करें: + +{* ../../docs_src/generate_clients/tutorial001_py310.py hl[7:9,12:13,16:17,21] *} + +ध्यान दें कि *path operations* उन models को define करते हैं जिनका उपयोग वे request payload और response payload के लिए करते हैं, `Item` और `ResponseMessage` models का उपयोग करके। + +### API Docs { #api-docs } + +यदि आप `/docs` पर जाते हैं, तो आप देखेंगे कि इसमें requests में भेजे जाने और responses में प्राप्त होने वाले data के लिए **schemas** हैं: + + + +आप वे schemas देख सकते हैं क्योंकि उन्हें app में models के साथ declare किया गया था। + +वह जानकारी app के **OpenAPI schema** में उपलब्ध होती है, और फिर API docs में दिखाई जाती है। + +Models से वही जानकारी जो OpenAPI में शामिल होती है, **client code जेनरेट करने** के लिए उपयोग की जा सकती है। + +### Hey API { #hey-api } + +जब हमारे पास models के साथ एक FastAPI app हो, तो हम Hey API का उपयोग करके TypeScript client जेनरेट कर सकते हैं। ऐसा करने का सबसे तेज़ तरीका npx के माध्यम से है। + +```sh +npx @hey-api/openapi-ts -i http://localhost:8000/openapi.json -o src/client +``` + +यह `./src/client` में TypeScript SDK जेनरेट करेगा। + +आप उनकी website पर [`@hey-api/openapi-ts` install करना](https://heyapi.dev/openapi-ts/get-started) सीख सकते हैं और [generated output](https://heyapi.dev/openapi-ts/output) के बारे में पढ़ सकते हैं। + +### SDK का उपयोग करना { #using-the-sdk } + +अब आप client code को import करके उपयोग कर सकते हैं। यह कुछ ऐसा दिख सकता है, ध्यान दें कि आपको methods के लिए autocompletion मिलता है: + + + +आपको भेजने के लिए payload के लिए भी autocompletion मिलेगा: + + + +/// tip | सुझाव + +`name` और `price` के लिए autocompletion पर ध्यान दें, जिसे FastAPI application में, `Item` model में define किया गया था। + +/// + +आपके द्वारा भेजे जाने वाले data के लिए inline errors होंगे: + + + +Response object में भी autocompletion होगा: + + + +## Tags के साथ FastAPI App { #fastapi-app-with-tags } + +कई मामलों में, आपका FastAPI app बड़ा होगा, और आप शायद *path operations* के अलग-अलग groups को separate करने के लिए tags का उपयोग करेंगे। + +उदाहरण के लिए, आपके पास **items** के लिए एक section और **users** के लिए दूसरा section हो सकता है, और उन्हें tags द्वारा separate किया जा सकता है: + +{* ../../docs_src/generate_clients/tutorial002_py310.py hl[21,26,34] *} + +### Tags के साथ TypeScript Client जेनरेट करें { #generate-a-typescript-client-with-tags } + +यदि आप tags का उपयोग करने वाले FastAPI app के लिए client जेनरेट करते हैं, तो यह सामान्यतः client code को भी tags के आधार पर separate करेगा। + +इस तरह, आप client code के लिए चीज़ों को सही तरह से ordered और grouped रख पाएँगे: + + + +इस मामले में, आपके पास हैं: + +* `ItemsService` +* `UsersService` + +### Client Method Names { #client-method-names } + +अभी, जेनरेट किए गए method names जैसे `createItemItemsPost` बहुत साफ़ नहीं दिखते: + +```TypeScript +ItemsService.createItemItemsPost({name: "Plumbus", price: 5}) +``` + +...ऐसा इसलिए है क्योंकि client generator प्रत्येक *path operation* के लिए OpenAPI internal **operation ID** का उपयोग करता है। + +OpenAPI required करता है कि प्रत्येक operation ID सभी *path operations* में unique हो, इसलिए FastAPI उस operation ID को जेनरेट करने के लिए **function name**, **path**, और **HTTP method/operation** का उपयोग करता है, क्योंकि इस तरह यह सुनिश्चित कर सकता है कि operation IDs unique हैं। + +लेकिन मैं आगे आपको दिखाऊँगा कि इसे कैसे बेहतर बनाया जाए। 🤓 + +## Custom Operation IDs और बेहतर Method Names { #custom-operation-ids-and-better-method-names } + +आप इन operation IDs को **जेनरेट** करने के तरीके को **modify** कर सकते हैं ताकि वे clients में सरल हों और **सरल method names** हों। + +इस मामले में, आपको किसी दूसरे तरीके से सुनिश्चित करना होगा कि प्रत्येक operation ID **unique** हो। + +उदाहरण के लिए, आप सुनिश्चित कर सकते हैं कि प्रत्येक *path operation* में एक tag हो, और फिर **tag** और *path operation* **name** (function name) के आधार पर operation ID जेनरेट करें। + +### Custom Generate Unique ID Function { #custom-generate-unique-id-function } + +FastAPI प्रत्येक *path operation* के लिए एक **unique ID** का उपयोग करता है, जिसका उपयोग **operation ID** के लिए और requests या responses के लिए आवश्यक किसी भी custom models के names के लिए भी किया जाता है। + +आप उस function को customize कर सकते हैं। यह एक `APIRoute` लेता है और एक string output करता है। + +उदाहरण के लिए, यहाँ यह पहले tag (आपके पास शायद केवल एक tag होगा) और *path operation* name (function name) का उपयोग कर रहा है। + +फिर आप उस custom function को `generate_unique_id_function` parameter के रूप में **FastAPI** को pass कर सकते हैं: + +{* ../../docs_src/generate_clients/tutorial003_py310.py hl[6:7,10] *} + +### Custom Operation IDs के साथ TypeScript Client जेनरेट करें { #generate-a-typescript-client-with-custom-operation-ids } + +अब, यदि आप client को फिर से जेनरेट करते हैं, तो आप देखेंगे कि इसमें बेहतर method names हैं: + + + +जैसा कि आप देखते हैं, method names में अब tag और फिर function name है, अब वे URL path और HTTP operation की जानकारी शामिल नहीं करते। + +### Client Generator के लिए OpenAPI Specification को Preprocess करें { #preprocess-the-openapi-specification-for-the-client-generator } + +जेनरेट किए गए code में अभी भी कुछ **duplicated information** है। + +हम पहले से जानते हैं कि यह method **items** से संबंधित है क्योंकि वह शब्द `ItemsService` (tag से लिया गया) में है, लेकिन method name में भी tag name prefixed है। 😕 + +हम शायद इसे सामान्य रूप से OpenAPI के लिए रखना चाहेंगे, क्योंकि यह सुनिश्चित करेगा कि operation IDs **unique** हैं। + +लेकिन generated client के लिए, हम clients जेनरेट करने से ठीक पहले OpenAPI operation IDs को **modify** कर सकते हैं, ताकि उन method names को अधिक अच्छे और **cleaner** बनाया जा सके। + +हम OpenAPI JSON को `openapi.json` file में download कर सकते हैं और फिर इस तरह के script से **उस prefixed tag को remove** कर सकते हैं: + +{* ../../docs_src/generate_clients/tutorial004_py310.py *} + +//// tab | Node.js + +```Javascript +{!> ../../docs_src/generate_clients/tutorial004.js!} +``` + +//// + +इसके साथ, operation IDs को `items-get_items` जैसी चीज़ों से बदलकर सिर्फ़ `get_items` कर दिया जाएगा, इस तरह client generator सरल method names जेनरेट कर सकता है। + +### Preprocessed OpenAPI के साथ TypeScript Client जेनरेट करें { #generate-a-typescript-client-with-the-preprocessed-openapi } + +क्योंकि अंतिम परिणाम अब `openapi.json` file में है, आपको अपनी input location update करनी होगी: + +```sh +npx @hey-api/openapi-ts -i ./openapi.json -o src/client +``` + +नया client जेनरेट करने के बाद, अब आपके पास **clean method names** होंगे, सभी **autocompletion**, **inline errors**, आदि के साथ: + + + +## लाभ { #benefits } + +Automatically generated clients का उपयोग करते समय, आपको इन चीज़ों के लिए **autocompletion** मिलेगा: + +* Methods. +* body में request payloads, query parameters, आदि। +* Response payloads. + +आपके पास हर चीज़ के लिए **inline errors** भी होंगे। + +और जब भी आप backend code update करते हैं, और frontend को **regenerate** करते हैं, तो इसमें methods के रूप में कोई भी नए *path operations* उपलब्ध होंगे, पुराने remove हो जाएँगे, और कोई भी अन्य change generated code में reflect होगा। 🤓 + +इसका मतलब यह भी है कि यदि कुछ बदलता है, तो वह client code में अपने-आप **reflect** होगा। और यदि आप client को **build** करते हैं, तो यदि उपयोग किए गए data में कोई **mismatch** है, तो यह error देगा। + +इसलिए, आप development cycle में बहुत जल्दी **कई errors detect** कर लेंगे, बजाय इसके कि errors के production में आपके अंतिम users को दिखने का इंतज़ार करना पड़े और फिर यह debug करने की कोशिश करनी पड़े कि समस्या कहाँ है। ✨ diff --git a/docs/hi/docs/advanced/index.md b/docs/hi/docs/advanced/index.md new file mode 100644 index 000000000..d2ecb69dc --- /dev/null +++ b/docs/hi/docs/advanced/index.md @@ -0,0 +1,21 @@ +# उन्नत उपयोगकर्ता गाइड { #advanced-user-guide } + +## अतिरिक्त feature { #additional-features } + +मुख्य [ट्यूटोरियल - उपयोगकर्ता गाइड](../tutorial/index.md) आपको **FastAPI** के सभी मुख्य feature का अवलोकन देने के लिए पर्याप्त होना चाहिए। + +अगले sections में आप अन्य विकल्प, configurations, और अतिरिक्त feature देखेंगे। + +/// tip | सुझाव + +अगले sections **ज़रूरी नहीं कि "उन्नत"** हों। + +और संभव है कि आपके उपयोग के मामले के लिए समाधान उनमें से किसी एक में हो। + +/// + +## पहले ट्यूटोरियल पढ़ें { #read-the-tutorial-first } + +आप मुख्य [ट्यूटोरियल - उपयोगकर्ता गाइड](../tutorial/index.md) से मिली जानकारी के साथ भी **FastAPI** के ज़्यादातर feature का उपयोग कर सकते हैं। + +और अगले sections मानते हैं कि आपने इसे पहले ही पढ़ लिया है, और यह भी मानते हैं कि आप उन मुख्य विचारों को जानते हैं। diff --git a/docs/hi/docs/advanced/json-base64-bytes.md b/docs/hi/docs/advanced/json-base64-bytes.md new file mode 100644 index 000000000..c99ffbc7f --- /dev/null +++ b/docs/hi/docs/advanced/json-base64-bytes.md @@ -0,0 +1,63 @@ +# Base64 के रूप में Bytes वाला JSON { #json-with-bytes-as-base64 } + +अगर आपके app को JSON data receive और send करना है, लेकिन आपको उसमें binary data शामिल करना है, तो आप उसे base64 के रूप में encode कर सकते हैं। + +## Base64 बनाम Files { #base64-vs-files } + +पहले यह विचार करें कि क्या आप binary data upload करने के लिए [Request Files](../tutorial/request-files.md) और binary data भेजने के लिए [कस्टम Response - FileResponse](./custom-response.md#fileresponse) का उपयोग कर सकते हैं, बजाय इसके कि उसे JSON में encode किया जाए। + +JSON में केवल UTF-8 encoded strings हो सकती हैं, इसलिए उसमें raw bytes नहीं हो सकते। + +Base64 binary data को strings में encode कर सकता है, लेकिन ऐसा करने के लिए उसे मूल binary data की तुलना में अधिक characters का उपयोग करना पड़ता है, इसलिए यह सामान्य files की तुलना में आमतौर पर कम efficient होगा। + +Base64 का उपयोग केवल तभी करें जब आपको निश्चित रूप से JSON में binary data शामिल करना हो, और आप उसके लिए files का उपयोग नहीं कर सकते। + +## Pydantic `bytes` { #pydantic-bytes } + +आप `bytes` fields वाला एक Pydantic model declare कर सकते हैं, और फिर model config में `val_json_bytes` का उपयोग करके उसे बता सकते हैं कि input JSON data को *validate* करने के लिए base64 का उपयोग करे; उस validation के हिस्से के रूप में यह base64 string को bytes में decode करेगा। + +{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:9,29:35] hl[9] *} + +अगर आप `/docs` देखें, तो वे दिखाएँगे कि field `data` base64 encoded bytes की अपेक्षा करता है: + +
+ +
+ +आप इस तरह का request भेज सकते हैं: + +```json +{ + "description": "Some data", + "data": "aGVsbG8=" +} +``` + +/// tip | सुझाव + +`aGVsbG8=` `hello` की base64 encoding है। + +/// + +और फिर Pydantic base64 string को decode करेगा और आपको model के `data` field में मूल bytes देगा। + +आपको इस तरह का response मिलेगा: + +```json +{ + "description": "Some data", + "content": "hello" +} +``` + +## Output Data के लिए Pydantic `bytes` { #pydantic-bytes-for-output-data } + +आप output data के लिए model config में `ser_json_bytes` के साथ `bytes` fields का भी उपयोग कर सकते हैं, और JSON response generate करते समय Pydantic bytes को base64 के रूप में *serialize* करेगा। + +{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,12:16,29,38:41] hl[16] *} + +## Input और Output Data के लिए Pydantic `bytes` { #pydantic-bytes-for-input-and-output-data } + +और बेशक, JSON data receive और send करते समय आप उसी model को base64 उपयोग करने के लिए configure कर सकते हैं, ताकि input (*validate*) को `val_json_bytes` के साथ और output (*serialize*) को `ser_json_bytes` के साथ handle किया जा सके। + +{* ../../docs_src/json_base64_bytes/tutorial001_py310.py ln[1:2,19:26,29,44:46] hl[23:26] *} diff --git a/docs/hi/docs/advanced/middleware.md b/docs/hi/docs/advanced/middleware.md new file mode 100644 index 000000000..a920ba224 --- /dev/null +++ b/docs/hi/docs/advanced/middleware.md @@ -0,0 +1,97 @@ +# उन्नत Middleware { #advanced-middleware } + +मुख्य tutorial में आपने पढ़ा कि अपनी application में [Custom Middleware](../tutorial/middleware.md) कैसे जोड़ें। + +और फिर आपने यह भी पढ़ा कि [`CORSMiddleware` के साथ CORS](../tutorial/cors.md) को कैसे handle करें। + +इस section में हम देखेंगे कि अन्य middleware का उपयोग कैसे करें। + +## ASGI middleware जोड़ना { #adding-asgi-middlewares } + +क्योंकि **FastAPI** Starlette पर आधारित है और ASGI specification को implement करता है, आप कोई भी ASGI middleware उपयोग कर सकते हैं। + +किसी middleware को काम करने के लिए FastAPI या Starlette के लिए बना होना required नहीं है, जब तक वह ASGI spec का पालन करता है। + +सामान्यतः, ASGI middleware ऐसी classes होती हैं जो पहले argument के रूप में एक ASGI app प्राप्त करने की अपेक्षा करती हैं। + +इसलिए, third-party ASGI middleware के documentation में वे शायद आपको कुछ ऐसा करने के लिए कहेंगे: + +```Python +from unicorn import UnicornMiddleware + +app = SomeASGIApp() + +new_app = UnicornMiddleware(app, some_config="rainbow") +``` + +लेकिन FastAPI (वास्तव में Starlette) इसे करने का एक सरल तरीका प्रदान करता है, जो सुनिश्चित करता है कि internal middleware server errors को handle करें और custom exception handlers सही तरीके से काम करें। + +इसके लिए, आप `app.add_middleware()` का उपयोग करते हैं (जैसे CORS के उदाहरण में)। + +```Python +from fastapi import FastAPI +from unicorn import UnicornMiddleware + +app = FastAPI() + +app.add_middleware(UnicornMiddleware, some_config="rainbow") +``` + +`app.add_middleware()` पहले argument के रूप में एक middleware class प्राप्त करता है और middleware को pass किए जाने वाले कोई भी अतिरिक्त arguments भी प्राप्त करता है। + +## एकीकृत middleware { #integrated-middlewares } + +**FastAPI** common use cases के लिए कई middleware शामिल करता है, आगे हम देखेंगे कि उनका उपयोग कैसे करें। + +/// note | तकनीकी विवरण + +अगले उदाहरणों के लिए, आप `from starlette.middleware.something import SomethingMiddleware` भी उपयोग कर सकते हैं। + +**FastAPI** `fastapi.middleware` में कई middleware सिर्फ आपकी, developer की, सुविधा के लिए प्रदान करता है। लेकिन उपलब्ध अधिकांश middleware सीधे Starlette से आते हैं। + +/// + +## `HTTPSRedirectMiddleware` { #httpsredirectmiddleware } + +यह enforce करता है कि सभी incoming requests या तो `https` या `wss` हों। + +`http` या `ws` पर आने वाली कोई भी incoming request इसके बजाय secure scheme पर redirect कर दी जाएगी। + +{* ../../docs_src/advanced_middleware/tutorial001_py310.py hl[2,6] *} + +## `TrustedHostMiddleware` { #trustedhostmiddleware } + +यह enforce करता है कि सभी incoming requests में `Host` header सही तरीके से set हो, ताकि HTTP Host Header attacks से बचाव हो सके। + +{* ../../docs_src/advanced_middleware/tutorial002_py310.py hl[2,6:8] *} + +निम्नलिखित arguments supported हैं: + +* `allowed_hosts` - domain names की एक सूची जिन्हें hostnames के रूप में allow किया जाना चाहिए। `*.example.com` जैसे Wildcard domains subdomains को match करने के लिए supported हैं। किसी भी hostname को allow करने के लिए या तो `allowed_hosts=["*"]` उपयोग करें या middleware को omit करें। +* `www_redirect` - यदि True पर set किया गया है, तो allowed hosts के non-www versions पर आने वाली requests उनके www counterparts पर redirect कर दी जाएँगी। Default `True` है। + +यदि कोई incoming request सही तरीके से validate नहीं होती है तो `400` response भेजा जाएगा। + +## `GZipMiddleware` { #gzipmiddleware } + +ऐसी किसी भी request के लिए GZip responses handle करता है जिसमें `Accept-Encoding` header में `"gzip"` शामिल हो। + +middleware standard और streaming दोनों responses को handle करेगा। + +{* ../../docs_src/advanced_middleware/tutorial003_py310.py hl[2,6] *} + +निम्नलिखित arguments supported हैं: + +* `minimum_size` - इस minimum size से छोटे responses को GZip न करें, size bytes में है। Default `500` है। +* `compresslevel` - GZip compression के दौरान उपयोग किया जाता है। यह 1 से 9 तक की range में एक integer है। Default `9` है। कम value से compression तेज़ होता है लेकिन file sizes बड़ी होती हैं, जबकि अधिक value से compression धीमा होता है लेकिन file sizes छोटी होती हैं। + +## अन्य middleware { #other-middlewares } + +कई अन्य ASGI middleware हैं। + +उदाहरण के लिए: + +* [Uvicorn का `ProxyHeadersMiddleware`](https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/proxy_headers.py) +* [MessagePack](https://github.com/florimondmanca/msgpack-asgi) + +अन्य उपलब्ध middleware देखने के लिए [Starlette के Middleware docs](https://www.starlette.dev/middleware/) और [ASGI Awesome List](https://github.com/florimondmanca/awesome-asgi) देखें। diff --git a/docs/hi/docs/advanced/openapi-callbacks.md b/docs/hi/docs/advanced/openapi-callbacks.md new file mode 100644 index 000000000..1cdbc823b --- /dev/null +++ b/docs/hi/docs/advanced/openapi-callbacks.md @@ -0,0 +1,186 @@ +# OpenAPI Callbacks { #openapi-callbacks } + +आप एक ऐसी API बना सकते हैं जिसमें एक *path operation* हो जो किसी और के द्वारा बनाई गई *external API* को request trigger कर सके (शायद वही developer जो आपकी API का *उपयोग* करेगा)। + +जब आपकी API app *external API* को call करती है, उस प्रक्रिया को "callback" कहा जाता है। क्योंकि external developer द्वारा लिखा गया software आपकी API को request भेजता है और फिर आपकी API *call back* करती है, यानी किसी *external API* को request भेजती है (जो शायद उसी developer द्वारा बनाई गई थी)। + +इस स्थिति में, आप यह document करना चाह सकते हैं कि वह external API कैसी *होनी चाहिए*। उसमें कौन-सा *path operation* होना चाहिए, उसे कौन-सा body expect करना चाहिए, उसे कौन-सा response लौटाना चाहिए, आदि। + +## Callbacks वाली एक app { #an-app-with-callbacks } + +आइए इसे एक उदाहरण के साथ देखते हैं। + +कल्पना करें कि आप एक ऐसी app develop करते हैं जो invoices बनाने देती है। + +इन invoices में एक `id`, `title` (optional), `customer`, और `total` होगा। + +आपकी API का user (एक external developer) आपकी API में POST request के साथ एक invoice बनाएगा। + +फिर आपकी API (कल्पना करें): + +* invoice को external developer के किसी customer को भेजेगी। +* पैसे collect करेगी। +* API user (external developer) को वापस एक notification भेजेगी। + * यह (*आपकी API* से) उस external developer द्वारा दी गई किसी *external API* को POST request भेजकर किया जाएगा (यही "callback" है)। + +## सामान्य **FastAPI** app { #the-normal-fastapi-app } + +Callback जोड़ने से पहले, पहले देखते हैं कि सामान्य API app कैसी दिखेगी। + +इसमें एक *path operation* होगा जो एक `Invoice` body receive करेगा, और एक query parameter `callback_url` होगा जिसमें callback के लिए URL होगा। + +यह हिस्सा काफ़ी सामान्य है, अधिकतर code शायद आपको पहले से परिचित होगा: + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[7:11,34:51] *} + +/// tip | सुझाव + +`callback_url` query parameter एक Pydantic [Url](https://docs.pydantic.dev/latest/api/networks/) type का उपयोग करता है। + +/// + +केवल नई चीज़ है *path operation decorator* के argument के रूप में `callbacks=invoices_callback_router.routes`। आगे हम देखेंगे कि यह क्या है। + +## Callback को document करना { #documenting-the-callback } + +वास्तविक callback code आपकी अपनी API app पर बहुत अधिक निर्भर करेगा। + +और यह एक app से दूसरी app में काफ़ी अलग हो सकता है। + +यह code की सिर्फ़ एक या दो lines भी हो सकती हैं, जैसे: + +```Python +callback_url = "https://example.com/api/v1/invoices/events/" +httpx.post(callback_url, json={"description": "Invoice paid", "paid": True}) +``` + +लेकिन संभवतः callback का सबसे महत्वपूर्ण हिस्सा यह सुनिश्चित करना है कि आपका API user (external developer) *external API* को सही तरह से implement करे, उस data के अनुसार जिसे *आपकी API* callback के request body में भेजने वाली है, आदि। + +तो, अब हम वह code जोड़ेंगे जो document करेगा कि *आपकी API* से callback receive करने के लिए वह *external API* कैसी दिखनी चाहिए। + +यह documentation आपकी API में `/docs` पर Swagger UI में दिखाई देगी, और यह external developers को बताएगी कि *external API* कैसे बनानी है। + +यह उदाहरण callback को स्वयं implement नहीं करता (वह केवल code की एक line हो सकती है), केवल documentation वाला हिस्सा करता है। + +/// tip | सुझाव + +वास्तविक callback सिर्फ़ एक HTTP request है। + +Callback को स्वयं implement करते समय, आप [HTTPX](https://www.python-httpx.org) या [Requests](https://requests.readthedocs.io/) जैसी किसी चीज़ का उपयोग कर सकते हैं। + +/// + +## Callback documentation code लिखें { #write-the-callback-documentation-code } + +यह code आपकी app में execute नहीं होगा, हमें इसकी आवश्यकता केवल यह *document* करने के लिए है कि वह *external API* कैसी दिखनी चाहिए। + +लेकिन, आप पहले से जानते हैं कि **FastAPI** के साथ किसी API के लिए automatic documentation आसानी से कैसे बनाई जाती है। + +इसलिए हम उसी ज्ञान का उपयोग करके document करेंगे कि *external API* कैसी दिखनी चाहिए... उन *path operation(s)* को बनाकर जिन्हें external API को implement करना चाहिए (जिन्हें आपकी API call करेगी)। + +/// tip | सुझाव + +Callback को document करने के लिए code लिखते समय, यह कल्पना करना उपयोगी हो सकता है कि आप वही *external developer* हैं। और इस समय आप *external API* implement कर रहे हैं, *अपनी API* नहीं। + +इस दृष्टिकोण को अस्थायी रूप से अपनाना (*external developer* का) आपको यह अधिक स्पष्ट महसूस कराने में मदद कर सकता है कि उस *external API* के लिए parameters, body के लिए Pydantic model, response के लिए model, आदि कहाँ रखने हैं। + +/// + +### Callback `APIRouter` बनाएँ { #create-a-callback-apirouter } + +पहले एक नया `APIRouter` बनाएँ जिसमें एक या अधिक callbacks होंगे। + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[1,23] *} + +### Callback *path operation* बनाएँ { #create-the-callback-path-operation } + +Callback *path operation* बनाने के लिए वही `APIRouter` उपयोग करें जो आपने ऊपर बनाया था। + +यह बिल्कुल सामान्य FastAPI *path operation* जैसा दिखना चाहिए: + +* इसमें शायद उस body की declaration होनी चाहिए जिसे इसे receive करना है, जैसे `body: InvoiceEvent`। +* और इसमें उस response की declaration भी हो सकती है जिसे इसे लौटाना चाहिए, जैसे `response_model=InvoiceEventReceived`। + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[14:16,19:20,26:30] *} + +सामान्य *path operation* से 2 मुख्य अंतर हैं: + +* इसमें कोई वास्तविक code होना required नहीं है, क्योंकि आपकी app इस code को कभी call नहीं करेगी। इसका उपयोग केवल *external API* को document करने के लिए किया जाता है। इसलिए, function में केवल `pass` हो सकता है। +* *path* में एक [OpenAPI 3 expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) (नीचे और देखें) हो सकता है, जहाँ यह *आपकी API* को भेजी गई original request के parameters और parts के साथ variables का उपयोग कर सकता है। + +### Callback path expression { #the-callback-path-expression } + +Callback *path* में एक [OpenAPI 3 expression](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md#key-expression) हो सकता है जो *आपकी API* को भेजी गई original request के parts शामिल कर सकता है। + +इस case में, यह `str` है: + +```Python +"{$callback_url}/invoices/{$request.body.id}" +``` + +तो, यदि आपका API user (external developer) *आपकी API* को request भेजता है: + +``` +https://yourapi.com/invoices/?callback_url=https://www.external.org/events +``` + +इस JSON body के साथ: + +```JSON +{ + "id": "2expen51ve", + "customer": "Mr. Richie Rich", + "total": "9999" +} +``` + +तो *आपकी API* invoice को process करेगी, और बाद में किसी समय, `callback_url` (*external API*) को callback request भेजेगी: + +``` +https://www.external.org/events/invoices/2expen51ve +``` + +ऐसे JSON body के साथ जिसमें कुछ इस तरह होगा: + +```JSON +{ + "description": "Payment celebration", + "paid": true +} +``` + +और यह उस *external API* से इस तरह के JSON body वाले response की अपेक्षा करेगी: + +```JSON +{ + "ok": true +} +``` + +/// tip | सुझाव + +ध्यान दें कि उपयोग किए गए callback URL में `callback_url` (`https://www.external.org/events`) में query parameter के रूप में प्राप्त URL और JSON body के अंदर से invoice `id` (`2expen51ve`) दोनों शामिल हैं। + +/// + +### Callback router जोड़ें { #add-the-callback-router } + +इस समय आपके पास ऊपर बनाए गए callback router में required *callback path operation(s)* हैं (वे operation जिन्हें *external developer* को *external API* में implement करना चाहिए)। + +अब *आपकी API के path operation decorator* में parameter `callbacks` का उपयोग करके उस callback router से attribute `.routes` pass करें: + +{* ../../docs_src/openapi_callbacks/tutorial001_py310.py hl[33] *} + +/// tip | सुझाव + +ध्यान दें कि आप router स्वयं (`invoices_callback_router`) को `callbacks=` में pass नहीं कर रहे हैं, बल्कि उसकी `.routes` को pass कर रहे हैं, जैसे `invoices_callback_router.routes`। FastAPI उन routes का उपयोग callback OpenAPI documentation generate करने के लिए करेगा। + +/// + +### Docs देखें { #check-the-docs } + +अब आप अपनी app start कर सकते हैं और [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) पर जा सकते हैं। + +आपको अपनी docs में अपने *path operation* के लिए एक "Callbacks" section दिखेगा, जो दिखाता है कि *external API* कैसी दिखनी चाहिए: + + diff --git a/docs/hi/docs/advanced/openapi-webhooks.md b/docs/hi/docs/advanced/openapi-webhooks.md new file mode 100644 index 000000000..65cafd0d6 --- /dev/null +++ b/docs/hi/docs/advanced/openapi-webhooks.md @@ -0,0 +1,55 @@ +# OpenAPI Webhooks { #openapi-webhooks } + +ऐसे मामले होते हैं जहाँ आप अपने API **users** को बताना चाहते हैं कि आपकी app कुछ data के साथ (एक request भेजते हुए) *उनकी* app को कॉल कर सकती है, सामान्यतः किसी प्रकार के **event** की **सूचना** देने के लिए। + +इसका मतलब है कि आपके users द्वारा आपकी API को requests भेजने की सामान्य प्रक्रिया के बजाय, **आपकी API** (या आपकी app) **उनके system को requests भेज** सकती है (उनकी API, उनकी app को)। + +इसे सामान्यतः **webhook** कहा जाता है। + +## Webhooks के चरण { #webhooks-steps } + +सामान्यतः प्रक्रिया यह होती है कि **आप अपने code में define करते हैं** कि आप कौन-सा message भेजेंगे, यानी **request का body**। + +आप यह भी किसी तरीके से define करते हैं कि आपकी app किन **क्षणों** पर वे requests या events भेजेगी। + +और **आपके users** किसी तरीके से (उदाहरण के लिए कहीं किसी web dashboard में) वह **URL** define करते हैं जहाँ आपकी app को वे requests भेजनी चाहिए। + +Webhooks के लिए URLs को register करने की सारी **logic** और वास्तव में उन requests को भेजने का code आपके ऊपर है। आप इसे **अपने खुद के code** में जैसे चाहें वैसे लिखते हैं। + +## **FastAPI** और OpenAPI के साथ webhooks का दस्तावेज़ीकरण { #documenting-webhooks-with-fastapi-and-openapi } + +**FastAPI** के साथ, OpenAPI का उपयोग करते हुए, आप इन webhooks के नाम, आपकी app द्वारा भेजे जा सकने वाले HTTP operations के प्रकार (जैसे `POST`, `PUT`, आदि) और आपकी app द्वारा भेजे जाने वाले request **bodies** define कर सकते हैं। + +इससे आपके users के लिए आपकी **webhook** requests प्राप्त करने के लिए **अपनी APIs implement करना** बहुत आसान हो सकता है, वे शायद अपने कुछ API code को autogenerate भी कर सकें। + +/// note | नोट + +Webhooks OpenAPI 3.1.0 और उससे ऊपर में उपलब्ध हैं, और FastAPI `0.99.0` और उससे ऊपर द्वारा समर्थित हैं। + +/// + +## Webhooks वाली app { #an-app-with-webhooks } + +जब आप एक **FastAPI** application बनाते हैं, तो एक `webhooks` attribute होता है जिसका उपयोग आप *webhooks* define करने के लिए कर सकते हैं, उसी तरह जैसे आप *path operations* define करते हैं, उदाहरण के लिए `@app.webhooks.post()` के साथ। + +{* ../../docs_src/openapi_webhooks/tutorial001_py310.py hl[9:12,15:20] *} + +आप जिन webhooks को define करते हैं वे **OpenAPI** schema और automatic **docs UI** में आ जाएँगे। + +/// note | नोट + +`app.webhooks` object वास्तव में सिर्फ़ एक `APIRouter` है, वही type जिसका उपयोग आप अपनी app को multiple files के साथ structure करते समय करेंगे। + +/// + +ध्यान दें कि webhooks के साथ आप वास्तव में कोई *path* declare नहीं कर रहे हैं (जैसे `/items/`), वहाँ आप जो text pass करते हैं वह केवल webhook का एक **identifier** है (event का नाम), उदाहरण के लिए `@app.webhooks.post("new-subscription")` में, webhook का नाम `new-subscription` है। + +ऐसा इसलिए है क्योंकि उम्मीद की जाती है कि **आपके users** उस वास्तविक **URL path** को किसी और तरीके से define करेंगे जहाँ वे webhook request प्राप्त करना चाहते हैं (जैसे कोई web dashboard)। + +### Docs देखें { #check-the-docs } + +अब आप अपनी app start कर सकते हैं और [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) पर जा सकते हैं। + +आप देखेंगे कि आपके docs में सामान्य *path operations* हैं और अब कुछ **webhooks** भी हैं: + + diff --git a/docs/hi/docs/advanced/path-operation-advanced-configuration.md b/docs/hi/docs/advanced/path-operation-advanced-configuration.md new file mode 100644 index 000000000..1636b4f0e --- /dev/null +++ b/docs/hi/docs/advanced/path-operation-advanced-configuration.md @@ -0,0 +1,166 @@ +# Path Operation की उन्नत Configuration { #path-operation-advanced-configuration } + +## OpenAPI operationId { #openapi-operationid } + +/// warning | चेतावनी + +अगर आप OpenAPI में "expert" नहीं हैं, तो शायद आपको इसकी ज़रूरत नहीं है। + +/// + +आप अपने *path operation* में उपयोग किए जाने वाले OpenAPI `operationId` को parameter `operation_id` के साथ सेट कर सकते हैं। + +आपको यह सुनिश्चित करना होगा कि यह प्रत्येक operation के लिए unique हो। + +{* ../../docs_src/path_operation_advanced_configuration/tutorial001_py310.py hl[6] *} + +### *path operation function* के नाम को operationId के रूप में उपयोग करना { #using-the-path-operation-function-name-as-the-operationid } + +अगर आप अपने APIs के function नामों को `operationId`s के रूप में उपयोग करना चाहते हैं, तो आप `FastAPI` को एक custom `generate_unique_id_function` पास कर सकते हैं। + +यह function प्रत्येक `APIRoute` प्राप्त करता है और उस path operation के लिए उपयोग करने वाला `operationId` return करता है। + +{* ../../docs_src/path_operation_advanced_configuration/tutorial002_py310.py hl[2,5:6,9] *} + +/// warning | चेतावनी + +अगर आप ऐसा करते हैं, तो आपको यह सुनिश्चित करना होगा कि आपके प्रत्येक *path operation functions* का नाम unique हो। + +भले ही वे अलग-अलग modules (Python files) में हों। + +/// + +## OpenAPI से बाहर रखना { #exclude-from-openapi } + +किसी *path operation* को generated OpenAPI schema से बाहर रखने के लिए (और इस प्रकार, automatic documentation systems से भी), parameter `include_in_schema` का उपयोग करें और इसे `False` पर सेट करें: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial003_py310.py hl[6] *} + +## Docstring से उन्नत description { #advanced-description-from-docstring } + +आप OpenAPI के लिए किसी *path operation function* की docstring से उपयोग की जाने वाली lines को सीमित कर सकते हैं। + +एक `\f` (एक escaped "form feed" character) जोड़ने से **FastAPI** इस बिंदु पर OpenAPI के लिए उपयोग किए जाने वाले output को truncate कर देता है। + +यह documentation में नहीं दिखेगा, लेकिन अन्य tools (जैसे Sphinx) बाकी हिस्से का उपयोग कर सकेंगे। + +{* ../../docs_src/path_operation_advanced_configuration/tutorial004_py310.py hl[17:27] *} + +## अतिरिक्त Responses { #additional-responses } + +आपने शायद देखा होगा कि किसी *path operation* के लिए `response_model` और `status_code` कैसे declare किए जाते हैं। + +यह किसी *path operation* के मुख्य response के बारे में metadata define करता है। + +आप उनके models, status codes आदि के साथ अतिरिक्त responses भी declare कर सकते हैं। + +इसके बारे में documentation में यहाँ एक पूरा chapter है, आप इसे [OpenAPI में अतिरिक्त Responses](additional-responses.md) पर पढ़ सकते हैं। + +## OpenAPI Extra { #openapi-extra } + +जब आप अपने application में कोई *path operation* declare करते हैं, तो **FastAPI** उस *path operation* के बारे में relevant metadata को automatically generate करता है, जिसे OpenAPI schema में शामिल किया जाता है। + +/// note | तकनीकी विवरण + +OpenAPI specification में इसे [Operation Object](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object) कहा जाता है। + +/// + +इसमें *path operation* के बारे में सारी जानकारी होती है और इसका उपयोग automatic documentation generate करने के लिए किया जाता है। + +इसमें `tags`, `parameters`, `requestBody`, `responses` आदि शामिल होते हैं। + +यह *path operation*-specific OpenAPI schema सामान्यतः **FastAPI** द्वारा automatically generate किया जाता है, लेकिन आप इसे extend भी कर सकते हैं। + +/// tip | सुझाव + +यह एक low level extension point है। + +अगर आपको केवल अतिरिक्त responses declare करने की ज़रूरत है, तो ऐसा करने का एक अधिक सुविधाजनक तरीका [OpenAPI में अतिरिक्त Responses](additional-responses.md) के साथ है। + +/// + +आप parameter `openapi_extra` का उपयोग करके किसी *path operation* के लिए OpenAPI schema को extend कर सकते हैं। + +### OpenAPI Extensions { #openapi-extensions } + +यह `openapi_extra` उपयोगी हो सकता है, उदाहरण के लिए, [OpenAPI Extensions](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#specificationExtensions) declare करने के लिए: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial005_py310.py hl[6] *} + +अगर आप automatic API docs खोलते हैं, तो आपका extension specific *path operation* के नीचे दिखाई देगा। + + + +और अगर आप resulting OpenAPI (आपकी API में `/openapi.json` पर) देखते हैं, तो आपको अपना extension specific *path operation* के हिस्से के रूप में भी दिखाई देगा: + +```JSON hl_lines="22" +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/items/": { + "get": { + "summary": "Read Items", + "operationId": "read_items_items__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "x-aperture-labs-portal": "blue" + } + } + } +} +``` + +### Custom OpenAPI *path operation* schema { #custom-openapi-path-operation-schema } + +`openapi_extra` में मौजूद dictionary को *path operation* के लिए automatically generated OpenAPI schema के साथ deeply merge किया जाएगा। + +तो, आप automatically generated schema में अतिरिक्त data जोड़ सकते हैं। + +उदाहरण के लिए, आप FastAPI की Pydantic के साथ automatic features का उपयोग किए बिना, अपने code से request को read और validate करने का निर्णय ले सकते हैं, लेकिन फिर भी आप OpenAPI schema में request को define करना चाह सकते हैं। + +आप यह `openapi_extra` के साथ कर सकते हैं: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial006_py310.py hl[19:36, 39:40] *} + +इस उदाहरण में, हमने कोई Pydantic model declare नहीं किया। वास्तव में, request body को JSON के रूप में parsed भी नहीं किया गया है, इसे सीधे `bytes` के रूप में read किया गया है, और function `magic_data_reader()` किसी तरीके से इसे parse करने का ज़िम्मेदार होगा। + +फिर भी, हम request body के लिए expected schema declare कर सकते हैं। + +### Custom OpenAPI content type { #custom-openapi-content-type } + +इसी trick का उपयोग करके, आप JSON Schema define करने के लिए Pydantic model का उपयोग कर सकते हैं, जिसे फिर *path operation* के लिए custom OpenAPI schema section में शामिल किया जाता है। + +और आप ऐसा तब भी कर सकते हैं जब request में data type JSON न हो। + +उदाहरण के लिए, इस application में हम Pydantic models से JSON Schema निकालने के लिए FastAPI की integrated functionality या JSON के लिए automatic validation का उपयोग नहीं करते हैं। वास्तव में, हम request content type को JSON नहीं, बल्कि YAML के रूप में declare कर रहे हैं: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[15:20, 22] *} + +फिर भी, हालांकि हम default integrated functionality का उपयोग नहीं कर रहे हैं, हम अभी भी उस data के लिए JSON Schema manually generate करने के लिए Pydantic model का उपयोग कर रहे हैं जिसे हम YAML में receive करना चाहते हैं। + +फिर हम request को सीधे उपयोग करते हैं, और body को `bytes` के रूप में extract करते हैं। इसका मतलब है कि FastAPI request payload को JSON के रूप में parse करने की कोशिश भी नहीं करेगा। + +और फिर अपने code में, हम उस YAML content को सीधे parse करते हैं, और फिर हम YAML content को validate करने के लिए फिर से उसी Pydantic model का उपयोग कर रहे हैं: + +{* ../../docs_src/path_operation_advanced_configuration/tutorial007_py310.py hl[24:31] *} + +/// tip | सुझाव + +यहाँ हम उसी Pydantic model को reuse करते हैं। + +लेकिन इसी तरह, हम इसे किसी और तरीके से भी validate कर सकते थे। + +/// diff --git a/docs/hi/docs/advanced/response-change-status-code.md b/docs/hi/docs/advanced/response-change-status-code.md new file mode 100644 index 000000000..3ef2f1b3f --- /dev/null +++ b/docs/hi/docs/advanced/response-change-status-code.md @@ -0,0 +1,31 @@ +# Response - Status Code बदलें { #response-change-status-code } + +आपने शायद पहले पढ़ा होगा कि आप एक default [Response Status Code](../tutorial/response-status-code.md) सेट कर सकते हैं। + +लेकिन कुछ मामलों में आपको default से अलग status code लौटाना पड़ता है। + +## उपयोग का मामला { #use-case } + +उदाहरण के लिए, कल्पना करें कि आप default रूप से "OK" `200` का HTTP status code लौटाना चाहते हैं। + +लेकिन अगर data मौजूद नहीं था, तो आप उसे बनाना चाहते हैं, और "CREATED" `201` का HTTP status code लौटाना चाहते हैं। + +लेकिन फिर भी आप `response_model` के साथ लौटाए गए data को filter और convert कर पाने में सक्षम रहना चाहते हैं। + +ऐसे मामलों के लिए, आप `Response` parameter का उपयोग कर सकते हैं। + +## `Response` parameter का उपयोग करें { #use-a-response-parameter } + +आप अपनी *path operation function* में `Response` type का parameter घोषित कर सकते हैं (जैसा कि आप cookies और headers के लिए कर सकते हैं)। + +और फिर आप उस *temporary* response object में `status_code` सेट कर सकते हैं। + +{* ../../docs_src/response_change_status_code/tutorial001_py310.py hl[1,9,12] *} + +और फिर आप अपनी ज़रूरत का कोई भी object लौटा सकते हैं, जैसा कि आप सामान्य रूप से करते हैं (एक `dict`, एक database model, आदि)। + +और अगर आपने `response_model` घोषित किया है, तो यह आपके लौटाए गए object को filter और convert करने के लिए अभी भी उपयोग किया जाएगा। + +**FastAPI** उस *temporary* response का उपयोग status code (साथ ही cookies और headers) निकालने के लिए करेगा, और उन्हें अंतिम response में डाल देगा जिसमें आपके द्वारा लौटाया गया value होगा, जिसे किसी भी `response_model` द्वारा filter किया गया होगा। + +आप dependencies में भी `Response` parameter घोषित कर सकते हैं, और उनमें status code सेट कर सकते हैं। लेकिन ध्यान रखें कि आख़िरी बार जो सेट किया जाएगा, वही प्रभावी होगा। diff --git a/docs/hi/docs/advanced/response-cookies.md b/docs/hi/docs/advanced/response-cookies.md new file mode 100644 index 000000000..ccd65dc0f --- /dev/null +++ b/docs/hi/docs/advanced/response-cookies.md @@ -0,0 +1,51 @@ +# Response Cookies { #response-cookies } + +## `Response` parameter का उपयोग करें { #use-a-response-parameter } + +आप अपने *path operation function* में `Response` प्रकार का parameter घोषित कर सकते हैं। + +और फिर आप उस *temporary* response object में cookies set कर सकते हैं। + +{* ../../docs_src/response_cookies/tutorial002_py310.py hl[1, 8:9] *} + +और फिर आप अपनी ज़रूरत का कोई भी object return कर सकते हैं, जैसा कि आप सामान्य रूप से करते हैं (एक `dict`, database model, आदि)। + +और अगर आपने `response_model` घोषित किया है, तो आपके द्वारा return किए गए object को filter और convert करने के लिए उसका अभी भी उपयोग किया जाएगा। + +**FastAPI** उस *temporary* response का उपयोग cookies (साथ ही headers और status code) निकालने के लिए करेगा, और उन्हें final response में डाल देगा जिसमें आपके द्वारा return किया गया value होगा, किसी भी `response_model` द्वारा filter किया हुआ। + +आप dependencies में भी `Response` parameter घोषित कर सकते हैं, और उनमें cookies (और headers) set कर सकते हैं। + +## सीधे `Response` return करें { #return-a-response-directly } + +आप अपने code में सीधे `Response` return करते समय भी cookies बना सकते हैं। + +ऐसा करने के लिए, आप [सीधे Response Return करें](response-directly.md) में बताए अनुसार एक response बना सकते हैं। + +फिर उसमें Cookies set करें, और फिर उसे return करें: + +{* ../../docs_src/response_cookies/tutorial001_py310.py hl[10:12] *} + +/// tip | सुझाव + +ध्यान रखें कि अगर आप `Response` parameter का उपयोग करने के बजाय सीधे response return करते हैं, तो FastAPI उसे सीधे return करेगा। + +इसलिए, आपको यह सुनिश्चित करना होगा कि आपका data सही प्रकार का है। उदाहरण के लिए, अगर आप `JSONResponse` return कर रहे हैं, तो वह JSON के साथ compatible हो। + +और यह भी कि आप कोई ऐसा data नहीं भेज रहे हैं जिसे `response_model` द्वारा filter किया जाना चाहिए था। + +/// + +### अधिक जानकारी { #more-info } + +/// note | तकनीकी विवरण + +आप `from starlette.responses import Response` या `from starlette.responses import JSONResponse` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.responses` `fastapi.responses` के रूप में प्रदान करता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। + +और क्योंकि `Response` का उपयोग अक्सर headers और cookies set करने के लिए किया जा सकता है, **FastAPI** इसे `fastapi.Response` पर भी प्रदान करता है। + +/// + +सभी उपलब्ध parameters और options देखने के लिए, [Starlette में documentation](https://www.starlette.dev/responses/#set-cookie) देखें। diff --git a/docs/hi/docs/advanced/response-directly.md b/docs/hi/docs/advanced/response-directly.md new file mode 100644 index 000000000..1f91a875e --- /dev/null +++ b/docs/hi/docs/advanced/response-directly.md @@ -0,0 +1,83 @@ +# सीधे एक Response लौटाएँ { #return-a-response-directly } + +जब आप **FastAPI** *path operation* बनाते हैं, तो सामान्यतः आप उससे कोई भी data लौटा सकते हैं: एक `dict`, एक `list`, एक Pydantic model, एक database model, आदि। + +अगर आप [Response Model](../tutorial/response-model.md) declare करते हैं, तो FastAPI Pydantic का उपयोग करके data को JSON में serialize करने के लिए उसका उपयोग करेगा। + +अगर आप response model declare नहीं करते, तो FastAPI [JSON Compatible Encoder](../tutorial/encoder.md) में समझाए गए `jsonable_encoder` का उपयोग करेगा और उसे एक `JSONResponse` में रखेगा। + +आप सीधे एक `JSONResponse` भी बना सकते हैं और उसे लौटा सकते हैं। + +/// tip | सुझाव + +आम तौर पर सीधे `JSONResponse` लौटाने की तुलना में [Response Model](../tutorial/response-model.md) का उपयोग करने पर performance काफी बेहतर होगी, क्योंकि उस तरीके से यह Rust में Pydantic का उपयोग करके data serialize करता है। + +/// + +## एक `Response` लौटाएँ { #return-a-response } + +आप एक `Response` या उसकी कोई भी sub-class लौटा सकते हैं। + +/// note | नोट + +`JSONResponse` खुद `Response` की एक sub-class है। + +/// + +और जब आप एक `Response` लौटाते हैं, तो **FastAPI** उसे सीधे pass कर देगा। + +यह Pydantic models के साथ कोई data conversion नहीं करेगा, contents को किसी भी type में convert नहीं करेगा, आदि। + +यह आपको बहुत अधिक **flexibility** देता है। आप कोई भी data type लौटा सकते हैं, किसी भी data declaration या validation को override कर सकते हैं, आदि। + +यह आपको बहुत अधिक **responsibility** भी देता है। आपको यह सुनिश्चित करना होगा कि आप जो data लौटा रहे हैं वह सही है, सही format में है, वह serialize किया जा सकता है, आदि। + +## `Response` में `jsonable_encoder` का उपयोग करना { #using-the-jsonable-encoder-in-a-response } + +क्योंकि **FastAPI** आपके लौटाए गए `Response` में कोई बदलाव नहीं करता, आपको सुनिश्चित करना होगा कि उसके contents इसके लिए तैयार हैं। + +उदाहरण के लिए, आप किसी Pydantic model को पहले `dict` में convert किए बिना `JSONResponse` में नहीं रख सकते, जिसमें सभी data types (जैसे `datetime`, `UUID`, आदि) JSON-compatible types में convert किए गए हों। + +ऐसे मामलों के लिए, response को pass करने से पहले आप अपने data को convert करने के लिए `jsonable_encoder` का उपयोग कर सकते हैं: + +{* ../../docs_src/response_directly/tutorial001_py310.py hl[5:6,20:21] *} + +/// note | तकनीकी विवरण + +आप `from starlette.responses import JSONResponse` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.responses` `fastapi.responses` के रूप में उपलब्ध कराता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। + +/// + +## custom `Response` लौटाना { #returning-a-custom-response } + +ऊपर दिया गया उदाहरण वे सभी हिस्से दिखाता है जिनकी आपको जरूरत है, लेकिन यह अभी बहुत उपयोगी नहीं है, क्योंकि आप सीधे `item` लौटा सकते थे, और **FastAPI** उसे आपके लिए `JSONResponse` में रख देता, उसे `dict` में convert करता, आदि। यह सब default रूप से होता है। + +अब, देखते हैं कि आप इसका उपयोग custom response लौटाने के लिए कैसे कर सकते हैं। + +मान लें कि आप एक [XML](https://en.wikipedia.org/wiki/XML) response लौटाना चाहते हैं। + +आप अपना XML content एक string में रख सकते हैं, उसे `Response` में रख सकते हैं, और उसे लौटा सकते हैं: + +{* ../../docs_src/response_directly/tutorial002_py310.py hl[1,18] *} + +## Response Model कैसे काम करता है { #how-a-response-model-works } + +जब आप किसी path operation में [Response Model - Return Type](../tutorial/response-model.md) declare करते हैं, तो **FastAPI** Pydantic का उपयोग करके data को JSON में serialize करने के लिए उसका उपयोग करेगा। + +{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} + +क्योंकि यह Rust side पर होगा, performance regular Python और `JSONResponse` class के साथ किए जाने की तुलना में काफी बेहतर होगी। + +`response_model` या return type का उपयोग करते समय, FastAPI data को convert करने के लिए `jsonable_encoder` का उपयोग नहीं करेगा (जो धीमा होता), और न ही `JSONResponse` class का उपयोग करेगा। + +इसके बजाय यह response model (या return type) का उपयोग करके Pydantic के साथ generate किए गए JSON bytes लेता है और JSON के लिए सही media type (`application/json`) के साथ सीधे एक `Response` लौटाता है। + +## नोट्स { #notes } + +जब आप सीधे एक `Response` लौटाते हैं, तो उसका data अपने-आप validate, convert (serialize), या document नहीं किया जाता। + +लेकिन आप फिर भी उसे [OpenAPI में अतिरिक्त Responses](additional-responses.md) में बताए अनुसार document कर सकते हैं। + +बाद के sections में आप देख सकते हैं कि automatic data conversion, documentation, आदि रखते हुए इन custom `Response`s का उपयोग/declare कैसे करें। diff --git a/docs/hi/docs/advanced/response-headers.md b/docs/hi/docs/advanced/response-headers.md new file mode 100644 index 000000000..9d62e2f47 --- /dev/null +++ b/docs/hi/docs/advanced/response-headers.md @@ -0,0 +1,41 @@ +# Response Headers { #response-headers } + +## `Response` parameter का उपयोग करें { #use-a-response-parameter } + +आप अपने *path operation function* में `Response` प्रकार का parameter घोषित कर सकते हैं (जैसा कि आप cookies के लिए कर सकते हैं)। + +और फिर आप उस *अस्थायी* response object में headers सेट कर सकते हैं। + +{* ../../docs_src/response_headers/tutorial002_py310.py hl[1, 7:8] *} + +और फिर आप अपनी ज़रूरत का कोई भी object return कर सकते हैं, जैसा कि आप सामान्य रूप से करते हैं (एक `dict`, database model, आदि)। + +और अगर आपने `response_model` घोषित किया है, तो वह अब भी आपके return किए गए object को filter और convert करने के लिए उपयोग किया जाएगा। + +**FastAPI** headers (साथ ही cookies और status code) निकालने के लिए उस *अस्थायी* response का उपयोग करेगा, और उन्हें अंतिम response में डाल देगा जिसमें आपके द्वारा return किया गया value होता है, जिसे किसी भी `response_model` द्वारा filter किया गया होता है। + +आप dependencies में भी `Response` parameter घोषित कर सकते हैं, और उनमें headers (और cookies) सेट कर सकते हैं। + +## सीधे `Response` return करें { #return-a-response-directly } + +जब आप सीधे `Response` return करते हैं, तब भी आप headers जोड़ सकते हैं। + +[सीधे Response Return करें](response-directly.md) में वर्णित तरीके से response बनाएँ और headers को एक अतिरिक्त parameter के रूप में पास करें: + +{* ../../docs_src/response_headers/tutorial001_py310.py hl[10:12] *} + +/// note | तकनीकी विवरण + +आप `from starlette.responses import Response` या `from starlette.responses import JSONResponse` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी, developer की, सुविधा के लिए वही `starlette.responses` `fastapi.responses` के रूप में प्रदान करता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। + +और क्योंकि `Response` का उपयोग अक्सर headers और cookies सेट करने के लिए किया जा सकता है, **FastAPI** इसे `fastapi.Response` पर भी प्रदान करता है। + +/// + +## Custom Headers { #custom-headers } + +ध्यान रखें कि custom proprietary headers को [`X-` prefix का उपयोग करके](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) जोड़ा जा सकता है। + +लेकिन अगर आपके पास custom headers हैं जिन्हें आप चाहते हैं कि browser में कोई client देख सके, तो आपको उन्हें अपनी CORS configurations में जोड़ना होगा ([CORS (Cross-Origin Resource Sharing)](../tutorial/cors.md) में और पढ़ें), इसके लिए [Starlette के CORS docs](https://www.starlette.dev/middleware/#corsmiddleware) में documented parameter `expose_headers` का उपयोग करें। diff --git a/docs/hi/docs/advanced/security/http-basic-auth.md b/docs/hi/docs/advanced/security/http-basic-auth.md new file mode 100644 index 000000000..172694ef9 --- /dev/null +++ b/docs/hi/docs/advanced/security/http-basic-auth.md @@ -0,0 +1,107 @@ +# HTTP Basic Auth { #http-basic-auth } + +सबसे सरल मामलों के लिए, आप HTTP Basic Auth का उपयोग कर सकते हैं। + +HTTP Basic Auth में, application एक header की अपेक्षा करता है जिसमें username और password होता है। + +अगर उसे यह नहीं मिलता, तो यह HTTP 401 "Unauthorized" error लौटाता है। + +और `WWW-Authenticate` header लौटाता है जिसका value `Basic` होता है, और एक optional `realm` parameter होता है। + +यह browser को username और password के लिए integrated prompt दिखाने को कहता है। + +फिर, जब आप वह username और password टाइप करते हैं, तो browser उन्हें header में अपने-आप भेज देता है। + +## Simple HTTP Basic Auth { #simple-http-basic-auth } + +* `HTTPBasic` और `HTTPBasicCredentials` import करें। +* `HTTPBasic` का उपयोग करके एक "`security` scheme" बनाएँ। +* अपने *path operation* में dependency के साथ उस `security` का उपयोग करें। +* यह `HTTPBasicCredentials` type का एक object लौटाता है: + * इसमें भेजे गए `username` और `password` होते हैं। + +{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *} + +जब आप पहली बार URL खोलने की कोशिश करते हैं (या docs में "Execute" button पर क्लिक करते हैं), तो browser आपसे आपका username और password पूछेगा: + + + +## Username जाँचें { #check-the-username } + +यहाँ एक अधिक complete example है। + +यह जाँचने के लिए dependency का उपयोग करें कि username और password सही हैं या नहीं। + +इसके लिए, username और password जाँचने के लिए Python standard module [`secrets`](https://docs.python.org/3/library/secrets.html) का उपयोग करें। + +`secrets.compare_digest()` को `bytes` या ऐसा `str` लेना होता है जिसमें केवल ASCII characters (English वाले) हों, इसका मतलब है कि यह `á` जैसे characters के साथ काम नहीं करेगा, जैसे `Sebastián` में। + +इसे handle करने के लिए, हम पहले `username` और `password` को UTF-8 से encode करके `bytes` में convert करते हैं। + +फिर हम `secrets.compare_digest()` का उपयोग करके यह सुनिश्चित कर सकते हैं कि `credentials.username` `"stanleyjobson"` है, और `credentials.password` `"swordfish"` है। + +{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *} + +यह इसके समान होगा: + +```Python +if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"): + # कोई error लौटाएँ + ... +``` + +लेकिन `secrets.compare_digest()` का उपयोग करने से यह "timing attacks" नाम के attacks के एक type के विरुद्ध सुरक्षित रहेगा। + +### Timing Attacks { #timing-attacks } + +लेकिन "timing attack" क्या होता है? + +मान लीजिए कुछ attackers username और password का अनुमान लगाने की कोशिश कर रहे हैं। + +और वे username `johndoe` और password `love123` के साथ एक request भेजते हैं। + +तब आपकी application में Python code कुछ इस तरह के बराबर होगा: + +```Python +if "johndoe" == "stanleyjobson" and "love123" == "swordfish": + ... +``` + +लेकिन जैसे ही Python `johndoe` में पहले `j` की तुलना `stanleyjobson` में पहले `s` से करता है, यह `False` लौटा देगा, क्योंकि उसे पहले से पता है कि ये दोनों strings समान नहीं हैं, यह सोचते हुए कि "बाकी अक्षरों की तुलना करके और computation खर्च करने की जरूरत नहीं है"। और आपकी application कहेगी "Incorrect username or password"। + +लेकिन फिर attackers username `stanleyjobsox` और password `love123` के साथ कोशिश करते हैं। + +और आपका application code कुछ ऐसा करता है: + +```Python +if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish": + ... +``` + +Python को यह समझने से पहले कि दोनों strings समान नहीं हैं, `stanleyjobsox` और `stanleyjobson` दोनों में पूरे `stanleyjobso` की तुलना करनी पड़ेगी। इसलिए "Incorrect username or password" का reply वापस देने में कुछ extra microseconds लगेंगे। + +#### जवाब देने में लगा समय attackers की मदद करता है { #the-time-to-answer-helps-the-attackers } + +उस समय, यह देखकर कि server ने "Incorrect username or password" response भेजने में कुछ microseconds ज्यादा लिए, attackers जान जाएँगे कि उन्होंने _कुछ_ सही पाया है, शुरुआती अक्षरों में से कुछ सही थे। + +और फिर वे यह जानते हुए फिर कोशिश कर सकते हैं कि यह शायद `johndoe` की तुलना में `stanleyjobsox` से ज्यादा मिलता-जुलता है। + +#### एक "professional" attack { #a-professional-attack } + +बेशक, attackers यह सब हाथ से नहीं करेंगे, वे इसे करने के लिए एक program लिखेंगे, संभवतः प्रति सेकंड हजारों या लाखों tests के साथ। और उन्हें एक समय में बस एक extra सही अक्षर मिलेगा। + +लेकिन ऐसा करते हुए, कुछ minutes या hours में attackers ने हमारी application की "help" से सही username और password का अनुमान लगा लिया होगा, सिर्फ जवाब देने में लगे समय का उपयोग करके। + +#### इसे `secrets.compare_digest()` से ठीक करें { #fix-it-with-secrets-compare-digest } + +लेकिन हमारे code में हम वास्तव में `secrets.compare_digest()` का उपयोग कर रहे हैं। + +संक्षेप में, `stanleyjobsox` की तुलना `stanleyjobson` से करने में उतना ही समय लगेगा जितना `johndoe` की तुलना `stanleyjobson` से करने में लगता है। और password के लिए भी वही। + +इस तरह, अपने application code में `secrets.compare_digest()` का उपयोग करके, यह security attacks की इस पूरी range के विरुद्ध सुरक्षित रहेगा। + +### Error लौटाएँ { #return-the-error } + +यह detect करने के बाद कि credentials incorrect हैं, status code 401 (वही जो तब लौटाया जाता है जब कोई credentials provide नहीं किए जाते) के साथ एक `HTTPException` लौटाएँ और browser को login prompt फिर से दिखाने के लिए `WWW-Authenticate` header जोड़ें: + +{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *} diff --git a/docs/hi/docs/advanced/security/index.md b/docs/hi/docs/advanced/security/index.md new file mode 100644 index 000000000..cd5d222c6 --- /dev/null +++ b/docs/hi/docs/advanced/security/index.md @@ -0,0 +1,19 @@ +# उन्नत सुरक्षा { #advanced-security } + +## अतिरिक्त Features { #additional-features } + +[Tutorial - User Guide: Security](../../tutorial/security/index.md) में शामिल चीज़ों के अलावा सुरक्षा संभालने के लिए कुछ अतिरिक्त features हैं। + +/// tip | सुझाव + +अगले sections **ज़रूरी नहीं कि "उन्नत" ही हों**। + +और यह संभव है कि आपके use case के लिए समाधान उनमें से किसी एक में हो। + +/// + +## पहले Tutorial पढ़ें { #read-the-tutorial-first } + +अगले sections मानकर चलते हैं कि आपने मुख्य [Tutorial - User Guide: Security](../../tutorial/security/index.md) पहले ही पढ़ लिया है। + +वे सभी समान concepts पर आधारित हैं, लेकिन कुछ अतिरिक्त functionalities की अनुमति देते हैं। diff --git a/docs/hi/docs/advanced/security/oauth2-scopes.md b/docs/hi/docs/advanced/security/oauth2-scopes.md new file mode 100644 index 000000000..1bf01f366 --- /dev/null +++ b/docs/hi/docs/advanced/security/oauth2-scopes.md @@ -0,0 +1,274 @@ +# OAuth2 scopes { #oauth2-scopes } + +आप **FastAPI** के साथ OAuth2 scopes सीधे उपयोग कर सकते हैं, वे निर्बाध रूप से काम करने के लिए एकीकृत हैं। + +यह आपको OAuth2 standard का पालन करते हुए, आपकी OpenAPI application (और API docs) में एक अधिक fine-grained permission system रखने की अनुमति देगा। + +Scopes के साथ OAuth2 वह mechanism है जिसे कई बड़े authentication providers, जैसे Facebook, Google, GitHub, Microsoft, X (Twitter), आदि उपयोग करते हैं। वे इसका उपयोग users और applications को विशिष्ट permissions देने के लिए करते हैं। + +हर बार जब आप Facebook, Google, GitHub, Microsoft, X (Twitter) के साथ "log in with" करते हैं, वह application scopes के साथ OAuth2 का उपयोग कर रही होती है। + +इस section में आप देखेंगे कि अपनी **FastAPI** application में उसी scopes वाले OAuth2 के साथ authentication और authorization को कैसे manage करें। + +/// warning | चेतावनी + +यह थोड़ा-बहुत advanced section है। यदि आप अभी शुरू कर रहे हैं, तो आप इसे छोड़ सकते हैं। + +आपको अनिवार्य रूप से OAuth2 scopes की आवश्यकता नहीं है, और आप authentication और authorization को जैसे चाहें handle कर सकते हैं। + +लेकिन scopes के साथ OAuth2 को आपकी API (OpenAPI के साथ) और आपकी API docs में अच्छी तरह integrate किया जा सकता है। + +फिर भी, आप उन scopes, या किसी भी अन्य security/authorization requirement को अपने code में अपनी आवश्यकता के अनुसार enforce करते हैं। + +कई मामलों में, scopes के साथ OAuth2 overkill हो सकता है। + +लेकिन यदि आप जानते हैं कि आपको इसकी आवश्यकता है, या आप curious हैं, तो पढ़ते रहें। + +/// + +## OAuth2 scopes और OpenAPI { #oauth2-scopes-and-openapi } + +OAuth2 specification "scopes" को spaces से अलग की गई strings की list के रूप में define करती है। + +इनमें से प्रत्येक string का content किसी भी format में हो सकता है, लेकिन उसमें spaces नहीं होने चाहिए। + +ये scopes "permissions" को दर्शाते हैं। + +OpenAPI में (जैसे API docs), आप "security schemes" define कर सकते हैं। + +जब इनमें से कोई security scheme OAuth2 का उपयोग करती है, तो आप scopes declare और उपयोग भी कर सकते हैं। + +प्रत्येक "scope" बस एक string है (spaces के बिना)। + +वे सामान्यतः विशिष्ट security permissions declare करने के लिए उपयोग किए जाते हैं, उदाहरण के लिए: + +* `users:read` या `users:write` आम examples हैं। +* `instagram_basic` Facebook / Instagram द्वारा उपयोग किया जाता है। +* `https://www.googleapis.com/auth/drive` Google द्वारा उपयोग किया जाता है। + +/// note | नोट + +OAuth2 में "scope" बस एक string है जो required विशिष्ट permission declare करती है। + +इससे फर्क नहीं पड़ता कि इसमें `:` जैसे अन्य characters हैं या यह एक URL है। + +वे details implementation specific हैं। + +OAuth2 के लिए वे बस strings हैं। + +/// + +## Global view { #global-view } + +पहले, आइए जल्दी से देखें कि मुख्य **Tutorial - User Guide** में [Password के साथ OAuth2 (और hashing), JWT tokens के साथ Bearer](../../tutorial/security/oauth2-jwt.md) के examples से कौन से parts बदलते हैं। अब OAuth2 scopes का उपयोग करते हुए: + +{* ../../docs_src/security/tutorial005_an_py310.py hl[5,9,13,47,65,106,108:116,122:126,130:136,141,157] *} + +अब आइए उन बदलावों को step by step review करें। + +## OAuth2 Security scheme { #oauth2-security-scheme } + +पहला बदलाव यह है कि अब हम OAuth2 security scheme को दो उपलब्ध scopes, `me` और `items`, के साथ declare कर रहे हैं। + +`scopes` parameter एक `dict` receive करता है जिसमें प्रत्येक scope key के रूप में और description value के रूप में होती है: + +{* ../../docs_src/security/tutorial005_an_py310.py hl[63:66] *} + +क्योंकि अब हम उन scopes को declare कर रहे हैं, वे API docs में तब दिखाई देंगे जब आप log-in/authorize करेंगे। + +और आप select कर सकेंगे कि आप किन scopes को access देना चाहते हैं: `me` और `items`। + +यह वही mechanism है जिसका उपयोग तब होता है जब आप Facebook, Google, GitHub, आदि के साथ log in करते समय permissions देते हैं: + + + +## Scopes के साथ JWT token { #jwt-token-with-scopes } + +अब, token *path operation* को modify करें ताकि requested scopes return हों। + +हम अभी भी वही `OAuth2PasswordRequestForm` उपयोग कर रहे हैं। इसमें `scopes` property शामिल है जिसमें `str` की `list` होती है, और request में received प्रत्येक scope होता है। + +और हम scopes को JWT token के part के रूप में return करते हैं। + +/// danger | खतरा + +सरलता के लिए, यहाँ हम received scopes को सीधे token में जोड़ रहे हैं। + +लेकिन आपकी application में, security के लिए, आपको सुनिश्चित करना चाहिए कि आप केवल वे scopes जोड़ें जिन्हें user वास्तव में रख सकता है, या जिन्हें आपने predefine किया है। + +/// + +{* ../../docs_src/security/tutorial005_an_py310.py hl[157] *} + +## *path operations* और dependencies में scopes declare करें { #declare-scopes-in-path-operations-and-dependencies } + +अब हम declare करते हैं कि `/users/me/items/` के लिए *path operation* को scope `items` required है। + +इसके लिए, हम `fastapi` से `Security` import और उपयोग करते हैं। + +आप dependencies declare करने के लिए `Security` का उपयोग कर सकते हैं (बिल्कुल `Depends` की तरह), लेकिन `Security` एक parameter `scopes` भी receive करता है जिसमें scopes (strings) की list होती है। + +इस case में, हम dependency function `get_current_active_user` को `Security` में pass करते हैं (उसी तरह जैसे हम `Depends` के साथ करते)। + +लेकिन हम scopes की एक `list` भी pass करते हैं, इस case में केवल एक scope के साथ: `items` (इसमें और भी हो सकते थे)। + +और dependency function `get_current_active_user` sub-dependencies भी declare कर सकता है, न केवल `Depends` के साथ बल्कि `Security` के साथ भी। अपना sub-dependency function (`get_current_user`) और अधिक scope requirements declare करते हुए। + +इस case में, इसे scope `me` required है (इसे एक से अधिक scope required हो सकते थे)। + +/// note | नोट + +आपको अलग-अलग जगहों पर अलग-अलग scopes जोड़ना अनिवार्य नहीं है। + +हम यहाँ यह demonstrate करने के लिए कर रहे हैं कि **FastAPI** अलग-अलग levels पर declared scopes को कैसे handle करता है। + +/// + +{* ../../docs_src/security/tutorial005_an_py310.py hl[5,141,172] *} + +/// note | तकनीकी विवरण + +`Security` वास्तव में `Depends` का subclass है, और इसमें केवल एक extra parameter है जिसे हम बाद में देखेंगे। + +लेकिन `Depends` के बजाय `Security` का उपयोग करके, **FastAPI** जान जाएगा कि यह security scopes declare कर सकता है, उन्हें internally उपयोग कर सकता है, और API को OpenAPI के साथ document कर सकता है। + +लेकिन जब आप `fastapi` से `Query`, `Path`, `Depends`, `Security` और अन्य import करते हैं, तो वे वास्तव में functions हैं जो special classes return करते हैं। + +/// + +## `SecurityScopes` का उपयोग करें { #use-securityscopes } + +अब dependency `get_current_user` को update करें। + +यह वही है जिसका उपयोग ऊपर की dependencies द्वारा किया जाता है। + +यहीं हम पहले बनाई गई उसी OAuth2 scheme का उपयोग कर रहे हैं, इसे dependency के रूप में declare करते हुए: `oauth2_scheme`। + +क्योंकि इस dependency function की अपनी कोई scope requirements नहीं हैं, हम `oauth2_scheme` के साथ `Depends` उपयोग कर सकते हैं, जब हमें security scopes specify करने की आवश्यकता नहीं है तो हमें `Security` उपयोग करने की आवश्यकता नहीं है। + +हम `SecurityScopes` type का एक special parameter भी declare करते हैं, जिसे `fastapi.security` से import किया गया है। + +यह `SecurityScopes` class `Request` के समान है (`Request` का उपयोग request object को सीधे प्राप्त करने के लिए किया गया था)। + +{* ../../docs_src/security/tutorial005_an_py310.py hl[9,106] *} + +## `scopes` का उपयोग करें { #use-the-scopes } + +Parameter `security_scopes` का type `SecurityScopes` होगा। + +इसमें property `scopes` होगी जिसमें एक list होगी, जिसमें स्वयं और इसे sub-dependency के रूप में उपयोग करने वाली सभी dependencies द्वारा required सभी scopes शामिल होंगे। इसका मतलब है, सभी "dependants"... यह confusing लग सकता है, इसे नीचे फिर से समझाया गया है। + +`security_scopes` object (`SecurityScopes` class का) एक `scope_str` attribute भी provide करता है जिसमें एक single string होती है, जिसमें वे scopes spaces से अलग होते हैं (हम इसका उपयोग करेंगे)। + +हम एक `HTTPException` बनाते हैं जिसे हम बाद में कई points पर reuse (`raise`) कर सकते हैं। + +इस exception में, हम required scopes (यदि कोई हों) को spaces से अलग की गई string के रूप में शामिल करते हैं (`scope_str` का उपयोग करके)। हम scopes वाली उस string को `WWW-Authenticate` header में रखते हैं (यह spec का part है)। + +{* ../../docs_src/security/tutorial005_an_py310.py hl[106,108:116] *} + +## `username` और data shape verify करें { #verify-the-username-and-data-shape } + +हम verify करते हैं कि हमें `username` मिलता है, और scopes extract करते हैं। + +और फिर हम उस data को Pydantic model के साथ validate करते हैं (`ValidationError` exception को catch करते हुए), और यदि JWT token पढ़ने या Pydantic के साथ data validate करने में error मिलता है, तो हम पहले बनाया हुआ `HTTPException` raise करते हैं। + +इसके लिए, हम Pydantic model `TokenData` को नई property `scopes` के साथ update करते हैं। + +Pydantic के साथ data validate करके हम यह सुनिश्चित कर सकते हैं कि हमारे पास, उदाहरण के लिए, scopes के साथ बिल्कुल `str` की `list` और `username` के साथ `str` है। + +उदाहरण के लिए, `dict`, या कुछ और नहीं, क्योंकि यह बाद में किसी point पर application को break कर सकता है, जिससे यह security risk बन सकता है। + +हम यह भी verify करते हैं कि हमारे पास उस username वाला user है, और यदि नहीं, तो हम वही exception raise करते हैं जो हमने पहले बनाया था। + +{* ../../docs_src/security/tutorial005_an_py310.py hl[47,117:129] *} + +## `scopes` verify करें { #verify-the-scopes } + +अब हम verify करते हैं कि इस dependency और सभी dependants (जिसमें *path operations* शामिल हैं) द्वारा required सभी scopes, received token में provided scopes में शामिल हैं, अन्यथा `HTTPException` raise करते हैं। + +इसके लिए, हम `security_scopes.scopes` का उपयोग करते हैं, जिसमें इन सभी scopes की `list` `str` के रूप में होती है। + +{* ../../docs_src/security/tutorial005_an_py310.py hl[130:136] *} + +## Dependency tree और scopes { #dependency-tree-and-scopes } + +आइए इस dependency tree और scopes को फिर से review करें। + +क्योंकि `get_current_active_user` dependency में `get_current_user` sub-dependency के रूप में है, `get_current_active_user` पर declared scope `"me"` required scopes की उस list में शामिल होगा जो `get_current_user` को pass किए गए `security_scopes.scopes` में होती है। + +*path operation* स्वयं भी एक scope, `"items"`, declare करता है, इसलिए यह भी `get_current_user` को pass किए गए `security_scopes.scopes` की list में होगा। + +Dependencies और scopes की hierarchy इस तरह दिखती है: + +* *path operation* `read_own_items` में है: + * Dependency के साथ required scopes `["items"]`: + * `get_current_active_user`: + * Dependency function `get_current_active_user` में है: + * Dependency के साथ required scopes `["me"]`: + * `get_current_user`: + * Dependency function `get_current_user` में है: + * स्वयं द्वारा required कोई scopes नहीं। + * `oauth2_scheme` का उपयोग करने वाली dependency। + * `SecurityScopes` type का एक `security_scopes` parameter: + * इस `security_scopes` parameter में property `scopes` है जिसमें ऊपर declared इन सभी scopes वाली `list` है, इसलिए: + * *path operation* `read_own_items` के लिए `security_scopes.scopes` में `["me", "items"]` होगा। + * *path operation* `read_users_me` के लिए `security_scopes.scopes` में `["me"]` होगा, क्योंकि यह dependency `get_current_active_user` में declared है। + * *path operation* `read_system_status` के लिए `security_scopes.scopes` में `[]` (कुछ नहीं) होगा, क्योंकि उसने `scopes` के साथ कोई `Security` declare नहीं किया, और उसकी dependency, `get_current_user`, भी कोई `scopes` declare नहीं करती। + +/// tip | सुझाव + +यहाँ महत्वपूर्ण और "magic" बात यह है कि प्रत्येक *path operation* के लिए `get_current_user` के पास check करने हेतु `scopes` की अलग list होगी। + +यह सब उस specific *path operation* के dependency tree में प्रत्येक *path operation* और प्रत्येक dependency में declared `scopes` पर निर्भर करता है। + +/// + +## `SecurityScopes` के बारे में अधिक details { #more-details-about-securityscopes } + +आप `SecurityScopes` का उपयोग किसी भी point पर, और multiple जगहों पर कर सकते हैं, इसका "root" dependency पर होना ज़रूरी नहीं है। + +इसमें हमेशा current `Security` dependencies और **उस specific** *path operation* तथा **उस specific** dependency tree के सभी dependants में declared security scopes होंगे। + +क्योंकि `SecurityScopes` में dependants द्वारा declared सभी scopes होंगे, आप इसका उपयोग यह verify करने के लिए कर सकते हैं कि token में required scopes हैं, एक central dependency function में, और फिर अलग-अलग *path operations* में अलग-अलग scope requirements declare कर सकते हैं। + +उन्हें प्रत्येक *path operation* के लिए independently check किया जाएगा। + +## इसे check करें { #check-it } + +यदि आप API docs खोलते हैं, तो आप authenticate कर सकते हैं और specify कर सकते हैं कि आप किन scopes को authorize करना चाहते हैं। + + + +यदि आप कोई scope select नहीं करते हैं, तो आप "authenticated" होंगे, लेकिन जब आप `/users/me/` या `/users/me/items/` access करने की कोशिश करेंगे तो आपको error मिलेगा कि आपके पास पर्याप्त permissions नहीं हैं। आप फिर भी `/status/` access कर पाएंगे। + +और यदि आप scope `me` select करते हैं लेकिन scope `items` नहीं, तो आप `/users/me/` access कर पाएंगे लेकिन `/users/me/items/` नहीं। + +ऐसा ही किसी third party application के साथ होगा जो user द्वारा provided token के साथ इन *path operations* में से किसी एक को access करने की कोशिश करती, यह इस पर निर्भर करता है कि user ने application को कितनी permissions दीं। + +## Third party integrations के बारे में { #about-third-party-integrations } + +इस example में हम OAuth2 "password" flow का उपयोग कर रहे हैं। + +यह तब appropriate है जब हम अपनी ही application में log in कर रहे हों, शायद अपने ही frontend के साथ। + +क्योंकि हम इस पर भरोसा कर सकते हैं कि यह `username` और `password` receive करे, क्योंकि हम इसे control करते हैं। + +लेकिन यदि आप ऐसी OAuth2 application बना रहे हैं जिससे दूसरे connect करेंगे (अर्थात, यदि आप Facebook, Google, GitHub, आदि के बराबर authentication provider बना रहे हैं) तो आपको अन्य flows में से किसी एक का उपयोग करना चाहिए। + +सबसे common implicit flow है। + +सबसे secure code flow है, लेकिन इसे implement करना अधिक complex है क्योंकि इसमें अधिक steps required हैं। क्योंकि यह अधिक complex है, कई providers अंततः implicit flow suggest करते हैं। + +/// note | नोट + +यह common है कि प्रत्येक authentication provider अपने flows को अलग तरीके से name करता है, ताकि इसे अपने brand का part बना सके। + +लेकिन अंततः, वे वही OAuth2 standard implement कर रहे होते हैं। + +/// + +**FastAPI** में इन सभी OAuth2 authentication flows के लिए utilities `fastapi.security.oauth2` में शामिल हैं। + +## Decorator `dependencies` में `Security` { #security-in-decorator-dependencies } + +जिस तरह आप decorator के `dependencies` parameter में `Depends` की `list` define कर सकते हैं (जैसा कि [path operation decorators में Dependencies](../../tutorial/dependencies/dependencies-in-path-operation-decorators.md) में समझाया गया है), आप वहाँ `scopes` के साथ `Security` भी उपयोग कर सकते हैं। diff --git a/docs/hi/docs/advanced/settings.md b/docs/hi/docs/advanced/settings.md new file mode 100644 index 000000000..0c4d3c061 --- /dev/null +++ b/docs/hi/docs/advanced/settings.md @@ -0,0 +1,302 @@ +# Settings और Environment Variables { #settings-and-environment-variables } + +कई मामलों में आपकी application को कुछ बाहरी settings या configurations की ज़रूरत हो सकती है, उदाहरण के लिए secret keys, database credentials, email services के लिए credentials, आदि। + +इनमें से ज़्यादातर settings variable होती हैं (बदल सकती हैं), जैसे database URLs। और कई sensitive हो सकती हैं, जैसे secrets। + +इसी कारण उन्हें आम तौर पर environment variables में दिया जाता है जिन्हें application पढ़ती है। + +/// tip | सुझाव + +Environment variables को समझने के लिए आप [Environment Variables](../environment-variables.md) पढ़ सकते हैं। + +/// + +## Types और validation { #types-and-validation } + +ये environment variables केवल text strings को handle कर सकते हैं, क्योंकि ये Python के बाहर होते हैं और इन्हें दूसरे programs और system के बाकी हिस्सों के साथ compatible होना होता है (और अलग-अलग operating systems, जैसे Linux, Windows, और macOS के साथ भी)। + +इसका मतलब है कि Python में किसी environment variable से पढ़ी गई कोई भी value एक `str` होगी, और किसी अलग type में कोई भी conversion या कोई भी validation code में करनी होगी। + +## Pydantic `Settings` { #pydantic-settings } + +सौभाग्य से, Pydantic environment variables से आने वाली इन settings को handle करने के लिए एक बेहतरीन utility देता है: [Pydantic: Settings management](https://docs.pydantic.dev/latest/concepts/pydantic_settings/)। + +### `pydantic-settings` install करें { #install-pydantic-settings } + +सबसे पहले, सुनिश्चित करें कि आप अपना [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर `pydantic-settings` package install करते हैं: + +
+ +```console +$ pip install pydantic-settings +---> 100% +``` + +
+ +जब आप `all` extras को install करते हैं, तो यह भी शामिल आता है: + +
+ +```console +$ pip install "fastapi[all]" +---> 100% +``` + +
+ +### `Settings` object बनाएँ { #create-the-settings-object } + +Pydantic से `BaseSettings` import करें और एक sub-class बनाएँ, बिल्कुल Pydantic model की तरह। + +Pydantic models की तरह ही, आप type annotations के साथ class attributes घोषित करते हैं, और संभवतः default values भी। + +आप वे सभी validation features और tools इस्तेमाल कर सकते हैं जिन्हें आप Pydantic models के लिए इस्तेमाल करते हैं, जैसे अलग-अलग data types और `Field()` के साथ अतिरिक्त validations। + +{* ../../docs_src/settings/tutorial001_py310.py hl[2,5:8,11] *} + +/// tip | सुझाव + +अगर आप जल्दी copy और paste करने के लिए कुछ चाहते हैं, तो यह example इस्तेमाल न करें, नीचे वाला आखिरी example इस्तेमाल करें। + +/// + +फिर, जब आप उस `Settings` class का instance बनाते हैं (इस case में, `settings` object में), Pydantic environment variables को case-insensitive तरीके से पढ़ेगा, इसलिए upper-case variable `APP_NAME` भी attribute `app_name` के लिए पढ़ा जाएगा। + +इसके बाद यह data को convert और validate करेगा। इसलिए, जब आप उस `settings` object का उपयोग करेंगे, तो आपके पास उन types का data होगा जिन्हें आपने घोषित किया था (जैसे `items_per_user` एक `int` होगा)। + +### `settings` का उपयोग करें { #use-the-settings } + +फिर आप अपनी application में नए `settings` object का उपयोग कर सकते हैं: + +{* ../../docs_src/settings/tutorial001_py310.py hl[18:20] *} + +### Server चलाएँ { #run-the-server } + +इसके बाद, आप configurations को environment variables के रूप में pass करते हुए server चलाएँगे, उदाहरण के लिए आप `ADMIN_EMAIL` और `APP_NAME` set कर सकते हैं: + +
+ +```console +$ ADMIN_EMAIL="deadpool@example.com" APP_NAME="ChimichangApp" fastapi run main.py + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +/// tip | सुझाव + +एक ही command के लिए कई env vars set करने के लिए बस उन्हें space से अलग करें, और उन सभी को command से पहले रखें। + +/// + +और फिर `admin_email` setting `"deadpool@example.com"` पर set हो जाएगी। + +`app_name` `"ChimichangApp"` होगा। + +और `items_per_user` अपनी default value `50` बनाए रखेगा। + +## किसी दूसरे module में Settings { #settings-in-another-module } + +आप उन settings को किसी दूसरे module file में रख सकते हैं, जैसा आपने [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md) में देखा था। + +उदाहरण के लिए, आपके पास `config.py` file हो सकती है: + +{* ../../docs_src/settings/app01_py310/config.py *} + +और फिर उसे `main.py` file में उपयोग करें: + +{* ../../docs_src/settings/app01_py310/main.py hl[3,11:13] *} + +/// tip | सुझाव + +आपको एक `__init__.py` file की भी ज़रूरत होगी, जैसा आपने [Bigger Applications - Multiple Files](../tutorial/bigger-applications.md) में देखा था। + +/// + +## Dependency में Settings { #settings-in-a-dependency } + +कुछ मौकों पर settings को dependency से देना उपयोगी हो सकता है, बजाय इसके कि `settings` के साथ एक global object हो जिसे हर जगह इस्तेमाल किया जाए। + +यह testing के दौरान विशेष रूप से उपयोगी हो सकता है, क्योंकि dependency को अपनी custom settings से override करना बहुत आसान है। + +### Config file { #the-config-file } + +पिछले example से आगे बढ़ते हुए, आपकी `config.py` file इस तरह दिख सकती है: + +{* ../../docs_src/settings/app02_an_py310/config.py hl[10] *} + +ध्यान दें कि अब हम default instance `settings = Settings()` नहीं बनाते। + +### Main app file { #the-main-app-file } + +अब हम एक dependency बनाते हैं जो नया `config.Settings()` return करती है। + +{* ../../docs_src/settings/app02_an_py310/main.py hl[6,12:13] *} + +/// tip | सुझाव + +हम थोड़ी देर में `@lru_cache` पर चर्चा करेंगे। + +अभी के लिए आप मान सकते हैं कि `get_settings()` एक normal function है। + +/// + +और फिर हम इसे *path operation function* से dependency के रूप में require कर सकते हैं और जहाँ भी ज़रूरत हो वहाँ इस्तेमाल कर सकते हैं। + +{* ../../docs_src/settings/app02_an_py310/main.py hl[17,19:21] *} + +### Settings और testing { #settings-and-testing } + +फिर testing के दौरान `get_settings` के लिए dependency override बनाकर अलग settings object देना बहुत आसान होगा: + +{* ../../docs_src/settings/app02_an_py310/test_main.py hl[9:10,13,21] *} + +Dependency override में हम नया `Settings` object बनाते समय `admin_email` के लिए नई value set करते हैं, और फिर उस नए object को return करते हैं। + +फिर हम test कर सकते हैं कि इसका उपयोग हुआ है। + +## `.env` file पढ़ना { #reading-a-env-file } + +अगर आपके पास कई settings हैं जो संभवतः बहुत बदलती हैं, शायद अलग-अलग environments में, तो उन्हें एक file में रखना और फिर वहाँ से ऐसे पढ़ना उपयोगी हो सकता है जैसे वे environment variables हों। + +यह practice इतनी आम है कि इसका एक नाम है, ये environment variables आम तौर पर `.env` file में रखे जाते हैं, और file को "dotenv" कहा जाता है। + +/// tip | सुझाव + +dot (`.`) से शुरू होने वाली file Unix-like systems, जैसे Linux और macOS में hidden file होती है। + +लेकिन dotenv file का वास्तव में वही exact filename होना ज़रूरी नहीं है। + +/// + +Pydantic के पास external library का उपयोग करके इस प्रकार की files से पढ़ने के लिए support है। आप [Pydantic Settings: Dotenv (.env) support](https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support) पर और पढ़ सकते हैं। + +/// tip | सुझाव + +इसे काम करने के लिए, आपको `pip install python-dotenv` करना होगा। + +/// + +### `.env` file { #the-env-file } + +आपके पास इस तरह की `.env` file हो सकती है: + +```bash +ADMIN_EMAIL="deadpool@example.com" +APP_NAME="ChimichangApp" +``` + +### `.env` से settings पढ़ें { #read-settings-from-env } + +और फिर अपनी `config.py` को update करें: + +{* ../../docs_src/settings/app03_an_py310/config.py hl[9] *} + +/// tip | सुझाव + +`model_config` attribute केवल Pydantic configuration के लिए उपयोग किया जाता है। आप [Pydantic: Concepts: Configuration](https://docs.pydantic.dev/latest/concepts/config/) पर और पढ़ सकते हैं। + +/// + +यहाँ हम आपकी Pydantic `Settings` class के अंदर config `env_file` define करते हैं, और value को उस dotenv file के filename पर set करते हैं जिसे हम उपयोग करना चाहते हैं। + +### `lru_cache` के साथ `Settings` को केवल एक बार बनाना { #creating-the-settings-only-once-with-lru-cache } + +Disk से file पढ़ना सामान्यतः costly (slow) operation होता है, इसलिए आप शायद इसे केवल एक बार करना चाहेंगे और फिर हर request के लिए पढ़ने के बजाय उसी settings object को reuse करना चाहेंगे। + +लेकिन हर बार जब हम करते हैं: + +```Python +Settings() +``` + +तो एक नया `Settings` object बनेगा, और बनते समय यह `.env` file को फिर से पढ़ेगा। + +अगर dependency function बस ऐसी होती: + +```Python +def get_settings(): + return Settings() +``` + +तो हम हर request के लिए वह object बनाते, और हर request के लिए `.env` file पढ़ते। ⚠️ + +लेकिन क्योंकि हम ऊपर `@lru_cache` decorator इस्तेमाल कर रहे हैं, `Settings` object केवल एक बार बनाया जाएगा, पहली बार जब इसे call किया जाएगा। ✔️ + +{* ../../docs_src/settings/app03_an_py310/main.py hl[1,11] *} + +फिर अगले requests के लिए dependencies में `get_settings()` की किसी भी बाद की call पर, `get_settings()` के internal code को execute करने और नया `Settings` object बनाने के बजाय, यह वही object return करेगा जो पहली call पर return किया गया था, बार-बार। + +#### `lru_cache` Technical Details { #lru-cache-technical-details } + +`@lru_cache` जिस function को decorate करता है उसे modify करता है ताकि वह हर बार function का code execute करके फिर से compute करने के बजाय वही value return करे जो पहली बार return की गई थी। + +इसलिए, उसके नीचे वाला function arguments के प्रत्येक combination के लिए एक बार execute होगा। और फिर उन arguments के प्रत्येक combination द्वारा return की गई values बार-बार उपयोग की जाएँगी, जब भी function को ठीक उसी arguments combination के साथ call किया जाएगा। + +उदाहरण के लिए, अगर आपके पास एक function है: + +```Python +@lru_cache +def say_hi(name: str, salutation: str = "Ms."): + return f"Hello {salutation} {name}" +``` + +आपका program इस तरह execute हो सकता है: + +```mermaid +sequenceDiagram + +participant code as Code +participant function as say_hi() +participant execute as Execute function + + rect rgba(0, 255, 0, .1) + code ->> function: say_hi(name="Camila") + function ->> execute: execute function code + execute ->> code: return the result + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Camila") + function ->> code: return stored result + end + + rect rgba(0, 255, 0, .1) + code ->> function: say_hi(name="Rick") + function ->> execute: execute function code + execute ->> code: return the result + end + + rect rgba(0, 255, 0, .1) + code ->> function: say_hi(name="Rick", salutation="Mr.") + function ->> execute: execute function code + execute ->> code: return the result + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Rick") + function ->> code: return stored result + end + + rect rgba(0, 255, 255, .1) + code ->> function: say_hi(name="Camila") + function ->> code: return stored result + end +``` + +हमारी dependency `get_settings()` के case में, function कोई arguments भी नहीं लेती, इसलिए यह हमेशा वही value return करती है। + +इस तरह, यह लगभग ऐसे behave करती है जैसे यह बस एक global variable हो। लेकिन क्योंकि यह dependency function का उपयोग करती है, इसलिए हम testing के लिए इसे आसानी से override कर सकते हैं। + +`@lru_cache` `functools` का हिस्सा है, जो Python की standard library का हिस्सा है। आप इसके बारे में [Python docs for `@lru_cache`](https://docs.python.org/3/library/functools.html#functools.lru_cache) में और पढ़ सकते हैं। + +## Recap { #recap } + +आप अपनी application की settings या configurations को handle करने के लिए Pydantic Settings का उपयोग कर सकते हैं, Pydantic models की पूरी power के साथ। + +* Dependency का उपयोग करके आप testing को सरल बना सकते हैं। +* आप इसके साथ `.env` files का उपयोग कर सकते हैं। +* `@lru_cache` का उपयोग करने से आप हर request के लिए dotenv file को बार-बार पढ़ने से बच सकते हैं, साथ ही testing के दौरान इसे override करने की अनुमति भी मिलती है। diff --git a/docs/hi/docs/advanced/stream-data.md b/docs/hi/docs/advanced/stream-data.md new file mode 100644 index 000000000..f1dd15de5 --- /dev/null +++ b/docs/hi/docs/advanced/stream-data.md @@ -0,0 +1,117 @@ +# Data Stream करें { #stream-data } + +अगर आप ऐसा data stream करना चाहते हैं जिसे JSON के रूप में संरचित किया जा सके, तो आपको [JSON Lines Stream करें](../tutorial/stream-json-lines.md)। + +लेकिन अगर आप **शुद्ध binary data** या strings stream करना चाहते हैं, तो यह आप ऐसे कर सकते हैं। + +/// note | नोट + +FastAPI 0.134.0 में जोड़ा गया। + +/// + +## उपयोग के मामले { #use-cases } + +आप इसका उपयोग तब कर सकते हैं जब आप शुद्ध strings stream करना चाहते हों, उदाहरण के लिए सीधे किसी **AI LLM** service के output से। + +आप इसका उपयोग **बड़ी binary files** stream करने के लिए भी कर सकते हैं, जहाँ आप data के प्रत्येक chunk को पढ़ते समय stream करते हैं, बिना पूरे data को एक साथ memory में पढ़े। + +आप इसी तरह **video** या **audio** भी stream कर सकते हैं, यह process और send करते समय generate भी किया जा सकता है। + +## `yield` के साथ एक `StreamingResponse` { #a-streamingresponse-with-yield } + +अगर आप अपने *path operation function* में `response_class=StreamingResponse` declare करते हैं, तो आप data के प्रत्येक chunk को क्रम से भेजने के लिए `yield` का उपयोग कर सकते हैं। + +{* ../../docs_src/stream_data/tutorial001_py310.py ln[1:23] hl[20,23] *} + +FastAPI data के प्रत्येक chunk को `StreamingResponse` को जैसा है वैसा ही देगा, यह उसे JSON या किसी समान चीज़ में convert करने की कोशिश नहीं करेगा। + +### Non-async *path operation functions* { #non-async-path-operation-functions } + +आप regular `def` functions (`async` के बिना) का भी उपयोग कर सकते हैं, और उसी तरह `yield` का उपयोग कर सकते हैं। + +{* ../../docs_src/stream_data/tutorial001_py310.py ln[26:29] hl[27] *} + +### Annotation नहीं { #no-annotation } + +Streaming binary data के लिए आपको return type annotation declare करने की वास्तव में आवश्यकता नहीं है। + +क्योंकि FastAPI data को Pydantic के साथ JSON में convert करने या किसी भी तरह serialize करने की कोशिश नहीं करेगा, इस मामले में type annotation केवल आपके editor और tools के उपयोग के लिए है, FastAPI इसका उपयोग नहीं करेगा। + +{* ../../docs_src/stream_data/tutorial001_py310.py ln[32:35] hl[33] *} + +इसका मतलब यह भी है कि `StreamingResponse` के साथ आपके पास type annotations से स्वतंत्र होकर data bytes को ठीक वैसे produce और encode करने की **स्वतंत्रता** और **ज़िम्मेदारी** है, जैसे उन्हें भेजा जाना चाहिए। 🤓 + +### Bytes Stream करें { #stream-bytes } + +मुख्य उपयोग मामलों में से एक strings के बजाय `bytes` stream करना होगा, और आप निश्चित रूप से ऐसा कर सकते हैं। + +{* ../../docs_src/stream_data/tutorial001_py310.py ln[44:47] hl[47] *} + +## एक Custom `PNGStreamingResponse` { #a-custom-pngstreamingresponse } + +ऊपर के उदाहरणों में, data bytes stream किए गए थे, लेकिन response में `Content-Type` header नहीं था, इसलिए client को पता नहीं था कि उसे किस प्रकार का data मिल रहा है। + +आप `StreamingResponse` की एक custom sub-class बना सकते हैं जो `Content-Type` header को उस प्रकार के data पर set करती है जिसे आप stream कर रहे हैं। + +उदाहरण के लिए, आप एक `PNGStreamingResponse` बना सकते हैं जो `media_type` attribute का उपयोग करके `Content-Type` header को `image/png` पर set करता है: + +{* ../../docs_src/stream_data/tutorial002_py310.py ln[6,19:20] hl[20] *} + +फिर आप अपने *path operation function* में `response_class=PNGStreamingResponse` में इस नई class का उपयोग कर सकते हैं: + +{* ../../docs_src/stream_data/tutorial002_py310.py ln[23:27] hl[23] *} + +### एक File का Simulation करें { #simulate-a-file } + +इस उदाहरण में, हम `io.BytesIO` के साथ एक file simulate कर रहे हैं, जो एक file-like object है जो केवल memory में रहता है, लेकिन हमें वही interface उपयोग करने देता है। + +उदाहरण के लिए, हम इसके contents consume करने के लिए इस पर iterate कर सकते हैं, जैसे हम किसी file के साथ कर सकते हैं। + +{* ../../docs_src/stream_data/tutorial002_py310.py ln[1:27] hl[3,12:13,25] *} + +/// note | तकनीकी विवरण + +अन्य दो variables, `image_base64` और `binary_image`, Base64 में encoded एक image हैं, और फिर bytes में convert किए गए हैं, ताकि फिर उन्हें `io.BytesIO` को pass किया जा सके। + +सिर्फ इसलिए ताकि इस उदाहरण के लिए यह उसी file में रह सके और आप इसे copy करके जैसा है वैसा ही run कर सकें। 🥚 + +/// + +`with` block का उपयोग करके, हम यह सुनिश्चित करते हैं कि generator function (`yield` वाला function) पूरा होने के बाद file-like object बंद हो जाए। यानी, response भेजना पूरा होने के बाद। + +इस विशिष्ट उदाहरण में यह उतना महत्वपूर्ण नहीं होगा क्योंकि यह एक fake in-memory file है (`io.BytesIO` के साथ), लेकिन एक वास्तविक file के साथ, यह सुनिश्चित करना महत्वपूर्ण होगा कि इसके साथ काम पूरा होने के बाद file बंद हो जाए। + +### Files और Async { #files-and-async } + +अधिकांश मामलों में, file-like objects default रूप से async और await के साथ compatible नहीं होते। + +उदाहरण के लिए, उनके पास `await file.read()` या `async for chunk in file` नहीं होता। + +और कई मामलों में, उन्हें पढ़ना एक blocking operation होगा (जो event loop को block कर सकता है), क्योंकि उन्हें disk या network से पढ़ा जाता है। + +/// note | नोट + +ऊपर दिया गया उदाहरण वास्तव में एक exception है, क्योंकि `io.BytesIO` object पहले से memory में है, इसलिए उसे पढ़ना किसी चीज़ को block नहीं करेगा। + +लेकिन कई मामलों में किसी file या file-like object को पढ़ना block करेगा। + +/// + +event loop को block करने से बचने के लिए, आप बस *path operation function* को `async def` के बजाय regular `def` के साथ declare कर सकते हैं, इस तरह FastAPI इसे main loop को block करने से बचाने के लिए threadpool worker पर run करेगा। + +{* ../../docs_src/stream_data/tutorial002_py310.py ln[30:34] hl[31] *} + +/// tip | सुझाव + +अगर आपको किसी async function के अंदर से blocking code call करना हो, या किसी blocking function के अंदर से async function call करना हो, तो आप [Asyncer](https://asyncer.tiangolo.com) का उपयोग कर सकते हैं, जो FastAPI की एक sibling library है। + +/// + +### `yield from` { #yield-from } + +जब आप किसी चीज़ पर iterate कर रहे हों, जैसे किसी file-like object पर, और फिर प्रत्येक item के लिए `yield` कर रहे हों, तो आप प्रत्येक item को सीधे yield करने और `for` loop को skip करने के लिए `yield from` का भी उपयोग कर सकते हैं। + +यह FastAPI के लिए विशेष नहीं है, यह सिर्फ Python है, लेकिन यह जानने लायक एक अच्छा trick है। 😎 + +{* ../../docs_src/stream_data/tutorial002_py310.py ln[37:40] hl[40] *} diff --git a/docs/hi/docs/advanced/strict-content-type.md b/docs/hi/docs/advanced/strict-content-type.md new file mode 100644 index 000000000..a63081024 --- /dev/null +++ b/docs/hi/docs/advanced/strict-content-type.md @@ -0,0 +1,88 @@ +# सख्त Content-Type जाँच { #strict-content-type-checking } + +default रूप से, **FastAPI** JSON request bodies के लिए सख्त `Content-Type` header जाँच का उपयोग करता है, इसका मतलब है कि body को JSON के रूप में parse करने के लिए JSON requests में वैध `Content-Type` header (जैसे `application/json`) **होना ही चाहिए**। + +## CSRF जोखिम { #csrf-risk } + +यह default व्यवहार एक बहुत विशिष्ट परिस्थिति में **Cross-Site Request Forgery (CSRF)** हमलों के एक वर्ग से सुरक्षा प्रदान करता है। + +ये हमले इस बात का फायदा उठाते हैं कि browsers scripts को बिना कोई CORS preflight check किए requests भेजने देते हैं, जब वे: + +* `Content-Type` header नहीं रखते (जैसे `Blob` body के साथ `fetch()` का उपयोग करना) +* और कोई authentication credentials नहीं भेजते। + +इस प्रकार का हमला मुख्य रूप से तब relevant होता है जब: + +* application स्थानीय रूप से चल रही हो (जैसे `localhost` पर) या किसी internal network में +* और application में कोई authentication न हो, वह यह मानती हो कि उसी network से आने वाली कोई भी request भरोसेमंद हो सकती है। + +## उदाहरण हमला { #example-attack } + +कल्पना करें कि आप एक local AI agent चलाने का तरीका बनाते हैं। + +यह यहाँ एक API प्रदान करता है + +``` +http://localhost:8000/v1/agents/multivac +``` + +यहाँ एक frontend भी है + +``` +http://localhost:8000 +``` + +/// tip | सुझाव + +ध्यान दें कि दोनों का host समान है। + +/// + +फिर frontend का उपयोग करके आप AI agent से अपनी ओर से काम करवा सकते हैं। + +क्योंकि यह **स्थानीय रूप से** चल रहा है, और खुले internet पर नहीं है, आप **कोई authentication setup न करने** का निर्णय लेते हैं, बस local network तक access पर भरोसा करते हुए। + +फिर आपके users में से कोई इसे install करके locally चला सकता है। + +फिर वे कोई malicious website खोल सकते हैं, जैसे कुछ इस तरह + +``` +https://evilhackers.example.com +``` + +और वह malicious website `Blob` body के साथ `fetch()` का उपयोग करके local API पर requests भेजती है + +``` +http://localhost:8000/v1/agents/multivac +``` + +भले ही malicious website और local app का host अलग हो, browser CORS preflight request trigger नहीं करेगा क्योंकि: + +* यह बिना किसी authentication के चल रहा है, इसे कोई credentials भेजने की जरूरत नहीं है। +* browser को लगता है कि यह JSON नहीं भेज रहा है (`Content-Type` header गायब होने के कारण)। + +फिर malicious website local AI agent से user के ex-boss को गुस्से भरे messages भेजवा सकती है... या उससे भी बुरा। 😅 + +## खुला Internet { #open-internet } + +अगर आपकी app खुले internet पर है, तो आप "network पर भरोसा" नहीं करेंगे और किसी को भी बिना authentication के privileged requests भेजने नहीं देंगे। + +Attackers सीधे आपकी API पर requests भेजने के लिए script चला सकते हैं, browser interaction की कोई जरूरत नहीं, इसलिए आप शायद पहले से ही किसी भी privileged endpoints को secure कर रहे होंगे। + +उस स्थिति में **यह हमला / जोखिम आप पर लागू नहीं होता**। + +यह जोखिम और हमला मुख्य रूप से तब relevant होता है जब app **local network** पर चलती है और वही **एकमात्र मानी गई सुरक्षा** होती है। + +## Content-Type के बिना Requests की अनुमति देना { #allowing-requests-without-content-type } + +अगर आपको ऐसे clients को support करना है जो `Content-Type` header नहीं भेजते, तो आप `strict_content_type=False` set करके strict checking disable कर सकते हैं: + +{* ../../docs_src/strict_content_type/tutorial001_py310.py hl[4] *} + +इस setting के साथ, जिन requests में `Content-Type` header नहीं होगा, उनकी body JSON के रूप में parse की जाएगी, जो FastAPI के पुराने versions जैसा ही व्यवहार है। + +/// note | नोट + +यह व्यवहार और configuration FastAPI 0.132.0 में जोड़ा गया था। + +/// diff --git a/docs/hi/docs/advanced/sub-applications.md b/docs/hi/docs/advanced/sub-applications.md new file mode 100644 index 000000000..3f4ea6da7 --- /dev/null +++ b/docs/hi/docs/advanced/sub-applications.md @@ -0,0 +1,67 @@ +# Sub Applications - Mounts { #sub-applications-mounts } + +अगर आपको दो स्वतंत्र FastAPI applications चाहिए, जिनका अपना स्वतंत्र OpenAPI और अपनी docs UIs हों, तो आप एक मुख्य app रख सकते हैं और एक (या अधिक) sub-application(s) को "mount" कर सकते हैं। + +## **FastAPI** application को Mount करना { #mounting-a-fastapi-application } + +"Mounting" का मतलब है किसी विशिष्ट path में पूरी तरह "स्वतंत्र" application जोड़ना, जो फिर उस path के अंतर्गत सब कुछ handle करने का ध्यान रखता है, उस sub-application में घोषित _path operations_ के साथ। + +### Top-level application { #top-level-application } + +सबसे पहले, मुख्य, top-level **FastAPI** application और उसके *path operations* बनाएँ: + +{* ../../docs_src/sub_applications/tutorial001_py310.py hl[3, 6:8] *} + +### Sub-application { #sub-application } + +फिर, अपनी sub-application और उसके *path operations* बनाएँ। + +यह sub-application बस एक और standard FastAPI application है, लेकिन यही वह है जिसे "mounted" किया जाएगा: + +{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 14:16] *} + +### Sub-application को mount करें { #mount-the-sub-application } + +अपने top-level application, `app`, में sub-application, `subapi`, को mount करें। + +इस मामले में, इसे path `/subapi` पर mount किया जाएगा: + +{* ../../docs_src/sub_applications/tutorial001_py310.py hl[11, 19] *} + +### Automatic API docs देखें { #check-the-automatic-api-docs } + +अब, `fastapi` command चलाएँ: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +और docs को [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) पर खोलें। + +आप मुख्य app के लिए automatic API docs देखेंगे, जिसमें केवल उसके अपने _path operations_ शामिल होंगे: + + + +और फिर, sub-application के लिए docs को [http://127.0.0.1:8000/subapi/docs](http://127.0.0.1:8000/subapi/docs) पर खोलें। + +आप sub-application के लिए automatic API docs देखेंगे, जिसमें केवल उसके अपने _path operations_ शामिल होंगे, सभी सही sub-path prefix `/subapi` के अंतर्गत: + + + +अगर आप दोनों user interfaces में से किसी के साथ interact करने की कोशिश करते हैं, तो वे सही तरह काम करेंगे, क्योंकि browser हर specific app या sub-app से बात कर पाएगा। + +### तकनीकी विवरण: `root_path` { #technical-details-root-path } + +जब आप ऊपर बताए गए तरीके से कोई sub-application mount करते हैं, तो FastAPI sub-application के लिए mount path communicate करने का ध्यान रखेगा, ASGI specification के एक mechanism का उपयोग करके जिसे `root_path` कहा जाता है। + +इस तरह, sub-application को पता होगा कि docs UI के लिए उस path prefix का उपयोग करना है। + +और sub-application की अपनी mounted sub-applications भी हो सकती हैं और सब कुछ सही तरह काम करेगा, क्योंकि FastAPI इन सभी `root_path`s को अपने आप handle करता है। + +आप `root_path` के बारे में और इसे स्पष्ट रूप से कैसे उपयोग करें, यह [Behind a Proxy](behind-a-proxy.md) वाले section में और सीखेंगे। diff --git a/docs/hi/docs/advanced/templates.md b/docs/hi/docs/advanced/templates.md new file mode 100644 index 000000000..b16ef4a6e --- /dev/null +++ b/docs/hi/docs/advanced/templates.md @@ -0,0 +1,126 @@ +# Templates { #templates } + +आप **FastAPI** के साथ अपनी पसंद का कोई भी template engine उपयोग कर सकते हैं। + +एक आम विकल्प Jinja2 है, वही जिसे Flask और अन्य tools उपयोग करते हैं। + +इसे आसानी से configure करने के लिए utilities उपलब्ध हैं जिन्हें आप सीधे अपने **FastAPI** application में उपयोग कर सकते हैं (Starlette द्वारा प्रदान की गई)। + +## Dependencies install करें { #install-dependencies } + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाएँ, उसे activate करें, और `jinja2` install करें: + +
+ +```console +$ pip install jinja2 + +---> 100% +``` + +
+ +## `Jinja2Templates` का उपयोग करना { #using-jinja2templates } + +* `Jinja2Templates` import करें। +* एक `templates` object बनाएँ जिसे आप बाद में फिर से उपयोग कर सकें। +* उस *path operation* में एक `Request` parameter declare करें जो एक template return करेगा। +* आपने जो `templates` बनाया है, उसका उपयोग करके एक `TemplateResponse` render और return करें; template का नाम, request object, और key-value pairs वाली एक "context" dictionary pass करें, जिनका उपयोग Jinja2 template के अंदर किया जाएगा। + +{* ../../docs_src/templates/tutorial001_py310.py hl[4,11,15:18] *} + +/// note | नोट + +FastAPI 0.108.0, Starlette 0.29.0 से पहले, `name` पहला parameter था। + +साथ ही, उससे पहले के versions में, `request` object को Jinja2 के context में key-value pairs के हिस्से के रूप में pass किया जाता था। + +/// + +/// tip | सुझाव + +`response_class=HTMLResponse` declare करने से docs UI यह जान पाएगा कि response HTML होगा। + +/// + +/// note | तकनीकी विवरण + +आप `from starlette.templating import Jinja2Templates` भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.templating` `fastapi.templating` के रूप में प्रदान करता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। `Request` और `StaticFiles` के साथ भी यही है। + +/// + +## Templates लिखना { #writing-templates } + +फिर आप `templates/item.html` पर एक template लिख सकते हैं, उदाहरण के लिए: + +```jinja hl_lines="7" +{!../../docs_src/templates/templates/item.html!} +``` + +### Template Context Values { #template-context-values } + +उस HTML में जिसमें यह शामिल है: + +{% raw %} + +```jinja +Item ID: {{ id }} +``` + +{% endraw %} + +...यह आपके द्वारा pass किए गए "context" `dict` से लिया गया `id` दिखाएगा: + +```Python +{"id": id} +``` + +उदाहरण के लिए, `42` की ID के साथ, यह render होगा: + +```html +Item ID: 42 +``` + +### Template `url_for` Arguments { #template-url-for-arguments } + +आप template के अंदर `url_for()` का भी उपयोग कर सकते हैं, यह arguments के रूप में वही arguments लेता है जो आपके *path operation function* द्वारा उपयोग किए जाते। + +इसलिए, इस section के साथ: + +{% raw %} + +```jinja + +``` + +{% endraw %} + +...यह उसी URL का link generate करेगा जिसे *path operation function* `read_item(id=id)` handle करेगा। + +उदाहरण के लिए, `42` की ID के साथ, यह render होगा: + +```html + +``` + +## Templates और static files { #templates-and-static-files } + +आप template के अंदर `url_for()` का भी उपयोग कर सकते हैं, और इसे, उदाहरण के लिए, उन `StaticFiles` के साथ उपयोग कर सकते हैं जिन्हें आपने `name="static"` के साथ mount किया है। + +```jinja hl_lines="4" +{!../../docs_src/templates/templates/item.html!} +``` + +इस उदाहरण में, यह `static/styles.css` पर मौजूद CSS file से link करेगा: + +```CSS hl_lines="4" +{!../../docs_src/templates/static/styles.css!} +``` + +और क्योंकि आप `StaticFiles` उपयोग कर रहे हैं, वह CSS file आपके **FastAPI** application द्वारा URL `/static/styles.css` पर automatic रूप से serve की जाएगी। + +## अधिक विवरण { #more-details } + +अधिक विवरण के लिए, जिसमें templates को test करना भी शामिल है, [templates पर Starlette के docs](https://www.starlette.dev/templates/) देखें। diff --git a/docs/hi/docs/advanced/testing-dependencies.md b/docs/hi/docs/advanced/testing-dependencies.md new file mode 100644 index 000000000..f8eb0f312 --- /dev/null +++ b/docs/hi/docs/advanced/testing-dependencies.md @@ -0,0 +1,53 @@ +# Overrides के साथ Dependencies की Testing { #testing-dependencies-with-overrides } + +## Testing के दौरान dependencies को override करना { #overriding-dependencies-during-testing } + +कुछ scenarios होते हैं जहाँ आप testing के दौरान किसी dependency को override करना चाह सकते हैं। + +आप नहीं चाहते कि original dependency चले (और न ही उसकी कोई sub-dependencies चलें)। + +इसके बजाय, आप एक अलग dependency देना चाहते हैं जो केवल tests के दौरान इस्तेमाल होगी (संभवतः केवल कुछ खास tests में), और वह एक ऐसा value देगी जिसे वहाँ इस्तेमाल किया जा सके जहाँ original dependency का value इस्तेमाल किया जाता था। + +### Use cases: external service { #use-cases-external-service } + +एक उदाहरण यह हो सकता है कि आपके पास एक external authentication provider हो जिसे आपको call करना हो। + +आप उसे एक token भेजते हैं और वह एक authenticated user लौटाता है। + +यह provider आपसे प्रति request शुल्क ले सकता है, और इसे call करने में tests के लिए एक fixed mock user रखने की तुलना में कुछ अतिरिक्त समय लग सकता है। + +आप शायद external provider को एक बार test करना चाहेंगे, लेकिन हर चलने वाले test के लिए उसे call करना जरूरी नहीं होगा। + +इस मामले में, आप उस dependency को override कर सकते हैं जो उस provider को call करती है, और अपनी tests के लिए एक custom dependency इस्तेमाल कर सकते हैं जो एक mock user लौटाती है। + +### `app.dependency_overrides` attribute का उपयोग करें { #use-the-app-dependency-overrides-attribute } + +इन मामलों के लिए, आपकी **FastAPI** application में एक attribute `app.dependency_overrides` होता है, यह एक simple `dict` है। + +Testing के लिए किसी dependency को override करने के लिए, आप key के रूप में original dependency (एक function) रखते हैं, और value के रूप में अपना dependency override (दूसरा function) रखते हैं। + +और फिर **FastAPI** original dependency की बजाय उस override को call करेगा। + +{* ../../docs_src/dependency_testing/tutorial001_an_py310.py hl[26:27,30] *} + +/// tip | सुझाव + +आप अपनी **FastAPI** application में कहीं भी इस्तेमाल की गई dependency के लिए dependency override set कर सकते हैं। + +Original dependency किसी *path operation function*, किसी *path operation decorator* (जब आप return value का उपयोग नहीं करते), किसी `.include_router()` call आदि में इस्तेमाल हो सकती है। + +FastAPI फिर भी उसे override कर पाएगा। + +/// + +फिर आप `app.dependency_overrides` को एक खाली `dict` पर set करके अपने overrides reset कर सकते हैं (उन्हें हटा सकते हैं): + +```Python +app.dependency_overrides = {} +``` + +/// tip | सुझाव + +यदि आप किसी dependency को केवल कुछ tests के दौरान override करना चाहते हैं, तो आप test की शुरुआत में (test function के अंदर) override set कर सकते हैं और अंत में (test function के अंत में) उसे reset कर सकते हैं। + +/// diff --git a/docs/hi/docs/advanced/testing-events.md b/docs/hi/docs/advanced/testing-events.md new file mode 100644 index 000000000..3f09fe193 --- /dev/null +++ b/docs/hi/docs/advanced/testing-events.md @@ -0,0 +1,12 @@ +# Testing Events: lifespan और startup - shutdown { #testing-events-lifespan-and-startup-shutdown } + +जब आपको अपने tests में `lifespan` चलाने की ज़रूरत हो, तो आप `with` statement के साथ `TestClient` का उपयोग कर सकते हैं: + +{* ../../docs_src/app_testing/tutorial004_py310.py hl[9:15,18,27:28,30:32,41:43] *} + + +आप इसके बारे में अधिक विवरण ["आधिकारिक Starlette documentation site में tests में lifespan चलाना।"](https://www.starlette.dev/lifespan/#running-lifespan-in-tests) में पढ़ सकते हैं। + +deprecated `startup` और `shutdown` event के लिए, आप `TestClient` का उपयोग इस प्रकार कर सकते हैं: + +{* ../../docs_src/app_testing/tutorial003_py310.py hl[9:12,20:24] *} diff --git a/docs/hi/docs/advanced/testing-websockets.md b/docs/hi/docs/advanced/testing-websockets.md new file mode 100644 index 000000000..470b079a8 --- /dev/null +++ b/docs/hi/docs/advanced/testing-websockets.md @@ -0,0 +1,13 @@ +# WebSockets की Testing { #testing-websockets } + +आप WebSockets को test करने के लिए उसी `TestClient` का उपयोग कर सकते हैं। + +इसके लिए, आप `TestClient` को एक `with` statement में उपयोग करते हैं, WebSocket से connect करते हुए: + +{* ../../docs_src/app_testing/tutorial002_py310.py hl[27:31] *} + +/// note | नोट + +अधिक जानकारी के लिए, Starlette की documentation में [WebSockets की testing](https://www.starlette.dev/testclient/#testing-websocket-sessions) देखें। + +/// diff --git a/docs/hi/docs/advanced/using-request-directly.md b/docs/hi/docs/advanced/using-request-directly.md new file mode 100644 index 000000000..8110e550f --- /dev/null +++ b/docs/hi/docs/advanced/using-request-directly.md @@ -0,0 +1,56 @@ +# Request को सीधे इस्तेमाल करना { #using-the-request-directly } + +अब तक, आप request के जिन हिस्सों की ज़रूरत है, उन्हें उनके types के साथ declare करते रहे हैं। + +Data लेना: + +* path से parameters के रूप में। +* Headers। +* Cookies। +* आदि। + +और ऐसा करके, **FastAPI** उस data को validate कर रहा है, उसे convert कर रहा है और आपकी API के लिए documentation अपने-आप generate कर रहा है। + +लेकिन ऐसी स्थितियाँ होती हैं जहाँ आपको `Request` object को सीधे access करने की ज़रूरत हो सकती है। + +## `Request` object के बारे में विवरण { #details-about-the-request-object } + +क्योंकि **FastAPI** असल में नीचे से **Starlette** है, जिसके ऊपर कई tools की एक layer है, इसलिए जब ज़रूरत हो, आप Starlette के [`Request`](https://www.starlette.dev/requests/) object को सीधे इस्तेमाल कर सकते हैं। + +इसका मतलब यह भी होगा कि अगर आप `Request` object से सीधे data लेते हैं (उदाहरण के लिए, body पढ़ते हैं), तो FastAPI उसे validate, convert या document नहीं करेगा (OpenAPI के साथ, automatic API user interface के लिए)। + +हालाँकि कोई भी अन्य parameter जो सामान्य रूप से declare किया गया हो (उदाहरण के लिए, Pydantic model के साथ body), वह फिर भी validate, convert, annotate आदि होगा। + +लेकिन कुछ विशिष्ट मामले हैं जहाँ `Request` object लेना उपयोगी होता है। + +## `Request` object को सीधे इस्तेमाल करें { #use-the-request-object-directly } + +मान लीजिए कि आप अपनी *path operation function* के अंदर client का IP address/host लेना चाहते हैं। + +इसके लिए आपको request को सीधे access करना होगा। + +{* ../../docs_src/using_request_directly/tutorial001_py310.py hl[1,7:8] *} + +`Request` type वाले *path operation function* parameter को declare करके, **FastAPI** जान जाएगा कि उस parameter में `Request` pass करना है। + +/// tip | सुझाव + +ध्यान दें कि इस मामले में, हम request parameter के साथ एक path parameter declare कर रहे हैं। + +इसलिए, path parameter extract किया जाएगा, validate किया जाएगा, specified type में convert किया जाएगा और OpenAPI के साथ annotate किया जाएगा। + +इसी तरह, आप किसी भी अन्य parameter को सामान्य रूप से declare कर सकते हैं, और साथ ही `Request` भी प्राप्त कर सकते हैं। + +/// + +## `Request` documentation { #request-documentation } + +आप [`Request` object के बारे में official Starlette documentation site](https://www.starlette.dev/requests/) पर और विवरण पढ़ सकते हैं। + +/// note | तकनीकी विवरण + +आप `from starlette.requests import Request` भी इस्तेमाल कर सकते हैं। + +**FastAPI** इसे सीधे सिर्फ आपकी, developer की, सुविधा के लिए प्रदान करता है। लेकिन यह सीधे Starlette से आता है। + +/// diff --git a/docs/hi/docs/advanced/websockets.md b/docs/hi/docs/advanced/websockets.md new file mode 100644 index 000000000..9e761a900 --- /dev/null +++ b/docs/hi/docs/advanced/websockets.md @@ -0,0 +1,186 @@ +# WebSockets { #websockets } + +आप **FastAPI** के साथ [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) का उपयोग कर सकते हैं। + +## `websockets` install करें { #install-websockets } + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाएँ, उसे activate करें, और `websockets` install करें (एक Python library जो "WebSocket" protocol का उपयोग आसान बनाती है): + +
+ +```console +$ pip install websockets + +---> 100% +``` + +
+ +## WebSockets client { #websockets-client } + +### production में { #in-production } + +आपके production system में, संभवतः आपके पास React, Vue.js या Angular जैसे आधुनिक framework से बना frontend होगा। + +और अपने backend के साथ WebSockets का उपयोग करके संवाद करने के लिए आप संभवतः अपने frontend की utilities का उपयोग करेंगे। + +या आपके पास एक native mobile application हो सकती है जो सीधे native code में आपके WebSocket backend से संवाद करती हो। + +या आपके पास WebSocket endpoint से संवाद करने का कोई और तरीका हो सकता है। + +--- + +लेकिन इस उदाहरण के लिए, हम कुछ JavaScript के साथ एक बहुत सरल HTML document का उपयोग करेंगे, सब कुछ एक लंबी string के अंदर। + +बेशक, यह optimal नहीं है और आप इसे production के लिए उपयोग नहीं करेंगे। + +production में आपके पास ऊपर दिए गए विकल्पों में से एक होगा। + +लेकिन WebSockets के server-side पर ध्यान केंद्रित करने और एक working उदाहरण पाने का यह सबसे सरल तरीका है: + +{* ../../docs_src/websockets_/tutorial001_py310.py hl[2,6:38,41:43] *} + +## एक `websocket` बनाएँ { #create-a-websocket } + +अपने **FastAPI** application में, एक `websocket` बनाएँ: + +{* ../../docs_src/websockets_/tutorial001_py310.py hl[1,46:47] *} + +/// note | तकनीकी विवरण + +आप `from starlette.websockets import WebSocket` का भी उपयोग कर सकते हैं। + +**FastAPI** वही `WebSocket` सीधे उपलब्ध कराता है, सिर्फ़ आपकी सुविधा के लिए, developer के रूप में। लेकिन यह सीधे Starlette से आता है। + +/// + +## messages का await करें और messages भेजें { #await-for-messages-and-send-messages } + +अपने WebSocket route में आप messages के लिए `await` कर सकते हैं और messages भेज सकते हैं। + +{* ../../docs_src/websockets_/tutorial001_py310.py hl[48:52] *} + +आप binary, text, और JSON data receive और send कर सकते हैं। + +## इसे आज़माएँ { #try-it } + +अपना code `main.py` file में रखें और फिर अपना application चलाएँ: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +अपने browser में [http://127.0.0.1:8000](http://127.0.0.1:8000) खोलें। + +आपको ऐसा एक सरल page दिखेगा: + + + +आप input box में messages टाइप कर सकते हैं, और उन्हें भेज सकते हैं: + + + +और WebSockets के साथ आपका **FastAPI** application जवाब देगा: + + + +आप कई messages भेज (और receive कर) सकते हैं: + + + +और वे सभी उसी WebSocket connection का उपयोग करेंगे। + +## `Depends` और अन्य का उपयोग { #using-depends-and-others } + +WebSocket endpoints में आप `fastapi` से import कर सकते हैं और उपयोग कर सकते हैं: + +* `Depends` +* `Security` +* `Cookie` +* `Header` +* `Path` +* `Query` + +वे अन्य FastAPI endpoints/*path operations* की तरह ही काम करते हैं: + +{* ../../docs_src/websockets_/tutorial002_an_py310.py hl[68:69,82] *} + +/// note | नोट + +क्योंकि यह एक WebSocket है, इसलिए `HTTPException` raise करना वास्तव में उचित नहीं है, इसके बजाय हम `WebSocketException` raise करते हैं। + +आप [specification में परिभाषित valid codes](https://tools.ietf.org/html/rfc6455#section-7.4.1) में से एक closing code का उपयोग कर सकते हैं। + +/// + +### dependencies के साथ WebSockets आज़माएँ { #try-the-websockets-with-dependencies } + +अपना application चलाएँ: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +अपने browser में [http://127.0.0.1:8000](http://127.0.0.1:8000) खोलें। + +वहाँ आप सेट कर सकते हैं: + +* path में उपयोग किया गया "Item ID"। +* query parameter के रूप में उपयोग किया गया "Token"। + +/// tip | सुझाव + +ध्यान दें कि query `token` को एक dependency द्वारा handle किया जाएगा। + +/// + +इसके साथ आप WebSocket connect कर सकते हैं और फिर messages भेज और receive कर सकते हैं: + + + +## disconnections और कई clients को handle करना { #handling-disconnections-and-multiple-clients } + +जब WebSocket connection बंद होता है, तो `await websocket.receive_text()` एक `WebSocketDisconnect` exception raise करेगा, जिसे आप इस उदाहरण की तरह catch और handle कर सकते हैं। + +{* ../../docs_src/websockets_/tutorial003_py310.py hl[79:81] *} + +इसे आज़माने के लिए: + +* app को कई browser tabs में खोलें। +* उनसे messages लिखें। +* फिर tabs में से एक को बंद करें। + +इससे `WebSocketDisconnect` exception raise होगा, और बाकी सभी clients को ऐसा message मिलेगा: + +``` +Client #1596980209979 left the chat +``` + +/// tip | सुझाव + +ऊपर दिया गया app एक minimal और सरल उदाहरण है, जो दिखाता है कि कई WebSocket connections को messages कैसे handle और broadcast किए जाएँ। + +लेकिन ध्यान रखें कि, चूँकि सब कुछ memory में, एक ही list में handle किया जाता है, यह केवल तब तक काम करेगा जब तक process चल रहा है, और केवल एक single process के साथ काम करेगा। + +अगर आपको कुछ ऐसा चाहिए जिसे FastAPI के साथ integrate करना आसान हो लेकिन जो अधिक robust हो, Redis, PostgreSQL या अन्य द्वारा supported हो, तो [encode/broadcaster](https://github.com/encode/broadcaster) देखें। + +/// + +## अधिक जानकारी { #more-info } + +विकल्पों के बारे में अधिक जानने के लिए, इनके लिए Starlette का documentation देखें: + +* [`WebSocket` class](https://www.starlette.dev/websockets/)। +* [Class-based WebSocket handling](https://www.starlette.dev/endpoints/#websocketendpoint)। diff --git a/docs/hi/docs/advanced/wsgi.md b/docs/hi/docs/advanced/wsgi.md new file mode 100644 index 000000000..1ea60d4e9 --- /dev/null +++ b/docs/hi/docs/advanced/wsgi.md @@ -0,0 +1,51 @@ +# WSGI शामिल करना - Flask, Django, अन्य { #including-wsgi-flask-django-others } + +आप WSGI applications को mount कर सकते हैं, जैसा आपने [Sub Applications - Mounts](sub-applications.md), [Proxy के पीछे](behind-a-proxy.md) में देखा। + +इसके लिए, आप `WSGIMiddleware` का उपयोग कर सकते हैं और इसे अपनी WSGI application को wrap करने के लिए इस्तेमाल कर सकते हैं, उदाहरण के लिए, Flask, Django, आदि। + +## `WSGIMiddleware` का उपयोग करना { #using-wsgimiddleware } + +/// note | नोट + +इसके लिए `a2wsgi` install करना required है, उदाहरण के लिए `pip install a2wsgi` के साथ। + +/// + +आपको `a2wsgi` से `WSGIMiddleware` import करना होगा। + +फिर WSGI (जैसे Flask) app को middleware के साथ wrap करें। + +और फिर उसे किसी path के अंतर्गत mount करें। + +{* ../../docs_src/wsgi/tutorial001_py310.py hl[1,3,23] *} + +/// note | नोट + +पहले, `fastapi.middleware.wsgi` से `WSGIMiddleware` का उपयोग करने की सलाह दी जाती थी, लेकिन अब यह deprecated है। + +इसके बजाय `a2wsgi` package का उपयोग करने की सलाह दी जाती है। उपयोग वही रहता है। + +बस यह सुनिश्चित करें कि आपके पास `a2wsgi` package install है और आप `a2wsgi` से `WSGIMiddleware` को सही ढंग से import करते हैं। + +/// + +## इसे जाँचें { #check-it } + +अब, path `/v1/` के अंतर्गत हर request को Flask application द्वारा handle किया जाएगा। + +और बाकी को **FastAPI** द्वारा handle किया जाएगा। + +यदि आप इसे run करते हैं और [http://localhost:8000/v1/](http://localhost:8000/v1/) पर जाते हैं, तो आपको Flask से response दिखाई देगा: + +```txt +Hello, World from Flask! +``` + +और यदि आप [http://localhost:8000/v2](http://localhost:8000/v2) पर जाते हैं, तो आपको FastAPI से response दिखाई देगा: + +```JSON +{ + "message": "Hello World" +} +``` diff --git a/docs/hi/docs/deployment/cloud.md b/docs/hi/docs/deployment/cloud.md new file mode 100644 index 000000000..f4c6087d3 --- /dev/null +++ b/docs/hi/docs/deployment/cloud.md @@ -0,0 +1,24 @@ +# Cloud Providers पर FastAPI Deploy करें { #deploy-fastapi-on-cloud-providers } + +आप अपनी FastAPI application deploy करने के लिए वस्तुतः **किसी भी cloud provider** का उपयोग कर सकते हैं। + +अधिकांश मामलों में, मुख्य cloud providers के पास उनके साथ FastAPI deploy करने के लिए guides होती हैं। + +## FastAPI Cloud { #fastapi-cloud } + +**[FastAPI Cloud](https://fastapicloud.com)** को **FastAPI** के पीछे मौजूद उसी author और team ने बनाया है। + +यह न्यूनतम प्रयास के साथ API को **build**, **deploy**, और **access** करने की प्रक्रिया को सरल बनाता है। + +यह FastAPI के साथ apps बनाने वाले उसी **developer experience** को उन्हें cloud पर **deploy** करने में भी लाता है। 🎉 + +FastAPI Cloud, *FastAPI and friends* open source projects का प्राथमिक sponsor और funding provider है। ✨ + +## Cloud Providers - Sponsors { #cloud-providers-sponsors } + +कुछ अन्य cloud providers भी ✨ [**FastAPI को sponsor करते हैं**](https://github.com/sponsors/tiangolo) ✨। 🙇 + +आप उनकी guides का पालन करने और उनकी services आज़माने के लिए उन पर भी विचार कर सकते हैं: + +* [Render](https://docs.render.com/deploy-fastapi?utm_source=deploydoc&utm_medium=referral&utm_campaign=fastapi) +* [Railway](https://docs.railway.com/guides/fastapi?utm_medium=integration&utm_source=docs&utm_campaign=fastapi) diff --git a/docs/hi/docs/deployment/concepts.md b/docs/hi/docs/deployment/concepts.md new file mode 100644 index 000000000..de085e34b --- /dev/null +++ b/docs/hi/docs/deployment/concepts.md @@ -0,0 +1,321 @@ +# Deployments की अवधारणाएँ { #deployments-concepts } + +जब आप एक **FastAPI** application, या वास्तव में किसी भी प्रकार की web API, deploy करते हैं, तो कई अवधारणाएँ होती हैं जिनकी आपको शायद परवाह होगी, और उनका उपयोग करके आप अपनी application को **deploy करने** का **सबसे उपयुक्त** तरीका ढूँढ सकते हैं। + +कुछ महत्वपूर्ण अवधारणाएँ हैं: + +* सुरक्षा - HTTPS +* startup पर चलना +* Restarts +* Replication (चल रहे processes की संख्या) +* Memory +* शुरू करने से पहले के पिछले steps + +हम देखेंगे कि ये **deployments** को कैसे प्रभावित करेंगे। + +अंत में, अंतिम उद्देश्य यह है कि आप अपने **API clients को serve** कर सकें, वह भी ऐसे तरीके से जो **सुरक्षित** हो, **disruptions से बचाए**, और **compute resources** (जैसे remote servers/virtual machines) का यथासंभव कुशलता से उपयोग करे। 🚀 + +मैं यहाँ इन **अवधारणाओं** के बारे में थोड़ा और बताऊँगा, और उम्मीद है कि इससे आपको वह **intuition** मिलेगी जिसकी आपको अपनी API को बहुत अलग-अलग environments में deploy करने का निर्णय लेने के लिए आवश्यकता होगी, संभवतः ऐसे **future** environments में भी जो अभी मौजूद नहीं हैं। + +इन अवधारणाओं पर विचार करके, आप **अपनी खुद की APIs** को deploy करने का सबसे अच्छा तरीका **evaluate और design** कर पाएँगे। + +अगले chapters में, मैं आपको FastAPI applications deploy करने के लिए अधिक **ठोस recipes** दूँगा। + +लेकिन अभी के लिए, आइए इन महत्वपूर्ण **conceptual ideas** को देखें। ये अवधारणाएँ किसी भी अन्य प्रकार की web API पर भी लागू होती हैं। 💡 + +## सुरक्षा - HTTPS { #security-https } + +[HTTPS के बारे में पिछले chapter](https.md) में हमने सीखा कि HTTPS आपकी API के लिए encryption कैसे प्रदान करता है। + +हमने यह भी देखा कि HTTPS सामान्यतः आपके application server से **external** एक component, एक **TLS Termination Proxy**, द्वारा प्रदान किया जाता है। + +और **HTTPS certificates renew** करने का प्रभारी कुछ होना चाहिए, यह वही component हो सकता है या कुछ अलग भी हो सकता है। + +### HTTPS के लिए उदाहरण Tools { #example-tools-for-https } + +TLS Termination Proxy के रूप में आप जिन tools का उपयोग कर सकते हैं, उनमें से कुछ हैं: + +* Traefik + * Certificate renewals को अपने-आप संभालता है ✨ +* Caddy + * Certificate renewals को अपने-आप संभालता है ✨ +* Nginx + * Certificate renewals के लिए Certbot जैसे external component के साथ +* HAProxy + * Certificate renewals के लिए Certbot जैसे external component के साथ +* Nginx जैसे Ingress Controller के साथ Kubernetes + * Certificate renewals के लिए cert-manager जैसे external component के साथ +* Cloud provider द्वारा उनकी services के हिस्से के रूप में internally संभाला गया (नीचे पढ़ें 👇) + +एक और विकल्प यह है कि आप एक **cloud service** का उपयोग कर सकते हैं जो HTTPS setup करने सहित अधिक काम करती है। इसमें कुछ restrictions हो सकती हैं या आपसे अधिक charge लिया जा सकता है, आदि। लेकिन उस स्थिति में, आपको स्वयं TLS Termination Proxy setup नहीं करना पड़ेगा। + +अगले chapters में मैं आपको कुछ ठोस उदाहरण दिखाऊँगा। + +--- + +फिर विचार करने के लिए अगले concepts उस program के बारे में हैं जो आपकी वास्तविक API चला रहा है (जैसे Uvicorn)। + +## Program और Process { #program-and-process } + +हम चल रहे "**process**" के बारे में बहुत बात करेंगे, इसलिए यह स्पष्ट होना उपयोगी है कि इसका क्या अर्थ है, और "**program**" शब्द से इसका क्या अंतर है। + +### Program क्या है { #what-is-a-program } + +**Program** शब्द का उपयोग आम तौर पर कई चीजों का वर्णन करने के लिए किया जाता है: + +* वह **code** जो आप लिखते हैं, **Python files**। +* वह **file** जिसे operating system द्वारा **execute** किया जा सकता है, उदाहरण के लिए: `python`, `python.exe` या `uvicorn`। +* कोई विशेष program जब वह operating system पर **चल रहा** हो, CPU का उपयोग कर रहा हो, और memory में चीजें store कर रहा हो। इसे **process** भी कहा जाता है। + +### Process क्या है { #what-is-a-process } + +**Process** शब्द सामान्यतः अधिक विशिष्ट तरीके से उपयोग किया जाता है, केवल उस चीज़ के लिए जो operating system में चल रही होती है (जैसे ऊपर के अंतिम point में): + +* कोई विशेष program जब वह operating system पर **चल रहा** हो। + * यह न तो file को refer करता है, न code को, यह **विशेष रूप से** उस चीज़ को refer करता है जिसे operating system द्वारा **execute** और manage किया जा रहा है। +* कोई भी program, कोई भी code, **केवल तभी कुछ कर सकता है** जब उसे **execute** किया जा रहा हो। यानी, जब कोई **process चल रहा** हो। +* Process को आपके द्वारा, या operating system द्वारा **terminate** (या "kill") किया जा सकता है। उस point पर, वह चलना/execute होना बंद कर देता है, और वह **अब कुछ नहीं कर सकता**। +* आपके computer पर चल रही प्रत्येक application के पीछे कोई process होता है, प्रत्येक running program, प्रत्येक window, आदि। और computer चालू होने पर सामान्यतः कई processes **एक ही समय में** चल रहे होते हैं। +* **एक ही program** के **multiple processes** एक ही समय में चल सकते हैं। + +यदि आप अपने operating system में "task manager" या "system monitor" (या समान tools) देखते हैं, तो आप उनमें से कई processes चलते हुए देख पाएँगे। + +और, उदाहरण के लिए, आप शायद देखेंगे कि एक ही browser program (Firefox, Chrome, Edge, आदि) को चलाने वाले multiple processes हैं। वे सामान्यतः प्रति tab एक process चलाते हैं, साथ में कुछ अन्य extra processes भी। + + + +--- + +अब जब हम **process** और **program** शब्दों के बीच अंतर जानते हैं, तो deployments के बारे में बात जारी रखते हैं। + +## startup पर चलना { #running-on-startup } + +अधिकांश मामलों में, जब आप एक web API बनाते हैं, तो आप चाहते हैं कि वह **हमेशा चलती रहे**, बिना interruption के, ताकि आपके clients हमेशा उसे access कर सकें। यह निश्चित रूप से तब तक है जब तक आपके पास कोई विशेष कारण न हो कि आप उसे केवल कुछ स्थितियों में ही चलाना चाहते हैं, लेकिन अधिकांश समय आप चाहते हैं कि वह लगातार चलती रहे और **available** रहे। + +### Remote Server में { #in-a-remote-server } + +जब आप एक remote server (एक cloud server, एक virtual machine, आदि) setup करते हैं, तो सबसे सरल चीज़ जो आप कर सकते हैं वह है `fastapi run` (जो Uvicorn का उपयोग करता है) या कुछ समान, manually, ठीक उसी तरह जैसे आप local development करते समय करते हैं। + +और यह काम करेगा और **development के दौरान** उपयोगी होगा। + +लेकिन यदि server से आपका connection खो जाता है, तो **running process** शायद मर जाएगा। + +और यदि server restart होता है (उदाहरण के लिए updates के बाद, या cloud provider से migrations के बाद) तो आप शायद **इसे notice नहीं करेंगे**। और इसके कारण, आपको यह भी पता नहीं चलेगा कि आपको process को manually restart करना है। इसलिए, आपकी API बस dead ही रहेगी। 😱 + +### Startup पर Automatically चलाना { #run-automatically-on-startup } + +सामान्यतः, आप शायद चाहेंगे कि server program (जैसे Uvicorn) server startup पर automatically start हो, और किसी **human intervention** की आवश्यकता के बिना, ताकि आपकी API के साथ हमेशा एक process चल रहा हो (जैसे Uvicorn आपकी FastAPI app चला रहा हो)। + +### अलग Program { #separate-program } + +इसे हासिल करने के लिए, आपके पास सामान्यतः एक **अलग program** होगा जो सुनिश्चित करेगा कि आपकी application startup पर चले। और कई मामलों में, यह यह भी सुनिश्चित करेगा कि अन्य components या applications भी चलें, उदाहरण के लिए, एक database। + +### Startup पर चलाने के लिए उदाहरण Tools { #example-tools-to-run-at-startup } + +इस काम को करने वाले tools के कुछ उदाहरण हैं: + +* Docker +* Kubernetes +* Docker Compose +* Docker in Swarm Mode +* Systemd +* Supervisor +* Cloud provider द्वारा उनकी services के हिस्से के रूप में internally संभाला गया +* अन्य... + +अगले chapters में मैं आपको अधिक ठोस उदाहरण दूँगा। + +## Restarts { #restarts } + +यह सुनिश्चित करने जैसा कि आपकी application startup पर चले, आप शायद यह भी सुनिश्चित करना चाहेंगे कि failures के बाद उसे **restart** किया जाए। + +### हम गलतियाँ करते हैं { #we-make-mistakes } + +हम, मनुष्य के रूप में, हर समय **गलतियाँ** करते हैं। Software में लगभग *हमेशा* अलग-अलग जगहों पर **bugs** छिपे होते हैं। 🐛 + +और हम developers उन bugs को खोजते हुए और नई features implement करते हुए code को बेहतर बनाते रहते हैं (संभवतः नए bugs भी जोड़ते हुए 😅)। + +### छोटे Errors Automatically संभाले जाते हैं { #small-errors-automatically-handled } + +FastAPI के साथ web APIs बनाते समय, यदि हमारे code में कोई error है, तो FastAPI सामान्यतः उसे उस single request तक सीमित रखेगा जिसने error trigger किया। 🛡 + +Client को उस request के लिए **500 Internal Server Error** मिलेगा, लेकिन application पूरी तरह crash होने के बजाय अगली requests के लिए काम करती रहेगी। + +### बड़े Errors - Crashes { #bigger-errors-crashes } + +फिर भी, ऐसे मामले हो सकते हैं जहाँ हम कुछ code लिखते हैं जो **पूरी application को crash** कर देता है, जिससे Uvicorn और Python crash हो जाते हैं। 💥 + +और फिर भी, आप शायद नहीं चाहेंगे कि application केवल इसलिए dead रहे क्योंकि एक जगह error था, आप शायद चाहेंगे कि वह कम से कम उन *path operations* के लिए **चलती रहे** जो broken नहीं हैं। + +### Crash के बाद Restart { #restart-after-crash } + +लेकिन उन मामलों में जहाँ वास्तव में खराब errors running **process** को crash कर देते हैं, आप चाहेंगे कि एक external component process को **restart** करने का प्रभारी हो, कम से कम कुछ बार... + +/// tip | सुझाव + +...हालाँकि यदि पूरी application बस **तुरंत crash** हो रही है तो शायद उसे हमेशा restart करते रहने का कोई अर्थ नहीं है। लेकिन ऐसे मामलों में, आप शायद इसे development के दौरान, या कम से कम deployment के ठीक बाद notice करेंगे। + +तो आइए मुख्य मामलों पर focus करें, जहाँ यह **future** में कुछ विशेष मामलों में पूरी तरह crash हो सकती है, और फिर भी उसे restart करना समझ में आता है। + +/// + +आप शायद चाहेंगे कि आपकी application को restart करने का प्रभारी एक **external component** हो, क्योंकि उस point तक, Uvicorn और Python वाली वही application पहले ही crash हो चुकी होती है, इसलिए उसी app के उसी code में ऐसा कुछ नहीं होता जो इसके बारे में कुछ कर सके। + +### Automatically Restart करने के लिए उदाहरण Tools { #example-tools-to-restart-automatically } + +अधिकांश मामलों में, वही tool जो **startup पर program चलाने** के लिए उपयोग होता है, automatic **restarts** संभालने के लिए भी उपयोग होता है। + +उदाहरण के लिए, इसे ये संभाल सकते हैं: + +* Docker +* Kubernetes +* Docker Compose +* Docker in Swarm Mode +* Systemd +* Supervisor +* Cloud provider द्वारा उनकी services के हिस्से के रूप में internally संभाला गया +* अन्य... + +## Replication - Processes और Memory { #replication-processes-and-memory } + +FastAPI application के साथ, Uvicorn चलाने वाले `fastapi` command जैसे server program का उपयोग करते हुए, उसे **एक process** में एक बार चलाना multiple clients को concurrently serve कर सकता है। + +लेकिन कई मामलों में, आप एक ही समय में कई worker processes चलाना चाहेंगे। + +### Multiple Processes - Workers { #multiple-processes-workers } + +यदि आपके पास single process द्वारा handle किए जा सकने से अधिक clients हैं (उदाहरण के लिए यदि virtual machine बहुत बड़ी नहीं है) और server के CPU में **multiple cores** हैं, तो आप एक ही application के साथ **multiple processes** एक ही समय में चला सकते हैं, और सभी requests को उनके बीच distribute कर सकते हैं। + +जब आप उसी API program के **multiple processes** चलाते हैं, तो उन्हें आम तौर पर **workers** कहा जाता है। + +### Worker Processes और Ports { #worker-processes-and-ports } + +Docs [About HTTPS](https.md) से याद करें कि server में port और IP address के एक combination पर केवल एक process listen कर सकता है? + +यह अभी भी सही है। + +इसलिए, एक ही समय में **multiple processes** रखने में सक्षम होने के लिए, एक **single process port पर listening** होना चाहिए जो फिर communication को किसी तरीके से प्रत्येक worker process तक transmit करे। + +### प्रति Process Memory { #memory-per-process } + +अब, जब program memory में चीजें load करता है, उदाहरण के लिए, किसी variable में machine learning model, या किसी बड़े file की contents किसी variable में, तो वह सब server की **memory (RAM) का थोड़ा हिस्सा consume** करता है। + +और multiple processes सामान्यतः **कोई memory share नहीं करते**। इसका मतलब है कि प्रत्येक running process की अपनी चीजें, variables, और memory होती है। और यदि आप अपने code में बड़ी मात्रा में memory consume कर रहे हैं, तो **प्रत्येक process** उतनी ही memory consume करेगा। + +### Server Memory { #server-memory } + +उदाहरण के लिए, यदि आपका code **1 GB size** वाला Machine Learning model load करता है, तो जब आप अपनी API के साथ एक process चलाते हैं, तो वह कम से कम 1 GB RAM consume करेगा। और यदि आप **4 processes** (4 workers) start करते हैं, तो प्रत्येक 1 GB RAM consume करेगा। इसलिए कुल मिलाकर, आपकी API **4 GB RAM** consume करेगी। + +और यदि आपके remote server या virtual machine में केवल 3 GB RAM है, तो 4 GB से अधिक RAM load करने की कोशिश problems पैदा करेगी। 🚨 + +### Multiple Processes - एक उदाहरण { #multiple-processes-an-example } + +इस उदाहरण में, एक **Manager Process** है जो दो **Worker Processes** start और control करता है। + +यह Manager Process शायद IP में **port** पर listen करने वाला होगा। और यह सभी communication को worker processes तक transmit करेगा। + +वे worker processes वे होंगे जो आपकी application चला रहे होंगे, वे **request** प्राप्त करने और **response** return करने के लिए मुख्य computations करेंगे, और वे RAM में variables में डाली गई कोई भी चीज़ load करेंगे। + + + +और निश्चित रूप से, उसी machine पर आपकी application के अलावा शायद **अन्य processes** भी चल रहे होंगे। + +एक दिलचस्प detail यह है कि प्रत्येक process द्वारा **उपयोग किए गए CPU** का percentage समय के साथ बहुत **बदल** सकता है, लेकिन **memory (RAM)** सामान्यतः कम या ज्यादा **stable** रहती है। + +यदि आपके पास एक API है जो हर बार comparable amount की computations करती है और आपके पास बहुत सारे clients हैं, तो **CPU utilization** शायद *stable भी रहेगा* (लगातार तेजी से ऊपर-नीचे जाने के बजाय)। + +### Replication Tools और Strategies के उदाहरण { #examples-of-replication-tools-and-strategies } + +इसे हासिल करने के कई approaches हो सकते हैं, और मैं अगले chapters में specific strategies के बारे में अधिक बताऊँगा, उदाहरण के लिए Docker और containers के बारे में बात करते समय। + +विचार करने की मुख्य constraint यह है कि **public IP** में **port** को handle करने वाला एक **single** component होना चाहिए। और फिर उसके पास replicated **processes/workers** तक communication **transmit** करने का कोई तरीका होना चाहिए। + +यहाँ कुछ संभावित combinations और strategies हैं: + +* **Uvicorn** `--workers` के साथ + * एक Uvicorn **process manager** **IP** और **port** पर listen करेगा, और यह **multiple Uvicorn worker processes** start करेगा। +* **Kubernetes** और अन्य distributed **container systems** + * **Kubernetes** layer में कुछ **IP** और **port** पर listen करेगा। Replication **multiple containers** रखने से होगी, प्रत्येक में **एक Uvicorn process** चल रहा होगा। +* **Cloud services** जो यह आपके लिए संभालती हैं + * Cloud service शायद **आपके लिए replication handle** करेगी। यह संभवतः आपको **चलाने के लिए process**, या उपयोग करने के लिए **container image** define करने देगी, किसी भी स्थिति में, यह बहुत संभवतः **एक single Uvicorn process** होगा, और cloud service उसे replicate करने की प्रभारी होगी। + +/// tip | सुझाव + +यदि **containers**, Docker, या Kubernetes के बारे में इनमें से कुछ items अभी अधिक समझ में नहीं आते हैं तो चिंता न करें। + +मैं future chapter में container images, Docker, Kubernetes, आदि के बारे में अधिक बताऊँगा: [Containers में FastAPI - Docker](docker.md)। + +/// + +## शुरू करने से पहले के पिछले Steps { #previous-steps-before-starting } + +कई मामले ऐसे होते हैं जहाँ आप अपनी application **start करने से पहले** कुछ steps perform करना चाहते हैं। + +उदाहरण के लिए, आप **database migrations** चलाना चाह सकते हैं। + +लेकिन अधिकांश मामलों में, आप इन steps को केवल **एक बार** perform करना चाहेंगे। + +इसलिए, आप application start करने से पहले उन **previous steps** को perform करने के लिए एक **single process** रखना चाहेंगे। + +और आपको यह सुनिश्चित करना होगा कि उन previous steps को चलाने वाला एक ही process हो, *भले ही* बाद में आप application के लिए **multiple processes** (multiple workers) start करें। यदि वे steps **multiple processes** द्वारा चलाए गए, तो वे उन्हें **parallel** में चलाकर काम को **duplicate** कर देंगे, और यदि steps database migration जैसी delicate चीज़ हैं, तो वे एक-दूसरे के साथ conflicts पैदा कर सकते हैं। + +बेशक, कुछ मामले ऐसे होते हैं जहाँ previous steps को multiple times चलाने में कोई समस्या नहीं होती, उस स्थिति में इसे handle करना बहुत आसान होता है। + +/// tip | सुझाव + +साथ ही, ध्यान रखें कि आपके setup पर निर्भर करते हुए, कुछ मामलों में आपकी application start करने से पहले आपको **शायद किसी previous steps की आवश्यकता भी न हो**। + +उस स्थिति में, आपको इनमें से किसी भी चीज़ की चिंता नहीं करनी होगी। 🤷 + +/// + +### Previous Steps Strategies के उदाहरण { #examples-of-previous-steps-strategies } + +यह इस बात पर **बहुत अधिक निर्भर** करेगा कि आप **अपना system कैसे deploy** करते हैं, और यह शायद programs start करने, restarts handle करने, आदि के तरीके से जुड़ा होगा। + +यहाँ कुछ संभावित ideas हैं: + +* Kubernetes में एक "Init Container" जो आपके app container से पहले चलता है +* एक bash script जो previous steps चलाती है और फिर आपकी application start करती है + * आपको फिर भी *उस* bash script को start/restart करने, errors detect करने, आदि का तरीका चाहिए होगा। + +/// tip | सुझाव + +Containers के साथ ऐसा करने के लिए मैं future chapter में अधिक ठोस उदाहरण दूँगा: [Containers में FastAPI - Docker](docker.md)। + +/// + +## Resource Utilization { #resource-utilization } + +आपके server(s) एक **resource** हैं, जिन्हें आप अपने programs के साथ consume या **utilize** कर सकते हैं, CPUs पर computation time और उपलब्ध RAM memory का उपयोग करके। + +आप system resources का कितना हिस्सा consume/utilize करना चाहते हैं? "बहुत ज्यादा नहीं" सोचना आसान हो सकता है, लेकिन वास्तव में, आप शायद **crash किए बिना जितना संभव हो उतना** consume करना चाहेंगे। + +यदि आप 3 servers के लिए भुगतान कर रहे हैं लेकिन उनकी RAM और CPU का केवल थोड़ा सा उपयोग कर रहे हैं, तो आप शायद **पैसा बर्बाद कर रहे हैं** 💸, और शायद **server की electric power बर्बाद कर रहे हैं** 🌎, आदि। + +उस स्थिति में, केवल 2 servers रखना और उनके resources (CPU, memory, disk, network bandwidth, आदि) का उच्च percentage उपयोग करना बेहतर हो सकता है। + +दूसरी ओर, यदि आपके पास 2 servers हैं और आप उनके **CPU और RAM का 100%** उपयोग कर रहे हैं, तो किसी point पर एक process अधिक memory माँगेगा, और server को disk को "memory" के रूप में उपयोग करना पड़ेगा (जो हजारों गुना धीमा हो सकता है), या वह **crash** भी हो सकता है। या किसी process को कुछ computation करनी हो सकती है और उसे CPU के फिर से free होने तक wait करना पड़ेगा। + +इस मामले में, **एक extra server** लेना और उस पर कुछ processes चलाना बेहतर होगा ताकि उन सभी के पास **पर्याप्त RAM और CPU time** हो। + +यह भी संभावना है कि किसी कारण से आपकी API के usage में **spike** हो। शायद यह viral हो गई, या शायद कुछ अन्य services या bots इसका उपयोग शुरू कर दें। और आप उन मामलों में safe रहने के लिए extra resources रखना चाह सकते हैं। + +आप target करने के लिए एक **arbitrary number** रख सकते हैं, उदाहरण के लिए, resource utilization का **50% से 90% के बीच** कुछ। बात यह है कि ये शायद वे मुख्य चीजें हैं जिन्हें आप measure करना और अपने deployments tweak करने के लिए उपयोग करना चाहेंगे। + +आप अपने server में उपयोग किए गए CPU और RAM या प्रत्येक process द्वारा उपयोग की गई मात्रा देखने के लिए `htop` जैसे simple tools का उपयोग कर सकते हैं। या आप अधिक complex monitoring tools का उपयोग कर सकते हैं, जो servers में distributed हो सकते हैं, आदि। + +## Recap { #recap } + +आपने यहाँ कुछ मुख्य अवधारणाएँ पढ़ी हैं जिन्हें अपनी application को कैसे deploy करना है, यह तय करते समय आपको शायद ध्यान में रखना होगा: + +* सुरक्षा - HTTPS +* startup पर चलना +* Restarts +* Replication (चल रहे processes की संख्या) +* Memory +* शुरू करने से पहले के पिछले steps + +इन ideas को समझना और उन्हें apply करना आपको अपने deployments configure और tweak करते समय कोई भी decisions लेने के लिए आवश्यक intuition देना चाहिए। 🤓 + +अगले sections में, मैं आपको उन संभावित strategies के अधिक ठोस उदाहरण दूँगा जिनका आप पालन कर सकते हैं। 🚀 diff --git a/docs/hi/docs/deployment/docker.md b/docs/hi/docs/deployment/docker.md new file mode 100644 index 000000000..6e8bea03a --- /dev/null +++ b/docs/hi/docs/deployment/docker.md @@ -0,0 +1,618 @@ +# Containers में FastAPI - Docker { #fastapi-in-containers-docker } + +FastAPI applications deploy करते समय एक आम तरीका **Linux container image** बनाना है। यह सामान्यतः [**Docker**](https://www.docker.com/) का उपयोग करके किया जाता है। फिर आप उस container image को कुछ संभावित तरीकों में से किसी एक में deploy कर सकते हैं। + +Linux containers का उपयोग करने के कई लाभ हैं, जिनमें **security**, **replicability**, **simplicity**, और अन्य शामिल हैं। + +/// tip | टिप + +जल्दी में हैं और यह सब पहले से जानते हैं? नीचे दिए गए [`Dockerfile` पर जाएँ 👇](#build-a-docker-image-for-fastapi). + +/// + +
+Dockerfile Preview 👀 + +```Dockerfile +FROM python:3.14 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +COPY ./app /code/app + +CMD ["fastapi", "run", "app/main.py", "--port", "80"] + +# यदि Nginx या Traefik जैसे proxy के पीछे चला रहे हैं तो --proxy-headers जोड़ें +# CMD ["fastapi", "run", "app/main.py", "--port", "80", "--proxy-headers"] +``` + +
+ +## Container क्या है { #what-is-a-container } + +Containers (मुख्य रूप से Linux containers) applications को उनकी सभी dependencies और आवश्यक files सहित package करने का एक बहुत **lightweight** तरीका हैं, जबकि उन्हें उसी system में दूसरे containers (दूसरी applications या components) से isolated रखा जाता है। + +Linux containers host (machine, virtual machine, cloud server, आदि) के उसी Linux kernel का उपयोग करके चलते हैं। इसका मतलब बस इतना है कि वे बहुत lightweight होते हैं (पूरे operating system को emulate करने वाली full virtual machines की तुलना में)। + +इस तरह, containers **कम resources** consume करते हैं, लगभग उतने ही जितने processes को सीधे चलाने में लगते हैं (virtual machine बहुत अधिक consume करेगी)। + +Containers के अपने **isolated** running processes (आमतौर पर सिर्फ एक process), file system, और network भी होते हैं, जिससे deployment, security, development, आदि सरल हो जाते हैं। + +## Container Image क्या है { #what-is-a-container-image } + +एक **container** एक **container image** से चलाया जाता है। + +Container image उन सभी files, environment variables, और default command/program का **static** version होता है जो container में मौजूद होना चाहिए। यहाँ **Static** का मतलब है कि container **image** चल नहीं रही है, execute नहीं हो रही है, यह सिर्फ packaged files और metadata है। + +"**container image**" जो stored static contents है, उसके विपरीत, "**container**" सामान्यतः running instance को संदर्भित करता है, वह चीज़ जो **execute** हो रही है। + +जब **container** start होकर running होता है (**container image** से start किया गया), तो यह files, environment variables, आदि बना या बदल सकता है। वे बदलाव केवल उस container में मौजूद होंगे, लेकिन underlying container image में persist नहीं होंगे (disk पर save नहीं होंगे)। + +Container image की तुलना **program** file और contents से की जा सकती है, जैसे `python` और कोई file `main.py`। + +और **container** स्वयं (**container image** के विपरीत) image का वास्तविक running instance है, जिसकी तुलना **process** से की जा सकती है। वास्तव में, container केवल तब running होता है जब उसमें **process running** हो (और आमतौर पर यह केवल एक single process होता है)। जब उसमें कोई process running नहीं रहता, तो container stop हो जाता है। + +## Container Images { #container-images } + +Docker **container images** और **containers** बनाने और manage करने के मुख्य tools में से एक रहा है। + +और एक public [Docker Hub](https://hub.docker.com/) है जिसमें कई tools, environments, databases, और applications के लिए पहले से बनी **official container images** हैं। + +उदाहरण के लिए, एक official [Python Image](https://hub.docker.com/_/python) है। + +और databases जैसी अलग-अलग चीज़ों के लिए कई अन्य images हैं, उदाहरण के लिए: + +* [PostgreSQL](https://hub.docker.com/_/postgres) +* [MySQL](https://hub.docker.com/_/mysql) +* [MongoDB](https://hub.docker.com/_/mongo) +* [Redis](https://hub.docker.com/_/redis), आदि। + +पहले से बनी container image का उपयोग करके अलग-अलग tools को **combine** और use करना बहुत आसान है। उदाहरण के लिए, नया database try करने के लिए। अधिकतर मामलों में, आप **official images** का उपयोग कर सकते हैं, और उन्हें सिर्फ environment variables से configure कर सकते हैं। + +इस तरह, कई मामलों में आप containers और Docker के बारे में सीख सकते हैं और उस knowledge को कई अलग-अलग tools और components के साथ reuse कर सकते हैं। + +तो, आप अलग-अलग चीज़ों के साथ **multiple containers** चलाएँगे, जैसे database, Python application, React frontend application वाला web server, और उन्हें उनके internal network के माध्यम से connect करेंगे। + +सभी container management systems (जैसे Docker या Kubernetes) में ये networking features integrated होते हैं। + +## Containers और Processes { #containers-and-processes } + +एक **container image** सामान्यतः अपने metadata में default program या command शामिल करती है जिसे **container** start होने पर run किया जाना चाहिए, और उस program को pass किए जाने वाले parameters। यह बहुत हद तक वैसा ही है जैसा command line में होता। + +जब कोई **container** start होता है, तो वह उस command/program को run करेगा (हालाँकि आप इसे override करके कोई अलग command/program run करवा सकते हैं)। + +Container तब तक running रहता है जब तक **main process** (command या program) running रहता है। + +Container में सामान्यतः **single process** होता है, लेकिन main process से subprocesses start करना भी संभव है, और इस तरह उसी container में **multiple processes** हो सकते हैं। + +लेकिन **कम से कम एक running process** के बिना running container होना संभव नहीं है। यदि main process stop हो जाता है, तो container stop हो जाता है। + +## FastAPI के लिए Docker Image बनाएँ { #build-a-docker-image-for-fastapi } + +ठीक है, अब कुछ बनाते हैं! 🚀 + +मैं आपको दिखाऊँगा कि **official Python** image पर आधारित FastAPI के लिए **scratch से** **Docker image** कैसे बनाएँ। + +यह वही है जो आप **अधिकतर मामलों** में करना चाहेंगे, उदाहरण के लिए: + +* **Kubernetes** या समान tools का उपयोग करते समय +* **Raspberry Pi** पर चलाते समय +* ऐसा cloud service उपयोग करते समय जो आपके लिए container image run करेगा, आदि। + +### Package Requirements { #package-requirements } + +आपकी application के लिए **package requirements** सामान्यतः किसी file में होंगे। + +यह मुख्य रूप से उस tool पर निर्भर करेगा जिसका उपयोग आप उन requirements को **install** करने के लिए करते हैं। + +इसे करने का सबसे आम तरीका है `requirements.txt` file रखना, जिसमें package names और उनके versions हों, प्रति line एक। + +आप versions की ranges set करने के लिए निश्चित रूप से वही ideas उपयोग करेंगे जो आपने [FastAPI versions के बारे में](versions.md) में पढ़े हैं। + +उदाहरण के लिए, आपका `requirements.txt` ऐसा दिख सकता है: + +``` +fastapi[standard]>=0.113.0,<0.114.0 +pydantic>=2.7.0,<3.0.0 +``` + +और आप सामान्यतः उन package dependencies को `pip` से install करेंगे, उदाहरण के लिए: + +
+ +```console +$ pip install -r requirements.txt +---> 100% +Successfully installed fastapi pydantic +``` + +
+ +/// note | नोट + +Package dependencies define और install करने के लिए अन्य formats और tools भी हैं। + +/// + +### **FastAPI** Code बनाएँ { #create-the-fastapi-code } + +* एक `app` directory बनाएँ और उसमें enter करें। +* एक खाली file `__init__.py` बनाएँ। +* एक `main.py` file बनाएँ जिसमें हो: + +```Python +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: str | None = None): + return {"item_id": item_id, "q": q} +``` + +### Dockerfile { #dockerfile } + +अब उसी project directory में एक file `Dockerfile` बनाएँ जिसमें हो: + +```{ .dockerfile .annotate } +# (1)! +FROM python:3.14 + +# (2)! +WORKDIR /code + +# (3)! +COPY ./requirements.txt /code/requirements.txt + +# (4)! +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (5)! +COPY ./app /code/app + +# (6)! +CMD ["fastapi", "run", "app/main.py", "--port", "80"] +``` + +1. Official Python base image से start करें। + +2. Current working directory को `/code` पर set करें। + + यही वह जगह है जहाँ हम `requirements.txt` file और `app` directory रखेंगे। + +3. Requirements वाली file को `/code` directory में copy करें। + + पहले **केवल** requirements वाली file copy करें, बाकी code नहीं। + + क्योंकि यह file **अक्सर change नहीं होती**, Docker इसे detect करेगा और इस step के लिए **cache** का उपयोग करेगा, जिससे अगले step के लिए भी cache enable हो जाएगा। + +4. Requirements file में package dependencies install करें। + + `--no-cache-dir` option `pip` को बताता है कि downloaded packages को locally save न करे, क्योंकि यह केवल तब उपयोगी होता जब `pip` को उन्हीं packages को install करने के लिए फिर से run किया जाना हो, लेकिन containers के साथ काम करते समय ऐसा मामला नहीं है। + + /// note | नोट + + `--no-cache-dir` केवल `pip` से संबंधित है, इसका Docker या containers से कोई संबंध नहीं है। + + /// + + `--upgrade` option `pip` को बताता है कि यदि packages पहले से installed हैं तो उन्हें upgrade करे। + + क्योंकि पिछला step जिसमें file copy की गई थी **Docker cache** द्वारा detect किया जा सकता है, इसलिए यह step भी available होने पर **Docker cache का उपयोग** करेगा। + + इस step में cache का उपयोग development के दौरान image बार-बार build करते समय आपका बहुत **समय** **बचाएगा**, हर बार सभी dependencies को **download और install** करने के बजाय। + +5. `./app` directory को `/code` directory के अंदर copy करें। + + क्योंकि इसमें सारा code है, और यही वह चीज़ है जो **सबसे अधिक बार change होती** है, Docker **cache** इस या किसी भी **आगे के steps** के लिए आसानी से उपयोग नहीं होगा। + + इसलिए, container image build times optimize करने के लिए इसे `Dockerfile` के **end के पास** रखना महत्वपूर्ण है। + +6. **command** set करें ताकि `fastapi run` उपयोग हो, जो अंदर से Uvicorn का उपयोग करता है। + + `CMD` strings की list लेता है, इन strings में से प्रत्येक वही है जिसे आप command line में spaces से अलग करके type करेंगे। + + यह command **current working directory** से run होगा, वही `/code` directory जिसे आपने ऊपर `WORKDIR /code` से set किया है। + +/// tip | टिप + +Code में प्रत्येक number bubble पर click करके review करें कि हर line क्या करती है। 👆 + +/// + +/// warning | चेतावनी + +नीचे समझाए अनुसार, `CMD` instruction का **exec form** **हमेशा** use करना सुनिश्चित करें। + +/// + +#### `CMD` उपयोग करें - Exec Form { #use-cmd-exec-form } + +[`CMD`](https://docs.docker.com/reference/dockerfile/#cmd) Docker instruction दो forms में लिखा जा सकता है: + +✅ **Exec** form: + +```Dockerfile +# ✅ यह करें +CMD ["fastapi", "run", "app/main.py", "--port", "80"] +``` + +⛔️ **Shell** form: + +```Dockerfile +# ⛔️ यह न करें +CMD fastapi run app/main.py --port 80 +``` + +यह सुनिश्चित करने के लिए कि FastAPI gracefully shutdown कर सके और [lifespan events](../advanced/events.md) trigger हों, हमेशा **exec** form use करें। + +आप इसके बारे में [shell और exec form के लिए Docker docs](https://docs.docker.com/reference/dockerfile/#shell-and-exec-form) में और पढ़ सकते हैं। + +`docker compose` का उपयोग करते समय यह काफी noticeable हो सकता है। अधिक technical details के लिए यह Docker Compose FAQ section देखें: [मेरी services को recreate या stop होने में 10 seconds क्यों लगते हैं?](https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop). + +#### Directory Structure { #directory-structure } + +अब आपके पास ऐसी directory structure होनी चाहिए: + +``` +. +├── app +│   ├── __init__.py +│ └── main.py +├── Dockerfile +└── requirements.txt +``` + +#### TLS Termination Proxy के पीछे { #behind-a-tls-termination-proxy } + +यदि आप अपना container Nginx या Traefik जैसे TLS Termination Proxy (load balancer) के पीछे चला रहे हैं, तो option `--proxy-headers` जोड़ें, यह Uvicorn (FastAPI CLI के माध्यम से) को बताएगा कि उस proxy द्वारा भेजे गए headers पर trust करे, जो उसे बताता है कि application HTTPS के पीछे चल रही है, आदि। + +```Dockerfile +CMD ["fastapi", "run", "app/main.py", "--proxy-headers", "--port", "80"] +``` + +#### Docker Cache { #docker-cache } + +इस `Dockerfile` में एक महत्वपूर्ण trick है, हम पहले **केवल dependencies वाली file** copy करते हैं, बाकी code नहीं। मैं आपको बताता हूँ क्यों। + +```Dockerfile +COPY ./requirements.txt /code/requirements.txt +``` + +Docker और अन्य tools इन container images को **incrementally build** करते हैं, **एक layer के ऊपर दूसरी layer** जोड़ते हुए, `Dockerfile` के top से शुरू करके और `Dockerfile` के प्रत्येक instruction द्वारा बनाई गई कोई भी files जोड़ते हुए। + +Docker और समान tools image build करते समय **internal cache** भी use करते हैं, यदि कोई file पिछली बार container image build करने के बाद से change नहीं हुई है, तो वह file को फिर से copy करने और scratch से नई layer बनाने के बजाय पिछली बार बनाई गई **उसी layer को reuse** करेगा। + +सिर्फ files copy करने से बचना जरूरी नहीं कि चीज़ों को बहुत बेहतर कर दे, लेकिन क्योंकि उसने उस step के लिए cache use किया, वह **अगले step के लिए cache use** कर सकता है। उदाहरण के लिए, वह उस instruction के लिए cache use कर सकता है जो dependencies install करता है: + +```Dockerfile +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt +``` + +Package requirements वाली file **अक्सर change नहीं होगी**। इसलिए, केवल उस file को copy करके, Docker उस step के लिए **cache use** कर पाएगा। + +और फिर, Docker उन dependencies को download और install करने वाले **अगले step के लिए cache use** कर पाएगा। और यहीं हम **बहुत समय बचाते हैं**। ✨ ...और इंतज़ार की बोरियत से बचते हैं। 😪😆 + +Package dependencies को download और install करने में **minutes लग सकते हैं**, लेकिन **cache** का उपयोग करने में अधिकतम **seconds** लगेंगे। + +और क्योंकि development के दौरान आप अपने code changes काम कर रहे हैं या नहीं यह check करने के लिए container image बार-बार build करेंगे, इससे बहुत सारा जमा हुआ समय बचेगा। + +फिर, `Dockerfile` के end के पास, हम सारा code copy करते हैं। क्योंकि यही वह चीज़ है जो **सबसे अधिक बार change होती** है, हम इसे end के पास रखते हैं, क्योंकि लगभग हमेशा इस step के बाद कुछ भी cache use नहीं कर पाएगा। + +```Dockerfile +COPY ./app /code/app +``` + +### Docker Image Build करें { #build-the-docker-image } + +अब जब सभी files अपनी जगह पर हैं, चलिए container image build करते हैं। + +* Project directory में जाएँ (जहाँ आपका `Dockerfile` है, जिसमें आपकी `app` directory है)। +* अपनी FastAPI image build करें: + +
+ +```console +$ docker build -t myimage . + +---> 100% +``` + +
+ +/// tip | टिप + +अंत में `.` पर ध्यान दें, यह `./` के equivalent है, यह Docker को container image build करने के लिए use की जाने वाली directory बताता है। + +इस मामले में, यह वही current directory (`.`) है। + +/// + +### Docker Container Start करें { #start-the-docker-container } + +* अपनी image पर आधारित container run करें: + +
+ +```console +$ docker run -d --name mycontainer -p 80:80 myimage +``` + +
+ +## इसे Check करें { #check-it } + +आप इसे अपने Docker container के URL में check कर पाएँगे, उदाहरण के लिए: [http://192.168.99.100/items/5?q=somequery](http://192.168.99.100/items/5?q=somequery) या [http://127.0.0.1/items/5?q=somequery](http://127.0.0.1/items/5?q=somequery) (या equivalent, अपने Docker host का उपयोग करके)। + +आपको कुछ ऐसा दिखेगा: + +```JSON +{"item_id": 5, "q": "somequery"} +``` + +## Interactive API docs { #interactive-api-docs } + +अब आप [http://192.168.99.100/docs](http://192.168.99.100/docs) या [http://127.0.0.1/docs](http://127.0.0.1/docs) (या equivalent, अपने Docker host का उपयोग करके) पर जा सकते हैं। + +आप automatic interactive API documentation देखेंगे ([Swagger UI](https://github.com/swagger-api/swagger-ui) द्वारा provided): + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) + +## Alternative API docs { #alternative-api-docs } + +और आप [http://192.168.99.100/redoc](http://192.168.99.100/redoc) या [http://127.0.0.1/redoc](http://127.0.0.1/redoc) (या equivalent, अपने Docker host का उपयोग करके) पर भी जा सकते हैं। + +आप alternative automatic documentation देखेंगे ([ReDoc](https://github.com/Rebilly/ReDoc) द्वारा provided): + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) + +## Single-File FastAPI के साथ Docker Image बनाएँ { #build-a-docker-image-with-a-single-file-fastapi } + +यदि आपकी FastAPI एक single file है, उदाहरण के लिए, `./app` directory के बिना `main.py`, तो आपकी file structure ऐसी दिख सकती है: + +``` +. +├── Dockerfile +├── main.py +└── requirements.txt +``` + +फिर आपको बस `Dockerfile` के अंदर file copy करने के लिए संबंधित paths बदलने होंगे: + +```{ .dockerfile .annotate hl_lines="10 13" } +FROM python:3.14 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (1)! +COPY ./main.py /code/ + +# (2)! +CMD ["fastapi", "run", "main.py", "--port", "80"] +``` + +1. `main.py` file को सीधे `/code` directory में copy करें (बिना किसी `./app` directory के)। + +2. Single file `main.py` में अपनी application serve करने के लिए `fastapi run` use करें। + +जब आप file को `fastapi run` में pass करते हैं, तो यह automatically detect करेगा कि यह single file है और किसी package का हिस्सा नहीं है, और यह जान जाएगा कि इसे कैसे import करना है और आपकी FastAPI app को serve करना है। 😎 + +## Deployment Concepts { #deployment-concepts } + +आइए containers के संदर्भ में फिर से उन्हीं कुछ [Deployment Concepts](concepts.md) के बारे में बात करें। + +Containers मुख्य रूप से application को **build और deploy** करने की process को सरल बनाने का tool हैं, लेकिन वे इन **deployment concepts** को handle करने के लिए कोई particular approach enforce नहीं करते, और कई संभावित strategies हैं। + +**अच्छी खबर** यह है कि हर अलग strategy के साथ सभी deployment concepts को cover करने का एक तरीका है। 🎉 + +आइए इन **deployment concepts** को containers के संदर्भ में review करें: + +* HTTPS +* startup पर Running +* Restarts +* Replication (running processes की संख्या) +* Memory +* Start करने से पहले previous steps + +## HTTPS { #https } + +यदि हम FastAPI application के लिए सिर्फ **container image** (और बाद में running **container**) पर focus करें, तो HTTPS सामान्यतः किसी अन्य tool द्वारा **externally** handle किया जाएगा। + +यह कोई दूसरा container हो सकता है, उदाहरण के लिए [Traefik](https://traefik.io/) के साथ, जो **HTTPS** और **certificates** की **automatic** acquisition handle करता है। + +/// tip | टिप + +Traefik के Docker, Kubernetes, और अन्य के साथ integrations हैं, इसलिए इसके साथ अपने containers के लिए HTTPS set up और configure करना बहुत आसान है। + +/// + +वैकल्पिक रूप से, HTTPS को किसी cloud provider द्वारा उनकी services में से एक के रूप में handle किया जा सकता है (application अभी भी container में चल रही हो)। + +## Startup पर Running और Restarts { #running-on-startup-and-restarts } + +सामान्यतः कोई दूसरा tool आपके container को **start और run** करने के लिए charge में होता है। + +यह सीधे **Docker**, **Docker Compose**, **Kubernetes**, कोई **cloud service**, आदि हो सकता है। + +अधिकतर (या सभी) मामलों में, startup पर container run करने और failures पर restarts enable करने के लिए एक simple option होता है। उदाहरण के लिए, Docker में यह command line option `--restart` है। + +Containers का उपयोग किए बिना, applications को startup पर और restarts के साथ run कराना cumbersome और difficult हो सकता है। लेकिन **containers के साथ काम करते समय** अधिकतर मामलों में यह functionality default रूप से शामिल होती है। ✨ + +## Replication - Processes की संख्या { #replication-number-of-processes } + +यदि आपके पास machines का cluster है जिसमें **Kubernetes**, Docker Swarm Mode, Nomad, या multiple machines पर distributed containers manage करने के लिए कोई अन्य similar complex system है, तो आप शायद प्रत्येक container में **process manager** (जैसे workers के साथ Uvicorn) उपयोग करने के बजाय **cluster level** पर **replication handle** करना चाहेंगे। + +Kubernetes जैसे distributed container management systems में incoming requests के लिए **load balancing** support करते हुए **containers की replication** handle करने का कोई integrated तरीका सामान्यतः होता है। सब **cluster level** पर। + +उन मामलों में, आप शायद ऊपर [समझाए अनुसार](#dockerfile) **scratch से Docker image** build करना चाहेंगे, अपनी dependencies install करके, और multiple Uvicorn workers उपयोग करने के बजाय **single Uvicorn process** run करना चाहेंगे। + +### Load Balancer { #load-balancer } + +Containers का उपयोग करते समय, आपके पास सामान्यतः कोई component होगा जो **main port पर listening** कर रहा होगा। संभवतः यह कोई दूसरा container हो सकता है जो **HTTPS** handle करने के लिए **TLS Termination Proxy** भी हो, या कोई similar tool। + +क्योंकि यह component requests का **load** लेगा और उसे workers में (उम्मीद है) **balanced** तरीके से distribute करेगा, इसे सामान्यतः **Load Balancer** भी कहा जाता है। + +/// tip | टिप + +HTTPS के लिए उपयोग किया गया वही **TLS Termination Proxy** component शायद **Load Balancer** भी होगा। + +/// + +और containers के साथ काम करते समय, उन्हें start और manage करने के लिए आप जो system use करते हैं, उसमें उस **load balancer** (जो **TLS Termination Proxy** भी हो सकता है) से आपकी app वाले container(s) तक **network communication** (जैसे HTTP requests) transmit करने के internal tools पहले से होंगे। + +### One Load Balancer - Multiple Worker Containers { #one-load-balancer-multiple-worker-containers } + +**Kubernetes** या similar distributed container management systems के साथ काम करते समय, उनके internal networking mechanisms का उपयोग main **port** पर listening करने वाले single **load balancer** को communication (requests) आपकी app चला रहे संभवतः **multiple containers** तक transmit करने देगा। + +आपकी app चला रहे इन containers में से प्रत्येक में सामान्यतः **सिर्फ एक process** होगा (जैसे आपकी FastAPI application चलाने वाला Uvicorn process)। वे सभी **identical containers** होंगे, वही चीज़ चला रहे होंगे, लेकिन प्रत्येक का अपना process, memory, आदि होगा। इस तरह आप CPU के **different cores** में, या यहाँ तक कि **different machines** में **parallelization** का लाभ उठाएँगे। + +और **load balancer** वाला distributed container system requests को आपकी app वाले प्रत्येक container तक **बारी-बारी से distribute** करेगा। इसलिए, प्रत्येक request आपकी app चला रहे multiple **replicated containers** में से किसी एक द्वारा handle की जा सकती है। + +और सामान्यतः यह **load balancer** आपके cluster में *other* apps पर जाने वाली requests handle कर पाएगा (जैसे अलग domain पर, या अलग URL path prefix के तहत), और उस communication को आपके cluster में चल रही *उस other* application के सही containers तक transmit करेगा। + +### प्रति Container एक Process { #one-process-per-container } + +इस तरह के scenario में, आप शायद **प्रति container एक single (Uvicorn) process** रखना चाहेंगे, क्योंकि आप पहले से ही cluster level पर replication handle कर रहे होंगे। + +तो, इस मामले में, आप container में multiple workers **नहीं** रखना चाहेंगे, उदाहरण के लिए `--workers` command line option के साथ। आप प्रति container बस **single Uvicorn process** रखना चाहेंगे (लेकिन शायद multiple containers)। + +Container के अंदर एक और process manager रखना (जैसा multiple workers के साथ होगा) केवल **unnecessary complexity** जोड़ेगा, जिसे आप बहुत संभव है कि अपने cluster system के साथ पहले से संभाल रहे हैं। + +### Multiple Processes वाले Containers और Special Cases { #containers-with-multiple-processes-and-special-cases } + +बेशक, ऐसे **special cases** हैं जहाँ आप अंदर कई **Uvicorn worker processes** वाला **container** रखना चाह सकते हैं। + +उन मामलों में, आप run किए जाने वाले workers की संख्या set करने के लिए `--workers` command line option उपयोग कर सकते हैं: + +```{ .dockerfile .annotate } +FROM python:3.14 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +COPY ./app /code/app + +# (1)! +CMD ["fastapi", "run", "app/main.py", "--port", "80", "--workers", "4"] +``` + +1. यहाँ हम workers की संख्या 4 पर set करने के लिए `--workers` command line option उपयोग करते हैं। + +यहाँ कुछ examples हैं कि यह कब meaningful हो सकता है: + +#### एक Simple App { #a-simple-app } + +यदि आपकी application **इतनी simple** है कि आप इसे **single server** पर run कर सकते हैं, cluster पर नहीं, तो आप container में process manager चाह सकते हैं। + +#### Docker Compose { #docker-compose } + +आप **Docker Compose** के साथ **single server** (cluster नहीं) पर deploy कर रहे हो सकते हैं, इसलिए shared network और **load balancing** preserve करते हुए containers की replication (Docker Compose के साथ) manage करने का आसान तरीका आपके पास नहीं होगा। + +फिर आप **single container** रखना चाह सकते हैं जिसमें **process manager** अंदर **कई worker processes** start करे। + +--- + +मुख्य बात यह है कि इनमें से **कोई भी** ऐसी **पत्थर पर लिखी rules** नहीं हैं जिन्हें आपको आँख बंद करके follow करना हो। आप इन ideas का उपयोग **अपने use case का evaluate** करने और अपने system के लिए best approach तय करने के लिए कर सकते हैं, यह check करते हुए कि इन concepts को कैसे manage करना है: + +* Security - HTTPS +* startup पर Running +* Restarts +* Replication (running processes की संख्या) +* Memory +* Start करने से पहले previous steps + +## Memory { #memory } + +यदि आप **प्रति container single process** run करते हैं, तो उन containers में से प्रत्येक (यदि replicated हैं तो एक से अधिक) द्वारा consumed memory की quantity कमोबेश well-defined, stable, और limited होगी। + +और फिर आप अपने container management system (उदाहरण के लिए **Kubernetes** में) की configurations में वही memory limits और requirements set कर सकते हैं। इस तरह वह उन containers द्वारा आवश्यक memory की मात्रा, और cluster में machines में available मात्रा को ध्यान में रखते हुए **available machines** में **containers replicate** कर पाएगा। + +यदि आपकी application **simple** है, तो यह शायद **problem नहीं होगी**, और आपको hard memory limits specify करने की आवश्यकता नहीं हो सकती। लेकिन यदि आप **बहुत memory use** कर रहे हैं (उदाहरण के लिए **machine learning** models के साथ), तो आपको check करना चाहिए कि आप कितनी memory consume कर रहे हैं और **प्रत्येक machine** पर run होने वाले **containers की संख्या** adjust करनी चाहिए (और शायद अपने cluster में और machines add करनी चाहिए)। + +यदि आप **प्रति container multiple processes** run करते हैं, तो आपको सुनिश्चित करना होगा कि start किए गए processes की संख्या available memory से **अधिक memory consume** न करे। + +## Start करने से पहले Previous Steps और Containers { #previous-steps-before-starting-and-containers } + +यदि आप containers (जैसे Docker, Kubernetes) उपयोग कर रहे हैं, तो दो मुख्य approaches हैं जिनका आप उपयोग कर सकते हैं। + +### Multiple Containers { #multiple-containers } + +यदि आपके पास **multiple containers** हैं, शायद प्रत्येक **single process** run कर रहा है (उदाहरण के लिए, **Kubernetes** cluster में), तो आप replicated worker containers run करने से **पहले**, single container में, single process चलाते हुए, **previous steps** का काम करने वाला **separate container** रखना चाहेंगे। + +/// note | नोट + +यदि आप Kubernetes उपयोग कर रहे हैं, तो यह शायद [Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) होगा। + +/// + +यदि आपके use case में उन previous steps को **parallel में multiple times** run करने में कोई problem नहीं है (उदाहरण के लिए यदि आप database migrations नहीं चला रहे, बल्कि बस check कर रहे हैं कि database अभी ready है या नहीं), तो आप उन्हें हर container में main process start करने से ठीक पहले भी रख सकते हैं। + +### Single Container { #single-container } + +यदि आपका setup simple है, जिसमें **single container** है जो फिर multiple **worker processes** start करता है (या सिर्फ एक process), तो आप app के साथ process start करने से ठीक पहले, उन previous steps को उसी container में run कर सकते हैं। + +### Base Docker Image { #base-docker-image } + +एक official FastAPI Docker image हुआ करती थी: [tiangolo/uvicorn-gunicorn-fastapi](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker)। लेकिन अब यह deprecated है। ⛔️ + +आपको शायद इस base Docker image (या किसी अन्य similar one) का उपयोग **नहीं** करना चाहिए। + +यदि आप **Kubernetes** (या अन्य) उपयोग कर रहे हैं और पहले से ही cluster level पर, multiple **containers** के साथ **replication** set कर रहे हैं। उन मामलों में, ऊपर बताए अनुसार **scratch से image build** करना बेहतर है: [FastAPI के लिए Docker Image बनाएँ](#build-a-docker-image-for-fastapi). + +और यदि आपको multiple workers की आवश्यकता है, तो आप बस `--workers` command line option उपयोग कर सकते हैं। + +/// note | Technical Details + +Docker image तब बनाई गई थी जब Uvicorn dead workers को manage और restart करने का support नहीं करता था, इसलिए Uvicorn के साथ Gunicorn का उपयोग करना required था, जिससे काफी complexity जुड़ती थी, सिर्फ इसलिए कि Gunicorn Uvicorn worker processes को manage और restart कर सके। + +लेकिन अब जब Uvicorn (और `fastapi` command) `--workers` use करने का support करते हैं, तो अपनी खुद की build करने के बजाय base Docker image उपयोग करने का कोई कारण नहीं है (यह लगभग उतनी ही code की मात्रा है 😅). + +/// + +## Container Image Deploy करें { #deploy-the-container-image } + +Container (Docker) Image होने के बाद इसे deploy करने के कई तरीके हैं। + +उदाहरण के लिए: + +* Single server में **Docker Compose** के साथ +* **Kubernetes** cluster के साथ +* Docker Swarm Mode cluster के साथ +* Nomad जैसे किसी अन्य tool के साथ +* किसी cloud service के साथ जो आपकी container image लेता है और उसे deploy करता है + +## `uv` के साथ Docker Image { #docker-image-with-uv } + +यदि आप अपने project को install और manage करने के लिए [uv](https://github.com/astral-sh/uv) उपयोग कर रहे हैं, तो आप उनकी [uv Docker guide](https://docs.astral.sh/uv/guides/integration/docker/) follow कर सकते हैं। + +## Recap { #recap } + +Container systems (जैसे **Docker** और **Kubernetes** के साथ) का उपयोग करने पर सभी **deployment concepts** handle करना काफी straightforward हो जाता है: + +* HTTPS +* startup पर Running +* Restarts +* Replication (running processes की संख्या) +* Memory +* Start करने से पहले previous steps + +अधिकतर मामलों में, आप शायद कोई base image use नहीं करना चाहेंगे, और इसके बजाय official Python Docker image पर आधारित **scratch से container image build** करेंगे। + +`Dockerfile` में instructions के **order** और **Docker cache** का ध्यान रखकर आप **build times minimize** कर सकते हैं, ताकि आपकी productivity maximize हो (और बोरियत से बचें)। 😎 diff --git a/docs/hi/docs/deployment/fastapicloud.md b/docs/hi/docs/deployment/fastapicloud.md new file mode 100644 index 000000000..06a2a2d3c --- /dev/null +++ b/docs/hi/docs/deployment/fastapicloud.md @@ -0,0 +1,47 @@ +# FastAPI Cloud { #fastapi-cloud } + +आप अपनी FastAPI app को सिर्फ **एक command** से [FastAPI Cloud](https://fastapicloud.com) पर deploy कर सकते हैं। 🚀 + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +CLI अपने-आप आपकी FastAPI application का पता लगा लेगा और उसे cloud पर deploy कर देगा। अगर आप logged in नहीं हैं, तो authentication प्रक्रिया पूरी करने के लिए आपका browser खुलेगा। + +बस इतना ही! अब आप उस URL पर अपनी app access कर सकते हैं। ✨ + +## FastAPI Cloud के बारे में { #about-fastapi-cloud } + +**[FastAPI Cloud](https://fastapicloud.com)** उसी author और team द्वारा बनाया गया है जो **FastAPI** के पीछे हैं। + +यह कम से कम प्रयास में API को **build** करने, **deploy** करने, और **access** करने की प्रक्रिया को सरल बनाता है। + +यह FastAPI के साथ apps बनाने वाले उसी **developer experience** को उन्हें cloud पर **deploy** करने में भी लाता है। 🎉 + +यह app deploy करते समय ज़रूरी अधिकांश चीज़ों का भी ध्यान रखेगा, जैसे: + +* HTTPS +* Replication, requests के आधार पर autoscaling के साथ +* आदि। + +FastAPI Cloud, *FastAPI and friends* open source projects का मुख्य sponsor और funding provider है। ✨ + +## दूसरे cloud providers पर deploy करें { #deploy-to-other-cloud-providers } + +FastAPI open source है और standards पर आधारित है। आप FastAPI apps को अपने चुने हुए किसी भी cloud provider पर deploy कर सकते हैं। + +उनके साथ FastAPI apps deploy करने के लिए अपने cloud provider की guides follow करें। 🤓 + +## अपने खुद के server पर deploy करें { #deploy-your-own-server } + +मैं आपको इस **Deployment** guide में आगे सभी details भी सिखाऊँगा, ताकि आप समझ सकें कि क्या हो रहा है, क्या होना चाहिए, या FastAPI apps को अपने-आप, अपने खुद के servers के साथ भी कैसे deploy करना है। 🤓 diff --git a/docs/hi/docs/deployment/https.md b/docs/hi/docs/deployment/https.md new file mode 100644 index 000000000..834712835 --- /dev/null +++ b/docs/hi/docs/deployment/https.md @@ -0,0 +1,231 @@ +# HTTPS के बारे में { #about-https } + +यह मान लेना आसान है कि HTTPS कोई ऐसी चीज़ है जिसे बस "enabled" किया जाता है या नहीं। + +लेकिन यह उससे कहीं ज़्यादा जटिल है। + +/// tip | सुझाव + +अगर आप जल्दी में हैं या आपको परवाह नहीं है, तो अलग-अलग तकनीकों के साथ सब कुछ setup करने के लिए step by step निर्देशों हेतु अगले sections पर जाएँ। + +/// + +**HTTPS की मूल बातें सीखने** के लिए, एक उपभोक्ता के दृष्टिकोण से, देखें [https://howhttps.works/](https://howhttps.works/)। + +अब, एक **developer के दृष्टिकोण** से, HTTPS के बारे में सोचते समय ध्यान रखने योग्य कई बातें यहाँ हैं: + +* HTTPS के लिए, **server** के पास **तीसरे पक्ष** द्वारा बनाए गए "certificates" होने चाहिए। + * वे certificates वास्तव में तीसरे पक्ष से **प्राप्त** किए जाते हैं, "generated" नहीं। +* Certificates की एक **lifespan** होती है। + * वे **expire** हो जाते हैं। + * और फिर उन्हें **renew** करना पड़ता है, तीसरे पक्ष से **फिर से प्राप्त** करना पड़ता है। +* connection का encryption **TCP level** पर होता है। + * यह **HTTP से एक layer नीचे** है। + * इसलिए, **certificate और encryption** handling **HTTP से पहले** की जाती है। +* **TCP को "domains" के बारे में पता नहीं होता**। केवल IP addresses के बारे में। + * जिस **विशिष्ट domain** का request किया गया है, उसकी जानकारी **HTTP data** में जाती है। +* **HTTPS certificates** किसी **विशिष्ट domain** को "certify" करते हैं, लेकिन protocol और encryption TCP level पर होते हैं, इस domain को जानने से **पहले** कि किस domain से निपटा जा रहा है। +* **By default**, इसका मतलब होगा कि आपके पास **प्रति IP address केवल एक HTTPS certificate** हो सकता है। + * इससे कोई फर्क नहीं पड़ता कि आपका server कितना बड़ा है या उस पर मौजूद हर application कितनी छोटी हो सकती है। + * हालांकि, इसका एक **समाधान** है। +* **TLS** protocol (जो HTTP से पहले, TCP level पर encryption संभालता है) के लिए एक **extension** है जिसे **[SNI](https://en.wikipedia.org/wiki/Server_Name_Indication)** कहा जाता है। + * यह SNI extension एक ही server (एक **single IP address** के साथ) को **कई HTTPS certificates** रखने और **कई HTTPS domains/applications** serve करने की अनुमति देता है। + * इसे काम करने के लिए, server पर चल रहे, **public IP address** पर listen कर रहे, एक **single** component (program) के पास server में **सभी HTTPS certificates** होने चाहिए। +* सुरक्षित connection प्राप्त करने के **बाद**, communication protocol **अभी भी HTTP** रहता है। + * contents **encrypted** होते हैं, भले ही उन्हें **HTTP protocol** के साथ भेजा जा रहा हो। + +Server (machine, host, आदि) पर **एक program/HTTP server** चलाना और **HTTPS के सभी हिस्सों को manage** करना एक सामान्य practice है: **encrypted HTTPS requests** प्राप्त करना, उसी server में चल रही वास्तविक HTTP application (इस मामले में **FastAPI** application) को **decrypted HTTP requests** भेजना, application से **HTTP response** लेना, उपयुक्त **HTTPS certificate** का उपयोग करके उसे **encrypt** करना और **HTTPS** का उपयोग करके client को वापस भेजना। इस server को अक्सर **[TLS Termination Proxy](https://en.wikipedia.org/wiki/TLS_termination_proxy)** कहा जाता है। + +TLS Termination Proxy के रूप में आप जिन options का उपयोग कर सकते हैं, उनमें से कुछ हैं: + +* Traefik (जो certificate renewals भी handle कर सकता है) +* Caddy (जो certificate renewals भी handle कर सकता है) +* Nginx +* HAProxy + +## Let's Encrypt { #lets-encrypt } + +Let's Encrypt से पहले, ये **HTTPS certificates** trusted तीसरे पक्षों द्वारा बेचे जाते थे। + +इनमें से किसी certificate को प्राप्त करने की प्रक्रिया कठिन होती थी, काफी paperwork की जरूरत होती थी और certificates काफी महंगे होते थे। + +लेकिन फिर **[Let's Encrypt](https://letsencrypt.org/)** बनाया गया। + +यह Linux Foundation का एक project है। यह automated तरीके से **मुफ्त में HTTPS certificates** प्रदान करता है। ये certificates सभी standard cryptographic security का उपयोग करते हैं, और short-lived होते हैं (लगभग 3 महीने), इसलिए उनकी कम lifespan के कारण **security वास्तव में बेहतर** होती है। + +Domains को सुरक्षित रूप से verify किया जाता है और certificates automatically generate किए जाते हैं। इससे इन certificates के renewal को automate करना भी संभव होता है। + +विचार यह है कि इन certificates की acquisition और renewal को automate किया जाए ताकि आपके पास **secure HTTPS, मुफ्त में, हमेशा के लिए** हो सके। + +## Developers के लिए HTTPS { #https-for-developers } + +यहाँ step by step एक उदाहरण है कि HTTPS API कैसी दिख सकती है, मुख्य रूप से developers के लिए महत्वपूर्ण विचारों पर ध्यान देते हुए। + +### Domain Name { #domain-name } + +सब कुछ शायद आपके द्वारा कोई **domain name** **प्राप्त** करने से शुरू होगा। फिर, आप इसे DNS server में configure करेंगे (संभवतः आपके उसी cloud provider में)। + +आप शायद एक cloud server (एक virtual machine) या कुछ समान प्राप्त करेंगे, और उसके पास एक स्थिर **public IP address** होगा। + +DNS server(s) में आप एक record (एक "`A record`") configure करेंगे ताकि **आपका domain** आपके server के public **IP address** की ओर point करे। + +आप शायद यह सिर्फ एक बार करेंगे, पहली बार, जब सब कुछ setup कर रहे होंगे। + +/// tip | सुझाव + +यह Domain Name वाला हिस्सा HTTPS से बहुत पहले का है, लेकिन चूँकि सब कुछ domain और IP address पर निर्भर करता है, इसलिए इसे यहाँ mention करना उचित है। + +/// + +### DNS { #dns } + +अब आइए वास्तविक HTTPS parts पर focus करें। + +सबसे पहले, browser **DNS servers** से check करेगा कि **domain के लिए IP** क्या है, इस मामले में, `someapp.example.com`। + +DNS servers browser को किसी विशिष्ट **IP address** का उपयोग करने के लिए कहेंगे। यह आपके server द्वारा उपयोग किया जाने वाला public IP address होगा, जिसे आपने DNS servers में configure किया है। + + + +### TLS Handshake Start { #tls-handshake-start } + +Browser फिर उस IP address से **port 443** (HTTPS port) पर communicate करेगा। + +Communication का पहला हिस्सा केवल client और server के बीच connection establish करना और वे कौन-सी cryptographic keys उपयोग करेंगे आदि तय करना है। + + + +TLS connection establish करने के लिए client और server के बीच इस interaction को **TLS handshake** कहा जाता है। + +### SNI Extension के साथ TLS { #tls-with-sni-extension } + +Server में **केवल एक process** किसी विशिष्ट **IP address** में किसी विशिष्ट **port** पर listen कर सकता है। उसी IP address में दूसरे ports पर अन्य processes listen कर सकते हैं, लेकिन IP address और port के हर combination के लिए केवल एक। + +TLS (HTTPS) by default विशिष्ट port `443` का उपयोग करता है। इसलिए हमें इसी port की जरूरत होगी। + +क्योंकि इस port पर केवल एक process listen कर सकता है, जो process यह करेगा वह **TLS Termination Proxy** होगा। + +TLS Termination Proxy के पास एक या अधिक **TLS certificates** (HTTPS certificates) तक access होगा। + +ऊपर चर्चा किए गए **SNI extension** का उपयोग करके, TLS Termination Proxy check करेगा कि इस connection के लिए उपलब्ध TLS (HTTPS) certificates में से किसका उपयोग करना चाहिए, client द्वारा expected domain से match करने वाले certificate का उपयोग करते हुए। + +इस मामले में, यह `someapp.example.com` के लिए certificate का उपयोग करेगा। + + + +Client पहले से ही उस entity पर **trust** करता है जिसने वह TLS certificate generate किया है (इस मामले में Let's Encrypt, लेकिन हम इसके बारे में बाद में देखेंगे), इसलिए यह **verify** कर सकता है कि certificate valid है। + +फिर, certificate का उपयोग करके, client और TLS Termination Proxy **तय करते हैं कि बाकी TCP communication को कैसे encrypt करना है**। इससे **TLS Handshake** वाला हिस्सा पूरा होता है। + +इसके बाद, client और server के पास एक **encrypted TCP connection** होता है, यही TLS प्रदान करता है। और फिर वे उस connection का उपयोग वास्तविक **HTTP communication** शुरू करने के लिए कर सकते हैं। + +और **HTTPS** यही है, यह pure (unencrypted) TCP connection के बजाय एक **secure TLS connection** के अंदर साधारण **HTTP** ही है। + +/// tip | सुझाव + +ध्यान दें कि communication का encryption **TCP level** पर होता है, HTTP level पर नहीं। + +/// + +### HTTPS Request { #https-request } + +अब जबकि client और server (विशेष रूप से browser और TLS Termination Proxy) के पास एक **encrypted TCP connection** है, वे **HTTP communication** शुरू कर सकते हैं। + +तो, client एक **HTTPS request** भेजता है। यह encrypted TLS connection के माध्यम से बस एक HTTP request है। + + + +### Request को Decrypt करें { #decrypt-the-request } + +TLS Termination Proxy सहमत किए गए encryption का उपयोग **request को decrypt** करने के लिए करेगा, और **साधारण (decrypted) HTTP request** को application चलाने वाले process तक transmit करेगा (उदाहरण के लिए FastAPI application चलाने वाले Uvicorn के साथ एक process)। + + + +### HTTP Response { #http-response } + +Application request को process करेगी और TLS Termination Proxy को एक **साधारण (unencrypted) HTTP response** भेजेगी। + + + +### HTTPS Response { #https-response } + +TLS Termination Proxy फिर पहले सहमत cryptography (जो `someapp.example.com` के लिए certificate से शुरू हुई थी) का उपयोग करके **response को encrypt** करेगा, और इसे browser को वापस भेजेगा। + +इसके बाद, browser verify करेगा कि response valid है और सही cryptographic key आदि से encrypted है। फिर वह **response को decrypt** करेगा और process करेगा। + + + +Client (browser) को पता होगा कि response सही server से आया है क्योंकि यह उस cryptography का उपयोग कर रहा है जिस पर उन्होंने पहले **HTTPS certificate** का उपयोग करके सहमति की थी। + +### Multiple Applications { #multiple-applications } + +उसी server (या servers) में, **multiple applications** हो सकती हैं, उदाहरण के लिए, अन्य API programs या database। + +केवल एक process विशिष्ट IP और port handle कर सकता है (हमारे उदाहरण में TLS Termination Proxy) लेकिन अन्य applications/processes भी server(s) पर चल सकते हैं, जब तक वे **public IP और port के उसी combination** का उपयोग करने की कोशिश नहीं करते। + + + +इस तरह, TLS Termination Proxy **multiple domains** के लिए, multiple applications के लिए HTTPS और certificates handle कर सकता है, और फिर हर मामले में requests को सही application तक transmit कर सकता है। + +### Certificate Renewal { #certificate-renewal } + +भविष्य में किसी समय, प्रत्येक certificate **expire** हो जाएगा (इसे प्राप्त करने के लगभग 3 महीने बाद)। + +और फिर, कोई दूसरा program होगा (कुछ मामलों में यह दूसरा program होता है, कुछ मामलों में यह वही TLS Termination Proxy हो सकता है) जो Let's Encrypt से बात करेगा, और certificate(s) को renew करेगा। + + + +**TLS certificates** किसी **domain name** से **associated** होते हैं, IP address से नहीं। + +इसलिए, certificates renew करने के लिए, renewal program को authority (Let's Encrypt) को **prove** करना होगा कि वह वास्तव में उस domain को **"own" और control** करता है। + +ऐसा करने के लिए, और अलग-अलग application needs को accommodate करने के लिए, इसे करने के कई तरीके हैं। कुछ लोकप्रिय तरीके हैं: + +* **कुछ DNS records modify करें**। + * इसके लिए, renewal program को DNS provider की APIs support करनी होंगी, इसलिए, आप जिस DNS provider का उपयोग कर रहे हैं, उसके आधार पर यह option हो भी सकता है या नहीं भी। +* Domain से associated public IP address पर (कम से कम certificate acquisition process के दौरान) **server के रूप में चलें**। + * जैसा कि हमने ऊपर कहा, केवल एक process किसी विशिष्ट IP और port पर listen कर सकता है। + * यह उन कारणों में से एक है कि जब वही TLS Termination Proxy certificate renewal process का भी ध्यान रखता है तो यह बहुत उपयोगी होता है। + * अन्यथा, आपको TLS Termination Proxy को momentarily stop करना पड़ सकता है, certificates प्राप्त करने के लिए renewal program start करना पड़ सकता है, फिर उन्हें TLS Termination Proxy के साथ configure करना पड़ सकता है, और फिर TLS Termination Proxy को restart करना पड़ सकता है। यह ideal नहीं है, क्योंकि जिस समय TLS Termination Proxy off होगा, उस दौरान आपकी app(s) available नहीं होंगी। + +App को serve करते हुए भी यह पूरा renewal process, उन मुख्य कारणों में से एक है कि आप application server के साथ सीधे TLS certificates (जैसे Uvicorn) का उपयोग करने के बजाय TLS Termination Proxy के साथ **HTTPS handle करने के लिए एक अलग system** रखना चाहेंगे। + +## Proxy Forwarded Headers { #proxy-forwarded-headers } + +HTTPS handle करने के लिए proxy का उपयोग करते समय, आपका **application server** (उदाहरण के लिए FastAPI CLI के माध्यम से Uvicorn) HTTPS process के बारे में कुछ नहीं जानता, यह **TLS Termination Proxy** के साथ plain HTTP में communicate करता है। + +यह **proxy** सामान्यतः request को **application server** तक transmit करने से पहले कुछ HTTP headers on the fly set करेगा, ताकि application server को पता चल सके कि request proxy द्वारा **forwarded** की जा रही है। + +/// note | तकनीकी विवरण + +Proxy headers हैं: + +* [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For) +* [X-Forwarded-Proto](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto) +* [X-Forwarded-Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host) + +/// + +फिर भी, क्योंकि **application server** नहीं जानता कि वह trusted **proxy** के पीछे है, by default, वह उन headers पर trust नहीं करेगा। + +लेकिन आप **application server** को **proxy** द्वारा भेजे गए *forwarded* headers पर trust करने के लिए configure कर सकते हैं। अगर आप FastAPI CLI का उपयोग कर रहे हैं, तो आप *CLI Option* `--forwarded-allow-ips` का उपयोग करके उसे बता सकते हैं कि उसे किन IPs से आने वाले उन *forwarded* headers पर trust करना चाहिए। + +उदाहरण के लिए, अगर **application server** केवल trusted **proxy** से communication receive कर रहा है, तो आप इसे `--forwarded-allow-ips="*"` पर set कर सकते हैं ताकि यह सभी incoming IPs पर trust करे, क्योंकि यह केवल उसी IP से requests receive करेगा जिसका उपयोग **proxy** कर रहा है। + +इस तरह application यह जान पाएगी कि उसका अपना public URL क्या है, क्या वह HTTPS का उपयोग कर रही है, domain, आदि। + +यह उदाहरण के लिए redirects को ठीक से handle करने में उपयोगी होगा। + +/// tip | सुझाव + +आप इसके बारे में documentation में और सीख सकते हैं: [Proxy के पीछे - Proxy Forwarded Headers सक्षम करें](../advanced/behind-a-proxy.md#enable-proxy-forwarded-headers) + +/// + +## Recap { #recap } + +**HTTPS** होना बहुत महत्वपूर्ण है, और अधिकांश मामलों में काफी **critical** है। HTTPS के आसपास एक developer के रूप में आपको जो ज़्यादातर effort लगाना होता है, वह बस **इन concepts को समझने** और वे कैसे काम करते हैं, इसे समझने के बारे में है। + +लेकिन एक बार जब आप **developers के लिए HTTPS** की मूल जानकारी जान लेते हैं, तो आप सब कुछ सरल तरीके से manage करने में मदद के लिए अलग-अलग tools को आसानी से combine और configure कर सकते हैं। + +अगले कुछ chapters में, मैं आपको **FastAPI** applications के लिए **HTTPS** setup करने के कई ठोस उदाहरण दिखाऊँगा। 🔒 diff --git a/docs/hi/docs/deployment/index.md b/docs/hi/docs/deployment/index.md new file mode 100644 index 000000000..7b0c5b2d5 --- /dev/null +++ b/docs/hi/docs/deployment/index.md @@ -0,0 +1,23 @@ +# Deployment { #deployment } + +**FastAPI** application को deploy करना अपेक्षाकृत आसान है। + +## Deployment का क्या अर्थ है { #what-does-deployment-mean } + +किसी application को **deploy** करने का अर्थ है उसे **users के लिए उपलब्ध** कराने के लिए आवश्यक चरण पूरे करना। + +किसी **web API** के लिए, इसमें सामान्यतः उसे एक **remote machine** में रखना शामिल होता है, एक **server program** के साथ जो अच्छा performance, stability आदि प्रदान करता है, ताकि आपके **users** application को कुशलता से और बिना रुकावटों या समस्याओं के **access** कर सकें। + +यह **development** चरणों के विपरीत है, जहाँ आप लगातार code बदल रहे होते हैं, उसे तोड़ते और ठीक करते हैं, development server को रोकते और फिर से शुरू करते हैं, आदि। + +## Deployment Strategies { #deployment-strategies } + +इसे करने के कई तरीके हैं, जो आपके विशिष्ट use case और आपके द्वारा उपयोग किए जाने वाले tools पर निर्भर करते हैं। + +आप tools के संयोजन का उपयोग करके स्वयं **server deploy** कर सकते हैं, आप कोई **cloud service** उपयोग कर सकते हैं जो आपके लिए काम का कुछ हिस्सा करती है, या अन्य संभावित विकल्प चुन सकते हैं। + +उदाहरण के लिए, हमने, FastAPI के पीछे की team ने, [**FastAPI Cloud**](https://fastapicloud.com) बनाया, ताकि FastAPI apps को cloud पर deploy करना जितना संभव हो उतना streamlined हो, FastAPI के साथ काम करने के समान developer experience के साथ। + +मैं आपको कुछ मुख्य concepts दिखाऊँगा जिन्हें **FastAPI** application deploy करते समय शायद ध्यान में रखना चाहिए (हालाँकि इनमें से अधिकतर किसी भी अन्य प्रकार की web application पर लागू होता है)। + +अगले sections में आपको ध्यान में रखने के लिए अधिक details और इसे करने की कुछ techniques दिखेंगी। ✨ diff --git a/docs/hi/docs/deployment/manually.md b/docs/hi/docs/deployment/manually.md new file mode 100644 index 000000000..840421513 --- /dev/null +++ b/docs/hi/docs/deployment/manually.md @@ -0,0 +1,156 @@ +# Server को मैन्युअली चलाएँ { #run-a-server-manually } + +## `fastapi run` Command का उपयोग करें { #use-the-fastapi-run-command } + +संक्षेप में, अपनी FastAPI application serve करने के लिए `fastapi run` का उपयोग करें: + +
+ +```console +$ fastapi run main.py + + FastAPI Starting production server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://0.0.0.0:8000 + server Documentation at http://0.0.0.0:8000/docs + + Logs: + + INFO Started server process [2306215] + INFO Waiting for application startup. + INFO Application startup complete. + INFO Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C + to quit) +``` + +
+ +यह ज़्यादातर मामलों में काम करेगा। 😎 + +आप उस command का उपयोग, उदाहरण के लिए, अपनी **FastAPI** app को किसी container में, किसी server में, आदि शुरू करने के लिए कर सकते हैं। + +## ASGI Servers { #asgi-servers } + +आइए details में थोड़ा और गहराई से देखें। + +FastAPI Python web frameworks और servers बनाने के लिए एक standard का उपयोग करता है जिसे ASGI कहा जाता है। FastAPI एक ASGI web framework है। + +किसी remote server machine में **FastAPI** application (या कोई भी दूसरी ASGI application) चलाने के लिए आपको मुख्य रूप से एक ASGI server program चाहिए, जैसे **Uvicorn**; यही `fastapi` command में default रूप से आता है। + +कई विकल्प हैं, जिनमें शामिल हैं: + +* [Uvicorn](https://www.uvicorn.dev/): एक high performance ASGI server। +* [Hypercorn](https://hypercorn.readthedocs.io/): एक ASGI server जो अन्य features के साथ HTTP/2 और Trio के साथ compatible है। +* [Daphne](https://github.com/django/daphne): Django Channels के लिए बनाया गया ASGI server। +* [Granian](https://github.com/emmett-framework/granian): Python applications के लिए एक Rust HTTP server। + +## Server Machine और Server Program { #server-machine-and-server-program } + +नामों के बारे में ध्यान रखने योग्य एक छोटा-सा detail है। 💡 + +शब्द "**server**" आमतौर पर remote/cloud computer (physical या virtual machine) और उस machine पर चल रहे program (जैसे Uvicorn), दोनों के लिए उपयोग किया जाता है। + +बस ध्यान रखें कि जब आप सामान्य रूप से "server" पढ़ते हैं, तो यह उन दो चीज़ों में से किसी एक को संदर्भित कर सकता है। + +Remote machine का संदर्भ देते समय, इसे **server** कहना आम है, लेकिन **machine**, **VM** (virtual machine), **node** भी कहा जाता है। ये सभी किसी प्रकार की remote machine को संदर्भित करते हैं, जो सामान्यतः Linux चला रही होती है, जहाँ आप programs चलाते हैं। + +## Server Program Install करें { #install-the-server-program } + +जब आप FastAPI install करते हैं, तो यह एक production server, Uvicorn, के साथ आता है, और आप इसे `fastapi run` command से शुरू कर सकते हैं। + +लेकिन आप एक ASGI server को मैन्युअली भी install कर सकते हैं। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाएँ, उसे activate करें, और फिर आप server application install कर सकते हैं। + +उदाहरण के लिए, Uvicorn install करने के लिए: + +
+ +```console +$ pip install "uvicorn[standard]" + +---> 100% +``` + +
+ +किसी भी अन्य ASGI server program के लिए भी इसी तरह की प्रक्रिया लागू होगी। + +/// tip | टिप + +`standard` जोड़ने पर, Uvicorn कुछ recommended extra dependencies install और use करेगा। + +इसमें `uvloop` शामिल है, जो `asyncio` के लिए high-performance drop-in replacement है, और बड़ा concurrency performance boost देता है। + +जब आप `pip install "fastapi[standard]"` जैसी किसी command से FastAPI install करते हैं, तो आपको `uvicorn[standard]` भी मिल जाता है। + +/// + +## Server Program चलाएँ { #run-the-server-program } + +यदि आपने ASGI server मैन्युअली install किया है, तो आमतौर पर आपको अपनी FastAPI application import कराने के लिए एक खास format में import string पास करनी होगी: + +
+ +```console +$ uvicorn main:app --host 0.0.0.0 --port 80 + +INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit) +``` + +
+ +/// note | नोट + +Command `uvicorn main:app` इनका संदर्भ देती है: + +* `main`: file `main.py` (Python "module")। +* `app`: `main.py` के अंदर `app = FastAPI()` line से बनाया गया object। + +यह इसके equivalent है: + +```Python +from main import app +``` + +/// + +हर alternative ASGI server program की command मिलती-जुलती होगी, आप उनके संबंधित documentation में और पढ़ सकते हैं। + +/// warning | चेतावनी + +Uvicorn और अन्य servers एक `--reload` option support करते हैं जो development के दौरान उपयोगी होता है। + +`--reload` option बहुत अधिक resources consume करता है, अधिक unstable होता है, आदि। + +यह **development** के दौरान बहुत मदद करता है, लेकिन आपको इसे **production** में use **नहीं** करना चाहिए। + +/// + +## Deployment Concepts { #deployment-concepts } + +ये examples server program (जैसे Uvicorn) चलाते हैं, **एक single process** शुरू करते हैं, जो predefined port (जैसे `80`) पर सभी IPs (`0.0.0.0`) को listen करता है। + +यह basic idea है। लेकिन आप शायद कुछ अतिरिक्त चीज़ों का ध्यान रखना चाहेंगे, जैसे: + +* Security - HTTPS +* startup पर चलना +* Restarts +* Replication (चल रहे processes की संख्या) +* Memory +* शुरू करने से पहले के previous steps + +अगले chapters में मैं आपको इन concepts में से हर एक के बारे में, उनके बारे में कैसे सोचें, और उन्हें handle करने की strategies के साथ कुछ concrete examples के बारे में और बताऊँगा। 🚀 diff --git a/docs/hi/docs/deployment/server-workers.md b/docs/hi/docs/deployment/server-workers.md new file mode 100644 index 000000000..540c2da5e --- /dev/null +++ b/docs/hi/docs/deployment/server-workers.md @@ -0,0 +1,139 @@ +# Server Workers - Workers के साथ Uvicorn { #server-workers-uvicorn-with-workers } + +आइए पहले वाले deployment concepts को फिर से देखें: + +* Security - HTTPS +* startup पर चलना +* Restarts +* **Replication (चल रहे processes की संख्या)** +* Memory +* शुरू करने से पहले के पिछले steps + +इस बिंदु तक, docs के सभी tutorials में, आपने शायद एक **server program** चलाया होगा, उदाहरण के लिए, `fastapi` command का उपयोग करके, जो Uvicorn चलाता है, और एक **single process** चलाता है। + +Applications deploy करते समय आप शायद **processes की कुछ replication** रखना चाहेंगे ताकि **multiple cores** का लाभ लिया जा सके और अधिक requests handle की जा सकें। + +जैसा कि आपने पिछले chapter में [Deployment Concepts](concepts.md) के बारे में देखा, कई strategies हैं जिनका आप उपयोग कर सकते हैं। + +यहाँ मैं आपको दिखाऊँगा कि `fastapi` command या सीधे `uvicorn` command का उपयोग करके **worker processes** के साथ **Uvicorn** कैसे उपयोग करें। + +/// note | नोट + +यदि आप containers का उपयोग कर रहे हैं, उदाहरण के लिए Docker या Kubernetes के साथ, तो मैं आपको इसके बारे में अगले chapter में और बताऊँगा: [Containers में FastAPI - Docker](docker.md)। + +विशेष रूप से, **Kubernetes** पर चलते समय आप शायद workers का उपयोग **नहीं** करना चाहेंगे और इसके बजाय **प्रति container एक single Uvicorn process** चलाना चाहेंगे, लेकिन मैं आपको इसके बारे में उस chapter में बाद में बताऊँगा। + +/// + +## Multiple Workers { #multiple-workers } + +आप `--workers` command line option के साथ multiple workers शुरू कर सकते हैं: + +//// tab | `fastapi` + +यदि आप `fastapi` command का उपयोग करते हैं: + +
+ +```console +$ fastapi run --workers 4 main.py + + FastAPI Starting production server 🚀 + + Searching for package file structure from directories with + __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with the + following code: + + from main import app + + app Using import string: main:app + + server Server started at http://0.0.0.0:8000 + server Documentation at http://0.0.0.0:8000/docs + + Logs: + + INFO Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to + quit) + INFO Started parent process [27365] + INFO Started server process [27368] + INFO Started server process [27369] + INFO Started server process [27370] + INFO Started server process [27367] + INFO Waiting for application startup. + INFO Waiting for application startup. + INFO Waiting for application startup. + INFO Waiting for application startup. + INFO Application startup complete. + INFO Application startup complete. + INFO Application startup complete. + INFO Application startup complete. +``` + +
+ +//// + +//// tab | `uvicorn` + +यदि आप सीधे `uvicorn` command का उपयोग करना पसंद करते हैं: + +
+ +```console +$ uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4 +INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit) +INFO: Started parent process [27365] +INFO: Started server process [27368] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Started server process [27369] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Started server process [27370] +INFO: Waiting for application startup. +INFO: Application startup complete. +INFO: Started server process [27367] +INFO: Waiting for application startup. +INFO: Application startup complete. +``` + +
+ +//// + +यहाँ एकमात्र नया option `--workers` है, जो Uvicorn को 4 worker processes शुरू करने के लिए कहता है। + +आप यह भी देख सकते हैं कि यह प्रत्येक process का **PID** दिखाता है, parent process के लिए `27365` (यह **process manager** है) और प्रत्येक worker process के लिए एक: `27368`, `27369`, `27370`, और `27367`। + +## Deployment Concepts { #deployment-concepts } + +यहाँ आपने देखा कि application के execution को **parallelize** करने, CPU में **multiple cores** का लाभ लेने, और **अधिक requests** serve करने में सक्षम होने के लिए multiple **workers** का उपयोग कैसे किया जाता है। + +ऊपर दी गई deployment concepts की सूची से, workers का उपयोग मुख्य रूप से **replication** वाले हिस्से में मदद करेगा, और थोड़ा बहुत **restarts** में भी, लेकिन आपको अभी भी बाकी चीज़ों का ध्यान रखना होगा: + +* **Security - HTTPS** +* **startup पर चलना** +* ***Restarts*** +* Replication (चल रहे processes की संख्या) +* **Memory** +* **शुरू करने से पहले के पिछले steps** + +## Containers और Docker { #containers-and-docker } + +अगले chapter में [Containers में FastAPI - Docker](docker.md) के बारे में मैं कुछ strategies समझाऊँगा जिनका उपयोग आप बाकी **deployment concepts** को handle करने के लिए कर सकते हैं। + +मैं आपको दिखाऊँगा कि single Uvicorn process चलाने के लिए **शुरू से अपनी खुद की image कैसे build करें**। यह एक सरल process है और शायद यही आप तब करना चाहेंगे जब आप **Kubernetes** जैसे distributed container management system का उपयोग कर रहे हों। + +## Recap { #recap } + +आप `fastapi` या `uvicorn` commands के साथ `--workers` CLI option का उपयोग करके multiple worker processes का उपयोग कर सकते हैं, ताकि **multi-core CPUs** का लाभ लिया जा सके और **multiple processes parallel में** चलाए जा सकें। + +यदि आप बाकी deployment concepts का खुद ध्यान रखते हुए **अपना खुद का deployment system** setup कर रहे हैं, तो आप इन tools और ideas का उपयोग कर सकते हैं। + +Containers (जैसे Docker और Kubernetes) के साथ **FastAPI** के बारे में जानने के लिए अगला chapter देखें। आप देखेंगे कि उन tools में बाकी **deployment concepts** को भी हल करने के सरल तरीके हैं। ✨ diff --git a/docs/hi/docs/deployment/versions.md b/docs/hi/docs/deployment/versions.md new file mode 100644 index 000000000..cabf4bc31 --- /dev/null +++ b/docs/hi/docs/deployment/versions.md @@ -0,0 +1,93 @@ +# FastAPI संस्करणों के बारे में { #about-fastapi-versions } + +**FastAPI** पहले से ही कई applications और प्रणालियों में production में उपयोग किया जा रहा है। और test coverage 100% पर रखा जाता है। लेकिन इसका development अभी भी तेजी से आगे बढ़ रहा है। + +नई features अक्सर जोड़ी जाती हैं, bugs नियमित रूप से ठीक किए जाते हैं, और code अभी भी लगातार बेहतर हो रहा है। + +इसीलिए वर्तमान संस्करण अभी भी `0.x.x` हैं, यह दर्शाता है कि हर संस्करण में संभावित रूप से breaking changes हो सकते हैं। यह [Semantic Versioning](https://semver.org/) conventions का पालन करता है। + +आप अभी **FastAPI** के साथ production applications बना सकते हैं (और संभव है कि आप कुछ समय से ऐसा कर भी रहे हों), आपको बस यह सुनिश्चित करना होगा कि आप ऐसा संस्करण उपयोग करें जो आपके बाकी code के साथ सही ढंग से काम करता हो। + +## अपने `fastapi` संस्करण को pin करें { #pin-your-fastapi-version } + +सबसे पहले आपको **FastAPI** के जिस संस्करण का उपयोग आप कर रहे हैं उसे उस विशिष्ट नवीनतम संस्करण पर "pin" करना चाहिए जिसके बारे में आपको पता है कि वह आपके application के लिए सही ढंग से काम करता है। + +उदाहरण के लिए, मान लें कि आप अपने app में संस्करण `0.112.0` उपयोग कर रहे हैं। + +यदि आप `requirements.txt` file उपयोग करते हैं, तो आप संस्करण को इस तरह निर्दिष्ट कर सकते हैं: + +```txt +fastapi[standard]==0.112.0 +``` + +इसका मतलब होगा कि आप ठीक संस्करण `0.112.0` का उपयोग करेंगे। + +या आप इसे इस तरह भी pin कर सकते हैं: + +```txt +fastapi[standard]>=0.112.0,<0.113.0 +``` + +इसका मतलब होगा कि आप संस्करण `0.112.0` या उससे ऊपर, लेकिन `0.113.0` से कम उपयोग करेंगे, उदाहरण के लिए, संस्करण `0.112.2` अभी भी स्वीकार किया जाएगा। + +यदि आप अपनी installations प्रबंधित करने के लिए कोई अन्य tool उपयोग करते हैं, जैसे `uv`, Poetry, Pipenv, या अन्य, तो उन सभी में ऐसा तरीका होता है जिसका उपयोग करके आप अपने packages के लिए विशिष्ट संस्करण परिभाषित कर सकते हैं। + +## उपलब्ध संस्करण { #available-versions } + +आप उपलब्ध संस्करणों को (जैसे वर्तमान नवीनतम क्या है यह जांचने के लिए) [Release Notes](../release-notes.md) में देख सकते हैं। + +## संस्करणों के बारे में { #about-versions } + +Semantic Versioning conventions के अनुसार, `1.0.0` से नीचे का कोई भी संस्करण संभावित रूप से breaking changes जोड़ सकता है। + +FastAPI इस convention का भी पालन करता है कि कोई भी "PATCH" संस्करण परिवर्तन bug fixes और non-breaking changes के लिए होता है। + +/// tip | सुझाव + +"PATCH" आखिरी संख्या होती है, उदाहरण के लिए, `0.2.3` में, PATCH संस्करण `3` है। + +/// + +तो, आपको इस तरह के संस्करण पर pin करने में सक्षम होना चाहिए: + +```txt +fastapi>=0.45.0,<0.46.0 +``` + +Breaking changes और नई features "MINOR" संस्करणों में जोड़ी जाती हैं। + +/// tip | सुझाव + +"MINOR" बीच की संख्या होती है, उदाहरण के लिए, `0.2.3` में, MINOR संस्करण `2` है। + +/// + +## FastAPI संस्करणों को upgrade करना { #upgrading-the-fastapi-versions } + +आपको अपने app के लिए tests जोड़ने चाहिए। + +**FastAPI** के साथ यह बहुत आसान है (Starlette के लिए धन्यवाद), docs देखें: [Testing](../tutorial/testing.md) + +Tests होने के बाद, आप **FastAPI** संस्करण को अधिक हाल के संस्करण में upgrade कर सकते हैं, और अपने tests चलाकर सुनिश्चित कर सकते हैं कि आपका पूरा code सही ढंग से काम कर रहा है। + +यदि सब कुछ काम कर रहा है, या आवश्यक बदलाव करने के बाद, और आपके सभी tests pass हो रहे हैं, तो आप अपने `fastapi` को उस नए हालिया संस्करण पर pin कर सकते हैं। + +## Starlette के बारे में { #about-starlette } + +आपको `starlette` के संस्करण को pin नहीं करना चाहिए। + +**FastAPI** के अलग-अलग संस्करण Starlette के किसी विशिष्ट नए संस्करण का उपयोग करेंगे। + +इसलिए, आप बस **FastAPI** को सही Starlette संस्करण उपयोग करने दे सकते हैं। + +## Pydantic के बारे में { #about-pydantic } + +Pydantic अपने tests में **FastAPI** के tests शामिल करता है, इसलिए Pydantic के नए संस्करण (`1.0.0` से ऊपर) हमेशा FastAPI के साथ compatible होते हैं। + +आप Pydantic को `1.0.0` से ऊपर किसी भी ऐसे संस्करण पर pin कर सकते हैं जो आपके लिए काम करता हो। + +उदाहरण के लिए: + +```txt +pydantic>=2.7.0,<3.0.0 +``` diff --git a/docs/hi/docs/environment-variables.md b/docs/hi/docs/environment-variables.md new file mode 100644 index 000000000..0545d03b7 --- /dev/null +++ b/docs/hi/docs/environment-variables.md @@ -0,0 +1,298 @@ +# Environment Variables { #environment-variables } + +/// tip | टिप + +अगर आप पहले से जानते हैं कि "environment variables" क्या होते हैं और उनका उपयोग कैसे करना है, तो आप इसे छोड़ सकते हैं। + +/// + +एक environment variable (जिसे "**env var**" भी कहा जाता है) एक variable है जो Python code के **बाहर**, **ऑपरेटिंग सिस्टम** में रहता है, और जिसे आपका Python code (या दूसरे programs भी) पढ़ सकते हैं। + +Environment variables application **settings** संभालने, Python की **installation** के हिस्से के रूप में, आदि में उपयोगी हो सकते हैं। + +## Env Vars बनाएं और उपयोग करें { #create-and-use-env-vars } + +आप Python की ज़रूरत के बिना, **shell (terminal)** में environment variables **बना** और उपयोग कर सकते हैं: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +// आप MY_NAME नाम का env var ऐसे बना सकते हैं +$ export MY_NAME="Wade Wilson" + +// फिर आप इसे दूसरे programs के साथ उपयोग कर सकते हैं, जैसे +$ echo "Hello $MY_NAME" + +Hello Wade Wilson +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +// MY_NAME नाम का env var बनाएं +$ $Env:MY_NAME = "Wade Wilson" + +// इसे दूसरे programs के साथ उपयोग करें, जैसे +$ echo "Hello $Env:MY_NAME" + +Hello Wade Wilson +``` + +
+ +//// + +## Python में env vars पढ़ें { #read-env-vars-in-python } + +आप Python के **बाहर**, terminal में (या किसी भी दूसरे तरीके से) environment variables बना सकते हैं, और फिर **उन्हें Python में पढ़** सकते हैं। + +उदाहरण के लिए, आपके पास `main.py` नाम की file हो सकती है जिसमें: + +```Python hl_lines="3" +import os + +name = os.getenv("MY_NAME", "World") +print(f"Hello {name} from Python") +``` + +/// tip | टिप + +[`os.getenv()`](https://docs.python.org/3.8/library/os.html#os.getenv) का दूसरा argument लौटाने के लिए default value है। + +अगर यह दिया नहीं गया है, तो default रूप से यह `None` होता है, यहाँ हम उपयोग करने के लिए default value के रूप में `"World"` देते हैं। + +/// + +फिर आप उस Python program को call कर सकते हैं: + +//// tab | Linux, macOS, Windows Bash + +
+ +```console +// यहाँ हमने अभी env var set नहीं किया है +$ python main.py + +// क्योंकि हमने env var set नहीं किया, हमें default value मिलती है + +Hello World from Python + +// लेकिन अगर हम पहले एक environment variable बनाते हैं +$ export MY_NAME="Wade Wilson" + +// और फिर program को फिर से call करते हैं +$ python main.py + +// अब यह environment variable पढ़ सकता है + +Hello Wade Wilson from Python +``` + +
+ +//// + +//// tab | Windows PowerShell + +
+ +```console +// यहाँ हमने अभी env var set नहीं किया है +$ python main.py + +// क्योंकि हमने env var set नहीं किया, हमें default value मिलती है + +Hello World from Python + +// लेकिन अगर हम पहले एक environment variable बनाते हैं +$ $Env:MY_NAME = "Wade Wilson" + +// और फिर program को फिर से call करते हैं +$ python main.py + +// अब यह environment variable पढ़ सकता है + +Hello Wade Wilson from Python +``` + +
+ +//// + +क्योंकि environment variables code के बाहर set किए जा सकते हैं, लेकिन code द्वारा पढ़े जा सकते हैं, और उन्हें बाकी files के साथ store (`git` में commit) करने की ज़रूरत नहीं होती, इसलिए configurations या **settings** के लिए उनका उपयोग करना आम है। + +आप किसी **specific program invocation** के लिए भी एक environment variable बना सकते हैं, जो केवल उसी program के लिए उपलब्ध होता है, और केवल उसकी अवधि तक। + +ऐसा करने के लिए, program से ठीक पहले, उसी line पर इसे बनाएं: + +
+ +```console +// इस program call के लिए line में MY_NAME नाम का env var बनाएं +$ MY_NAME="Wade Wilson" python main.py + +// अब यह environment variable पढ़ सकता है + +Hello Wade Wilson from Python + +// इसके बाद env var मौजूद नहीं रहता +$ python main.py + +Hello World from Python +``` + +
+ +/// tip | टिप + +आप इसके बारे में [The Twelve-Factor App: Config](https://12factor.net/config) पर और पढ़ सकते हैं। + +/// + +## Types और Validation { #types-and-validation } + +ये environment variables केवल **text strings** को ही संभाल सकते हैं, क्योंकि ये Python से बाहरी होते हैं और इन्हें दूसरे programs तथा बाकी system (और अलग-अलग ऑपरेटिंग सिस्टम, जैसे Linux, Windows, और macOS) के साथ compatible होना होता है। + +इसका मतलब है कि Python में environment variable से पढ़ा गया **कोई भी value** **`str` होगा**, और किसी अलग type में कोई भी conversion या कोई भी validation code में करना होगा। + +आप [Advanced User Guide - Settings and Environment Variables](./advanced/settings.md) में **application settings** संभालने के लिए environment variables के उपयोग के बारे में और सीखेंगे। + +## `PATH` Environment Variable { #path-environment-variable } + +**`PATH`** नाम का एक **special** environment variable होता है जिसका उपयोग ऑपरेटिंग सिस्टम (Linux, macOS, Windows) चलाने के लिए programs खोजने में करते हैं। + +`PATH` variable का value एक लंबी string होती है जो Linux और macOS पर colon `:` से, और Windows पर semicolon `;` से अलग की गई directories से बनी होती है। + +उदाहरण के लिए, `PATH` environment variable ऐसा दिख सकता है: + +//// tab | Linux, macOS + +```plaintext +/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin +``` + +इसका मतलब है कि system को इन directories में programs ढूंढने चाहिए: + +* `/usr/local/bin` +* `/usr/bin` +* `/bin` +* `/usr/sbin` +* `/sbin` + +//// + +//// tab | Windows + +```plaintext +C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32 +``` + +इसका मतलब है कि system को इन directories में programs ढूंढने चाहिए: + +* `C:\Program Files\Python312\Scripts` +* `C:\Program Files\Python312` +* `C:\Windows\System32` + +//// + +जब आप terminal में कोई **command** type करते हैं, तो ऑपरेटिंग सिस्टम `PATH` environment variable में listed **उनमें से प्रत्येक directory** में program को **ढूंढता है**। + +उदाहरण के लिए, जब आप terminal में `python` type करते हैं, तो ऑपरेटिंग सिस्टम उस list की **पहली directory** में `python` नाम का program ढूंढता है। + +अगर उसे यह मिल जाता है, तो वह **इसे उपयोग** करेगा। नहीं तो वह **दूसरी directories** में ढूंढना जारी रखता है। + +### Python install करना और `PATH` update करना { #installing-python-and-updating-the-path } + +जब आप Python install करते हैं, तो आपसे पूछा जा सकता है कि क्या आप `PATH` environment variable को update करना चाहते हैं। + +//// tab | Linux, macOS + +मान लें कि आप Python install करते हैं और वह `/opt/custompython/bin` directory में जाता है। + +अगर आप `PATH` environment variable को update करने के लिए yes कहते हैं, तो installer `/opt/custompython/bin` को `PATH` environment variable में जोड़ देगा। + +यह ऐसा दिख सकता है: + +```plaintext +/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/custompython/bin +``` + +इस तरह, जब आप terminal में `python` type करते हैं, तो system Python program को `/opt/custompython/bin` (आखिरी directory) में ढूंढेगा और उसी का उपयोग करेगा। + +//// + +//// tab | Windows + +मान लें कि आप Python install करते हैं और वह `C:\opt\custompython\bin` directory में जाता है। + +अगर आप `PATH` environment variable को update करने के लिए yes कहते हैं, तो installer `C:\opt\custompython\bin` को `PATH` environment variable में जोड़ देगा। + +```plaintext +C:\Program Files\Python312\Scripts;C:\Program Files\Python312;C:\Windows\System32;C:\opt\custompython\bin +``` + +इस तरह, जब आप terminal में `python` type करते हैं, तो system Python program को `C:\opt\custompython\bin` (आखिरी directory) में ढूंढेगा और उसी का उपयोग करेगा। + +//// + +तो, अगर आप type करते हैं: + +
+ +```console +$ python +``` + +
+ +//// tab | Linux, macOS + +System `/opt/custompython/bin` में `python` program को **ढूंढेगा** और उसे चलाएगा। + +यह लगभग ऐसा type करने के बराबर होगा: + +
+ +```console +$ /opt/custompython/bin/python +``` + +
+ +//// + +//// tab | Windows + +System `C:\opt\custompython\bin\python` में `python` program को **ढूंढेगा** और उसे चलाएगा। + +यह लगभग ऐसा type करने के बराबर होगा: + +
+ +```console +$ C:\opt\custompython\bin\python +``` + +
+ +//// + +यह जानकारी [Virtual Environments](virtual-environments.md) के बारे में सीखते समय उपयोगी होगी। + +## निष्कर्ष { #conclusion } + +इससे आपको यह basic समझ मिल जानी चाहिए कि **environment variables** क्या होते हैं और Python में उनका उपयोग कैसे करना है। + +आप इनके बारे में [Wikipedia for Environment Variable](https://en.wikipedia.org/wiki/Environment_variable) में भी और पढ़ सकते हैं। + +कई मामलों में तुरंत यह बहुत स्पष्ट नहीं होता कि environment variables कैसे उपयोगी और applicable होंगे। लेकिन जब आप developing कर रहे होते हैं, तो ये कई अलग-अलग scenarios में बार-बार सामने आते हैं, इसलिए इनके बारे में जानना अच्छा है। + +उदाहरण के लिए, अगले section में, [Virtual Environments](virtual-environments.md) के बारे में, आपको इस जानकारी की ज़रूरत होगी। diff --git a/docs/hi/docs/help-fastapi.md b/docs/hi/docs/help-fastapi.md new file mode 100644 index 000000000..bbeb2f16d --- /dev/null +++ b/docs/hi/docs/help-fastapi.md @@ -0,0 +1,87 @@ +# मदद { #help } + +क्या आप FastAPI की मदद करना चाहते हैं या FastAPI के बारे में मदद पाना चाहते हैं? + +मदद करने और मदद पाने के बहुत सरल तरीके हैं। + +## newsletter की सदस्यता लें { #subscribe-to-the-newsletter } + +आप (कभी-कभार आने वाले) [**FastAPI and friends** newsletter](newsletter.md) की सदस्यता ले सकते हैं ताकि आप इन चीज़ों से अपडेट रहें: + +* FastAPI और friends के बारे में खबरें 🚀 +* गाइड्स 📝 +* Features ✨ +* Breaking changes 🚨 +* टिप्स और ट्रिक्स ✅ + +## FastAPI को ऑनलाइन फ़ॉलो करें { #follow-fastapi-online } + +आप **FastAPI** को कई जगहों पर ऑनलाइन फ़ॉलो कर सकते हैं: + +* [**X / Twitter** पर @fastapi](https://x.com/fastapi) +* [**Bluesky** पर @fastapi.tiangolo.com](https://bsky.app/profile/fastapi.tiangolo.com) +* [**LinkedIn** पर FastAPI](https://www.linkedin.com/company/fastapi/) + +## GitHub में **FastAPI** को Star करें { #star-fastapi-in-github } + +आप GitHub में FastAPI को "star" कर सकते हैं (ऊपर दाईं ओर star बटन पर क्लिक करके): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). ⭐️ + +Star जोड़ने से, अन्य users इसे अधिक आसानी से ढूंढ पाएंगे और देख पाएंगे कि यह दूसरों के लिए पहले से उपयोगी रहा है। + +## Releases के लिए GitHub repository को Watch करें { #watch-the-github-repository-for-releases } + +आप GitHub में FastAPI को "watch" कर सकते हैं (ऊपर दाईं ओर "watch" बटन पर क्लिक करके): [https://github.com/fastapi/fastapi](https://github.com/fastapi/fastapi). 👀 + +वहाँ आप "Releases only" चुन सकते हैं। + +ऐसा करने पर, जब भी bug fixes और नए features के साथ **FastAPI** की कोई नई release (एक नया version) आएगी, आपको notifications (आपके email में) मिलेंगे। + +## लेखक को फ़ॉलो करें { #follow-the-author } + +आप [मुझे (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com), लेखक को कुछ जगहों पर फ़ॉलो कर सकते हैं, ताकि जब मेरे पास FastAPI और friends के बारे में साझा करने के लिए खबरें हों तो आपको पता चले: + +* [**GitHub** पर @tiangolo](https://github.com/tiangolo). +* [**X (Twitter)** पर @tiangolo](https://x.com/tiangolo) +* [**Bluesky** पर @tiangolo.com](https://bsky.app/profile/tiangolo.com) +* [**LinkedIn** पर @tiangolo](https://www.linkedin.com/in/tiangolo/). + +## GitHub में प्रश्नों के साथ दूसरों की मदद करें { #help-others-with-questions-in-github } + +आप [GitHub Discussions](https://github.com/fastapi/fastapi/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered) में दूसरों के प्रश्नों में मदद करने की कोशिश कर सकते हैं। + +कई मामलों में आपको उन प्रश्नों का उत्तर पहले से पता हो सकता है। 🤓 + +यदि आप बहुत से लोगों के प्रश्नों में उनकी मदद कर रहे हैं, तो आप आधिकारिक [FastAPI Expert](fastapi-people.md#fastapi-experts) बन जाएंगे। 🎉 + +बस याद रखें, सबसे महत्वपूर्ण बात है: विनम्र रहने की कोशिश करें। 🤗 + +### मदद कैसे करें { #how-to-help } + +यहाँ [मदद कैसे करें वाली गाइड](https://tiangolo.com/open-source/help/#help-others-with-questions-in-github) का पालन करें। + +## प्रश्न पूछें { #ask-questions } + +आप GitHub repository में [एक नया प्रश्न बना सकते हैं](https://github.com/fastapi/fastapi/discussions/new?category=questions), उदाहरण के लिए: + +* कोई **प्रश्न** पूछें या किसी **समस्या** के बारे में पूछें। +* कोई नया **feature** सुझाएँ। + +## Chat से जुड़ें { #join-the-chat } + +👥 [Discord chat server](https://discord.gg/VQjSZaeJmf) 👥 से जुड़ें और FastAPI community में दूसरों के साथ बातचीत करें। + +/// tip | सुझाव + +प्रश्नों के लिए, उन्हें GitHub Discussions में पूछें, वहाँ आपको मदद मिलने की संभावना कहीं बेहतर है। + +Chat का उपयोग केवल अन्य सामान्य बातचीत के लिए करें। + +/// + +### प्रश्नों के लिए Chat का उपयोग न करें { #dont-use-the-chat-for-questions } + +ध्यान रखें कि chats अधिक "मुक्त बातचीत" की अनुमति देते हैं, इसलिए बहुत सामान्य और उत्तर देने में अधिक कठिन प्रश्न पूछना आसान हो जाता है, इसलिए हो सकता है आपको उत्तर न मिलें। + +GitHub में, template आपको सही प्रश्न लिखने में मार्गदर्शन करेगा ताकि आप अधिक आसानी से अच्छा उत्तर पा सकें, या पूछने से पहले ही समस्या को स्वयं भी हल कर सकें। + +Chat systems में बातचीत GitHub जितनी आसानी से searchable भी नहीं होती, वे खो जाती हैं। diff --git a/docs/hi/docs/how-to/authentication-error-status-code.md b/docs/hi/docs/how-to/authentication-error-status-code.md new file mode 100644 index 000000000..267743d44 --- /dev/null +++ b/docs/hi/docs/how-to/authentication-error-status-code.md @@ -0,0 +1,17 @@ +# पुराने 403 Authentication Error Status Codes का उपयोग करें { #use-old-403-authentication-error-status-codes } + +FastAPI version `0.122.0` से पहले, जब integrated security utilities failed authentication के बाद client को error लौटाती थीं, तो वे HTTP status code `403 Forbidden` का उपयोग करती थीं। + +FastAPI version `0.122.0` से शुरू होकर, वे अधिक उपयुक्त HTTP status code `401 Unauthorized` का उपयोग करती हैं, और HTTP specifications, [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235#section-3.1), [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110#name-401-unauthorized) का पालन करते हुए response में एक उचित `WWW-Authenticate` header लौटाती हैं। + +लेकिन अगर किसी कारण से आपके clients पुराने behavior पर निर्भर हैं, तो आप अपनी security classes में method `make_not_authenticated_error` को override करके उस पर वापस जा सकते हैं। + +उदाहरण के लिए, आप `HTTPBearer` का एक subclass बना सकते हैं जो default `401 Unauthorized` error के बजाय `403 Forbidden` error लौटाता है: + +{* ../../docs_src/authentication_error_status_code/tutorial001_an_py310.py hl[9:13] *} + +/// tip | टिप + +ध्यान दें कि function exception instance लौटाता है, उसे raise नहीं करता। raising बाकी internal code में किया जाता है। + +/// diff --git a/docs/hi/docs/how-to/conditional-openapi.md b/docs/hi/docs/how-to/conditional-openapi.md new file mode 100644 index 000000000..3a39cd326 --- /dev/null +++ b/docs/hi/docs/how-to/conditional-openapi.md @@ -0,0 +1,56 @@ +# सशर्त OpenAPI { #conditional-openapi } + +अगर आपको ज़रूरत हो, तो आप environment के आधार पर OpenAPI को सशर्त रूप से configure करने के लिए settings और environment variables का उपयोग कर सकते हैं, और इसे पूरी तरह disable भी कर सकते हैं। + +## security, APIs, और docs के बारे में { #about-security-apis-and-docs } + +production में अपनी documentation user interfaces को छिपाना आपकी API को सुरक्षित करने का तरीका *नहीं होना चाहिए*। + +इससे आपकी API में कोई अतिरिक्त security नहीं जुड़ती, *path operations* अभी भी वहीं उपलब्ध रहेंगी जहाँ वे हैं। + +अगर आपके code में कोई security flaw है, तो वह अभी भी मौजूद रहेगा। + +documentation को छिपाना बस यह समझना अधिक कठिन बना देता है कि आपकी API के साथ कैसे interact किया जाए, और production में इसे debug करना आपके लिए अधिक कठिन बना सकता है। इसे बस [Security through obscurity](https://en.wikipedia.org/wiki/Security_through_obscurity) का एक रूप माना जा सकता है। + +अगर आप अपनी API को secure करना चाहते हैं, तो कई बेहतर चीज़ें हैं जो आप कर सकते हैं, उदाहरण के लिए: + +* सुनिश्चित करें कि आपके request bodies और responses के लिए अच्छी तरह defined Pydantic models हैं। +* dependencies का उपयोग करके कोई भी required permissions और roles configure करें। +* plaintext passwords कभी store न करें, केवल password hashes store करें। +* pwdlib और JWT tokens आदि जैसे जाने-माने cryptographic tools implement और use करें। +* जहाँ ज़रूरत हो, OAuth2 scopes के साथ अधिक granular permission controls जोड़ें। +* ...आदि। + +फिर भी, आपके पास कोई बहुत विशिष्ट use case हो सकता है जहाँ आपको सच में किसी environment (जैसे production) के लिए या environment variables से मिली configurations के आधार पर API docs को disable करने की ज़रूरत हो। + +## settings और env vars से सशर्त OpenAPI { #conditional-openapi-from-settings-and-env-vars } + +आप अपनी generated OpenAPI और docs UIs को configure करने के लिए आसानी से वही Pydantic settings use कर सकते हैं। + +उदाहरण के लिए: + +{* ../../docs_src/conditional_openapi/tutorial001_py310.py hl[6,11] *} + +यहाँ हम setting `openapi_url` को उसी default `"/openapi.json"` के साथ declare करते हैं। + +और फिर `FastAPI` app बनाते समय हम इसका उपयोग करते हैं। + +फिर आप environment variable `OPENAPI_URL` को empty string पर set करके OpenAPI (UI docs सहित) को disable कर सकते हैं, जैसे: + +
+ +```console +$ OPENAPI_URL= uvicorn main:app + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +फिर अगर आप `/openapi.json`, `/docs`, या `/redoc` URLs पर जाते हैं, तो आपको बस इस तरह का `404 Not Found` error मिलेगा: + +```JSON +{ + "detail": "Not Found" +} +``` diff --git a/docs/hi/docs/how-to/configure-swagger-ui.md b/docs/hi/docs/how-to/configure-swagger-ui.md new file mode 100644 index 000000000..9df59d68f --- /dev/null +++ b/docs/hi/docs/how-to/configure-swagger-ui.md @@ -0,0 +1,70 @@ +# Swagger UI configure करें { #configure-swagger-ui } + +आप कुछ अतिरिक्त [Swagger UI parameters](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) configure कर सकते हैं। + +इन्हें configure करने के लिए, `FastAPI()` app object बनाते समय या `get_swagger_ui_html()` function में `swagger_ui_parameters` argument pass करें। + +`swagger_ui_parameters` एक dictionary प्राप्त करता है जिसमें configurations सीधे Swagger UI को pass की जाती हैं। + +FastAPI configurations को **JSON** में बदलता है ताकि वे JavaScript के साथ compatible हों, क्योंकि Swagger UI को यही चाहिए। + +## Syntax Highlighting disable करें { #disable-syntax-highlighting } + +उदाहरण के लिए, आप Swagger UI में syntax highlighting disable कर सकते हैं। + +Settings बदले बिना, syntax highlighting default रूप से enabled रहती है: + + + +लेकिन आप `syntaxHighlight` को `False` पर set करके इसे disable कर सकते हैं: + +{* ../../docs_src/configure_swagger_ui/tutorial001_py310.py hl[3] *} + +...और फिर Swagger UI अब syntax highlighting नहीं दिखाएगा: + + + +## Theme बदलें { #change-the-theme } + +इसी तरह आप key `"syntaxHighlight.theme"` के साथ syntax highlighting theme set कर सकते हैं (ध्यान दें कि इसके बीच में एक dot है): + +{* ../../docs_src/configure_swagger_ui/tutorial002_py310.py hl[3] *} + +वह configuration syntax highlighting color theme बदल देगी: + + + +## Default Swagger UI Parameters बदलें { #change-default-swagger-ui-parameters } + +FastAPI में ज़्यादातर use cases के लिए उपयुक्त कुछ default configuration parameters शामिल हैं। + +इसमें ये default configurations शामिल हैं: + +{* ../../fastapi/openapi/docs.py ln[9:24] hl[18:24] *} + +आप `swagger_ui_parameters` argument में अलग value set करके इनमें से किसी को भी override कर सकते हैं। + +उदाहरण के लिए, `deepLinking` disable करने के लिए आप ये settings `swagger_ui_parameters` में pass कर सकते हैं: + +{* ../../docs_src/configure_swagger_ui/tutorial003_py310.py hl[3] *} + +## अन्य Swagger UI Parameters { #other-swagger-ui-parameters } + +आप जिन अन्य सभी संभावित configurations का उपयोग कर सकते हैं, उन्हें देखने के लिए आधिकारिक [Swagger UI parameters के docs](https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/) पढ़ें। + +## केवल JavaScript settings { #javascript-only-settings } + +Swagger UI अन्य configurations को **केवल JavaScript** objects (उदाहरण के लिए, JavaScript functions) होने की भी अनुमति देता है। + +FastAPI में ये केवल JavaScript `presets` settings भी शामिल हैं: + +```JavaScript +presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIBundle.SwaggerUIStandalonePreset +] +``` + +ये **JavaScript** objects हैं, strings नहीं, इसलिए आप इन्हें सीधे Python code से pass नहीं कर सकते। + +अगर आपको ऐसी केवल JavaScript configurations का उपयोग करना है, तो आप ऊपर दिए गए methods में से किसी एक का उपयोग कर सकते हैं। पूरी Swagger UI *path operation* को override करें और आपको जो भी JavaScript चाहिए उसे manually लिखें। diff --git a/docs/hi/docs/how-to/custom-docs-ui-assets.md b/docs/hi/docs/how-to/custom-docs-ui-assets.md new file mode 100644 index 000000000..5f22186df --- /dev/null +++ b/docs/hi/docs/how-to/custom-docs-ui-assets.md @@ -0,0 +1,185 @@ +# कस्टम Docs UI Static Assets (Self-Hosting) { #custom-docs-ui-static-assets-self-hosting } + +API docs **Swagger UI** और **ReDoc** का उपयोग करते हैं, और उनमें से प्रत्येक को कुछ JavaScript और CSS files की जरूरत होती है। + +Default रूप से, वे files एक CDN से serve की जाती हैं। + +लेकिन इसे customize करना संभव है, आप कोई विशिष्ट CDN set कर सकते हैं, या files को स्वयं serve कर सकते हैं। + +## JavaScript और CSS के लिए कस्टम CDN { #custom-cdn-for-javascript-and-css } + +मान लें कि आप कोई अलग CDN उपयोग करना चाहते हैं, उदाहरण के लिए आप `https://unpkg.com/` उपयोग करना चाहते हैं। + +यह उपयोगी हो सकता है अगर, उदाहरण के लिए, आप ऐसे देश में रहते हैं जो कुछ URLs को restrict करता है। + +### Automatic docs को disable करें { #disable-the-automatic-docs } + +पहला step automatic docs को disable करना है, क्योंकि default रूप से, वे default CDN का उपयोग करते हैं। + +उन्हें disable करने के लिए, अपना `FastAPI` app बनाते समय उनके URLs को `None` पर set करें: + +{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[8] *} + +### कस्टम docs शामिल करें { #include-the-custom-docs } + +अब आप कस्टम docs के लिए *path operations* बना सकते हैं। + +आप docs के लिए HTML pages बनाने हेतु FastAPI के internal functions को reuse कर सकते हैं, और उन्हें जरूरी arguments pass कर सकते हैं: + +* `openapi_url`: वह URL जहां docs के लिए HTML page आपके API के लिए OpenAPI schema प्राप्त कर सकता है। आप यहां attribute `app.openapi_url` का उपयोग कर सकते हैं। +* `title`: आपके API का title। +* `oauth2_redirect_url`: default उपयोग करने के लिए आप यहां `app.swagger_ui_oauth2_redirect_url` का उपयोग कर सकते हैं। +* `swagger_js_url`: वह URL जहां आपके Swagger UI docs के लिए HTML **JavaScript** file प्राप्त कर सकता है। यह कस्टम CDN URL है। +* `swagger_css_url`: वह URL जहां आपके Swagger UI docs के लिए HTML **CSS** file प्राप्त कर सकता है। यह कस्टम CDN URL है। + +और ReDoc के लिए भी इसी तरह... + +{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[2:6,11:19,22:24,27:33] *} + +/// tip | सुझाव + +`swagger_ui_redirect` के लिए *path operation* तब एक helper है जब आप OAuth2 का उपयोग करते हैं। + +यदि आप अपने API को किसी OAuth2 provider के साथ integrate करते हैं, तो आप authenticate कर पाएंगे और प्राप्त credentials के साथ API docs पर वापस आ पाएंगे। और वास्तविक OAuth2 authentication का उपयोग करके उससे interact कर पाएंगे। + +Swagger UI आपके लिए इसे behind the scenes handle करेगा, लेकिन इसके लिए इस "redirect" helper की जरूरत होती है। + +/// + +### इसे test करने के लिए एक *path operation* बनाएं { #create-a-path-operation-to-test-it } + +अब, यह test करने के लिए कि सब कुछ काम करता है, एक *path operation* बनाएं: + +{* ../../docs_src/custom_docs_ui/tutorial001_py310.py hl[36:38] *} + +### इसे test करें { #test-it } + +अब, आप अपने docs पर [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) जा सकेंगे, और page reload करने पर, यह उन assets को नए CDN से load करेगा। + +## Docs के लिए JavaScript और CSS की Self-hosting { #self-hosting-javascript-and-css-for-docs } + +JavaScript और CSS की self-hosting उपयोगी हो सकती है अगर, उदाहरण के लिए, आपको चाहिए कि आपका app offline रहने पर भी, खुले Internet access के बिना, या local network में काम करता रहे। + +यहां आप देखेंगे कि उन files को स्वयं, उसी FastAPI app में कैसे serve किया जाए, और docs को उनका उपयोग करने के लिए कैसे configure किया जाए। + +### Project file structure { #project-file-structure } + +मान लें आपके project की file structure ऐसी दिखती है: + +``` +. +├── app +│ ├── __init__.py +│ ├── main.py +``` + +अब उन static files को store करने के लिए एक directory बनाएं। + +आपकी नई file structure ऐसी दिख सकती है: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +└── static/ +``` + +### Files download करें { #download-the-files } + +Docs के लिए जरूरी static files download करें और उन्हें उस `static/` directory में रखें। + +आप शायद प्रत्येक link पर right-click करके "Save link as..." जैसा कोई option select कर सकते हैं। + +**Swagger UI** files का उपयोग करता है: + +* [`swagger-ui-bundle.js`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js) +* [`swagger-ui.css`](https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css) + +और **ReDoc** file का उपयोग करता है: + +* [`redoc.standalone.js`](https://cdn.jsdelivr.net/npm/redoc@2/bundles/redoc.standalone.js) + +उसके बाद, आपकी file structure ऐसी दिख सकती है: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +└── static + ├── redoc.standalone.js + ├── swagger-ui-bundle.js + └── swagger-ui.css +``` + +### Static files serve करें { #serve-the-static-files } + +* `StaticFiles` import करें। +* किसी विशिष्ट path में `StaticFiles()` instance को "Mount" करें। + +{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[7,11] *} + +### Static files को test करें { #test-the-static-files } + +अपना application start करें और [http://127.0.0.1:8000/static/redoc.standalone.js](http://127.0.0.1:8000/static/redoc.standalone.js) पर जाएं। + +आपको **ReDoc** के लिए एक बहुत लंबी JavaScript file दिखनी चाहिए। + +यह कुछ इस तरह से शुरू हो सकती है: + +```JavaScript +/*! For license information please see redoc.standalone.js.LICENSE.txt */ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")): +... +``` + +यह confirm करता है कि आप अपने app से static files serve कर पा रहे हैं, और आपने docs के लिए static files को सही जगह पर रखा है। + +अब हम app को docs के लिए उन static files का उपयोग करने के लिए configure कर सकते हैं। + +### Static files के लिए automatic docs को disable करें { #disable-the-automatic-docs-for-static-files } + +कस्टम CDN का उपयोग करने जैसा ही, पहला step automatic docs को disable करना है, क्योंकि वे default रूप से CDN का उपयोग करते हैं। + +उन्हें disable करने के लिए, अपना `FastAPI` app बनाते समय उनके URLs को `None` पर set करें: + +{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[9] *} + +### Static files के लिए कस्टम docs शामिल करें { #include-the-custom-docs-for-static-files } + +और कस्टम CDN की तरह ही, अब आप कस्टम docs के लिए *path operations* बना सकते हैं। + +फिर से, आप docs के लिए HTML pages बनाने हेतु FastAPI के internal functions को reuse कर सकते हैं, और उन्हें जरूरी arguments pass कर सकते हैं: + +* `openapi_url`: वह URL जहां docs के लिए HTML page आपके API के लिए OpenAPI schema प्राप्त कर सकता है। आप यहां attribute `app.openapi_url` का उपयोग कर सकते हैं। +* `title`: आपके API का title। +* `oauth2_redirect_url`: default उपयोग करने के लिए आप यहां `app.swagger_ui_oauth2_redirect_url` का उपयोग कर सकते हैं। +* `swagger_js_url`: वह URL जहां आपके Swagger UI docs के लिए HTML **JavaScript** file प्राप्त कर सकता है। **यह वही है जिसे अब आपका अपना app serve कर रहा है**। +* `swagger_css_url`: वह URL जहां आपके Swagger UI docs के लिए HTML **CSS** file प्राप्त कर सकता है। **यह वही है जिसे अब आपका अपना app serve कर रहा है**। + +और ReDoc के लिए भी इसी तरह... + +{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[2:6,14:22,25:27,30:36] *} + +/// tip | सुझाव + +`swagger_ui_redirect` के लिए *path operation* तब एक helper है जब आप OAuth2 का उपयोग करते हैं। + +यदि आप अपने API को किसी OAuth2 provider के साथ integrate करते हैं, तो आप authenticate कर पाएंगे और प्राप्त credentials के साथ API docs पर वापस आ पाएंगे। और वास्तविक OAuth2 authentication का उपयोग करके उससे interact कर पाएंगे। + +Swagger UI आपके लिए इसे behind the scenes handle करेगा, लेकिन इसके लिए इस "redirect" helper की जरूरत होती है। + +/// + +### Static files को test करने के लिए एक *path operation* बनाएं { #create-a-path-operation-to-test-static-files } + +अब, यह test करने के लिए कि सब कुछ काम करता है, एक *path operation* बनाएं: + +{* ../../docs_src/custom_docs_ui/tutorial002_py310.py hl[39:41] *} + +### Static Files UI को test करें { #test-static-files-ui } + +अब, आप अपना WiFi disconnect कर सकेंगे, अपने docs पर [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) जा सकेंगे, और page reload कर सकेंगे। + +और Internet के बिना भी, आप अपने API के docs देख पाएंगे और उससे interact कर पाएंगे। diff --git a/docs/hi/docs/how-to/custom-request-and-route.md b/docs/hi/docs/how-to/custom-request-and-route.md new file mode 100644 index 000000000..97743d169 --- /dev/null +++ b/docs/hi/docs/how-to/custom-request-and-route.md @@ -0,0 +1,109 @@ +# Custom Request और APIRoute class { #custom-request-and-apiroute-class } + +कुछ मामलों में, आप `Request` और `APIRoute` classes द्वारा उपयोग किए जाने वाले logic को override करना चाह सकते हैं। + +विशेष रूप से, यह middleware में logic का एक अच्छा विकल्प हो सकता है। + +उदाहरण के लिए, अगर आप request body को आपके application द्वारा process किए जाने से पहले पढ़ना या manipulate करना चाहते हैं। + +/// danger | खतरा + +यह एक "advanced" feature है। + +अगर आप अभी-अभी **FastAPI** के साथ शुरुआत कर रहे हैं, तो शायद आप इस section को छोड़ना चाहेंगे। + +/// + +## Use cases { #use-cases } + +कुछ use cases में शामिल हैं: + +* non-JSON request bodies को JSON में convert करना (जैसे [`msgpack`](https://msgpack.org/index.html)). +* gzip-compressed request bodies को decompress करना। +* सभी request bodies को automatically log करना। + +## Custom request body encodings को संभालना { #handling-custom-request-body-encodings } + +आइए देखें कि gzip requests को decompress करने के लिए custom `Request` subclass का उपयोग कैसे करें। + +और उस custom request class का उपयोग करने के लिए एक `APIRoute` subclass। + +### Custom `GzipRequest` class बनाएँ { #create-a-custom-gziprequest-class } + +/// tip | सुझाव + +यह दिखाने के लिए एक सरल उदाहरण है कि यह कैसे काम करता है, अगर आपको Gzip support चाहिए, तो आप दिए गए [`GzipMiddleware`](../advanced/middleware.md#gzipmiddleware) का उपयोग कर सकते हैं। + +/// + +सबसे पहले, हम एक `GzipRequest` class बनाते हैं, जो उपयुक्त header की मौजूदगी में body को decompress करने के लिए `Request.body()` method को overwrite करेगी। + +अगर header में `gzip` नहीं है, तो यह body को decompress करने की कोशिश नहीं करेगी। + +इस तरह, वही route class gzip compressed या uncompressed requests को संभाल सकती है। + +{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[9:16] *} + +### Custom `GzipRoute` class बनाएँ { #create-a-custom-gziproute-class } + +इसके बाद, हम `fastapi.routing.APIRoute` की एक custom subclass बनाते हैं जो `GzipRequest` का उपयोग करेगी। + +इस बार, यह `APIRoute.get_route_handler()` method को overwrite करेगी। + +यह method एक function return करता है। और वही function एक request प्राप्त करेगा और response return करेगा। + +यहाँ हम इसका उपयोग original request से `GzipRequest` बनाने के लिए करते हैं। + +{* ../../docs_src/custom_request_and_route/tutorial001_an_py310.py hl[19:27] *} + +/// note | तकनीकी विवरण + +एक `Request` में `request.scope` attribute होता है, जो बस एक Python `dict` है जिसमें request से संबंधित metadata होता है। + +एक `Request` में `request.receive` भी होता है, जो request की body को "receive" करने के लिए एक function है। + +`scope` `dict` और `receive` function दोनों ASGI specification का हिस्सा हैं। + +और ये दो चीजें, `scope` और `receive`, नई `Request` instance बनाने के लिए आवश्यक हैं। + +`Request` के बारे में अधिक जानने के लिए [Requests के बारे में Starlette के docs](https://www.starlette.dev/requests/) देखें। + +/// + +`GzipRequest.get_route_handler` द्वारा return किए गए function का केवल एक अलग काम है: `Request` को `GzipRequest` में convert करना। + +ऐसा करने से, हमारा `GzipRequest` data को हमारे *path operations* तक पास करने से पहले decompress करने का ध्यान रखेगा (अगर आवश्यक हो)। + +उसके बाद, processing logic सब वही रहता है। + +लेकिन `GzipRequest.body` में हमारे बदलावों की वजह से, request body जरूरत पड़ने पर **FastAPI** द्वारा load किए जाने पर automatically decompress हो जाएगी। + +## Exception handler में request body तक पहुँचना { #accessing-the-request-body-in-an-exception-handler } + +/// tip | सुझाव + +इसी समस्या को हल करने के लिए, `RequestValidationError` के custom handler में `body` का उपयोग करना शायद बहुत आसान है ([Errors संभालना](../tutorial/handling-errors.md#use-the-requestvalidationerror-body))। + +लेकिन यह उदाहरण अभी भी valid है और यह दिखाता है कि internal components के साथ कैसे interact करें। + +/// + +हम इसी approach का उपयोग exception handler में request body तक पहुँचने के लिए भी कर सकते हैं। + +हमें बस request को `try`/`except` block के अंदर संभालना है: + +{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[14,16] *} + +अगर कोई exception होता है, तो `Request` instance अभी भी scope में होगी, इसलिए error संभालते समय हम request body को पढ़ सकते हैं और उसका उपयोग कर सकते हैं: + +{* ../../docs_src/custom_request_and_route/tutorial002_an_py310.py hl[17:19] *} + +## Router में custom `APIRoute` class { #custom-apiroute-class-in-a-router } + +आप `APIRouter` का `route_class` parameter भी set कर सकते हैं: + +{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[26] *} + +इस उदाहरण में, `router` के अंतर्गत *path operations* custom `TimedRoute` class का उपयोग करेंगे, और response में एक अतिरिक्त `X-Response-Time` header होगा जिसमें response generate करने में लगा समय होगा: + +{* ../../docs_src/custom_request_and_route/tutorial003_py310.py hl[13:20] *} diff --git a/docs/hi/docs/how-to/extending-openapi.md b/docs/hi/docs/how-to/extending-openapi.md new file mode 100644 index 000000000..f3bebfa66 --- /dev/null +++ b/docs/hi/docs/how-to/extending-openapi.md @@ -0,0 +1,88 @@ +# OpenAPI को विस्तारित करना { #extending-openapi } + +कुछ मामलों में आपको generated OpenAPI schema को संशोधित करने की ज़रूरत हो सकती है। + +इस section में आप देखेंगे कि कैसे। + +## सामान्य process { #the-normal-process } + +सामान्य (default) process इस प्रकार है। + +एक `FastAPI` application (instance) में एक `.openapi()` method होता है, जिससे OpenAPI schema return करने की अपेक्षा की जाती है। + +application object बनाने के हिस्से के रूप में, `/openapi.json` के लिए (या आपने अपने `openapi_url` में जो भी set किया है उसके लिए) एक *path operation* registered होता है। + +यह बस application के `.openapi()` method के result के साथ एक JSON response return करता है। + +Default रूप से, method `.openapi()` जो करता है वह यह है कि property `.openapi_schema` को check करता है कि उसमें contents हैं या नहीं, और उन्हें return करता है। + +अगर नहीं हैं, तो यह उन्हें `fastapi.openapi.utils.get_openapi` में utility function का उपयोग करके generate करता है। + +और वह function `get_openapi()` parameters के रूप में ये प्राप्त करता है: + +* `title`: OpenAPI title, जो docs में दिखाया जाता है। +* `version`: आपके API का version, जैसे `2.5.0`। +* `openapi_version`: उपयोग की गई OpenAPI specification का version। Default रूप से, latest: `3.1.0`। +* `summary`: API का एक छोटा summary। +* `description`: आपके API का description, इसमें markdown शामिल हो सकता है और यह docs में दिखाया जाएगा। +* `routes`: application से routes, जो `app.routes` से लिए जाते हैं। FastAPI इन्हें registered *path operations* collect करने के लिए उपयोग करता है, जिनमें included routers से आने वाले भी शामिल हैं। + +/// tip | तकनीकी विवरण + +`app.routes` एक lower-level route tree है। इसमें route candidates शामिल हो सकते हैं जिन्हें FastAPI internally included routers के लिए उपयोग करता है, केवल final `APIRoute` objects ही नहीं। + +आप फिर भी `app.routes` को `get_openapi()` में pass कर सकते हैं। FastAPI effective path operations collect करने के लिए उस route tree को traverse करेगा। + +/// + +/// note | नोट + +parameter `summary` OpenAPI 3.1.0 और उससे ऊपर में उपलब्ध है, जिसे FastAPI 0.99.0 और उससे ऊपर support करता है। + +/// + +## Defaults को override करना { #overriding-the-defaults } + +ऊपर दी गई जानकारी का उपयोग करके, आप OpenAPI schema generate करने और अपनी ज़रूरत के अनुसार प्रत्येक हिस्से को override करने के लिए उसी utility function का उपयोग कर सकते हैं। + +उदाहरण के लिए, आइए [custom logo शामिल करने के लिए ReDoc का OpenAPI extension](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md#x-logo) जोड़ें। + +### सामान्य **FastAPI** { #normal-fastapi } + +सबसे पहले, अपनी पूरी **FastAPI** application सामान्य रूप से लिखें: + +{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[1,4,7:9] *} + +### OpenAPI schema generate करें { #generate-the-openapi-schema } + +फिर, `custom_openapi()` function के अंदर, OpenAPI schema generate करने के लिए उसी utility function का उपयोग करें: + +{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[2,15:21] *} + +### OpenAPI schema को संशोधित करें { #modify-the-openapi-schema } + +अब आप OpenAPI schema में `info` "object" में custom `x-logo` जोड़कर ReDoc extension जोड़ सकते हैं: + +{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[22:24] *} + +### OpenAPI schema को cache करें { #cache-the-openapi-schema } + +आप अपनी generated schema store करने के लिए property `.openapi_schema` को "cache" के रूप में उपयोग कर सकते हैं। + +इस तरह, जब भी कोई user आपके API docs खोलेगा, आपकी application को हर बार schema generate नहीं करना पड़ेगा। + +यह केवल एक बार generate होगा, और फिर अगली requests के लिए वही cached schema उपयोग किया जाएगा। + +{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[13:14,25:26] *} + +### Method को override करें { #override-the-method } + +अब आप `.openapi()` method को अपने नए function से replace कर सकते हैं। + +{* ../../docs_src/extending_openapi/tutorial001_py310.py hl[29] *} + +### इसे check करें { #check-it } + +जब आप [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) पर जाएंगे, तो आप देखेंगे कि आप अपना custom logo उपयोग कर रहे हैं (इस उदाहरण में, **FastAPI** का logo): + + diff --git a/docs/hi/docs/how-to/general.md b/docs/hi/docs/how-to/general.md new file mode 100644 index 000000000..95c8bb653 --- /dev/null +++ b/docs/hi/docs/how-to/general.md @@ -0,0 +1,43 @@ +# सामान्य - How To - Recipes { #general-how-to-recipes } + +सामान्य या अक्सर पूछे जाने वाले प्रश्नों के लिए, docs में अन्य जगहों के कई pointers यहाँ दिए गए हैं। + +## Data फ़िल्टर करें - सुरक्षा { #filter-data-security } + +यह सुनिश्चित करने के लिए कि आप जितना data लौटाना चाहिए उससे अधिक न लौटाएँ, [Tutorial - Response Model - Return Type](../tutorial/response-model.md) के docs पढ़ें। + +## Response Performance को optimize करें - Response Model - Return Type { #optimize-response-performance-response-model-return-type } + +JSON data लौटाते समय performance को optimize करने के लिए, return type या response model का उपयोग करें, इस तरह Pydantic JSON में serialization को Rust side पर संभालेगा, Python से गुज़रे बिना। अधिक पढ़ें [Tutorial - Response Model - Return Type](../tutorial/response-model.md) के docs में। + +## Documentation Tags - OpenAPI { #documentation-tags-openapi } + +अपने *path operations* में tags जोड़ने और उन्हें docs UI में group करने के लिए, [Tutorial - Path Operation Configurations - Tags](../tutorial/path-operation-configuration.md#tags) के docs पढ़ें। + +## Documentation Summary और Description - OpenAPI { #documentation-summary-and-description-openapi } + +अपने *path operations* में summary और description जोड़ने, और उन्हें docs UI में दिखाने के लिए, [Tutorial - Path Operation Configurations - Summary and Description](../tutorial/path-operation-configuration.md#summary-and-description) के docs पढ़ें। + +## Documentation Response description - OpenAPI { #documentation-response-description-openapi } + +response का description define करने के लिए, जो docs UI में दिखाया जाता है, [Tutorial - Path Operation Configurations - Response description](../tutorial/path-operation-configuration.md#response-description) के docs पढ़ें। + +## Documentation में *Path Operation* को Deprecate करें - OpenAPI { #documentation-deprecate-a-path-operation-openapi } + +किसी *path operation* को deprecate करने और उसे docs UI में दिखाने के लिए, [Tutorial - Path Operation Configurations - Deprecation](../tutorial/path-operation-configuration.md#deprecate-a-path-operation) के docs पढ़ें। + +## किसी भी Data को JSON-compatible में convert करें { #convert-any-data-to-json-compatible } + +किसी भी data को JSON-compatible में convert करने के लिए, [Tutorial - JSON Compatible Encoder](../tutorial/encoder.md) के docs पढ़ें। + +## OpenAPI Metadata - Docs { #openapi-metadata-docs } + +अपने OpenAPI schema में metadata जोड़ने के लिए, जिसमें license, version, contact, आदि शामिल हैं, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md) के docs पढ़ें। + +## OpenAPI Custom URL { #openapi-custom-url } + +OpenAPI URL को customize करने (या हटाने) के लिए, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#openapi-url) के docs पढ़ें। + +## OpenAPI Docs URLs { #openapi-docs-urls } + +अपने-आप generate किए गए docs user interfaces के लिए उपयोग किए जाने वाले URLs को update करने के लिए, [Tutorial - Metadata and Docs URLs](../tutorial/metadata.md#docs-urls) के docs पढ़ें। diff --git a/docs/hi/docs/how-to/graphql.md b/docs/hi/docs/how-to/graphql.md new file mode 100644 index 000000000..7a8fbdb3d --- /dev/null +++ b/docs/hi/docs/how-to/graphql.md @@ -0,0 +1,60 @@ +# GraphQL { #graphql } + +क्योंकि **FastAPI** **ASGI** standard पर आधारित है, इसलिए ASGI के साथ compatible किसी भी **GraphQL** library को integrate करना बहुत आसान है। + +आप उसी application में सामान्य FastAPI *path operations* को GraphQL के साथ combine कर सकते हैं। + +/// tip | सुझाव + +**GraphQL** कुछ बहुत specific use cases को solve करता है। + +Common **web APIs** की तुलना में इसके **advantages** और **disadvantages** हैं। + +सुनिश्चित करें कि आप evaluate करें कि आपके use case के लिए **benefits**, **drawbacks** की भरपाई करते हैं या नहीं। 🤓 + +/// + +## GraphQL Libraries { #graphql-libraries } + +यहाँ कुछ **GraphQL** libraries हैं जिनमें **ASGI** support है। आप उन्हें **FastAPI** के साथ उपयोग कर सकते हैं: + +* [Strawberry](https://strawberry.rocks/) 🍓 + * [FastAPI के लिए docs](https://strawberry.rocks/docs/integrations/fastapi) के साथ +* [Ariadne](https://ariadnegraphql.org/) + * [FastAPI के लिए docs](https://ariadnegraphql.org/docs/fastapi-integration) के साथ +* [Tartiflette](https://tartiflette.io/) + * ASGI integration प्रदान करने के लिए [Tartiflette ASGI](https://tartiflette.github.io/tartiflette-asgi/) के साथ +* [Graphene](https://graphene-python.org/) + * [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3) के साथ + +## Strawberry के साथ GraphQL { #graphql-with-strawberry } + +यदि आपको **GraphQL** के साथ काम करने की ज़रूरत है या आप करना चाहते हैं, तो [**Strawberry**](https://strawberry.rocks/) **recommended** library है क्योंकि इसका design **FastAPI** के design के सबसे करीब है, यह पूरी तरह **type annotations** पर आधारित है। + +आपके use case के आधार पर, आप कोई अलग library उपयोग करना पसंद कर सकते हैं, लेकिन अगर आप मुझसे पूछें, तो मैं शायद सुझाव दूँगा कि आप **Strawberry** आज़माएँ। + +यहाँ एक छोटा preview है कि आप Strawberry को FastAPI के साथ कैसे integrate कर सकते हैं: + +{* ../../docs_src/graphql_/tutorial001_py310.py hl[3,22,25] *} + +आप [Strawberry documentation](https://strawberry.rocks/) में Strawberry के बारे में और जान सकते हैं। + +और [FastAPI के साथ Strawberry](https://strawberry.rocks/docs/integrations/fastapi) के बारे में docs भी। + +## Starlette से पुराना `GraphQLApp` { #older-graphqlapp-from-starlette } + +Starlette के पिछले versions में [Graphene](https://graphene-python.org/) के साथ integrate करने के लिए एक `GraphQLApp` class शामिल थी। + +इसे Starlette से deprecated कर दिया गया था, लेकिन यदि आपके पास ऐसा code है जो इसका उपयोग करता था, तो आप आसानी से [starlette-graphene3](https://github.com/ciscorn/starlette-graphene3) पर **migrate** कर सकते हैं, जो वही use case cover करता है और जिसका **लगभग identical interface** है। + +/// tip | सुझाव + +यदि आपको GraphQL की ज़रूरत है, तो मैं फिर भी recommend करूँगा कि आप [Strawberry](https://strawberry.rocks/) देखें, क्योंकि यह custom classes और types की बजाय type annotations पर आधारित है। + +/// + +## और जानें { #learn-more } + +आप [official GraphQL documentation](https://graphql.org/) में **GraphQL** के बारे में और जान सकते हैं। + +आप ऊपर वर्णित उन libraries में से प्रत्येक के बारे में उनके links में और भी पढ़ सकते हैं। diff --git a/docs/hi/docs/how-to/index.md b/docs/hi/docs/how-to/index.md new file mode 100644 index 000000000..c42c2dd0c --- /dev/null +++ b/docs/hi/docs/how-to/index.md @@ -0,0 +1,13 @@ +# कैसे करें - रेसिपी { #how-to-recipes } + +यहाँ आप **कई topics** के लिए अलग-अलग recipes या "how to" guides देखेंगे। + +इनमें से ज़्यादातर ideas कमोबेश **स्वतंत्र** होंगे, और ज़्यादातर मामलों में आपको इन्हें केवल तभी पढ़ने की ज़रूरत होगी जब वे सीधे **आपके project** पर लागू होते हों। + +अगर कुछ आपके project के लिए रोचक और उपयोगी लगता है, तो आगे बढ़कर उसे देखें, लेकिन नहीं तो आप शायद उन्हें छोड़ सकते हैं। + +/// tip | सुझाव + +अगर आप **FastAPI सीखना** structured तरीके से चाहते हैं (recommended), तो इसके बजाय [ट्यूटोरियल - यूज़र गाइड](../tutorial/index.md) को अध्याय-दर-अध्याय पढ़ें। + +/// diff --git a/docs/hi/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md b/docs/hi/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md new file mode 100644 index 000000000..68bc07e30 --- /dev/null +++ b/docs/hi/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md @@ -0,0 +1,153 @@ +# Pydantic v1 से Pydantic v2 में माइग्रेट करें { #migrate-from-pydantic-v1-to-pydantic-v2 } + +अगर आपके पास कोई पुराना FastAPI app है, तो हो सकता है कि आप Pydantic version 1 का उपयोग कर रहे हों। + +FastAPI version 0.100.0 में Pydantic v1 या v2, दोनों में से किसी के लिए support था। यह वही उपयोग करता था जो आपने install किया हुआ था। + +FastAPI version 0.119.0 ने Pydantic v2 के अंदर से Pydantic v1 के लिए आंशिक support पेश किया (`pydantic.v1` के रूप में), ताकि v2 में migration आसान हो सके। + +FastAPI 0.126.0 ने Pydantic v1 के लिए support हटा दिया, लेकिन थोड़े समय के लिए `pydantic.v1` को support करना जारी रखा। + +FastAPI 0.128.0 ने `pydantic.v1` के लिए support भी हटा दिया, इसलिए FastAPI के नवीनतम versions के लिए Pydantic v2 required है। + +/// warning | चेतावनी + +Pydantic team ने **Python 3.14** से शुरू करते हुए, Python के नवीनतम versions के लिए Pydantic v1 का support बंद कर दिया। + +इसमें `pydantic.v1` भी शामिल है, जो अब Python 3.14 और उससे ऊपर में supported नहीं है। + +अगर आप Python की नवीनतम features का उपयोग करना चाहते हैं, तो आपको यह सुनिश्चित करना होगा कि आप Pydantic v2 का उपयोग करें। + +/// + +अगर आपके पास Pydantic v1 वाला कोई पुराना FastAPI app है, तो यहाँ मैं आपको दिखाऊँगा कि उसे Pydantic v2 में कैसे migrate करें, और gradual migration में मदद के लिए **FastAPI 0.119.0 की features** भी दिखाऊँगा। + +## आधिकारिक गाइड { #official-guide } + +Pydantic के पास v1 से v2 के लिए एक आधिकारिक [Migration Guide](https://docs.pydantic.dev/latest/migration/) है। + +इसमें यह भी शामिल है कि क्या बदला है, validations अब कैसे अधिक सही और strict हैं, संभावित caveats आदि। + +क्या बदला है इसे बेहतर समझने के लिए आप इसे पढ़ सकते हैं। + +## Tests { #tests } + +सुनिश्चित करें कि आपके app के लिए [tests](../tutorial/testing.md) हैं और आप उन्हें continuous integration (CI) पर चलाते हैं। + +इस तरह, आप upgrade कर सकते हैं और सुनिश्चित कर सकते हैं कि सब कुछ अभी भी अपेक्षा के अनुसार काम कर रहा है। + +## `bump-pydantic` { #bump-pydantic } + +कई मामलों में, जब आप customizations के बिना regular Pydantic models का उपयोग करते हैं, तो आप Pydantic v1 से Pydantic v2 में migration की अधिकांश प्रक्रिया automate कर पाएँगे। + +आप उसी Pydantic team का [`bump-pydantic`](https://github.com/pydantic/bump-pydantic) उपयोग कर सकते हैं। + +यह tool आपको उस अधिकांश code को अपने आप बदलने में मदद करेगा जिसे बदलने की ज़रूरत है। + +इसके बाद, आप tests चला सकते हैं और जाँच सकते हैं कि सब कुछ काम करता है या नहीं। अगर करता है, तो आपका काम हो गया। 😎 + +## v2 में Pydantic v1 { #pydantic-v1-in-v2 } + +Pydantic v2 में Pydantic v1 की सभी चीज़ें `pydantic.v1` submodule के रूप में शामिल हैं। लेकिन यह Python 3.13 से ऊपर के versions में अब supported नहीं है। + +इसका मतलब है कि आप Pydantic v2 का नवीनतम version install कर सकते हैं और इस submodule से पुराने Pydantic v1 components को import और उपयोग कर सकते हैं, जैसे कि आपके पास पुराना Pydantic v1 install हो। + +{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *} + +### v2 में Pydantic v1 के लिए FastAPI support { #fastapi-support-for-pydantic-v1-in-v2 } + +/// warning | चेतावनी + +`pydantic.v1` models के लिए यह FastAPI support **FastAPI 0.119.0** में जोड़ा गया था और **FastAPI 0.128.0** में हटा दिया गया। इसका उद्देश्य Pydantic v2 में migration के लिए अस्थायी सहायता होना था। + +FastAPI के वर्तमान versions में, अपने app में `pydantic.v1` model का उपयोग करने पर error आएगा। + +इस section का बाकी हिस्सा केवल उन पुराने versions में उपलब्ध अस्थायी support का वर्णन करता है। + +/// + +FastAPI 0.119.0 से, Pydantic v2 के अंदर से Pydantic v1 के लिए आंशिक support भी है, ताकि v2 में migration आसान हो सके। + +इसलिए, आप Pydantic को नवीनतम version 2 में upgrade कर सकते थे, और imports को `pydantic.v1` submodule का उपयोग करने के लिए बदल सकते थे, और कई मामलों में यह बस काम कर जाता। + +{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *} + +/// warning | चेतावनी + +ध्यान रखें कि Pydantic team अब Python के हाल के versions में Pydantic v1 को support नहीं करती, Python 3.14 से शुरू करते हुए, इसलिए `pydantic.v1` का उपयोग भी Python 3.14 और उससे ऊपर में supported नहीं है। + +/// + +### एक ही app में Pydantic v1 और v2 { #pydantic-v1-and-v2-on-the-same-app } + +Pydantic द्वारा यह **supported नहीं है** कि Pydantic v2 का कोई model हो जिसके अपने fields Pydantic v1 models के रूप में defined हों, या इसका उल्टा। + +```mermaid +graph TB + subgraph "❌ Not Supported" + direction TB + subgraph V2["Pydantic v2 Model"] + V1Field["Pydantic v1 Model"] + end + subgraph V1["Pydantic v1 Model"] + V2Field["Pydantic v2 Model"] + end + end + + style V2 fill:#f9fff3 + style V1 fill:#fff6f0 + style V1Field fill:#fff6f0 + style V2Field fill:#f9fff3 +``` + +...लेकिन आपके पास एक ही app में अलग-अलग models हो सकते हैं, कुछ Pydantic v1 का उपयोग करते हुए और कुछ Pydantic v2 का उपयोग करते हुए। + +```mermaid +graph TB + subgraph "✅ Supported" + direction TB + subgraph V2["Pydantic v2 Model"] + V2Field["Pydantic v2 Model"] + end + subgraph V1["Pydantic v1 Model"] + V1Field["Pydantic v1 Model"] + end + end + + style V2 fill:#f9fff3 + style V1 fill:#fff6f0 + style V1Field fill:#fff6f0 + style V2Field fill:#f9fff3 +``` + +कुछ मामलों में, आपके FastAPI app में एक ही **path operation** में Pydantic v1 और v2 दोनों models होना भी संभव है: + +{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *} + +ऊपर दिए गए इस उदाहरण में, input model एक Pydantic v1 model है, और output model (`response_model=ItemV2` में defined) एक Pydantic v2 model है। + +### Pydantic v1 parameters { #pydantic-v1-parameters } + +अगर आपको Pydantic v1 models के साथ parameters के लिए FastAPI-specific tools जैसे `Body`, `Query`, `Form` आदि का उपयोग करना है, तो Pydantic v2 में migration पूरा करते समय आप उन्हें `fastapi.temp_pydantic_v1_params` से import कर सकते हैं: + +{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *} + +### चरणों में माइग्रेट करें { #migrate-in-steps } + +/// warning | चेतावनी + +नीचे वर्णित, एक ही app में Pydantic v1 और v2 दोनों models का उपयोग करके gradual migration केवल **FastAPI 0.119.0 से 0.127.x** में काम करता है। इसे **FastAPI 0.128.0** में हटा दिया गया, नवीनतम versions के लिए **Pydantic v2** models required हैं। + +/// + +/// tip | सुझाव + +पहले `bump-pydantic` के साथ कोशिश करें, अगर आपके tests pass हो जाते हैं और वह काम करता है, तो आपका काम एक command में हो गया। ✨ + +/// + +अगर `bump-pydantic` आपके use case के लिए काम नहीं करता, तो आप Pydantic v2 में gradual migration करने के लिए एक ही app में Pydantic v1 और v2 दोनों models के support का उपयोग कर सकते हैं। + +आप पहले Pydantic को नवीनतम version 2 का उपयोग करने के लिए upgrade कर सकते हैं, और अपने सभी models के लिए `pydantic.v1` का उपयोग करने के लिए imports बदल सकते हैं। + +फिर, आप gradual steps में, groups के रूप में अपने models को Pydantic v1 से v2 में migrate करना शुरू कर सकते हैं। 🚶 diff --git a/docs/hi/docs/how-to/separate-openapi-schemas.md b/docs/hi/docs/how-to/separate-openapi-schemas.md new file mode 100644 index 000000000..c5d2b17a0 --- /dev/null +++ b/docs/hi/docs/how-to/separate-openapi-schemas.md @@ -0,0 +1,102 @@ +# Input और Output के लिए अलग OpenAPI Schemas या नहीं { #separate-openapi-schemas-for-input-and-output-or-not } + +जबसे **Pydantic v2** रिलीज़ हुआ है, generated OpenAPI पहले की तुलना में थोड़ा अधिक सटीक और **सही** है। 😎 + +वास्तव में, कुछ मामलों में, एक ही Pydantic model के लिए OpenAPI में **दो JSON Schemas** भी होंगे, input और output के लिए, इस पर निर्भर करते हुए कि उनमें **default values** हैं या नहीं। + +आइए देखते हैं कि यह कैसे काम करता है और अगर आपको ज़रूरत हो तो इसे कैसे बदलना है। + +## Input और Output के लिए Pydantic Models { #pydantic-models-for-input-and-output } + +मान लीजिए आपके पास default values वाला एक Pydantic model है, जैसे यह: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:7] hl[7] *} + +### Input के लिए Model { #model-for-input } + +अगर आप इस model को यहाँ की तरह input के रूप में उपयोग करते हैं: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py ln[1:15] hl[14] *} + +...तो `description` field **required नहीं होगा**। क्योंकि इसका default value `None` है। + +### Docs में Input Model { #input-model-in-docs } + +आप docs में इसकी पुष्टि कर सकते हैं, `description` field के पास **लाल asterisk** नहीं है, इसे required के रूप में mark नहीं किया गया है: + +
+ +
+ +### Output के लिए Model { #model-for-output } + +लेकिन अगर आप उसी model को output के रूप में उपयोग करते हैं, जैसे यहाँ: + +{* ../../docs_src/separate_openapi_schemas/tutorial001_py310.py hl[19] *} + +...तो क्योंकि `description` का default value है, अगर आप उस field के लिए **कुछ भी return नहीं करते**, तब भी इसका वही **default value** रहेगा। + +### Output Response Data के लिए Model { #model-for-output-response-data } + +अगर आप docs के साथ interact करते हैं और response जाँचते हैं, तो भले ही code ने `description` fields में से किसी एक में कुछ भी add नहीं किया, JSON response में default value (`null`) शामिल होता है: + +
+ +
+ +इसका मतलब है कि इसमें **हमेशा एक value होगा**, बस कभी-कभी value `None` हो सकता है (या JSON में `null`)। + +इसका मतलब है कि आपकी API का उपयोग करने वाले clients को यह जाँचने की ज़रूरत नहीं है कि value मौजूद है या नहीं, वे **मान सकते हैं कि field हमेशा मौजूद रहेगा**, बस कुछ मामलों में इसका default value `None` होगा। + +OpenAPI में इसे describe करने का तरीका है कि उस field को **required** के रूप में mark किया जाए, क्योंकि वह हमेशा मौजूद रहेगा। + +इस वजह से, किसी model के लिए JSON Schema अलग हो सकता है, यह इस पर निर्भर करता है कि उसे **input या output** के लिए उपयोग किया गया है: + +* **input** के लिए `description` **required नहीं होगा** +* **output** के लिए यह **required** होगा (और संभवतः `None`, या JSON terms में, `null`) + +### Docs में Output के लिए Model { #model-for-output-in-docs } + +आप docs में output model भी देख सकते हैं, **दोनों** `name` और `description` को **लाल asterisk** के साथ **required** के रूप में mark किया गया है: + +
+ +
+ +### Docs में Input और Output के लिए Model { #model-for-input-and-output-in-docs } + +और अगर आप OpenAPI में उपलब्ध सभी Schemas (JSON Schemas) जाँचते हैं, तो आप देखेंगे कि दो हैं, एक `Item-Input` और एक `Item-Output`। + +`Item-Input` के लिए, `description` **required नहीं है**, इसमें लाल asterisk नहीं है। + +लेकिन `Item-Output` के लिए, `description` **required** है, इसमें लाल asterisk है। + +
+ +
+ +**Pydantic v2** की इस feature के साथ, आपकी API documentation अधिक **precise** होती है, और अगर आपके पास autogenerated clients और SDKs हैं, तो वे भी अधिक precise होंगे, बेहतर **developer experience** और consistency के साथ। 🎉 + +## Schemas को अलग न करें { #do-not-separate-schemas } + +अब, कुछ मामले ऐसे हैं जहाँ आप **input और output के लिए same schema** रखना चाह सकते हैं। + +शायद इसका मुख्य use case यह है कि अगर आपके पास पहले से कुछ autogenerated client code/SDKs हैं और आप अभी सभी autogenerated client code/SDKs को update नहीं करना चाहते, तो शायद आप इसे किसी समय करना चाहेंगे, लेकिन शायद अभी नहीं। + +उस स्थिति में, आप **FastAPI** में इस feature को parameter `separate_input_output_schemas=False` के साथ disable कर सकते हैं। + +/// note | नोट + +`separate_input_output_schemas` के लिए support FastAPI `0.102.0` में add किया गया था। 🤓 + +/// + +{* ../../docs_src/separate_openapi_schemas/tutorial002_py310.py hl[10] *} + +### Docs में Input और Output Models के लिए Same Schema { #same-schema-for-input-and-output-models-in-docs } + +और अब model के लिए input और output के लिए केवल एक single schema होगा, सिर्फ `Item`, और इसमें `description` **required नहीं** होगा: + +
+ +
diff --git a/docs/hi/docs/how-to/testing-database.md b/docs/hi/docs/how-to/testing-database.md new file mode 100644 index 000000000..c5189ca21 --- /dev/null +++ b/docs/hi/docs/how-to/testing-database.md @@ -0,0 +1,7 @@ +# Database की Testing { #testing-a-database } + +आप databases, SQL, और SQLModel के बारे में [SQLModel docs](https://sqlmodel.tiangolo.com/) में पढ़ सकते हैं। 🤓 + +[FastAPI के साथ SQLModel इस्तेमाल करने पर एक छोटा tutorial](https://sqlmodel.tiangolo.com/tutorial/fastapi/) है। ✨ + +उस tutorial में [SQL databases की testing](https://sqlmodel.tiangolo.com/tutorial/fastapi/tests/) के बारे में एक section शामिल है। 😎 diff --git a/docs/hi/docs/learn/index.md b/docs/hi/docs/learn/index.md new file mode 100644 index 000000000..20f63c173 --- /dev/null +++ b/docs/hi/docs/learn/index.md @@ -0,0 +1,5 @@ +# सीखें { #learn } + +यहाँ **FastAPI** सीखने के लिए परिचयात्मक section और ट्यूटोरियल हैं। + +आप इसे FastAPI सीखने का एक **book**, एक **course**, **आधिकारिक** और अनुशंसित तरीका मान सकते हैं। 😎 diff --git a/docs/hi/docs/resources/index.md b/docs/hi/docs/resources/index.md new file mode 100644 index 000000000..d60effdc7 --- /dev/null +++ b/docs/hi/docs/resources/index.md @@ -0,0 +1,3 @@ +# संसाधन { #resources } + +अतिरिक्त संसाधन, external links, और भी बहुत कुछ। ✈️ diff --git a/docs/hi/docs/tutorial/background-tasks.md b/docs/hi/docs/tutorial/background-tasks.md new file mode 100644 index 000000000..f1c685644 --- /dev/null +++ b/docs/hi/docs/tutorial/background-tasks.md @@ -0,0 +1,86 @@ +# Background Tasks { #background-tasks } + +आप background tasks define कर सकते हैं जिन्हें response लौटाने के *बाद* चलाया जाए। + +यह उन operations के लिए उपयोगी है जिन्हें request के बाद होना होता है, लेकिन client को response पाने से पहले operation के पूरा होने का इंतज़ार करने की वास्तव में ज़रूरत नहीं होती। + +इसमें, उदाहरण के लिए, ये शामिल हैं: + +* कोई action करने के बाद भेजे गए email notifications: + * क्योंकि email server से connect करना और email भेजना आम तौर पर "slow" (कई seconds) होता है, आप response तुरंत लौटा सकते हैं और email notification को background में भेज सकते हैं। +* data process करना: + * उदाहरण के लिए, मान लीजिए आपको एक file मिलती है जिसे किसी slow process से गुजरना है, आप "Accepted" (HTTP 202) का response लौटा सकते हैं और file को background में process कर सकते हैं। + +## `BackgroundTasks` का उपयोग करना { #using-backgroundtasks } + +सबसे पहले, `BackgroundTasks` import करें और अपनी *path operation function* में `BackgroundTasks` की type declaration के साथ एक parameter define करें: + +{* ../../docs_src/background_tasks/tutorial001_py310.py hl[1,13] *} + +**FastAPI** आपके लिए `BackgroundTasks` type का object बनाएगा और उसे उस parameter के रूप में pass करेगा। + +## task function बनाएँ { #create-a-task-function } + +background task के रूप में चलाने के लिए एक function बनाएँ। + +यह बस एक standard function है जो parameters receive कर सकता है। + +यह `async def` या normal `def` function हो सकता है, **FastAPI** जानता होगा कि इसे सही तरीके से कैसे handle करना है। + +इस case में, task function एक file में लिखेगा (email भेजने का simulation करते हुए)। + +और क्योंकि write operation `async` और `await` का उपयोग नहीं करता, हम function को normal `def` के साथ define करते हैं: + +{* ../../docs_src/background_tasks/tutorial001_py310.py hl[6:9] *} + +## background task जोड़ें { #add-the-background-task } + +अपनी *path operation function* के अंदर, अपनी task function को *background tasks* object में method `.add_task()` के साथ pass करें: + +{* ../../docs_src/background_tasks/tutorial001_py310.py hl[14] *} + +`.add_task()` arguments के रूप में receive करता है: + +* background में चलाने के लिए एक task function (`write_notification`)। +* arguments की कोई भी sequence जो order में task function को pass की जानी चाहिए (`email`)। +* कोई भी keyword arguments जो task function को pass किए जाने चाहिए (`message="some notification"`)। + +## Dependency Injection { #dependency-injection } + +`BackgroundTasks` का उपयोग dependency injection system के साथ भी काम करता है, आप कई levels पर `BackgroundTasks` type का parameter declare कर सकते हैं: किसी *path operation function* में, dependency (dependable) में, sub-dependency में, आदि। + +**FastAPI** जानता है कि हर case में क्या करना है और same object को कैसे reuse करना है, ताकि सभी background tasks merge हो जाएँ और बाद में background में run हों: + + +{* ../../docs_src/background_tasks/tutorial002_an_py310.py hl[13,15,22,25] *} + + +इस example में, response भेजे जाने के *बाद* messages `log.txt` file में लिखे जाएँगे। + +अगर request में कोई query थी, तो उसे एक background task में log में लिखा जाएगा। + +और फिर *path operation function* पर generate हुआ एक और background task `email` path parameter का उपयोग करके एक message लिखेगा। + +## Technical Details { #technical-details } + +class `BackgroundTasks` सीधे [`starlette.background`](https://www.starlette.dev/background/) से आती है। + +इसे सीधे FastAPI में import/include किया गया है ताकि आप इसे `fastapi` से import कर सकें और गलती से `starlette.background` से alternative `BackgroundTask` (अंत में `s` के बिना) import करने से बच सकें। + +सिर्फ `BackgroundTasks` (और `BackgroundTask` नहीं) का उपयोग करने से, इसे *path operation function* parameter के रूप में use करना संभव होता है और **FastAPI** आपके लिए बाकी चीज़ें handle करता है, ठीक वैसे ही जैसे `Request` object को सीधे use करते समय होता है। + +FastAPI में अकेले `BackgroundTask` का उपयोग करना अभी भी संभव है, लेकिन आपको अपने code में object बनाना होगा और उसे शामिल करते हुए Starlette `Response` return करना होगा। + +आप [Background Tasks के लिए Starlette के official docs](https://www.starlette.dev/background/) में अधिक details देख सकते हैं। + +## सावधानी { #caveat } + +अगर आपको heavy background computation perform करनी है और यह ज़रूरी नहीं है कि वह same process द्वारा run हो (उदाहरण के लिए, आपको memory, variables, आदि share करने की ज़रूरत नहीं है), तो आपको [Celery](https://docs.celeryq.dev) जैसे दूसरे बड़े tools का उपयोग करने से लाभ हो सकता है। + +उन्हें आम तौर पर अधिक complex configurations, RabbitMQ या Redis जैसे message/job queue manager की ज़रूरत होती है, लेकिन वे आपको multiple processes में, और खासकर multiple servers में, background tasks run करने देते हैं। + +लेकिन अगर आपको उसी **FastAPI** app से variables और objects access करने हैं, या आपको छोटे background tasks perform करने हैं (जैसे email notification भेजना), तो आप आसानी से `BackgroundTasks` का उपयोग कर सकते हैं। + +## Recap { #recap } + +background tasks जोड़ने के लिए *path operation functions* और dependencies में parameters के साथ `BackgroundTasks` import और use करें। diff --git a/docs/hi/docs/tutorial/bigger-applications.md b/docs/hi/docs/tutorial/bigger-applications.md new file mode 100644 index 000000000..263a59397 --- /dev/null +++ b/docs/hi/docs/tutorial/bigger-applications.md @@ -0,0 +1,547 @@ +# बड़े Applications - Multiple Files { #bigger-applications-multiple-files } + +अगर आप कोई application या web API बना रहे हैं, तो ऐसा कम ही होता है कि आप सब कुछ एक ही file में रख सकें। + +**FastAPI** आपके application को structure करने के लिए एक सुविधाजनक tool देता है, और साथ ही पूरी flexibility भी बनाए रखता है। + +/// note | नोट + +अगर आप Flask से आए हैं, तो यह Flask के Blueprints के बराबर होगा। + +/// + +## एक उदाहरण file structure { #an-example-file-structure } + +मान लीजिए आपके पास ऐसा file structure है: + +``` +. +├── app +│ ├── __init__.py +│ ├── main.py +│ ├── dependencies.py +│ └── routers +│ │ ├── __init__.py +│ │ ├── items.py +│ │ └── users.py +│ └── internal +│ ├── __init__.py +│ └── admin.py +``` + +/// tip | टिप + +कई `__init__.py` files हैं: हर directory या subdirectory में एक। + +यही एक file से दूसरी file में code import करने की अनुमति देता है। + +उदाहरण के लिए, `app/main.py` में आपके पास ऐसी line हो सकती है: + +``` +from app.routers import items +``` + +/// + +* `app` directory में सब कुछ है। और इसमें एक खाली file `app/__init__.py` है, इसलिए यह एक "Python package" है ("Python modules" का संग्रह): `app`. +* इसमें एक `app/main.py` file है। क्योंकि यह एक Python package (एक ऐसी directory जिसमें `__init__.py` file है) के अंदर है, यह उस package का एक "module" है: `app.main`. +* एक `app/dependencies.py` file भी है, `app/main.py` की तरह, यह एक "module" है: `app.dependencies`. +* एक subdirectory `app/routers/` है जिसमें एक और file `__init__.py` है, इसलिए यह एक "Python subpackage" है: `app.routers`. +* file `app/routers/items.py` एक package, `app/routers/`, के अंदर है, इसलिए यह एक submodule है: `app.routers.items`. +* `app/routers/users.py` के साथ भी वही है, यह एक और submodule है: `app.routers.users`. +* एक subdirectory `app/internal/` भी है जिसमें एक और file `__init__.py` है, इसलिए यह एक और "Python subpackage" है: `app.internal`. +* और file `app/internal/admin.py` एक और submodule है: `app.internal.admin`. + + + +वही file structure comments के साथ: + +```bash +. +├── app # "app" एक Python package है +│   ├── __init__.py # यह file "app" को "Python package" बनाती है +│   ├── main.py # "main" module, जैसे import app.main +│   ├── dependencies.py # "dependencies" module, जैसे import app.dependencies +│   └── routers # "routers" एक "Python subpackage" है +│   │ ├── __init__.py # "routers" को "Python subpackage" बनाता है +│   │ ├── items.py # "items" submodule, जैसे import app.routers.items +│   │ └── users.py # "users" submodule, जैसे import app.routers.users +│   └── internal # "internal" एक "Python subpackage" है +│   ├── __init__.py # "internal" को "Python subpackage" बनाता है +│   └── admin.py # "admin" submodule, जैसे import app.internal.admin +``` + +## `APIRouter` { #apirouter } + +मान लीजिए सिर्फ users को handle करने के लिए dedicated file `/app/routers/users.py` पर submodule है। + +आप अपने users से संबंधित *path operations* को बाकी code से अलग रखना चाहते हैं, ताकि यह व्यवस्थित रहे। + +लेकिन यह अभी भी उसी **FastAPI** application/web API का हिस्सा है (यह उसी "Python Package" का हिस्सा है)। + +आप उस module के लिए *path operations* `APIRouter` का उपयोग करके बना सकते हैं। + +### `APIRouter` import करें { #import-apirouter } + +आप इसे import करते हैं और उसी तरह एक "instance" बनाते हैं जैसे आप `FastAPI` class के साथ करते: + +{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[1,3] title["app/routers/users.py"] *} + +### `APIRouter` के साथ *Path operations* { #path-operations-with-apirouter } + +और फिर आप इसका उपयोग अपने *path operations* declare करने के लिए करते हैं। + +इसे उसी तरह उपयोग करें जैसे आप `FastAPI` class का उपयोग करते: + +{* ../../docs_src/bigger_applications/app_an_py310/routers/users.py hl[6,11,16] title["app/routers/users.py"] *} + +आप `APIRouter` को एक "mini `FastAPI`" class की तरह सोच सकते हैं। + +सभी वही options समर्थित हैं। + +सभी वही `parameters`, `responses`, `dependencies`, `tags`, आदि। + +/// tip | टिप + +इस उदाहरण में, variable को `router` कहा गया है, लेकिन आप इसे अपनी इच्छा अनुसार कोई भी नाम दे सकते हैं। + +/// + +हम इस `APIRouter` को main `FastAPI` app में शामिल करने जा रहे हैं, लेकिन पहले, dependencies और एक और `APIRouter` देखें। + +## Dependencies { #dependencies } + +हम देखते हैं कि हमें application के कई स्थानों पर उपयोग होने वाली कुछ dependencies की ज़रूरत होगी। + +इसलिए हम उन्हें उनके अपने `dependencies` module (`app/dependencies.py`) में रखते हैं। + +अब हम एक custom `X-Token` header पढ़ने के लिए एक सरल dependency का उपयोग करेंगे: + +{* ../../docs_src/bigger_applications/app_an_py310/dependencies.py hl[3,6:8] title["app/dependencies.py"] *} + +/// tip | टिप + +हम इस उदाहरण को सरल बनाने के लिए एक काल्पनिक header का उपयोग कर रहे हैं। + +लेकिन वास्तविक मामलों में आपको integrated [Security utilities](security/index.md) का उपयोग करके बेहतर परिणाम मिलेंगे। + +/// + +## `APIRouter` के साथ एक और module { #another-module-with-apirouter } + +मान लीजिए आपके application से "items" को handle करने के लिए dedicated endpoints भी `app/routers/items.py` module में हैं। + +आपके पास इनके लिए *path operations* हैं: + +* `/items/` +* `/items/{item_id}` + +यह सब `app/routers/users.py` जैसी ही structure है। + +लेकिन हम थोड़े अधिक smart होना चाहते हैं और code को थोड़ा simplify करना चाहते हैं। + +हम जानते हैं कि इस module के सभी *path operations* में वही हैं: + +* Path `prefix`: `/items`. +* `tags`: (सिर्फ एक tag: `items`). +* अतिरिक्त `responses`. +* `dependencies`: उन सभी को वह `X-Token` dependency चाहिए जो हमने बनाई है। + +इसलिए, यह सब प्रत्येक *path operation* में जोड़ने के बजाय, हम इसे `APIRouter` में जोड़ सकते हैं। + +{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[5:10,16,21] title["app/routers/items.py"] *} + +क्योंकि प्रत्येक *path operation* का path `/` से शुरू होना चाहिए, जैसे: + +```Python hl_lines="1" +@router.get("/{item_id}") +async def read_item(item_id: str): + ... +``` + +...prefix में अंत में `/` शामिल नहीं होना चाहिए। + +इसलिए, इस मामले में prefix `/items` है। + +हम `tags` की list और अतिरिक्त `responses` भी जोड़ सकते हैं जो इस router में शामिल सभी *path operations* पर लागू होंगे। + +और हम `dependencies` की list जोड़ सकते हैं जो router के सभी *path operations* में जोड़ी जाएगी और उन पर किए गए हर request के लिए execute/solve की जाएगी। + +/// tip | टिप + +ध्यान दें कि, [*path operation decorators* में dependencies](dependencies/dependencies-in-path-operation-decorators.md) की तरह ही, आपके *path operation function* को कोई value pass नहीं की जाएगी। + +/// + +अंतिम परिणाम यह है कि item paths अब ये हैं: + +* `/items/` +* `/items/{item_id}` + +...जैसा हमने चाहा था। + +* उन्हें tags की list से mark किया जाएगा जिसमें एक ही string `"items"` होगी। + * ये "tags" automatic interactive documentation systems (OpenAPI का उपयोग करते हुए) के लिए विशेष रूप से उपयोगी हैं। +* उन सभी में predefined `responses` शामिल होंगे। +* इन सभी *path operations* से पहले `dependencies` की list evaluate/execute की जाएगी। + * अगर आप किसी specific *path operation* में भी dependencies declare करते हैं, **तो वे भी execute होंगी**। + * router dependencies पहले execute होती हैं, फिर decorator में [`dependencies`](dependencies/dependencies-in-path-operation-decorators.md), और फिर normal parameter dependencies। + * आप [`Security` dependencies with `scopes`](../advanced/security/oauth2-scopes.md) भी जोड़ सकते हैं। + +/// tip | टिप + +`APIRouter` में `dependencies` होने का उपयोग, उदाहरण के लिए, *path operations* के पूरे group के लिए authentication require करने के लिए किया जा सकता है। भले ही dependencies उनमें से हर एक में individually न जोड़ी गई हों। + +/// + +/// tip | टिप + +`prefix`, `tags`, `responses`, और `dependencies` parameters (कई अन्य मामलों की तरह) **FastAPI** की एक feature हैं जो आपको code duplication से बचने में मदद करती है। + +/// + +### dependencies import करें { #import-the-dependencies } + +यह code `app.routers.items` module, file `app/routers/items.py` में रहता है। + +और हमें dependency function `app.dependencies` module, file `app/dependencies.py` से लेनी है। + +इसलिए हम dependencies के लिए `..` के साथ relative import का उपयोग करते हैं: + +{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[3] title["app/routers/items.py"] *} + +#### Relative imports कैसे काम करते हैं { #how-relative-imports-work } + +/// tip | टिप + +अगर आप पूरी तरह जानते हैं कि imports कैसे काम करते हैं, तो नीचे वाले अगले section पर जाएँ। + +/// + +एक single dot `.`, जैसे: + +```Python +from .dependencies import get_token_header +``` + +का मतलब होगा: + +* उसी package से शुरू करना जिसमें यह module (file `app/routers/items.py`) रहता है (directory `app/routers/`)... +* module `dependencies` ढूँढना (`app/routers/dependencies.py` पर एक काल्पनिक file)... +* और उससे, function `get_token_header` import करना। + +लेकिन वह file मौजूद नहीं है, हमारी dependencies `app/dependencies.py` पर एक file में हैं। + +याद रखें कि हमारी app/file structure कैसी दिखती है: + + + +--- + +दो dots `..`, जैसे: + +```Python +from ..dependencies import get_token_header +``` + +का मतलब है: + +* उसी package से शुरू करना जिसमें यह module (file `app/routers/items.py`) रहता है (directory `app/routers/`)... +* parent package (directory `app/`) पर जाना... +* और वहाँ, module `dependencies` ढूँढना (file `app/dependencies.py` पर)... +* और उससे, function `get_token_header` import करना। + +यह सही तरीके से काम करता है! 🎉 + +--- + +उसी तरह, अगर हमने तीन dots `...` का उपयोग किया होता, जैसे: + +```Python +from ...dependencies import get_token_header +``` + +तो उसका मतलब होगा: + +* उसी package से शुरू करना जिसमें यह module (file `app/routers/items.py`) रहता है (directory `app/routers/`)... +* parent package (directory `app/`) पर जाना... +* फिर उस package के parent पर जाना (कोई parent package नहीं है, `app` top level है 😱)... +* और वहाँ, module `dependencies` ढूँढना (file `app/dependencies.py` पर)... +* और उससे, function `get_token_header` import करना। + +यह `app/` के ऊपर किसी package को refer करेगा, जिसकी अपनी file `__init__.py` आदि होगी। लेकिन हमारे पास वह नहीं है। इसलिए, हमारे उदाहरण में इससे error आएगा। 🚨 + +लेकिन अब आप जानते हैं कि यह कैसे काम करता है, इसलिए आप अपने apps में relative imports का उपयोग कर सकते हैं, चाहे वे कितने भी complex हों। 🤓 + +### कुछ custom `tags`, `responses`, और `dependencies` जोड़ें { #add-some-custom-tags-responses-and-dependencies } + +हम प्रत्येक *path operation* में prefix `/items` या `tags=["items"]` नहीं जोड़ रहे हैं क्योंकि हमने उन्हें `APIRouter` में जोड़ दिया है। + +लेकिन हम अभी भी _अधिक_ `tags` जोड़ सकते हैं जो किसी specific *path operation* पर लागू होंगे, और उस *path operation* के लिए specific कुछ अतिरिक्त `responses` भी: + +{* ../../docs_src/bigger_applications/app_an_py310/routers/items.py hl[30:31] title["app/routers/items.py"] *} + +/// tip | टिप + +इस आखिरी path operation में tags का combination होगा: `["items", "custom"]`। + +और documentation में इसके दोनों responses भी होंगे, एक `404` के लिए और एक `403` के लिए। + +/// + +## मुख्य `FastAPI` { #the-main-fastapi } + +अब, `app/main.py` पर module देखें। + +यहीं आप `FastAPI` class import और use करते हैं। + +यह आपके application की main file होगी जो सब कुछ एक साथ जोड़ती है। + +और क्योंकि आपका अधिकतर logic अब अपने-अपने specific module में रहेगा, main file काफी सरल होगी। + +### `FastAPI` import करें { #import-fastapi } + +आप सामान्य रूप से `FastAPI` class import और create करते हैं। + +और हम [global dependencies](dependencies/global-dependencies.md) भी declare कर सकते हैं जिन्हें प्रत्येक `APIRouter` के लिए dependencies के साथ combine किया जाएगा: + +{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[1,3,7] title["app/main.py"] *} + +### `APIRouter` import करें { #import-the-apirouter } + +अब हम उन अन्य submodules को import करते हैं जिनके पास `APIRouter`s हैं: + +{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[4:5] title["app/main.py"] *} + +क्योंकि files `app/routers/users.py` और `app/routers/items.py` ऐसे submodules हैं जो उसी Python package `app` का हिस्सा हैं, हम "relative imports" का उपयोग करके उन्हें import करने के लिए single dot `.` का उपयोग कर सकते हैं। + +### importing कैसे काम करता है { #how-the-importing-works } + +section: + +```Python +from .routers import items, users +``` + +का मतलब है: + +* उसी package से शुरू करना जिसमें यह module (file `app/main.py`) रहता है (directory `app/`)... +* subpackage `routers` ढूँढना (directory `app/routers/` पर)... +* और उससे, submodule `items` (file `app/routers/items.py` पर) और `users` (file `app/routers/users.py` पर) import करना... + +module `items` में एक variable `router` (`items.router`) होगा। यह वही है जो हमने file `app/routers/items.py` में बनाया था, यह एक `APIRouter` object है। + +और फिर हम module `users` के लिए भी वही करते हैं। + +हम उन्हें इस तरह भी import कर सकते थे: + +```Python +from app.routers import items, users +``` + +/// note | नोट + +पहला version एक "relative import" है: + +```Python +from .routers import items, users +``` + +दूसरा version एक "absolute import" है: + +```Python +from app.routers import items, users +``` + +Python Packages और Modules के बारे में अधिक जानने के लिए, [Modules के बारे में official Python documentation](https://docs.python.org/3/tutorial/modules.html) पढ़ें। + +/// + +### नामों के collisions से बचें { #avoid-name-collisions } + +हम submodule `items` को directly import कर रहे हैं, केवल उसके variable `router` को import करने के बजाय। + +ऐसा इसलिए है क्योंकि हमारे पास submodule `users` में भी `router` नाम का एक और variable है। + +अगर हमने एक के बाद एक import किया होता, जैसे: + +```Python +from .routers.items import router +from .routers.users import router +``` + +तो `users` का `router`, `items` वाले को overwrite कर देता और हम उन्हें एक ही समय में use नहीं कर पाते। + +इसलिए, उन्हें एक ही file में दोनों को use करने में सक्षम होने के लिए, हम submodules को directly import करते हैं: + +{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[5] title["app/main.py"] *} + +### `users` और `items` के लिए `APIRouter`s include करें { #include-the-apirouters-for-users-and-items } + +अब, submodules `users` और `items` से `router`s include करें: + +{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[10:11] title["app/main.py"] *} + +/// note | नोट + +`users.router` file `app/routers/users.py` के अंदर मौजूद `APIRouter` को contain करता है। + +और `items.router` file `app/routers/items.py` के अंदर मौजूद `APIRouter` को contain करता है। + +/// + +`app.include_router()` के साथ हम प्रत्येक `APIRouter` को main `FastAPI` application में जोड़ सकते हैं। + +यह उस router के सभी routes को उसका हिस्सा बनाकर include करेगा। + +/// note | तकनीकी विवरण + +FastAPI मूल `APIRouter` और उसके `APIRoute`s को active रखता है जब router main application में include किया जाता है। + +इसका मतलब है कि custom `APIRouter` और `APIRoute` subclasses router include होने के बाद भी participate कर सकते हैं। + +/// + +/// tip | टिप + +routers include करते समय आपको performance के बारे में चिंता करने की ज़रूरत नहीं है। + +इसे lightweight होने और हर request में overhead जोड़ने से बचने के लिए design किया गया है। + +इसलिए यह performance को affect नहीं करेगा। ⚡ + +/// + +### custom `prefix`, `tags`, `responses`, और `dependencies` के साथ `APIRouter` include करें { #include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies } + +अब, कल्पना करें कि आपकी organization ने आपको `app/internal/admin.py` file दी है। + +इसमें कुछ admin *path operations* वाला `APIRouter` है जिसे आपकी organization कई projects के बीच share करती है। + +इस उदाहरण के लिए यह बहुत सरल होगा। लेकिन मान लीजिए कि क्योंकि यह organization में अन्य projects के साथ shared है, हम इसे modify नहीं कर सकते और `prefix`, `dependencies`, `tags`, आदि directly `APIRouter` में नहीं जोड़ सकते: + +{* ../../docs_src/bigger_applications/app_an_py310/internal/admin.py hl[3] title["app/internal/admin.py"] *} + +लेकिन हम फिर भी `APIRouter` include करते समय एक custom `prefix` set करना चाहते हैं ताकि इसके सभी *path operations* `/admin` से शुरू हों, हम इसे इस project के लिए पहले से मौजूद `dependencies` के साथ secure करना चाहते हैं, और हम `tags` और `responses` include करना चाहते हैं। + +हम original `APIRouter` को modify किए बिना यह सब declare कर सकते हैं, उन parameters को `app.include_router()` में pass करके: + +{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[14:17] title["app/main.py"] *} + +इस तरह, original `APIRouter` unmodified रहेगा, इसलिए हम वही `app/internal/admin.py` file organization में अन्य projects के साथ अब भी share कर सकते हैं। + +परिणाम यह है कि हमारी app में, `admin` module से प्रत्येक *path operation* में होगा: + +* prefix `/admin`. +* tag `admin`. +* dependency `get_token_header`. +* response `418`. 🍵 + +लेकिन यह केवल हमारी app में उस `APIRouter` को affect करेगा, उसे use करने वाले किसी अन्य code को नहीं। + +इसलिए, उदाहरण के लिए, अन्य projects उसी `APIRouter` को किसी अलग authentication method के साथ use कर सकते हैं। + +### एक *path operation* include करें { #include-a-path-operation } + +हम सीधे `FastAPI` app में भी *path operations* जोड़ सकते हैं। + +यहाँ हम ऐसा करते हैं... बस यह दिखाने के लिए कि हम कर सकते हैं 🤷: + +{* ../../docs_src/bigger_applications/app_an_py310/main.py hl[21:23] title["app/main.py"] *} + +और यह `app.include_router()` के साथ जोड़े गए सभी अन्य *path operations* के साथ सही तरीके से काम करेगा। + +/// note | बहुत तकनीकी विवरण + +**नोट**: यह बहुत technical detail है जिसे आप शायद **बस skip** कर सकते हैं। + +--- + +`APIRouter`s "mounted" नहीं होते, वे बाकी application से isolated नहीं होते। + +ऐसा इसलिए है क्योंकि हम उनके *path operations* को OpenAPI schema और user interfaces में include करना चाहते हैं। + +FastAPI original routers और path operations को active रखता है, और requests handle करते समय और OpenAPI generate करते समय router prefixes, dependencies, tags, responses, और अन्य metadata को combine करता है। + +/// + +## `pyproject.toml` में `entrypoint` configure करें { #configure-the-entrypoint-in-pyproject-toml } + +क्योंकि आपका FastAPI `app` object `app/main.py` में रहता है, आप अपने `pyproject.toml` file में `entrypoint` को इस तरह configure कर सकते हैं: + +```toml +[tool.fastapi] +entrypoint = "app.main:app" +``` + +यह इस तरह import करने के बराबर है: + +```python +from app.main import app +``` + +इस तरह `fastapi` command जान जाएगा कि आपकी app कहाँ मिलेगी। + +/// Note | नोट + +आप command को path भी pass कर सकते हैं, जैसे: + +```console +$ fastapi dev app/main.py +``` + +लेकिन हर बार `fastapi` command call करते समय आपको सही path pass करना याद रखना होगा। + +इसके अलावा, अन्य tools शायद इसे ढूँढ न पाएँ, उदाहरण के लिए [VS Code Extension](../editor-support.md) या [FastAPI Cloud](https://fastapicloud.com), इसलिए `pyproject.toml` में `entrypoint` का उपयोग करने की सलाह दी जाती है। + +/// + +## automatic API docs जाँचें { #check-the-automatic-api-docs } + +अब, अपनी app चलाएँ: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +और docs को [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) पर खोलें। + +आप automatic API docs देखेंगे, जिसमें सभी submodules से paths शामिल होंगे, सही paths (और prefixes) और सही tags का उपयोग करते हुए: + + + +## अलग-अलग `prefix` के साथ उसी router को multiple times include करें { #include-the-same-router-multiple-times-with-different-prefix } + +आप अलग-अलग prefixes का उपयोग करके *same* router के साथ `.include_router()` को multiple times भी use कर सकते हैं। + +यह उपयोगी हो सकता है, उदाहरण के लिए, उसी API को अलग-अलग prefixes के तहत expose करने के लिए, जैसे `/api/v1` और `/api/latest`। + +यह एक advanced usage है जिसकी आपको शायद सच में ज़रूरत न हो, लेकिन अगर हो तो यह मौजूद है। + +## एक `APIRouter` को दूसरे में include करें { #include-an-apirouter-in-another } + +जिस तरह आप `FastAPI` application में `APIRouter` include कर सकते हैं, उसी तरह आप एक `APIRouter` को दूसरे `APIRouter` में include कर सकते हैं: + +```Python +router.include_router(other_router) +``` + +आप यह `FastAPI` app में `router` include करने से पहले या बाद में कर सकते हैं। FastAPI फिर भी `other_router` से *path operations* को routing और OpenAPI में include करेगा। + +बाद में routers में जोड़े गए *path operations* पर भी यही लागू होता है। वे पहले वाली inclusion के माध्यम से भी visible होंगे। + +/// warning | तकनीकी विवरण + +router include करने के बाद `router.routes` को directly mutate करने से बचें। FastAPI router inclusion को live मानता है, इसलिए original router और उसके routes routing और OpenAPI generation का हिस्सा बने रहते हैं। + +routes और routers जोड़ने के लिए documented APIs जैसे path operation decorators और `.include_router()` का उपयोग करें। + +`router.routes` को एक lower-level route tree की तरह treat करें जिसमें route definitions और included routers हो सकते हैं, और इस पर final path operations की flat list की तरह rely करने से बचें। + +/// diff --git a/docs/hi/docs/tutorial/body-fields.md b/docs/hi/docs/tutorial/body-fields.md new file mode 100644 index 000000000..22f5d38fd --- /dev/null +++ b/docs/hi/docs/tutorial/body-fields.md @@ -0,0 +1,61 @@ +# Body - Fields { #body-fields } + +जिस तरह आप *path operation function* के parameters में `Query`, `Path` और `Body` के साथ अतिरिक्त validation और metadata घोषित कर सकते हैं, उसी तरह आप Pydantic के `Field` का उपयोग करके Pydantic models के अंदर validation और metadata घोषित कर सकते हैं। + +## `Field` import करें { #import-field } + +सबसे पहले, आपको इसे import करना होगा: + +{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[4] *} + + +/// warning | चेतावनी + +ध्यान दें कि `Field` को सीधे `pydantic` से import किया जाता है, `fastapi` से नहीं, जैसे बाकी सभी (`Query`, `Path`, `Body`, आदि) किए जाते हैं। + +/// + +## model attributes घोषित करें { #declare-model-attributes } + +फिर आप model attributes के साथ `Field` का उपयोग कर सकते हैं: + +{* ../../docs_src/body_fields/tutorial001_an_py310.py hl[11:14] *} + +`Field` उसी तरह काम करता है जैसे `Query`, `Path` और `Body`, इसमें वही सभी parameters आदि होते हैं। + +/// note | तकनीकी विवरण + +वास्तव में, `Query`, `Path` और अन्य जिन्हें आप आगे देखेंगे, एक सामान्य `Param` class के subclasses के objects बनाते हैं, जो स्वयं Pydantic की `FieldInfo` class का subclass है। + +और Pydantic का `Field` भी `FieldInfo` का एक instance लौटाता है। + +`Body` भी सीधे `FieldInfo` के subclass के objects लौटाता है। और कुछ अन्य भी हैं जिन्हें आप बाद में देखेंगे, जो `Body` class के subclasses हैं। + +याद रखें कि जब आप `fastapi` से `Query`, `Path` और अन्य import करते हैं, तो वे वास्तव में functions होते हैं जो विशेष classes लौटाते हैं। + +/// + +/// tip | सुझाव + +ध्यान दें कि type, default value और `Field` वाले हर model के attribute की संरचना *path operation function* के parameter जैसी ही होती है, बस `Path`, `Query` और `Body` की जगह `Field` होता है। + +/// + +## अतिरिक्त जानकारी जोड़ें { #add-extra-information } + +आप `Field`, `Query`, `Body` आदि में अतिरिक्त जानकारी घोषित कर सकते हैं। और यह जनरेट किए गए JSON Schema में शामिल होगी। + +आप docs में आगे examples घोषित करना सीखते समय अतिरिक्त जानकारी जोड़ने के बारे में और जानेंगे। + +/// warning | चेतावनी + +`Field` को दिए गए अतिरिक्त keys आपके application के परिणामी OpenAPI schema में भी मौजूद होंगे। +क्योंकि ये keys जरूरी नहीं कि OpenAPI specification का हिस्सा हों, इसलिए कुछ OpenAPI tools, उदाहरण के लिए [OpenAPI validator](https://validator.swagger.io/), आपके जनरेट किए गए schema के साथ काम नहीं कर सकते। + +/// + +## Recap { #recap } + +आप model attributes के लिए अतिरिक्त validations और metadata घोषित करने के लिए Pydantic के `Field` का उपयोग कर सकते हैं। + +आप अतिरिक्त JSON Schema metadata पास करने के लिए extra keyword arguments का भी उपयोग कर सकते हैं। diff --git a/docs/hi/docs/tutorial/body-multiple-params.md b/docs/hi/docs/tutorial/body-multiple-params.md new file mode 100644 index 000000000..0d095ddf3 --- /dev/null +++ b/docs/hi/docs/tutorial/body-multiple-params.md @@ -0,0 +1,169 @@ +# Body - कई Parameters { #body-multiple-parameters } + +अब जबकि हमने देख लिया है कि `Path` और `Query` का उपयोग कैसे करना है, आइए request body declarations के और उन्नत उपयोग देखें। + +## `Path`, `Query` और body parameters को मिलाएँ { #mix-path-query-and-body-parameters } + +सबसे पहले, बेशक, आप `Path`, `Query` और request body parameter declarations को स्वतंत्र रूप से मिला सकते हैं और **FastAPI** जान जाएगा कि क्या करना है। + +और आप body parameters को optional भी declare कर सकते हैं, default को `None` पर सेट करके: + +{* ../../docs_src/body_multiple_params/tutorial001_an_py310.py hl[18:20] *} + +/// note | नोट + +ध्यान दें कि, इस मामले में, body से लिया जाने वाला `item` optional है। क्योंकि इसका default value `None` है। + +/// + +## कई body parameters { #multiple-body-parameters } + +पिछले उदाहरण में, *path operations* एक JSON body की अपेक्षा करेंगे जिसमें `Item` के attributes हों, जैसे: + +```JSON +{ + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2 +} +``` + +लेकिन आप कई body parameters भी declare कर सकते हैं, उदाहरण के लिए `item` और `user`: + +{* ../../docs_src/body_multiple_params/tutorial002_py310.py hl[20] *} + + +इस मामले में, **FastAPI** ध्यान देगा कि function में एक से अधिक body parameter हैं (दो parameters हैं जो Pydantic models हैं)। + +तो, फिर यह parameter names को body में keys (field names) के रूप में उपयोग करेगा, और ऐसी body की अपेक्षा करेगा: + +```JSON +{ + "item": { + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2 + }, + "user": { + "username": "dave", + "full_name": "Dave Grohl" + } +} +``` + +/// note | नोट + +ध्यान दें कि भले ही `item` को पहले की तरह ही declare किया गया था, अब उससे अपेक्षा की जाती है कि वह body के अंदर key `item` के साथ हो। + +/// + +**FastAPI** request से automatic conversion करेगा, ताकि parameter `item` को उसकी विशिष्ट content मिले और `user` के लिए भी यही हो। + +यह compound data का validation करेगा, और इसे OpenAPI schema और automatic docs के लिए उसी तरह document करेगा। + +## body में एकल values { #singular-values-in-body } + +जिस तरह query और path parameters के लिए extra data define करने हेतु `Query` और `Path` हैं, **FastAPI** एक समान `Body` प्रदान करता है। + +उदाहरण के लिए, पिछले model को extend करते हुए, आप तय कर सकते हैं कि आप उसी body में `item` और `user` के अलावा एक और key `importance` रखना चाहते हैं। + +यदि आप इसे जैसे है वैसे declare करते हैं, क्योंकि यह एक single value है, **FastAPI** मान लेगा कि यह एक query parameter है। + +लेकिन आप **FastAPI** को `Body` का उपयोग करके इसे एक और body key के रूप में treat करने का निर्देश दे सकते हैं: + +{* ../../docs_src/body_multiple_params/tutorial003_an_py310.py hl[23] *} + + +इस मामले में, **FastAPI** ऐसी body की अपेक्षा करेगा: + +```JSON +{ + "item": { + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2 + }, + "user": { + "username": "dave", + "full_name": "Dave Grohl" + }, + "importance": 5 +} +``` + +फिर से, यह data types को convert करेगा, validate करेगा, document करेगा, आदि। + +## कई body params और query { #multiple-body-params-and-query } + +बेशक, आप जब भी ज़रूरत हो, किसी भी body parameters के अतिरिक्त, extra query parameters भी declare कर सकते हैं। + +क्योंकि default रूप से, single values को query parameters के रूप में interpret किया जाता है, आपको स्पष्ट रूप से `Query` जोड़ने की ज़रूरत नहीं है, आप बस ऐसा कर सकते हैं: + +```Python +q: str | None = None +``` + +उदाहरण के लिए: + +{* ../../docs_src/body_multiple_params/tutorial004_an_py310.py hl[28] *} + + +/// note | नोट + +`Body` में भी वही सभी extra validation और metadata parameters हैं जो `Query`, `Path` और अन्य में हैं जिन्हें आप बाद में देखेंगे। + +/// + +## एक single body parameter को embed करें { #embed-a-single-body-parameter } + +मान लीजिए आपके पास Pydantic model `Item` से केवल एक single `item` body parameter है। + +default रूप से, **FastAPI** फिर सीधे उसकी body की अपेक्षा करेगा। + +लेकिन यदि आप चाहते हैं कि यह key `item` के साथ JSON की अपेक्षा करे और उसके अंदर model contents हों, जैसा कि यह तब करता है जब आप extra body parameters declare करते हैं, तो आप special `Body` parameter `embed` का उपयोग कर सकते हैं: + +```Python +item: Annotated[Item, Body(embed=True)] +``` + +जैसे कि: + +{* ../../docs_src/body_multiple_params/tutorial005_an_py310.py hl[17] *} + + +इस मामले में **FastAPI** ऐसी body की अपेक्षा करेगा: + +```JSON hl_lines="2" +{ + "item": { + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2 + } +} +``` + +इसके बजाय: + +```JSON +{ + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2 +} +``` + +## Recap { #recap } + +आप अपनी *path operation function* में कई body parameters जोड़ सकते हैं, भले ही एक request में केवल एक ही body हो सकती है। + +लेकिन **FastAPI** इसे handle करेगा, आपको आपके function में सही data देगा, और *path operation* में सही schema को validate और document करेगा। + +आप single values को body के हिस्से के रूप में receive करने के लिए भी declare कर सकते हैं। + +और आप **FastAPI** को body को एक key में embed करने का निर्देश दे सकते हैं, भले ही केवल एक single parameter declare किया गया हो। diff --git a/docs/hi/docs/tutorial/body-nested-models.md b/docs/hi/docs/tutorial/body-nested-models.md new file mode 100644 index 000000000..04ac55bfd --- /dev/null +++ b/docs/hi/docs/tutorial/body-nested-models.md @@ -0,0 +1,221 @@ +# Body - Nested Models { #body-nested-models } + +**FastAPI** के साथ, आप arbitrarily deeply nested models को define, validate, document, और use कर सकते हैं (Pydantic की बदौलत)। + +## List fields { #list-fields } + +आप किसी attribute को subtype के रूप में define कर सकते हैं। उदाहरण के लिए, एक Python `list`: + +{* ../../docs_src/body_nested_models/tutorial001_py310.py hl[12] *} + +यह `tags` को एक list बना देगा, हालांकि यह list के elements का type declare नहीं करता। + +## Type parameter के साथ List fields { #list-fields-with-type-parameter } + +लेकिन Python में internal types, या "type parameters" के साथ lists declare करने का एक खास तरीका है: + +### Type parameter के साथ `list` declare करें { #declare-a-list-with-a-type-parameter } + +ऐसे types declare करने के लिए जिनमें type parameters (internal types) होते हैं, जैसे `list`, `dict`, `tuple`, +internal type(s) को square brackets: `[` और `]` का उपयोग करके "type parameters" के रूप में pass करें + +```Python +my_list: list[str] +``` + +Type declarations के लिए यह सब standard Python syntax है। + +Internal types वाले model attributes के लिए भी वही standard syntax उपयोग करें। + +तो, हमारे उदाहरण में, हम `tags` को खास तौर पर "strings की list" बना सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial002_py310.py hl[12] *} + +## Set types { #set-types } + +लेकिन फिर हम इस पर सोचते हैं, और समझते हैं कि tags repeat नहीं होने चाहिए, वे शायद unique strings होंगे। + +और Python में unique items के sets के लिए एक खास data type है, `set`। + +फिर हम `tags` को strings के set के रूप में declare कर सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial003_py310.py hl[12] *} + +इसके साथ, भले ही आपको duplicate data वाला request मिले, वह unique items के set में convert हो जाएगा। + +और जब भी आप उस data को output करेंगे, भले ही source में duplicates हों, वह unique items के set के रूप में output होगा। + +और इसे उसी अनुसार annotate / document भी किया जाएगा। + +## Nested Models { #nested-models } + +Pydantic model के हर attribute का एक type होता है। + +लेकिन वह type खुद भी कोई दूसरा Pydantic model हो सकता है। + +इसलिए, आप खास attribute names, types और validations के साथ deeply nested JSON "objects" declare कर सकते हैं। + +यह सब, मनचाही गहराई तक nested हो सकता है। + +### Submodel define करें { #define-a-submodel } + +उदाहरण के लिए, हम एक `Image` model define कर सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[7:9] *} + +### Submodel को type के रूप में उपयोग करें { #use-the-submodel-as-a-type } + +और फिर हम इसे किसी attribute के type के रूप में उपयोग कर सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial004_py310.py hl[18] *} + +इसका मतलब होगा कि **FastAPI** कुछ इस तरह के body की अपेक्षा करेगा: + +```JSON +{ + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2, + "tags": ["rock", "metal", "bar"], + "image": { + "url": "http://example.com/baz.jpg", + "name": "The Foo live" + } +} +``` + +फिर से, सिर्फ वह declaration करने से, **FastAPI** के साथ आपको मिलता है: + +* Editor support (completion, आदि), nested models के लिए भी +* Data conversion +* Data validation +* Automatic documentation + +## Special types और validation { #special-types-and-validation } + +`str`, `int`, `float`, आदि जैसे सामान्य singular types के अलावा, आप अधिक complex singular types उपयोग कर सकते हैं जो `str` से inherit करते हैं। + +आपके पास मौजूद सभी options देखने के लिए, [Pydantic का Type Overview](https://docs.pydantic.dev/latest/concepts/types/) देखें। अगले chapter में आपको कुछ उदाहरण दिखेंगे। + +उदाहरण के लिए, जैसा कि `Image` model में हमारे पास एक `url` field है, हम इसे `str` के बजाय Pydantic के `HttpUrl` का instance declare कर सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial005_py310.py hl[2,8] *} + +String को valid URL होने के लिए check किया जाएगा, और JSON Schema / OpenAPI में उसी तरह document किया जाएगा। + +## Submodels की lists वाले attributes { #attributes-with-lists-of-submodels } + +आप Pydantic models को `list`, `set`, आदि के subtypes के रूप में भी उपयोग कर सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial006_py310.py hl[18] *} + +यह इस तरह के JSON body की अपेक्षा करेगा (convert, validate, document, आदि): + +```JSON hl_lines="11" +{ + "name": "Foo", + "description": "The pretender", + "price": 42.0, + "tax": 3.2, + "tags": [ + "rock", + "metal", + "bar" + ], + "images": [ + { + "url": "http://example.com/baz.jpg", + "name": "The Foo live" + }, + { + "url": "http://example.com/dave.jpg", + "name": "The Baz" + } + ] +} +``` + +/// note | नोट + +ध्यान दें कि `images` key में अब image objects की एक list है। + +/// + +## Deeply nested models { #deeply-nested-models } + +आप arbitrarily deeply nested models define कर सकते हैं: + +{* ../../docs_src/body_nested_models/tutorial007_py310.py hl[7,12,18,21,25] *} + +/// note | नोट + +ध्यान दें कि `Offer` में `Item`s की एक list है, जिनमें आगे `Image`s की एक optional list है + +/// + +## Pure lists के bodies { #bodies-of-pure-lists } + +अगर जिस JSON body की आप अपेक्षा करते हैं उसका top level value एक JSON `array` (एक Python `list`) है, तो आप function के parameter में type declare कर सकते हैं, बिल्कुल Pydantic models की तरह: + +```Python +images: list[Image] +``` + +जैसे कि: + +{* ../../docs_src/body_nested_models/tutorial008_py310.py hl[13] *} + +## हर जगह editor support { #editor-support-everywhere } + +और आपको हर जगह editor support मिलता है। + +Lists के अंदर के items के लिए भी: + + + +अगर आप Pydantic models के बजाय सीधे `dict` के साथ काम कर रहे होते, तो आपको इस तरह का editor support नहीं मिल सकता था। + +लेकिन आपको उनकी चिंता भी करने की ज़रूरत नहीं है, आने वाले dicts अपने आप convert हो जाते हैं और आपका output भी अपने आप JSON में convert हो जाता है। + +## Arbitrary `dict`s के bodies { #bodies-of-arbitrary-dicts } + +आप body को एक `dict` के रूप में भी declare कर सकते हैं, जिसकी keys किसी type की हों और values किसी दूसरे type की। + +इस तरह, आपको पहले से यह जानने की ज़रूरत नहीं होती कि valid field/attribute names क्या हैं (जैसा कि Pydantic models के साथ होता)। + +यह तब उपयोगी होगा जब आप ऐसी keys receive करना चाहते हैं जिन्हें आप पहले से नहीं जानते। + +--- + +एक और उपयोगी case वह है जब आप किसी दूसरे type (जैसे, `int`) की keys रखना चाहते हैं। + +यही हम यहाँ देखने जा रहे हैं। + +इस case में, आप कोई भी `dict` accept करेंगे, जब तक कि उसमें `float` values वाली `int` keys हों: + +{* ../../docs_src/body_nested_models/tutorial009_py310.py hl[7] *} + +/// tip | टिप + +ध्यान रखें कि JSON केवल `str` को keys के रूप में support करता है। + +लेकिन Pydantic में automatic data conversion है। + +इसका मतलब है कि, भले ही आपके API clients केवल strings को keys के रूप में भेज सकते हैं, जब तक उन strings में pure integers हैं, Pydantic उन्हें convert और validate कर देगा। + +और `weights` के रूप में आपको जो `dict` receive होगा, उसमें वास्तव में `int` keys और `float` values होंगी। + +/// + +## Recap { #recap } + +**FastAPI** के साथ आपके पास Pydantic models द्वारा दी गई अधिकतम flexibility होती है, जबकि आपका code simple, short और elegant बना रहता है। + +लेकिन सभी benefits के साथ: + +* Editor support (हर जगह completion!) +* Data conversion (a.k.a. parsing / serialization) +* Data validation +* Schema documentation +* Automatic docs diff --git a/docs/hi/docs/tutorial/body-updates.md b/docs/hi/docs/tutorial/body-updates.md new file mode 100644 index 000000000..cab762e47 --- /dev/null +++ b/docs/hi/docs/tutorial/body-updates.md @@ -0,0 +1,100 @@ +# Body - अपडेट्स { #body-updates } + +## `PUT` के साथ बदलकर अपडेट करना { #update-replacing-with-put } + +किसी item को अपडेट करने के लिए आप [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) operation का उपयोग कर सकते हैं। + +आप input data को ऐसे data में बदलने के लिए `jsonable_encoder` का उपयोग कर सकते हैं जिसे JSON के रूप में संग्रहीत किया जा सके (जैसे NoSQL database के साथ)। उदाहरण के लिए, `datetime` को `str` में बदलना। + +{* ../../docs_src/body_updates/tutorial001_py310.py hl[28:33] *} + +`PUT` का उपयोग ऐसा data प्राप्त करने के लिए किया जाता है जो मौजूदा data को बदल दे। + +### बदलने के बारे में चेतावनी { #warning-about-replacing } + +इसका मतलब है कि अगर आप item `bar` को `PUT` का उपयोग करके ऐसे body के साथ अपडेट करना चाहते हैं जिसमें यह हो: + +```Python +{ + "name": "Barz", + "price": 3, + "description": None, +} +``` + +क्योंकि इसमें पहले से संग्रहीत attribute `"tax": 20.2` शामिल नहीं है, input model `"tax": 10.5` की default value लेगा। + +और data उस "नए" `tax` `10.5` के साथ सहेजा जाएगा। + +## `PATCH` के साथ आंशिक अपडेट्स { #partial-updates-with-patch } + +आप data को *आंशिक रूप से* अपडेट करने के लिए [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) operation का भी उपयोग कर सकते हैं। + +इसका मतलब है कि आप केवल वही data भेज सकते हैं जिसे आप अपडेट करना चाहते हैं, बाकी को वैसा ही छोड़ते हुए। + +/// note | नोट + +`PATCH`, `PUT` की तुलना में कम सामान्य रूप से उपयोग और जाना जाता है। + +और कई teams आंशिक अपडेट्स के लिए भी केवल `PUT` का उपयोग करती हैं। + +आप इन्हें जैसे चाहें वैसे उपयोग करने के लिए **स्वतंत्र** हैं, **FastAPI** कोई प्रतिबंध नहीं लगाता। + +लेकिन यह गाइड आपको मोटे तौर पर दिखाती है कि इन्हें कैसे उपयोग करने का इरादा है। + +/// + +### Pydantic के `exclude_unset` parameter का उपयोग करना { #using-pydantics-exclude-unset-parameter } + +अगर आप आंशिक अपडेट्स प्राप्त करना चाहते हैं, तो Pydantic के model के `.model_dump()` में parameter `exclude_unset` का उपयोग करना बहुत उपयोगी है। + +जैसे `item.model_dump(exclude_unset=True)`। + +इससे केवल उस data के साथ एक `dict` बनेगा जो `item` model बनाते समय सेट किया गया था, default values को छोड़कर। + +फिर आप इसका उपयोग केवल उस data के साथ एक `dict` बनाने के लिए कर सकते हैं जो सेट किया गया था (request में भेजा गया), default values को छोड़ते हुए: + +{* ../../docs_src/body_updates/tutorial002_py310.py hl[32] *} + +### Pydantic के `update` parameter का उपयोग करना { #using-pydantics-update-parameter } + +अब, आप `.model_copy()` का उपयोग करके मौजूदा model की एक copy बना सकते हैं, और अपडेट करने के लिए data वाले `dict` के साथ `update` parameter पास कर सकते हैं। + +जैसे `stored_item_model.model_copy(update=update_data)`: + +{* ../../docs_src/body_updates/tutorial002_py310.py hl[33] *} + +### आंशिक अपडेट्स Recap { #partial-updates-recap } + +संक्षेप में, आंशिक अपडेट्स लागू करने के लिए आप: + +* (वैकल्पिक रूप से) `PUT` के बजाय `PATCH` का उपयोग करें। +* संग्रहीत data प्राप्त करें। +* उस data को Pydantic model में रखें। +* input model से default values के बिना एक `dict` बनाएँ (`exclude_unset` का उपयोग करके)। + * इस तरह आप केवल उन values को अपडेट कर सकते हैं जिन्हें वास्तव में user ने सेट किया है, बजाय इसके कि आपके model में पहले से संग्रहीत values को default values से override कर दें। +* संग्रहीत model की एक copy बनाएँ, और प्राप्त आंशिक अपडेट्स के साथ उसके attributes को अपडेट करें (`update` parameter का उपयोग करके)। +* copied model को ऐसी चीज़ में बदलें जिसे आपकी DB में संग्रहीत किया जा सके (उदाहरण के लिए, `jsonable_encoder` का उपयोग करके)। + * यह model की `.model_dump()` method को फिर से उपयोग करने जैसा है, लेकिन यह सुनिश्चित करता है (और बदलता है) कि values ऐसे data types में हों जिन्हें JSON में बदला जा सके, उदाहरण के लिए, `datetime` को `str` में। +* data को अपनी DB में सहेजें। +* अपडेट किया गया model लौटाएँ। + +{* ../../docs_src/body_updates/tutorial002_py310.py hl[28:35] *} + +/// tip | टिप + +आप वास्तव में इसी तकनीक का उपयोग HTTP `PUT` operation के साथ भी कर सकते हैं। + +लेकिन यहाँ का उदाहरण `PATCH` का उपयोग करता है क्योंकि इसे इन्हीं use cases के लिए बनाया गया था। + +/// + +/// note | नोट + +ध्यान दें कि input model अभी भी validate किया जाता है। + +इसलिए, अगर आप ऐसे आंशिक अपडेट्स प्राप्त करना चाहते हैं जो सभी attributes को छोड़ सकते हैं, तो आपको ऐसा model चाहिए जिसमें सभी attributes optional के रूप में चिह्नित हों (default values या `None` के साथ)। + +**अपडेट्स** के लिए सभी optional values वाले models और **creation** के लिए required values वाले models में अंतर करने के लिए, आप [Extra Models](extra-models.md) में बताए गए विचारों का उपयोग कर सकते हैं। + +/// diff --git a/docs/hi/docs/tutorial/body.md b/docs/hi/docs/tutorial/body.md new file mode 100644 index 000000000..dfccf3726 --- /dev/null +++ b/docs/hi/docs/tutorial/body.md @@ -0,0 +1,166 @@ +# Request Body { #request-body } + +जब आपको किसी client (मान लें, एक browser) से अपनी API को data भेजना होता है, तो आप उसे **request body** के रूप में भेजते हैं। + +**request** body वह data है जो client आपकी API को भेजता है। **response** body वह data है जो आपकी API client को भेजती है। + +आपकी API को लगभग हमेशा **response** body भेजनी होती है। लेकिन clients को हर समय **request bodies** भेजने की ज़रूरत नहीं होती, कभी-कभी वे केवल एक path request करते हैं, शायद कुछ query parameters के साथ, लेकिन body नहीं भेजते। + +**request** body घोषित करने के लिए, आप [Pydantic](https://docs.pydantic.dev/) models का उनकी पूरी शक्ति और लाभों के साथ उपयोग करते हैं। + +/// note | नोट + +Data भेजने के लिए, आपको इनमें से किसी एक का उपयोग करना चाहिए: `POST` (सबसे आम), `PUT`, `DELETE` या `PATCH`. + +`GET` request के साथ body भेजने का व्यवहार specifications में undefined है, फिर भी, FastAPI इसे support करता है, केवल बहुत जटिल/चरम use cases के लिए। + +क्योंकि इसे discouraged किया जाता है, Swagger UI वाले interactive docs `GET` का उपयोग करते समय body के लिए documentation नहीं दिखाएँगे, और बीच में मौजूद proxies इसे support नहीं कर सकते। + +/// + +## Pydantic के `BaseModel` को import करें { #import-pydantics-basemodel } + +सबसे पहले, आपको `pydantic` से `BaseModel` import करना होगा: + +{* ../../docs_src/body/tutorial001_py310.py hl[2] *} + +## अपना data model बनाएँ { #create-your-data-model } + +फिर आप अपने data model को एक class के रूप में घोषित करते हैं जो `BaseModel` से inherit करती है। + +सभी attributes के लिए standard Python types का उपयोग करें: + +{* ../../docs_src/body/tutorial001_py310.py hl[5:9] *} + + +Query parameters घोषित करते समय की तरह, जब किसी model attribute का default value होता है, तो वह required नहीं होता। अन्यथा, वह required होता है। उसे केवल optional बनाने के लिए `None` का उपयोग करें। + +उदाहरण के लिए, ऊपर दिया गया यह model इस तरह का JSON "`object`" (या Python `dict`) घोषित करता है: + +```JSON +{ + "name": "Foo", + "description": "An optional description", + "price": 45.2, + "tax": 3.5 +} +``` + +...क्योंकि `description` और `tax` optional हैं (`None` के default value के साथ), यह JSON "`object`" भी valid होगा: + +```JSON +{ + "name": "Foo", + "price": 45.2 +} +``` + +## इसे एक parameter के रूप में घोषित करें { #declare-it-as-a-parameter } + +इसे अपनी *path operation* में जोड़ने के लिए, इसे उसी तरह घोषित करें जैसे आपने path और query parameters घोषित किए थे: + +{* ../../docs_src/body/tutorial001_py310.py hl[16] *} + +...और इसके type को आपके बनाए हुए model, `Item`, के रूप में घोषित करें। + +## परिणाम { #results } + +सिर्फ उस Python type declaration के साथ, **FastAPI** यह करेगा: + +* request के body को JSON के रूप में पढ़ेगा। +* संबंधित types को convert करेगा (यदि ज़रूरत हो)। +* data को validate करेगा। + * यदि data invalid है, तो यह एक अच्छा और स्पष्ट error return करेगा, जो ठीक-ठीक बताएगा कि गलत data कहाँ और क्या था। +* आपको प्राप्त data parameter `item` में देगा। + * क्योंकि आपने इसे function में `Item` type का घोषित किया है, आपको इसके सभी attributes और उनके types के लिए पूरा editor support (completion, आदि) भी मिलेगा। +* आपके model के लिए [JSON Schema](https://json-schema.org) definitions generate करेगा, यदि आपके project के लिए उचित हो तो आप उन्हें कहीं और भी उपयोग कर सकते हैं। +* वे schemas generated OpenAPI schema का हिस्सा होंगे, और automatic documentation UIs द्वारा उपयोग किए जाएँगे। + +## स्वचालित docs { #automatic-docs } + +आपके models के JSON Schemas आपके OpenAPI generated schema का हिस्सा होंगे, और interactive API docs में दिखाए जाएँगे: + + + +और वे API docs में हर उस *path operation* के अंदर भी उपयोग किए जाएँगे जिसे उनकी ज़रूरत है: + + + +## Editor support { #editor-support } + +अपने editor में, अपने function के अंदर आपको हर जगह type hints और completion मिलेंगे (यदि आपको Pydantic model के बजाय `dict` मिला होता, तो ऐसा नहीं होता): + + + +आपको incorrect type operations के लिए error checks भी मिलते हैं: + + + +यह संयोग से नहीं है, पूरा framework इसी design के इर्द-गिर्द बनाया गया था। + +और किसी भी implementation से पहले, design phase में इसे पूरी तरह test किया गया था, ताकि सुनिश्चित किया जा सके कि यह सभी editors के साथ काम करेगा। + +इसे support करने के लिए Pydantic में भी कुछ बदलाव किए गए थे। + +पिछले screenshots [Visual Studio Code](https://code.visualstudio.com) के साथ लिए गए थे। + +लेकिन आपको [PyCharm](https://www.jetbrains.com/pycharm/) और अधिकांश अन्य Python editors के साथ भी वही editor support मिलेगा: + + + +/// tip | सुझाव + +यदि आप [PyCharm](https://www.jetbrains.com/pycharm/) को अपने editor के रूप में उपयोग करते हैं, तो आप [Pydantic PyCharm Plugin](https://github.com/koxudaxi/pydantic-pycharm-plugin/) का उपयोग कर सकते हैं। + +यह Pydantic models के लिए editor support को बेहतर बनाता है, इन चीज़ों के साथ: + +* auto-completion +* type checks +* refactoring +* searching +* inspections + +/// + +## model का उपयोग करें { #use-the-model } + +Function के अंदर, आप model object के सभी attributes को सीधे access कर सकते हैं: + +{* ../../docs_src/body/tutorial002_py310.py *} + +## Request body + path parameters { #request-body-path-parameters } + +आप path parameters और request body को एक ही समय में घोषित कर सकते हैं। + +**FastAPI** पहचानेगा कि वे function parameters जो path parameters से match करते हैं, उन्हें **path से लिया जाना चाहिए**, और वे function parameters जो Pydantic models के रूप में घोषित हैं, उन्हें **request body से लिया जाना चाहिए**। + +{* ../../docs_src/body/tutorial003_py310.py hl[15:16] *} + + +## Request body + path + query parameters { #request-body-path-query-parameters } + +आप **body**, **path** और **query** parameters को भी एक ही समय में घोषित कर सकते हैं। + +**FastAPI** उनमें से प्रत्येक को पहचानेगा और data को सही जगह से लेगा। + +{* ../../docs_src/body/tutorial004_py310.py hl[16] *} + +Function parameters को इस प्रकार पहचाना जाएगा: + +* यदि parameter **path** में भी घोषित है, तो उसे path parameter के रूप में उपयोग किया जाएगा। +* यदि parameter **singular type** का है (जैसे `int`, `float`, `str`, `bool`, आदि), तो उसे **query** parameter के रूप में interpret किया जाएगा। +* यदि parameter को **Pydantic model** के type का घोषित किया गया है, तो उसे request **body** के रूप में interpret किया जाएगा। + +/// note | नोट + +FastAPI जान जाएगा कि `q` का value required नहीं है क्योंकि उसका default value `= None` है। + +`str | None` का उपयोग FastAPI यह निर्धारित करने के लिए नहीं करता कि value required नहीं है, वह जान जाएगा कि यह required नहीं है क्योंकि इसका default value `= None` है। + +लेकिन type annotations जोड़ने से आपका editor आपको बेहतर support दे सकेगा और errors detect कर सकेगा। + +/// + +## Pydantic के बिना { #without-pydantic } + +यदि आप Pydantic models का उपयोग नहीं करना चाहते, तो आप **Body** parameters का भी उपयोग कर सकते हैं। [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body) के docs देखें। diff --git a/docs/hi/docs/tutorial/cookie-param-models.md b/docs/hi/docs/tutorial/cookie-param-models.md new file mode 100644 index 000000000..38ea681b2 --- /dev/null +++ b/docs/hi/docs/tutorial/cookie-param-models.md @@ -0,0 +1,76 @@ +# Cookie Parameter Models { #cookie-parameter-models } + +अगर आपके पास संबंधित **cookies** का एक समूह है, तो आप उन्हें declare करने के लिए एक **Pydantic model** बना सकते हैं। 🍪 + +यह आपको **model को फिर से उपयोग** करने की अनुमति देगा, **कई जगहों** पर, और साथ ही सभी parameters के लिए validations और metadata एक साथ declare करने की भी। 😎 + +/// note | नोट + +यह FastAPI version `0.115.0` से supported है। 🤓 + +/// + +/// tip | सुझाव + +यही तकनीक `Query`, `Cookie`, और `Header` पर लागू होती है। 😎 + +/// + +## Pydantic Model के साथ Cookies { #cookies-with-a-pydantic-model } + +जिन **cookie** parameters की आपको ज़रूरत है, उन्हें एक **Pydantic model** में declare करें, और फिर parameter को `Cookie` के रूप में declare करें: + +{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *} + +**FastAPI** request में प्राप्त **cookies** से **हर field** के लिए data **extract** करेगा और आपको वह Pydantic model देगा जिसे आपने define किया है। + +## Docs देखें { #check-the-docs } + +आप `/docs` पर docs UI में defined cookies देख सकते हैं: + +
+ +
+ +/// note | नोट + +ध्यान रखें कि, क्योंकि **browsers cookies को** विशेष तरीकों से और पर्दे के पीछे handle करते हैं, वे **JavaScript** को आसानी से उन्हें छूने की अनुमति **नहीं** देते। + +अगर आप `/docs` पर **API docs UI** पर जाते हैं, तो आप अपने *path operations* के लिए cookies की **documentation** देख पाएँगे। + +लेकिन भले ही आप **data भरें** और "Execute" पर क्लिक करें, क्योंकि docs UI **JavaScript** के साथ काम करता है, cookies नहीं भेजे जाएँगे, और आपको एक **error** message दिखाई देगा जैसे कि आपने कोई values लिखी ही न हों। + +/// + +## Extra Cookies को forbid करें { #forbid-extra-cookies } + +कुछ विशेष use cases में (शायद बहुत आम नहीं), आप उन cookies को **restrict** करना चाह सकते हैं जिन्हें आप प्राप्त करना चाहते हैं। + +आपकी API के पास अब अपनी खुद की cookie सहमति को control करने की शक्ति है। 🤪🍪 + +आप Pydantic के model configuration का उपयोग करके किसी भी `extra` fields को `forbid` कर सकते हैं: + +{* ../../docs_src/cookie_param_models/tutorial002_an_py310.py hl[10] *} + +अगर कोई client कुछ **extra cookies** भेजने की कोशिश करता है, तो उन्हें एक **error** response मिलेगा। + +बेचारे cookie banners, जो API द्वारा उसे reject किए जाने के लिए आपकी सहमति पाने में इतनी मेहनत करते हैं। 🍪 + +उदाहरण के लिए, अगर client `good-list-please` value के साथ एक `santa_tracker` cookie भेजने की कोशिश करता है, तो client को एक **error** response मिलेगा जो बताएगा कि `santa_tracker` cookie की अनुमति नहीं है: + +```json +{ + "detail": [ + { + "type": "extra_forbidden", + "loc": ["cookie", "santa_tracker"], + "msg": "Extra inputs are not permitted", + "input": "good-list-please", + } + ] +} +``` + +## सारांश { #summary } + +आप **FastAPI** में **cookies** declare करने के लिए **Pydantic models** का उपयोग कर सकते हैं। 😎 diff --git a/docs/hi/docs/tutorial/cookie-params.md b/docs/hi/docs/tutorial/cookie-params.md new file mode 100644 index 000000000..9f6265dd6 --- /dev/null +++ b/docs/hi/docs/tutorial/cookie-params.md @@ -0,0 +1,45 @@ +# Cookie Parameters { #cookie-parameters } + +आप `Cookie` parameters को उसी तरह define कर सकते हैं जैसे आप `Query` और `Path` parameters define करते हैं। + +## `Cookie` import करें { #import-cookie } + +पहले `Cookie` import करें: + +{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[3] *} + +## `Cookie` parameters declare करें { #declare-cookie-parameters } + +फिर cookie parameters को `Path` और `Query` जैसी ही structure का उपयोग करके declare करें। + +आप default value के साथ-साथ सभी extra validation या annotation parameters भी define कर सकते हैं: + +{* ../../docs_src/cookie_params/tutorial001_an_py310.py hl[9] *} + +/// note | तकनीकी विवरण + +`Cookie`, `Path` और `Query` की एक "sister" class है। यह भी उसी common `Param` class से inherit करती है। + +लेकिन याद रखें कि जब आप `fastapi` से `Query`, `Path`, `Cookie` और अन्य चीज़ें import करते हैं, तो वे वास्तव में ऐसे functions होते हैं जो special classes return करते हैं। + +/// + +/// note | नोट + +Cookies declare करने के लिए, आपको `Cookie` का उपयोग करना होगा, क्योंकि अन्यथा parameters को query parameters के रूप में interpret किया जाएगा। + +/// + +/// note | नोट + +ध्यान रखें कि, क्योंकि **browsers cookies को** विशेष तरीकों से और पर्दे के पीछे handle करते हैं, वे **JavaScript** को उन्हें आसानी से access करने की अनुमति **नहीं** देते। + +यदि आप `/docs` पर **API docs UI** में जाते हैं, तो आप अपनी *path operations* के लिए cookies की **documentation** देख पाएँगे। + +लेकिन भले ही आप **data भरें** और "Execute" पर click करें, क्योंकि docs UI **JavaScript** के साथ काम करता है, cookies भेजी नहीं जाएँगी, और आपको ऐसा **error** message दिखेगा जैसे आपने कोई values लिखी ही नहीं हों। + +/// + +## Recap { #recap } + +`Query` और `Path` जैसे ही common pattern का उपयोग करके, `Cookie` के साथ cookies declare करें। diff --git a/docs/hi/docs/tutorial/cors.md b/docs/hi/docs/tutorial/cors.md new file mode 100644 index 000000000..cd707990c --- /dev/null +++ b/docs/hi/docs/tutorial/cors.md @@ -0,0 +1,89 @@ +# CORS (Cross-Origin Resource Sharing) { #cors-cross-origin-resource-sharing } + +[CORS या "Cross-Origin Resource Sharing"](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) उन स्थितियों को संदर्भित करता है जब browser में चल रहे frontend में JavaScript code होता है जो backend से communicate करता है, और backend frontend से अलग "origin" में होता है। + +## Origin { #origin } + +एक origin protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`), और port (`80`, `443`, `8080`) का combination होता है। + +तो, ये सभी अलग-अलग origins हैं: + +* `http://localhost` +* `https://localhost` +* `http://localhost:8080` + +भले ही वे सभी `localhost` में हों, वे अलग-अलग protocols या ports का उपयोग करते हैं, इसलिए वे अलग-अलग "origins" हैं। + +## Steps { #steps } + +तो, मान लें कि आपके browser में `http://localhost:8080` पर एक frontend चल रहा है, और उसका JavaScript `http://localhost` पर चल रहे backend से communicate करने की कोशिश कर रहा है (क्योंकि हम port specify नहीं करते, browser default port `80` मान लेगा)। + +फिर, browser `:80`-backend को एक HTTP `OPTIONS` request भेजेगा, और अगर backend इस अलग origin (`http://localhost:8080`) से communication को authorize करने वाले उचित headers भेजता है, तो `:8080`-browser frontend में JavaScript को अपना request `:80`-backend को भेजने देगा। + +इसे हासिल करने के लिए, `:80`-backend के पास "allowed origins" की एक list होनी चाहिए। + +इस मामले में, `:8080`-frontend के सही ढंग से काम करने के लिए list में `http://localhost:8080` शामिल होना चाहिए। + +## Wildcards { #wildcards } + +यह भी possible है कि list को `"*"` (एक "wildcard") के रूप में declare किया जाए, यह बताने के लिए कि सभी allowed हैं। + +लेकिन यह केवल कुछ प्रकार के communication को allow करेगा, उन सभी चीज़ों को छोड़कर जिनमें credentials शामिल हैं: Cookies, Authorization headers जैसे कि Bearer Tokens के साथ उपयोग किए जाने वाले, आदि। + +इसलिए, सब कुछ सही ढंग से काम करे, इसके लिए allowed origins को स्पष्ट रूप से specify करना बेहतर है। + +## `CORSMiddleware` का उपयोग करें { #use-corsmiddleware } + +आप `CORSMiddleware` का उपयोग करके इसे अपनी **FastAPI** application में configure कर सकते हैं। + +* `CORSMiddleware` import करें। +* allowed origins की एक list बनाएँ (strings के रूप में)। +* इसे अपनी **FastAPI** application में "middleware" के रूप में जोड़ें। + +आप यह भी specify कर सकते हैं कि आपका backend allow करता है या नहीं: + +* Credentials (Authorization headers, Cookies, आदि)। +* Specific HTTP methods (`POST`, `PUT`) या wildcard `"*"` के साथ सभी methods। +* Specific HTTP headers या wildcard `"*"` के साथ सभी headers। + +{* ../../docs_src/cors/tutorial001_py310.py hl[2,6:11,13:19] *} + + +`CORSMiddleware` implementation द्वारा उपयोग किए जाने वाले default parameters default रूप से restrictive होते हैं, इसलिए browsers को Cross-Domain context में उनका उपयोग करने की permission देने के लिए आपको particular origins, methods, या headers को स्पष्ट रूप से enable करना होगा। + +निम्नलिखित arguments supported हैं: + +* `allow_origins` - origins की एक list जिन्हें cross-origin requests करने की permission होनी चाहिए। जैसे `['https://example.org', 'https://www.example.org']`। आप किसी भी origin को allow करने के लिए `['*']` का उपयोग कर सकते हैं। +* `allow_origin_regex` - origins के against match करने के लिए एक regex string जिन्हें cross-origin requests करने की permission होनी चाहिए। जैसे `'https://.*\.example\.org'`। +* `allow_methods` - HTTP methods की एक list जिन्हें cross-origin requests के लिए allowed होना चाहिए। Defaults to `['GET']`। आप सभी standard methods को allow करने के लिए `['*']` का उपयोग कर सकते हैं। +* `allow_headers` - HTTP request headers की एक list जिन्हें cross-origin requests के लिए supported होना चाहिए। Defaults to `[]`। आप सभी headers को allow करने के लिए `['*']` का उपयोग कर सकते हैं। `Accept`, `Accept-Language`, `Content-Language` और `Content-Type` headers हमेशा [simple CORS requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) के लिए allowed होते हैं। +* `allow_credentials` - indicate करता है कि cookies cross-origin requests के लिए supported होनी चाहिए। Defaults to `False`. + + अगर `allow_credentials` को `True` पर set किया गया है, तो `allow_origins`, `allow_methods` और `allow_headers` में से किसी को भी `['*']` पर set नहीं किया जा सकता। उन सभी को [explicitly specified](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#credentialed_requests_and_wildcards) होना चाहिए। + +* `expose_headers` - indicate करता है कि कौन से response headers browser के लिए accessible बनाए जाने चाहिए। Defaults to `[]`। +* `max_age` - browsers के लिए CORS responses को cache करने का maximum समय seconds में set करता है। Defaults to `600`। + +middleware दो particular प्रकार के HTTP request का response देता है... + +### CORS preflight requests { #cors-preflight-requests } + +ये `Origin` और `Access-Control-Request-Method` headers वाले कोई भी `OPTIONS` request होते हैं। + +इस मामले में middleware incoming request को intercept करेगा और appropriate CORS headers के साथ respond करेगा, और informational purposes के लिए या तो `200` या `400` response देगा। + +### Simple requests { #simple-requests } + +`Origin` header वाला कोई भी request। इस मामले में middleware request को सामान्य रूप से pass through करेगा, लेकिन response पर appropriate CORS headers शामिल करेगा। + +## अधिक जानकारी { #more-info } + +CORS के बारे में अधिक जानकारी के लिए, [Mozilla CORS documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) देखें। + +/// note | तकनीकी विवरण + +आप `from starlette.middleware.cors import CORSMiddleware` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, `fastapi.middleware` में कई middlewares provide करता है। लेकिन available middlewares में से अधिकांश सीधे Starlette से आते हैं। + +/// diff --git a/docs/hi/docs/tutorial/debugging.md b/docs/hi/docs/tutorial/debugging.md new file mode 100644 index 000000000..0c214bb9a --- /dev/null +++ b/docs/hi/docs/tutorial/debugging.md @@ -0,0 +1,113 @@ +# Debugging { #debugging } + +आप अपने editor में debugger connect कर सकते हैं, उदाहरण के लिए Visual Studio Code या PyCharm के साथ। + +## `uvicorn` को call करें { #call-uvicorn } + +अपने FastAPI application में, `uvicorn` को सीधे import करके run करें: + +{* ../../docs_src/debugging/tutorial001_py310.py hl[1,15] *} + +### `__name__ == "__main__"` के बारे में { #about-name-main } + +`__name__ == "__main__"` का मुख्य उद्देश्य ऐसा कुछ code रखना है जो तब execute होता है जब आपकी file को इसके साथ call किया जाता है: + +
+ +```console +$ python myapp.py +``` + +
+ +लेकिन तब call नहीं होता जब कोई दूसरी file इसे import करती है, जैसे कि: + +```Python +from myapp import app +``` + +#### अधिक विवरण { #more-details } + +मान लीजिए आपकी file का नाम `myapp.py` है। + +अगर आप इसे इसके साथ run करते हैं: + +
+ +```console +$ python myapp.py +``` + +
+ +तो आपकी file में Python द्वारा अपने आप बनाई गई internal variable `__name__` का value string `"__main__"` होगा। + +तो, यह section: + +```Python + uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +run होगा। + +--- + +अगर आप उस module (file) को import करते हैं तो ऐसा नहीं होगा। + +तो, अगर आपके पास `importer.py` नाम की कोई दूसरी file है जिसमें यह है: + +```Python +from myapp import app + +# कुछ और code +``` + +उस स्थिति में, `myapp.py` के अंदर अपने आप बनाई गई variable `__name__` का value `"__main__"` नहीं होगा। + +तो, यह line: + +```Python + uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +execute नहीं होगी। + +/// note | नोट + +अधिक जानकारी के लिए, [आधिकारिक Python docs](https://docs.python.org/3/library/__main__.html) देखें। + +/// + +## अपने debugger के साथ अपना code run करें { #run-your-code-with-your-debugger } + +क्योंकि आप Uvicorn server को सीधे अपने code से run कर रहे हैं, आप अपने Python program (अपने FastAPI application) को सीधे debugger से call कर सकते हैं। + +--- + +उदाहरण के लिए, Visual Studio Code में, आप यह कर सकते हैं: + +* "Debug" panel पर जाएँ। +* "Add configuration..."। +* "Python" चुनें। +* "`Python: Current File (Integrated Terminal)`" option के साथ debugger run करें। + +फिर यह आपके **FastAPI** code के साथ server start करेगा, आपके breakpoints पर रुकेगा, आदि। + +यह कुछ ऐसा दिख सकता है: + + + +--- + +अगर आप PyCharm का उपयोग करते हैं, तो आप यह कर सकते हैं: + +* "Run" menu खोलें। +* "Debug..." option चुनें। +* फिर एक context menu दिखाई देता है। +* debug करने के लिए file चुनें (इस मामले में, `main.py`)। + +फिर यह आपके **FastAPI** code के साथ server start करेगा, आपके breakpoints पर रुकेगा, आदि। + +यह कुछ ऐसा दिख सकता है: + + diff --git a/docs/hi/docs/tutorial/dependencies/classes-as-dependencies.md b/docs/hi/docs/tutorial/dependencies/classes-as-dependencies.md new file mode 100644 index 000000000..0d166e7ea --- /dev/null +++ b/docs/hi/docs/tutorial/dependencies/classes-as-dependencies.md @@ -0,0 +1,288 @@ +# Dependencies के रूप में Classes { #classes-as-dependencies } + +**Dependency Injection** system में और गहराई में जाने से पहले, पिछले उदाहरण को बेहतर बनाते हैं। + +## पिछले उदाहरण से एक `dict` { #a-dict-from-the-previous-example } + +पिछले उदाहरण में, हम अपनी dependency ("dependable") से एक `dict` return कर रहे थे: + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[9] *} + +लेकिन फिर हमें *path operation function* के parameter `commons` में एक `dict` मिलता है। + +और हम जानते हैं कि editors `dict`s के लिए ज़्यादा support (जैसे completion) नहीं दे सकते, क्योंकि वे उनकी keys और value types नहीं जान सकते। + +हम इससे बेहतर कर सकते हैं... + +## Dependency किससे बनती है { #what-makes-a-dependency } + +अब तक आपने dependencies को functions के रूप में declare होते देखा है। + +लेकिन dependencies declare करने का यही एकमात्र तरीका नहीं है (हालाँकि शायद यह अधिक common होगा)। + +मुख्य बात यह है कि dependency एक "callable" होनी चाहिए। + +Python में "**callable**" वह कोई भी चीज़ है जिसे Python एक function की तरह "call" कर सकता है। + +तो, अगर आपके पास कोई object `something` है (जो शायद function _न_ हो) और आप उसे इस तरह "call" (execute) कर सकते हैं: + +```Python +something() +``` + +या + +```Python +something(some_argument, some_keyword_argument="foo") +``` + +तो वह एक "callable" है। + +## Dependencies के रूप में Classes { #classes-as-dependencies_1 } + +आप ध्यान दे सकते हैं कि Python class का instance बनाने के लिए भी आप वही syntax उपयोग करते हैं। + +उदाहरण के लिए: + +```Python +class Cat: + def __init__(self, name: str): + self.name = name + + +fluffy = Cat(name="Mr Fluffy") +``` + +इस case में, `fluffy` class `Cat` का एक instance है। + +और `fluffy` बनाने के लिए, आप `Cat` को "call" कर रहे हैं। + +इसलिए, Python class भी एक **callable** है। + +फिर, **FastAPI** में, आप Python class को dependency के रूप में उपयोग कर सकते हैं। + +FastAPI वास्तव में यह check करता है कि वह एक "callable" (function, class या कुछ और) है और उसमें parameters defined हैं। + +अगर आप **FastAPI** में dependency के रूप में कोई "callable" pass करते हैं, तो यह उस "callable" के parameters को analyze करेगा, और उन्हें *path operation function* के parameters की तरह ही process करेगा। इसमें sub-dependencies भी शामिल हैं। + +यह उन callables पर भी लागू होता है जिनमें कोई parameters नहीं होते। ठीक वैसे ही जैसे बिना parameters वाले *path operation functions* के लिए होता। + +फिर, हम ऊपर वाली dependency "dependable" `common_parameters` को class `CommonQueryParams` में बदल सकते हैं: + +{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[11:15] *} + +Class का instance बनाने के लिए उपयोग की गई `__init__` method पर ध्यान दें: + +{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[12] *} + +...इसमें वही parameters हैं जो हमारे पिछले `common_parameters` में थे: + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8] *} + +यही parameters **FastAPI** dependency को "solve" करने के लिए उपयोग करेगा। + +दोनों cases में, इसमें होगा: + +* एक optional `q` query parameter जो `str` है। +* एक `skip` query parameter जो `int` है, जिसका default `0` है। +* एक `limit` query parameter जो `int` है, जिसका default `100` है। + +दोनों cases में data converted, validated, OpenAPI schema पर documented, आदि किया जाएगा। + +## इसका उपयोग करें { #use-it } + +अब आप इस class का उपयोग करके अपनी dependency declare कर सकते हैं। + +{* ../../docs_src/dependencies/tutorial002_an_py310.py hl[19] *} + +**FastAPI** `CommonQueryParams` class को call करता है। यह उस class का एक "instance" बनाता है और वह instance आपके function को parameter `commons` के रूप में pass किया जाएगा। + +## Type annotation बनाम `Depends` { #type-annotation-vs-depends } + +ध्यान दें कि ऊपर के code में हम `CommonQueryParams` को दो बार कैसे लिखते हैं: + +//// tab | Python 3.10+ + +```Python +commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +संभव हो तो `Annotated` version का उपयोग करना बेहतर है। + +/// + +```Python +commons: CommonQueryParams = Depends(CommonQueryParams) +``` + +//// + +आखिरी `CommonQueryParams`, इसमें: + +```Python +... Depends(CommonQueryParams) +``` + +...वही है जिसे **FastAPI** वास्तव में यह जानने के लिए उपयोग करेगा कि dependency क्या है। + +FastAPI इसी से declared parameters extract करेगा और वास्तव में इसी को call करेगा। + +--- + +इस case में, पहला `CommonQueryParams`, इसमें: + +//// tab | Python 3.10+ + +```Python +commons: Annotated[CommonQueryParams, ... +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +संभव हो तो `Annotated` version का उपयोग करना बेहतर है। + +/// + +```Python +commons: CommonQueryParams ... +``` + +//// + +...का **FastAPI** के लिए कोई विशेष अर्थ नहीं है। FastAPI इसे data conversion, validation, आदि के लिए उपयोग नहीं करेगा। (क्योंकि वह इसके लिए `Depends(CommonQueryParams)` का उपयोग कर रहा है)। + +आप वास्तव में सिर्फ यह लिख सकते हैं: + +//// tab | Python 3.10+ + +```Python +commons: Annotated[Any, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +संभव हो तो `Annotated` version का उपयोग करना बेहतर है। + +/// + +```Python +commons = Depends(CommonQueryParams) +``` + +//// + +...जैसे: + +{* ../../docs_src/dependencies/tutorial003_an_py310.py hl[19] *} + +लेकिन type declare करने को प्रोत्साहित किया जाता है क्योंकि इस तरह आपका editor जान पाएगा कि parameter `commons` के रूप में क्या pass होगा, और फिर यह code completion, type checks, आदि में आपकी मदद कर सकता है: + + + +## Shortcut { #shortcut } + +लेकिन आप देखते हैं कि यहाँ कुछ code repetition हो रहा है, `CommonQueryParams` को दो बार लिखते हुए: + +//// tab | Python 3.10+ + +```Python +commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +संभव हो तो `Annotated` version का उपयोग करना बेहतर है। + +/// + +```Python +commons: CommonQueryParams = Depends(CommonQueryParams) +``` + +//// + +**FastAPI** ऐसे cases के लिए एक shortcut प्रदान करता है, जहाँ dependency *specifically* एक class है जिसे **FastAPI** class का instance बनाने के लिए "call" करेगा। + +उन specific cases के लिए, आप यह कर सकते हैं: + +यह लिखने के बजाय: + +//// tab | Python 3.10+ + +```Python +commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)] +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +संभव हो तो `Annotated` version का उपयोग करना बेहतर है। + +/// + +```Python +commons: CommonQueryParams = Depends(CommonQueryParams) +``` + +//// + +...आप लिखते हैं: + +//// tab | Python 3.10+ + +```Python +commons: Annotated[CommonQueryParams, Depends()] +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +संभव हो तो `Annotated` version का उपयोग करना बेहतर है। + +/// + +```Python +commons: CommonQueryParams = Depends() +``` + +//// + +आप dependency को parameter के type के रूप में declare करते हैं, और `Depends(CommonQueryParams)` के अंदर पूरी class को *फिर से* लिखने के बजाय, आप बिना किसी parameter के `Depends()` का उपयोग करते हैं। + +फिर वही उदाहरण इस तरह दिखेगा: + +{* ../../docs_src/dependencies/tutorial004_an_py310.py hl[19] *} + +...और **FastAPI** जान जाएगा कि क्या करना है। + +/// tip | सुझाव + +अगर यह मददगार से ज़्यादा confusing लगता है, तो इसे अनदेखा करें, आपको इसकी *ज़रूरत* नहीं है। + +यह सिर्फ एक shortcut है। क्योंकि **FastAPI** आपको code repetition कम करने में मदद करने की परवाह करता है। + +/// diff --git a/docs/hi/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md b/docs/hi/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md new file mode 100644 index 000000000..6e051ef5d --- /dev/null +++ b/docs/hi/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md @@ -0,0 +1,69 @@ +# path operation decorators में Dependencies { #dependencies-in-path-operation-decorators } + +कुछ मामलों में आपको अपनी *path operation function* के अंदर किसी dependency की return value की वास्तव में ज़रूरत नहीं होती। + +या dependency कोई value return नहीं करती। + +लेकिन फिर भी आपको उसका execute/solve होना चाहिए। + +ऐसे मामलों के लिए, `Depends` के साथ *path operation function* parameter declare करने के बजाय, आप *path operation decorator* में `dependencies` की एक `list` जोड़ सकते हैं। + +## *path operation decorator* में `dependencies` जोड़ें { #add-dependencies-to-the-path-operation-decorator } + +*path operation decorator* एक optional argument `dependencies` प्राप्त करता है। + +यह `Depends()` की एक `list` होनी चाहिए: + +{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[19] *} + +ये dependencies normal dependencies की तरह ही execute/solve होंगी। लेकिन उनकी value (यदि वे कोई return करती हैं) आपकी *path operation function* को pass नहीं की जाएगी। + +/// tip | सुझाव + +कुछ editors unused function parameters की जाँच करते हैं, और उन्हें errors के रूप में दिखाते हैं। + +*path operation decorator* में इन `dependencies` का उपयोग करके आप सुनिश्चित कर सकते हैं कि वे execute हों, साथ ही editor/tooling errors से बच सकें। + +यह नए developers के लिए भ्रम से बचने में भी मदद कर सकता है, जो आपके code में unused parameter देखकर सोच सकते हैं कि यह अनावश्यक है। + +/// + +/// note | नोट + +इस example में हम बनाए गए custom headers `X-Key` और `X-Token` का उपयोग करते हैं। + +लेकिन वास्तविक मामलों में, security implement करते समय, आपको integrated [Security utilities (अगला अध्याय)](../security/index.md) का उपयोग करने से अधिक लाभ मिलेंगे। + +/// + +## Dependencies errors और return values { #dependencies-errors-and-return-values } + +आप वही dependency *functions* उपयोग कर सकते हैं जिन्हें आप सामान्य रूप से उपयोग करते हैं। + +### Dependency requirements { #dependency-requirements } + +वे request requirements (जैसे headers) या अन्य sub-dependencies declare कर सकती हैं: + +{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[8,13] *} + +### Exceptions raise करें { #raise-exceptions } + +ये dependencies normal dependencies की तरह ही exceptions `raise` कर सकती हैं: + +{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[10,15] *} + +### Return values { #return-values } + +और वे values return कर सकती हैं या नहीं भी कर सकतीं, values का उपयोग नहीं किया जाएगा। + +इसलिए, आप एक normal dependency (जो value return करती है) को फिर से उपयोग कर सकते हैं जिसे आप पहले से कहीं और उपयोग करते हैं, और भले ही value का उपयोग न हो, dependency execute होगी: + +{* ../../docs_src/dependencies/tutorial006_an_py310.py hl[11,16] *} + +## *path operations* के समूह के लिए Dependencies { #dependencies-for-a-group-of-path-operations } + +बाद में, जब आप बड़े applications को structure करने के बारे में पढ़ेंगे ([बड़े Applications - कई Files](../../tutorial/bigger-applications.md)), संभवतः कई files के साथ, तो आप सीखेंगे कि *path operations* के समूह के लिए एक ही `dependencies` parameter कैसे declare किया जाए। + +## Global Dependencies { #global-dependencies } + +आगे हम देखेंगे कि पूरे `FastAPI` application में dependencies कैसे जोड़ी जाएँ, ताकि वे हर *path operation* पर लागू हों। diff --git a/docs/hi/docs/tutorial/dependencies/dependencies-with-yield.md b/docs/hi/docs/tutorial/dependencies/dependencies-with-yield.md new file mode 100644 index 000000000..e85d7401a --- /dev/null +++ b/docs/hi/docs/tutorial/dependencies/dependencies-with-yield.md @@ -0,0 +1,289 @@ +# `yield` वाली Dependencies { #dependencies-with-yield } + +FastAPI ऐसी dependencies को support करता है जो पूरा होने के बाद अतिरिक्त steps करती हैं। + +ऐसा करने के लिए, `return` की जगह `yield` का उपयोग करें, और अतिरिक्त steps (code) उसके बाद लिखें। + +/// tip | सुझाव + +हर dependency में `yield` का उपयोग केवल एक बार करना सुनिश्चित करें। + +/// + +/// note | तकनीकी विवरण + +कोई भी function जो इनके साथ उपयोग करने के लिए valid है: + +* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) या +* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager) + +वह **FastAPI** dependency के रूप में उपयोग करने के लिए valid होगी। + +वास्तव में, FastAPI internally इन दोनों decorators का उपयोग करता है। + +/// + +## `yield` वाली database dependency { #a-database-dependency-with-yield } + +उदाहरण के लिए, आप इसका उपयोग database session बनाने और पूरा होने के बाद उसे close करने के लिए कर सकते हैं। + +`yield` statement से पहले और उसे शामिल करते हुए केवल वही code response बनाने से पहले execute होता है: + +{* ../../docs_src/dependencies/tutorial007_py310.py hl[2:4] *} + +yield किया गया value वही होता है जिसे *path operations* और अन्य dependencies में inject किया जाता है: + +{* ../../docs_src/dependencies/tutorial007_py310.py hl[4] *} + +`yield` statement के बाद वाला code response के बाद execute होता है: + +{* ../../docs_src/dependencies/tutorial007_py310.py hl[5:6] *} + +/// tip | सुझाव + +आप `async` या regular functions का उपयोग कर सकते हैं। + +**FastAPI** हर एक के साथ सही काम करेगा, ठीक normal dependencies की तरह। + +/// + +## `yield` और `try` वाली dependency { #a-dependency-with-yield-and-try } + +अगर आप `yield` वाली dependency में `try` block का उपयोग करते हैं, तो dependency का उपयोग करते समय throw की गई कोई भी exception आपको मिलेगी। + +उदाहरण के लिए, अगर बीच में किसी point पर, किसी दूसरी dependency में या किसी *path operation* में, कुछ code ने database transaction को "rollback" किया या कोई अन्य exception बनाई, तो आपको अपनी dependency में वह exception मिलेगी। + +इसलिए, आप `except SomeException` के साथ dependency के अंदर उस specific exception को देख सकते हैं। + +इसी तरह, आप `finally` का उपयोग यह सुनिश्चित करने के लिए कर सकते हैं कि exit steps execute हों, चाहे exception आई हो या नहीं। + +{* ../../docs_src/dependencies/tutorial007_py310.py hl[3,5] *} + +## `yield` वाली Sub-dependencies { #sub-dependencies-with-yield } + +आपके पास किसी भी size और shape की sub-dependencies और sub-dependencies के "trees" हो सकते हैं, और उनमें से कोई भी या सभी `yield` का उपयोग कर सकती हैं। + +**FastAPI** यह सुनिश्चित करेगा कि `yield` वाली हर dependency में "exit code" सही क्रम में run हो। + +उदाहरण के लिए, `dependency_c` की dependency `dependency_b` पर हो सकती है, और `dependency_b` की `dependency_a` पर: + +{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[6,14,22] *} + +और वे सभी `yield` का उपयोग कर सकती हैं। + +इस case में `dependency_c` को अपना exit code execute करने के लिए `dependency_b` से मिला value (यहाँ `dep_b` नाम दिया गया है) अभी भी उपलब्ध चाहिए। + +और, बदले में, `dependency_b` को अपने exit code के लिए `dependency_a` से मिला value (यहाँ `dep_a` नाम दिया गया है) उपलब्ध चाहिए। + +{* ../../docs_src/dependencies/tutorial008_an_py310.py hl[18:19,26:27] *} + +इसी तरह, आपके पास कुछ dependencies `yield` वाली और कुछ अन्य dependencies `return` वाली हो सकती हैं, और उनमें से कुछ बाकी कुछ पर depend कर सकती हैं। + +और आपके पास एक single dependency हो सकती है जिसे `yield` वाली कई अन्य dependencies required हों, आदि। + +आप dependencies के कोई भी combinations रख सकते हैं। + +**FastAPI** यह सुनिश्चित करेगा कि सब कुछ सही क्रम में run हो। + +/// note | तकनीकी विवरण + +यह Python के [Context Managers](https://docs.python.org/3/library/contextlib.html) की वजह से काम करता है। + +**FastAPI** इसे हासिल करने के लिए internally उनका उपयोग करता है। + +/// + +## `yield` और `HTTPException` वाली Dependencies { #dependencies-with-yield-and-httpexception } + +आपने देखा कि आप `yield` वाली dependencies का उपयोग कर सकते हैं और ऐसे `try` blocks रख सकते हैं जो कुछ code execute करने की कोशिश करते हैं और फिर `finally` के बाद कुछ exit code run करते हैं। + +आप raise की गई exception को catch करने और उसके साथ कुछ करने के लिए `except` का भी उपयोग कर सकते हैं। + +उदाहरण के लिए, आप कोई अलग exception raise कर सकते हैं, जैसे `HTTPException`। + +/// tip | सुझाव + +यह थोड़ी advanced technique है, और ज्यादातर cases में आपको वास्तव में इसकी ज़रूरत नहीं होगी, क्योंकि आप अपने application code के बाकी हिस्से के अंदर से exceptions (जिसमें `HTTPException` भी शामिल है) raise कर सकते हैं, उदाहरण के लिए, *path operation function* में। + +लेकिन अगर आपको इसकी ज़रूरत हो तो यह उपलब्ध है। 🤓 + +/// + +{* ../../docs_src/dependencies/tutorial008b_an_py310.py hl[18:22,31] *} + +अगर आप exceptions को catch करके उसके आधार पर custom response बनाना चाहते हैं, तो [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers) बनाएँ। + +## `yield` और `except` वाली Dependencies { #dependencies-with-yield-and-except } + +अगर आप `yield` वाली dependency में `except` का उपयोग करके exception catch करते हैं और उसे फिर से raise नहीं करते (या कोई नई exception raise नहीं करते), तो FastAPI यह notice नहीं कर पाएगा कि कोई exception हुई थी, ठीक वैसे ही जैसे regular Python में होता: + +{* ../../docs_src/dependencies/tutorial008c_an_py310.py hl[15:16] *} + +इस case में, client को *HTTP 500 Internal Server Error* response दिखेगा, जैसा कि होना चाहिए, क्योंकि हम `HTTPException` या उसके जैसी कोई चीज़ raise नहीं कर रहे हैं, लेकिन server के पास **कोई logs नहीं होंगे** या error क्या था इसका कोई अन्य संकेत नहीं होगा। 😱 + +### `yield` और `except` वाली Dependencies में हमेशा `raise` करें { #always-raise-in-dependencies-with-yield-and-except } + +अगर आप `yield` वाली dependency में exception catch करते हैं, तो जब तक आप कोई दूसरी `HTTPException` या similar चीज़ raise नहीं कर रहे हैं, **आपको original exception को फिर से raise करना चाहिए**। + +आप `raise` का उपयोग करके उसी exception को फिर से raise कर सकते हैं: + +{* ../../docs_src/dependencies/tutorial008d_an_py310.py hl[17] *} + +अब client को वही *HTTP 500 Internal Server Error* response मिलेगा, लेकिन server के logs में हमारा custom `InternalError` होगा। 😎 + +## `yield` वाली dependencies का Execution { #execution-of-dependencies-with-yield } + +Execution का sequence कमोबेश इस diagram जैसा है। Time ऊपर से नीचे की ओर चलता है। और हर column code के साथ interact करने या execute करने वाले parts में से एक है। + +```mermaid +sequenceDiagram + +participant client as Client +participant handler as Exception handler +participant dep as Dep with yield +participant operation as Path Operation +participant tasks as Background tasks + + Note over client,operation: Can raise exceptions, including HTTPException + client ->> dep: Start request + Note over dep: Run code up to yield + opt raise Exception + dep -->> handler: Raise Exception + handler -->> client: HTTP error response + end + dep ->> operation: Run dependency, e.g. DB session + opt raise + operation -->> dep: Raise Exception (e.g. HTTPException) + opt handle + dep -->> dep: Can catch exception, raise a new HTTPException, raise other exception + end + handler -->> client: HTTP error response + end + + operation ->> client: Return response to client + Note over client,operation: Response is already sent, can't change it anymore + opt Tasks + operation -->> tasks: Send background tasks + end + opt Raise other exception + tasks -->> tasks: Handle exceptions in the background task code + end +``` + +/// note | नोट + +Client को केवल **एक response** भेजा जाएगा। यह error responses में से एक हो सकता है या *path operation* से आया response होगा। + +उन responses में से एक भेजे जाने के बाद, कोई अन्य response नहीं भेजा जा सकता। + +/// + +/// tip | सुझाव + +अगर आप *path operation function* के code में कोई भी exception raise करते हैं, तो उसे yield वाली dependencies को pass किया जाएगा, जिसमें `HTTPException` भी शामिल है। ज्यादातर cases में आप चाहेंगे कि `yield` वाली dependency से वही exception या कोई नई exception फिर से raise करें ताकि यह सुनिश्चित हो सके कि उसे सही तरीके से handle किया गया है। + +/// + +## Early exit और `scope` { #early-exit-and-scope } + +आम तौर पर `yield` वाली dependencies का exit code client को **response** भेजे जाने के बाद execute होता है। + +लेकिन अगर आपको पता है कि *path operation function* से return करने के बाद आपको dependency का उपयोग करने की ज़रूरत नहीं होगी, तो आप `Depends(scope="function")` का उपयोग करके FastAPI को बता सकते हैं कि उसे dependency को *path operation function* के return करने के बाद, लेकिन **response भेजे जाने से पहले** close करना चाहिए। + +{* ../../docs_src/dependencies/tutorial008e_an_py310.py hl[12,16] *} + +`Depends()` एक `scope` parameter receive करता है जो यह हो सकता है: + +* `"function"`: request handle करने वाले *path operation function* से पहले dependency शुरू करें, *path operation function* खत्म होने के बाद dependency खत्म करें, लेकिन client को response वापस भेजे जाने से **पहले**। यानी, dependency function *path operation **function*** के **around** execute होगा। +* `"request"`: request handle करने वाले *path operation function* से पहले dependency शुरू करें (`"function"` उपयोग करने जैसा), लेकिन client को response वापस भेजे जाने के **बाद** खत्म करें। यानी, dependency function **request** और response cycle के **around** execute होगा। + +अगर specify नहीं किया गया है और dependency में `yield` है, तो उसका `scope` default रूप से `"request"` होगा। + +### Sub-dependencies के लिए `scope` { #scope-for-sub-dependencies } + +जब आप `scope="request"` (default) वाली dependency declare करते हैं, तो किसी भी sub-dependency का `scope` भी `"request"` होना चाहिए। + +लेकिन `"function"` के `scope` वाली dependency के पास `"function"` और `"request"` scope वाली dependencies हो सकती हैं। + +ऐसा इसलिए है क्योंकि किसी भी dependency को sub-dependencies से पहले अपना exit code run करने में सक्षम होना चाहिए, क्योंकि उसे अपने exit code के दौरान अभी भी उनका उपयोग करने की ज़रूरत हो सकती है। + +```mermaid +sequenceDiagram + +participant client as Client +participant dep_req as Dep scope="request" +participant dep_func as Dep scope="function" +participant operation as Path Operation + + client ->> dep_req: Start request + Note over dep_req: Run code up to yield + dep_req ->> dep_func: Pass dependency + Note over dep_func: Run code up to yield + dep_func ->> operation: Run path operation with dependency + operation ->> dep_func: Return from path operation + Note over dep_func: Run code after yield + Note over dep_func: ✅ Dependency closed + dep_func ->> client: Send response to client + Note over client: Response sent + Note over dep_req: Run code after yield + Note over dep_req: ✅ Dependency closed +``` + +## `yield`, `HTTPException`, `except` और Background Tasks वाली Dependencies { #dependencies-with-yield-httpexception-except-and-background-tasks } + +`yield` वाली Dependencies समय के साथ अलग-अलग use cases cover करने और कुछ issues fix करने के लिए evolve हुई हैं। + +अगर आप देखना चाहते हैं कि FastAPI के अलग-अलग versions में क्या बदला है, तो आप इसके बारे में advanced guide में, [Advanced Dependencies - `yield`, `HTTPException`, `except` और Background Tasks वाली Dependencies](../../advanced/advanced-dependencies.md#dependencies-with-yield-httpexception-except-and-background-tasks) में और पढ़ सकते हैं। + +## Context Managers { #context-managers } + +### "Context Managers" क्या हैं { #what-are-context-managers } + +"Context Managers" उन Python objects में से कोई भी हैं जिनका उपयोग आप `with` statement में कर सकते हैं। + +उदाहरण के लिए, [आप file पढ़ने के लिए `with` का उपयोग कर सकते हैं](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files): + +```Python +with open("./somefile.txt") as f: + contents = f.read() + print(contents) +``` + +अंदर से, `open("./somefile.txt")` एक object बनाता है जिसे "Context Manager" कहा जाता है। + +जब `with` block खत्म होता है, तो यह file को close करना सुनिश्चित करता है, भले ही exceptions आई हों। + +जब आप `yield` वाली dependency बनाते हैं, तो **FastAPI** internally उसके लिए एक context manager बनाएगा, और उसे कुछ अन्य related tools के साथ combine करेगा। + +### `yield` वाली dependencies में context managers का उपयोग करना { #using-context-managers-in-dependencies-with-yield } + +/// warning | चेतावनी + +यह कमोबेश एक "advanced" idea है। + +अगर आप अभी **FastAPI** शुरू ही कर रहे हैं, तो शायद आप इसे अभी skip करना चाहेंगे। + +/// + +Python में, आप [दो methods वाली class बनाकर: `__enter__()` और `__exit__()`](https://docs.python.org/3/reference/datamodel.html#context-managers) Context Managers बना सकते हैं। + +आप dependency function के अंदर `with` या `async with` statements का उपयोग करके इन्हें `yield` वाली **FastAPI** dependencies के अंदर भी उपयोग कर सकते हैं: + +{* ../../docs_src/dependencies/tutorial010_py310.py hl[1:9,13] *} + +/// tip | सुझाव + +Context manager बनाने का एक और तरीका है: + +* [`@contextlib.contextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager) या +* [`@contextlib.asynccontextmanager`](https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager) + +इनका उपयोग single `yield` वाले function को decorate करने के लिए करना। + +**FastAPI** internally `yield` वाली dependencies के लिए यही उपयोग करता है। + +लेकिन आपको FastAPI dependencies के लिए decorators का उपयोग करने की ज़रूरत नहीं है (और आपको नहीं करना चाहिए)। + +FastAPI internally आपके लिए यह कर देगा। + +/// diff --git a/docs/hi/docs/tutorial/dependencies/global-dependencies.md b/docs/hi/docs/tutorial/dependencies/global-dependencies.md new file mode 100644 index 000000000..e313b40f0 --- /dev/null +++ b/docs/hi/docs/tutorial/dependencies/global-dependencies.md @@ -0,0 +1,16 @@ +# वैश्विक Dependencies { #global-dependencies } + +कुछ प्रकार के applications के लिए आप पूरे application में dependencies जोड़ना चाह सकते हैं। + +जिस तरह आप [*path operation decorators* में `dependencies` जोड़ सकते हैं](dependencies-in-path-operation-decorators.md), उसी तरह आप उन्हें `FastAPI` application में भी जोड़ सकते हैं। + +उस स्थिति में, वे application की सभी *path operations* पर लागू होंगी: + +{* ../../docs_src/dependencies/tutorial012_an_py310.py hl[17] *} + + +और [*path operation decorators* में `dependencies` जोड़ने](dependencies-in-path-operation-decorators.md) वाले section की सभी बातें अभी भी लागू होती हैं, लेकिन इस मामले में, app की सभी *path operations* पर। + +## *path operations* के समूहों के लिए Dependencies { #dependencies-for-groups-of-path-operations } + +बाद में, जब आप बड़े applications को संरचित करने के तरीके के बारे में पढ़ेंगे ([बड़े Applications - कई Files](../../tutorial/bigger-applications.md)), संभवतः कई files के साथ, तो आप सीखेंगे कि *path operations* के एक समूह के लिए एक ही `dependencies` parameter कैसे declare किया जाए। diff --git a/docs/hi/docs/tutorial/dependencies/index.md b/docs/hi/docs/tutorial/dependencies/index.md new file mode 100644 index 000000000..903e43391 --- /dev/null +++ b/docs/hi/docs/tutorial/dependencies/index.md @@ -0,0 +1,250 @@ +# Dependencies { #dependencies } + +**FastAPI** में एक बहुत शक्तिशाली लेकिन सहज **Dependency Injection** system है। + +इसे उपयोग में बहुत सरल होने के लिए, और किसी भी developer के लिए दूसरे components को **FastAPI** के साथ integrate करना बहुत आसान बनाने के लिए design किया गया है। + +## "Dependency Injection" क्या है { #what-is-dependency-injection } + +**"Dependency Injection"** का मतलब programming में यह है कि आपके code (इस मामले में, आपके *path operation functions*) के पास यह declare करने का एक तरीका होता है कि उसे काम करने और उपयोग करने के लिए किन चीज़ों की ज़रूरत है: "dependencies"। + +और फिर, वह system (इस मामले में **FastAPI**) आपके code को वे ज़रूरी dependencies उपलब्ध कराने के लिए जो भी required है, उसका ध्यान रखेगा (dependencies को "inject" करेगा)। + +यह तब बहुत उपयोगी होता है जब आपको: + +* shared logic चाहिए (वही code logic बार-बार)। +* database connections share करने हों। +* security, authentication, role requirements, आदि enforce करने हों। +* और भी कई चीज़ें... + +ये सब, code repetition को कम से कम रखते हुए। + +## पहले कदम { #first-steps } + +आइए एक बहुत सरल उदाहरण देखते हैं। यह इतना सरल होगा कि अभी के लिए बहुत उपयोगी नहीं है। + +लेकिन इस तरह हम इस बात पर focus कर सकते हैं कि **Dependency Injection** system कैसे काम करता है। + +### एक dependency, या "dependable" बनाएँ { #create-a-dependency-or-dependable } + +पहले dependency पर focus करते हैं। + +यह बस एक function है जो वे सभी समान parameters ले सकता है जो एक *path operation function* ले सकता है: + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[8:9] *} + +बस इतना ही। + +**2 lines**। + +और इसका shape और structure वही है जो आपके सभी *path operation functions* का होता है। + +आप इसे "decorator" के बिना एक *path operation function* के रूप में सोच सकते हैं (`@app.get("/some-path")` के बिना)। + +और यह आपकी इच्छा के अनुसार कुछ भी return कर सकता है। + +इस मामले में, यह dependency अपेक्षा करती है: + +* एक optional query parameter `q` जो `str` है। +* एक optional query parameter `skip` जो `int` है, और default रूप से `0` है। +* एक optional query parameter `limit` जो `int` है, और default रूप से `100` है। + +और फिर यह बस उन values वाला एक `dict` return करता है। + +/// note | नोट + +FastAPI ने version 0.95.0 में `Annotated` के लिए support जोड़ा (और इसे recommend करना शुरू किया)। + +अगर आपके पास पुराना version है, तो `Annotated` का उपयोग करने की कोशिश करते समय आपको errors मिलेंगे। + +`Annotated` का उपयोग करने से पहले सुनिश्चित करें कि आप [FastAPI version को Upgrade](../../deployment/versions.md#upgrading-the-fastapi-versions) करके कम से कम 0.95.1 कर लें। + +/// + +### `Depends` import करें { #import-depends } + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[3] *} + +### "dependant" में dependency declare करें { #declare-the-dependency-in-the-dependant } + +जिस तरह आप अपने *path operation function* parameters के साथ `Body`, `Query`, आदि का उपयोग करते हैं, उसी तरह एक नए parameter के साथ `Depends` का उपयोग करें: + +{* ../../docs_src/dependencies/tutorial001_an_py310.py hl[13,18] *} + +हालाँकि आप अपने function के parameters में `Depends` का उपयोग उसी तरह करते हैं जैसे आप `Body`, `Query`, आदि का उपयोग करते हैं, `Depends` थोड़ा अलग तरीके से काम करता है। + +आप `Depends` को केवल एक parameter देते हैं। + +यह parameter किसी function जैसा होना चाहिए। + +आप इसे सीधे **call नहीं करते** (अंत में parentheses नहीं जोड़ते), आप बस इसे `Depends()` को एक parameter के रूप में pass करते हैं। + +और वह function उसी तरह parameters लेता है जैसे *path operation functions* लेते हैं। + +/// tip | सुझाव + +अगले chapter में आप देखेंगे कि functions के अलावा कौन सी दूसरी "चीज़ें" dependencies के रूप में उपयोग की जा सकती हैं। + +/// + +जब भी कोई नया request आता है, **FastAPI** इन बातों का ध्यान रखेगा: + +* आपकी dependency ("dependable") function को सही parameters के साथ call करना। +* आपके function से result लेना। +* उस result को आपके *path operation function* के parameter को assign करना। + +```mermaid +graph TB + +common_parameters(["common_parameters"]) +read_items["/items/"] +read_users["/users/"] + +common_parameters --> read_items +common_parameters --> read_users +``` + +इस तरह आप shared code एक बार लिखते हैं और **FastAPI** आपके *path operations* के लिए उसे call करने का ध्यान रखता है। + +/// tip | सुझाव + +ध्यान दें कि आपको कोई special class बनाकर उसे **FastAPI** को "register" करने के लिए कहीं pass करने या ऐसा कुछ करने की ज़रूरत नहीं है। + +आप बस इसे `Depends` को pass करते हैं और **FastAPI** जानता है कि बाकी कैसे करना है। + +/// + +## `Annotated` dependencies share करें { #share-annotated-dependencies } + +ऊपर के उदाहरणों में, आप देखते हैं कि **code duplication** का थोड़ा सा हिस्सा है। + +जब आपको `common_parameters()` dependency का उपयोग करना हो, तो आपको type annotation और `Depends()` के साथ पूरा parameter लिखना पड़ता है: + +```Python +commons: Annotated[dict, Depends(common_parameters)] +``` + +लेकिन क्योंकि हम `Annotated` का उपयोग कर रहे हैं, हम उस `Annotated` value को एक variable में store कर सकते हैं और कई जगहों पर उपयोग कर सकते हैं: + +{* ../../docs_src/dependencies/tutorial001_02_an_py310.py hl[12,16,21] *} + +/// tip | सुझाव + +यह बस standard Python है, इसे "type alias" कहा जाता है, यह वास्तव में **FastAPI** के लिए specific नहीं है। + +लेकिन क्योंकि **FastAPI** Python standards पर आधारित है, जिसमें `Annotated` भी शामिल है, आप अपने code में इस trick का उपयोग कर सकते हैं। 😎 + +/// + +dependencies अपेक्षित रूप से काम करती रहेंगी, और **सबसे अच्छी बात** यह है कि **type information preserve रहेगी**, जिसका मतलब है कि आपका editor आपको **autocompletion**, **inline errors**, आदि प्रदान करना जारी रख सकेगा। यही बात `mypy` जैसे दूसरे tools के लिए भी लागू होती है। + +यह खास तौर पर तब उपयोगी होगा जब आप इसे एक **large code base** में उपयोग करते हैं जहाँ आप **वही dependencies** बार-बार **कई *path operations*** में उपयोग करते हैं। + +## `async` करें या `async` न करें { #to-async-or-not-to-async } + +क्योंकि dependencies को भी **FastAPI** द्वारा call किया जाएगा (आपके *path operation functions* की तरह), functions define करते समय वही rules लागू होते हैं। + +आप `async def` या सामान्य `def` का उपयोग कर सकते हैं। + +और आप normal `def` *path operation functions* के अंदर `async def` dependencies declare कर सकते हैं, या `async def` *path operation functions* के अंदर `def` dependencies, आदि। + +इससे फर्क नहीं पड़ता। **FastAPI** जानता होगा कि क्या करना है। + +/// note | नोट + +अगर आपको नहीं पता, तो docs में `async` और `await` के बारे में [Async: *"In a hurry?"*](../../async.md#in-a-hurry) section देखें। + +/// + +## OpenAPI के साथ integrated { #integrated-with-openapi } + +आपकी dependencies (और sub-dependencies) की सभी request declarations, validations और requirements उसी OpenAPI schema में integrate की जाएँगी। + +इसलिए, interactive docs में इन dependencies की सारी जानकारी भी होगी: + + + +## सरल उपयोग { #simple-usage } + +अगर आप इसे देखें, तो *path operation functions* इस तरह declare किए जाते हैं कि जब भी कोई *path* और *operation* match करता है, उनका उपयोग किया जाए, और फिर **FastAPI** सही parameters के साथ function को call करने और request से data extract करने का ध्यान रखता है। + +असल में, सभी (या अधिकांश) web frameworks इसी तरह काम करते हैं। + +आप उन functions को कभी सीधे call नहीं करते। वे आपके framework द्वारा call किए जाते हैं (इस मामले में, **FastAPI**)। + +Dependency Injection system के साथ, आप **FastAPI** को यह भी बता सकते हैं कि आपका *path operation function* किसी और चीज़ पर भी "depend" करता है जिसे आपके *path operation function* से पहले execute किया जाना चाहिए, और **FastAPI** उसे execute करने और results को "inject" करने का ध्यान रखेगा। + +"dependency injection" के इसी idea के लिए अन्य common terms हैं: + +* resources +* providers +* services +* injectables +* components + +## **FastAPI** plug-ins { #fastapi-plug-ins } + +Integrations और "plug-ins" **Dependency Injection** system का उपयोग करके बनाए जा सकते हैं। लेकिन वास्तव में, **"plug-ins" बनाने की कोई ज़रूरत नहीं है**, क्योंकि dependencies का उपयोग करके अनगिनत integrations और interactions declare किए जा सकते हैं जो आपके *path operation functions* के लिए उपलब्ध हो जाते हैं। + +और dependencies को बहुत सरल और सहज तरीके से बनाया जा सकता है, जिससे आप बस अपने required Python packages import कर सकते हैं, और उन्हें अपने API functions के साथ कुछ lines of code में integrate कर सकते हैं, *literally*। + +आप अगले chapters में relational और NoSQL databases, security, आदि के बारे में इसके examples देखेंगे। + +## **FastAPI** compatibility { #fastapi-compatibility } + +Dependency injection system की सरलता **FastAPI** को इनके साथ compatible बनाती है: + +* सभी relational databases +* NoSQL databases +* external packages +* external APIs +* authentication और authorization systems +* API usage monitoring systems +* response data injection systems +* आदि। + +## सरल और शक्तिशाली { #simple-and-powerful } + +हालाँकि hierarchical dependency injection system को define और use करना बहुत सरल है, फिर भी यह बहुत शक्तिशाली है। + +आप ऐसी dependencies define कर सकते हैं जो बदले में खुद dependencies define कर सकती हैं। + +अंत में, dependencies का एक hierarchical tree बनता है, और **Dependency Injection** system आपके लिए इन सभी dependencies (और उनकी sub-dependencies) को solve करने और हर step पर results प्रदान करने (inject करने) का ध्यान रखता है। + +उदाहरण के लिए, मान लें कि आपके पास 4 API endpoints (*path operations*) हैं: + +* `/items/public/` +* `/items/private/` +* `/users/{user_id}/activate` +* `/items/pro/` + +तो आप उनमें से हर एक के लिए अलग-अलग permission requirements केवल dependencies और sub-dependencies के साथ जोड़ सकते हैं: + +```mermaid +graph TB + +current_user(["current_user"]) +active_user(["active_user"]) +admin_user(["admin_user"]) +paying_user(["paying_user"]) + +public["/items/public/"] +private["/items/private/"] +activate_user["/users/{user_id}/activate"] +pro_items["/items/pro/"] + +current_user --> active_user +active_user --> admin_user +active_user --> paying_user + +current_user --> public +active_user --> private +admin_user --> activate_user +paying_user --> pro_items +``` + +## **OpenAPI** के साथ integrated { #integrated-with-openapi_1 } + +ये सभी dependencies, अपनी requirements declare करते समय, आपके *path operations* में parameters, validations, आदि भी जोड़ती हैं। + +**FastAPI** यह सब OpenAPI schema में जोड़ने का ध्यान रखेगा, ताकि यह interactive documentation systems में दिखाया जा सके। diff --git a/docs/hi/docs/tutorial/dependencies/sub-dependencies.md b/docs/hi/docs/tutorial/dependencies/sub-dependencies.md new file mode 100644 index 000000000..7fb4bbdb7 --- /dev/null +++ b/docs/hi/docs/tutorial/dependencies/sub-dependencies.md @@ -0,0 +1,105 @@ +# Sub-dependencies { #sub-dependencies } + +आप ऐसी dependencies बना सकते हैं जिनकी अपनी **sub-dependencies** हों। + +वे उतनी **deep** हो सकती हैं जितनी आपको चाहिए। + +**FastAPI** उन्हें solve करने का ध्यान रखेगा। + +## पहली dependency "dependable" { #first-dependency-dependable } + +आप पहली dependency ("dependable") इस तरह बना सकते हैं: + +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[8:9] *} + +यह एक optional query parameter `q` को `str` के रूप में declare करता है, और फिर बस उसे return करता है। + +यह काफी सरल है (बहुत उपयोगी नहीं), लेकिन इससे हमें यह समझने पर ध्यान देने में मदद मिलेगी कि sub-dependencies कैसे काम करती हैं। + +## दूसरी dependency, "dependable" और "dependant" { #second-dependency-dependable-and-dependant } + +फिर आप एक और dependency function (एक "dependable") बना सकते हैं जो उसी समय अपनी खुद की dependency declare करता है (इसलिए यह एक "dependant" भी है): + +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[13] *} + +आइए declared parameters पर ध्यान दें: + +* भले ही यह function खुद एक dependency ("dependable") है, यह एक और dependency भी declare करता है (यह किसी और चीज़ पर "depends" करता है)। + * यह `query_extractor` पर depends करता है, और उसके द्वारा return किए गए value को parameter `q` में assign करता है। +* यह एक optional `last_query` cookie को भी `str` के रूप में declare करता है। + * अगर user ने कोई query `q` provide नहीं की, तो हम पिछली इस्तेमाल की गई query का उपयोग करते हैं, जिसे हमने पहले एक cookie में save किया था। + +## dependency का उपयोग करें { #use-the-dependency } + +फिर हम dependency का उपयोग इस तरह कर सकते हैं: + +{* ../../docs_src/dependencies/tutorial005_an_py310.py hl[23] *} + +/// note | नोट + +ध्यान दें कि हम *path operation function* में केवल एक dependency declare कर रहे हैं, `query_or_cookie_extractor`। + +लेकिन **FastAPI** को पता होगा कि `query_or_cookie_extractor` को call करते समय उसके results pass करने के लिए पहले `query_extractor` को solve करना है। + +/// + +```mermaid +graph TB + +query_extractor(["query_extractor"]) +query_or_cookie_extractor(["query_or_cookie_extractor"]) + +read_query["/items/"] + +query_extractor --> query_or_cookie_extractor --> read_query +``` + +## उसी dependency को कई बार उपयोग करना { #using-the-same-dependency-multiple-times } + +अगर आपकी किसी dependency को उसी *path operation* के लिए कई बार declare किया गया है, उदाहरण के लिए, कई dependencies की कोई common sub-dependency है, तो **FastAPI** जानता होगा कि उस sub-dependency को प्रति request केवल एक बार call करना है। + +और यह return किए गए value को एक "cache" में save करेगा और उसे उन सभी "dependants" को pass करेगा जिन्हें उस specific request में इसकी ज़रूरत है, बजाय उसी request के लिए dependency को कई बार call करने के। + +एक advanced scenario में, जहाँ आप जानते हैं कि आपको उसी request में "cached" value का उपयोग करने के बजाय हर step पर dependency को call करवाना है (संभवतः कई बार), आप `Depends` का उपयोग करते समय parameter `use_cache=False` set कर सकते हैं: + +//// tab | Python 3.10+ + +```Python hl_lines="1" +async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]): + return {"fresh_value": fresh_value} +``` + +//// + +//// tab | Python 3.10+ non-Annotated + +/// tip | सुझाव + +अगर संभव हो तो `Annotated` version का उपयोग करना पसंद करें। + +/// + +```Python hl_lines="1" +async def needy_dependency(fresh_value: str = Depends(get_value, use_cache=False)): + return {"fresh_value": fresh_value} +``` + +//// + +## Recap { #recap } + +यहाँ इस्तेमाल किए गए सभी fancy words को छोड़ दें, तो **Dependency Injection** system काफी सरल है। + +बस ऐसे functions जो *path operation functions* जैसे ही दिखते हैं। + +लेकिन फिर भी, यह बहुत powerful है, और आपको arbitrarily deeply nested dependency "graphs" (trees) declare करने देता है। + +/// tip | सुझाव + +इन सरल examples के साथ यह सब इतना उपयोगी नहीं लग सकता। + +लेकिन **security** के बारे में chapters में आप देखेंगे कि यह कितना उपयोगी है। + +और आप यह भी देखेंगे कि यह आपका कितना code बचाएगा। + +/// diff --git a/docs/hi/docs/tutorial/encoder.md b/docs/hi/docs/tutorial/encoder.md new file mode 100644 index 000000000..66dbbca2b --- /dev/null +++ b/docs/hi/docs/tutorial/encoder.md @@ -0,0 +1,35 @@ +# JSON संगत Encoder { #json-compatible-encoder } + +कुछ मामलों में आपको किसी data type (जैसे Pydantic model) को JSON के साथ संगत किसी चीज़ (जैसे `dict`, `list`, आदि) में convert करने की ज़रूरत पड़ सकती है। + +उदाहरण के लिए, अगर आपको इसे database में store करना हो। + +इसके लिए, **FastAPI** एक `jsonable_encoder()` function प्रदान करता है। + +## `jsonable_encoder` का उपयोग करना { #using-the-jsonable-encoder } + +कल्पना करें कि आपके पास एक database `fake_db` है जो केवल JSON संगत data ही स्वीकार करता है। + +उदाहरण के लिए, यह `datetime` objects स्वीकार नहीं करता, क्योंकि वे JSON के साथ संगत नहीं होते। + +इसलिए, एक `datetime` object को [ISO format](https://en.wikipedia.org/wiki/ISO_8601) में data रखने वाले `str` में convert करना होगा। + +इसी तरह, यह database Pydantic model (attributes वाला एक object) स्वीकार नहीं करेगा, केवल एक `dict`। + +इसके लिए आप `jsonable_encoder` का उपयोग कर सकते हैं। + +यह एक object, जैसे Pydantic model, प्राप्त करता है और JSON संगत version लौटाता है: + +{* ../../docs_src/encoder/tutorial001_py310.py hl[4,21] *} + +इस उदाहरण में, यह Pydantic model को `dict` में और `datetime` को `str` में convert करेगा। + +इसे call करने का परिणाम कुछ ऐसा होता है जिसे Python standard [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps) के साथ encode किया जा सकता है। + +यह JSON format में data रखने वाला कोई बड़ा `str` (string के रूप में) return नहीं करता। यह एक Python standard data structure (जैसे `dict`) return करता है, जिसमें values और sub-values होती हैं जो सभी JSON के साथ संगत होती हैं। + +/// note | नोट + +`jsonable_encoder` वास्तव में **FastAPI** द्वारा internally data convert करने के लिए उपयोग किया जाता है। लेकिन यह कई अन्य scenarios में भी उपयोगी है। + +/// diff --git a/docs/hi/docs/tutorial/extra-data-types.md b/docs/hi/docs/tutorial/extra-data-types.md new file mode 100644 index 000000000..a157ea017 --- /dev/null +++ b/docs/hi/docs/tutorial/extra-data-types.md @@ -0,0 +1,62 @@ +# अतिरिक्त Data Types { #extra-data-types } + +अब तक, आप सामान्य data types का उपयोग करते रहे हैं, जैसे: + +* `int` +* `float` +* `str` +* `bool` + +लेकिन आप अधिक जटिल data types भी उपयोग कर सकते हैं। + +और आपको अब तक देखी गई वही features मिलती रहेंगी: + +* शानदार editor support. +* आने वाली requests से data conversion. +* response data के लिए data conversion. +* Data validation. +* Automatic annotation और documentation. + +## अन्य data types { #other-data-types } + +यहाँ कुछ अतिरिक्त data types हैं जिनका आप उपयोग कर सकते हैं: + +* `UUID`: + * एक standard "Universally Unique Identifier", जो कई databases और systems में ID के रूप में आम है। + * requests और responses में इसे `str` के रूप में दर्शाया जाएगा। +* `datetime.datetime`: + * एक Python `datetime.datetime`. + * requests और responses में इसे ISO 8601 format में `str` के रूप में दर्शाया जाएगा, जैसे: `2008-09-15T15:53:00+05:00`. +* `datetime.date`: + * Python `datetime.date`. + * requests और responses में इसे ISO 8601 format में `str` के रूप में दर्शाया जाएगा, जैसे: `2008-09-15`. +* `datetime.time`: + * एक Python `datetime.time`. + * requests और responses में इसे ISO 8601 format में `str` के रूप में दर्शाया जाएगा, जैसे: `14:23:55.003`. +* `datetime.timedelta`: + * एक Python `datetime.timedelta`. + * requests और responses में इसे कुल seconds के `float` के रूप में दर्शाया जाएगा। + * Pydantic इसे "ISO 8601 time diff encoding" के रूप में दर्शाने की अनुमति भी देता है, [अधिक जानकारी के लिए docs देखें](https://docs.pydantic.dev/latest/concepts/serialization/#custom-serializers). +* `frozenset`: + * requests और responses में, इसे `set` जैसा ही माना जाता है: + * requests में, एक list पढ़ी जाएगी, duplicates हटाए जाएँगे और उसे `set` में convert किया जाएगा। + * responses में, `set` को `list` में convert किया जाएगा। + * generate किया गया schema बताएगा कि `set` values unique हैं (JSON Schema के `uniqueItems` का उपयोग करते हुए)। +* `bytes`: + * Standard Python `bytes`. + * requests और responses में इसे `str` की तरह माना जाएगा। + * generate किया गया schema बताएगा कि यह `binary` "format" वाला `str` है। +* `Decimal`: + * Standard Python `Decimal`. + * requests और responses में, इसे `float` जैसा ही handle किया जाएगा। +* आप सभी valid Pydantic data types यहाँ देख सकते हैं: [Pydantic data types](https://docs.pydantic.dev/latest/usage/types/types/). + +## उदाहरण { #example } + +यहाँ ऊपर दिए गए कुछ types का उपयोग करते हुए parameters वाला एक उदाहरण *path operation* है। + +{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[1,3,12:16] *} + +ध्यान दें कि function के अंदर parameters के अपने natural data type होते हैं, और उदाहरण के लिए, आप सामान्य date manipulations कर सकते हैं, जैसे: + +{* ../../docs_src/extra_data_types/tutorial001_an_py310.py hl[18:19] *} diff --git a/docs/hi/docs/tutorial/extra-models.md b/docs/hi/docs/tutorial/extra-models.md new file mode 100644 index 000000000..93a4e91dc --- /dev/null +++ b/docs/hi/docs/tutorial/extra-models.md @@ -0,0 +1,211 @@ +# Extra Models { #extra-models } + +पिछले उदाहरण को आगे बढ़ाते हुए, एक से अधिक संबंधित model होना आम बात होगी। + +यह खासकर user models के मामले में होता है, क्योंकि: + +* **input model** में password हो सकने की क्षमता चाहिए। +* **output model** में password नहीं होना चाहिए। +* **database model** में शायद hashed password होना चाहिए। + +/// danger | खतरा + +user के plaintext passwords कभी store न करें। हमेशा एक "secure hash" store करें जिसे आप बाद में verify कर सकें। + +अगर आप नहीं जानते, तो आप [security chapters](security/simple-oauth2.md#password-hashing) में सीखेंगे कि "password hash" क्या होता है। + +/// + +## कई models { #multiple-models } + +यह एक सामान्य idea है कि models अपने password fields और जहाँ वे इस्तेमाल होते हैं, वहाँ कैसे दिख सकते हैं: + +{* ../../docs_src/extra_models/tutorial001_py310.py hl[7,9,14,20,22,27:28,31:33,38:39] *} + +### `**user_in.model_dump()` के बारे में { #about-user-in-model-dump } + +#### Pydantic का `.model_dump()` { #pydantics-model-dump } + +`user_in`, class `UserIn` का एक Pydantic model है। + +Pydantic models में एक `.model_dump()` method होता है जो model के data के साथ एक `dict` return करता है। + +तो, अगर हम इस तरह एक Pydantic object `user_in` बनाते हैं: + +```Python +user_in = UserIn(username="john", password="secret", email="john.doe@example.com") +``` + +और फिर हम call करते हैं: + +```Python +user_dict = user_in.model_dump() +``` + +तो अब हमारे पास variable `user_dict` में data के साथ एक `dict` है (यह Pydantic model object के बजाय एक `dict` है)। + +और अगर हम call करते हैं: + +```Python +print(user_dict) +``` + +तो हमें यह Python `dict` मिलेगा: + +```Python +{ + 'username': 'john', + 'password': 'secret', + 'email': 'john.doe@example.com', + 'full_name': None, +} +``` + +#### एक `dict` को unpack करना { #unpacking-a-dict } + +अगर हम `user_dict` जैसा एक `dict` लेते हैं और उसे किसी function (या class) को `**user_dict` के साथ pass करते हैं, तो Python उसे "unpack" करेगा। यह `user_dict` की keys और values को सीधे key-value arguments के रूप में pass करेगा। + +तो, ऊपर वाले `user_dict` को जारी रखते हुए, यह लिखना: + +```Python +UserInDB(**user_dict) +``` + +कुछ इस equivalent result देगा: + +```Python +UserInDB( + username="john", + password="secret", + email="john.doe@example.com", + full_name=None, +) +``` + +या अधिक सही रूप में, `user_dict` को सीधे इस्तेमाल करते हुए, उसमें भविष्य में जो भी contents हों: + +```Python +UserInDB( + username = user_dict["username"], + password = user_dict["password"], + email = user_dict["email"], + full_name = user_dict["full_name"], +) +``` + +#### दूसरे model के contents से एक Pydantic model { #a-pydantic-model-from-the-contents-of-another } + +जैसा कि ऊपर के उदाहरण में हमें `user_in.model_dump()` से `user_dict` मिला, यह code: + +```Python +user_dict = user_in.model_dump() +UserInDB(**user_dict) +``` + +इसके equivalent होगा: + +```Python +UserInDB(**user_in.model_dump()) +``` + +...क्योंकि `user_in.model_dump()` एक `dict` है, और फिर हम उसे `**` prefix के साथ `UserInDB` को pass करके Python से उसे "unpack" करवाते हैं। + +तो, हमें एक Pydantic model के data से दूसरा Pydantic model मिलता है। + +#### एक `dict` को unpack करना और extra keywords { #unpacking-a-dict-and-extra-keywords } + +और फिर extra keyword argument `hashed_password=hashed_password` जोड़ना, जैसे: + +```Python +UserInDB(**user_in.model_dump(), hashed_password=hashed_password) +``` + +...अंत में ऐसा बन जाता है: + +```Python +UserInDB( + username = user_dict["username"], + password = user_dict["password"], + email = user_dict["email"], + full_name = user_dict["full_name"], + hashed_password = hashed_password, +) +``` + +/// warning | चेतावनी + +supporting additional functions `fake_password_hasher` और `fake_save_user` सिर्फ data के एक possible flow को demo करने के लिए हैं, लेकिन वे निश्चित रूप से कोई real security नहीं दे रहे हैं। + +/// + +## Duplication कम करें { #reduce-duplication } + +Code duplication कम करना **FastAPI** के core ideas में से एक है। + +क्योंकि code duplication बढ़ने से bugs, security issues, code desynchronization issues (जब आप एक जगह update करते हैं लेकिन बाकी जगह नहीं), आदि की संभावना बढ़ जाती है। + +और ये models बहुत सारा data share कर रहे हैं और attribute names और types को duplicate कर रहे हैं। + +हम इससे बेहतर कर सकते हैं। + +हम एक `UserBase` model declare कर सकते हैं जो हमारे दूसरे models के लिए base की तरह काम करता है। और फिर हम उस model की subclasses बना सकते हैं जो उसके attributes (type declarations, validation, आदि) inherit करती हैं। + +सारा data conversion, validation, documentation, आदि सामान्य रूप से काम करता रहेगा। + +इस तरह, हम केवल models के बीच के अंतर declare कर सकते हैं (plaintext `password` के साथ, `hashed_password` के साथ और password के बिना): + +{* ../../docs_src/extra_models/tutorial002_py310.py hl[7,13:14,17:18,21:22] *} + +## `Union` या `anyOf` { #union-or-anyof } + +आप response को दो या अधिक types के `Union` के रूप में declare कर सकते हैं, जिसका मतलब है कि response उनमें से कोई भी हो सकता है। + +इसे OpenAPI में `anyOf` के साथ define किया जाएगा। + +ऐसा करने के लिए, standard Python type hint [`typing.Union`](https://docs.python.org/3/library/typing.html#typing.Union) का इस्तेमाल करें: + +/// note | नोट + +[`Union`](https://docs.pydantic.dev/latest/concepts/types/#unions) define करते समय, सबसे specific type को पहले include करें, उसके बाद कम specific type को। नीचे दिए गए उदाहरण में, अधिक specific `PlaneItem`, `Union[PlaneItem, CarItem]` में `CarItem` से पहले आता है। + +/// + +{* ../../docs_src/extra_models/tutorial003_py310.py hl[1,14:15,18:20,33] *} + +### Python 3.10 में `Union` { #union-in-python-3-10 } + +इस उदाहरण में हम argument `response_model` की value के रूप में `Union[PlaneItem, CarItem]` pass करते हैं। + +क्योंकि हम इसे **type annotation** में रखने के बजाय **argument को value** के रूप में pass कर रहे हैं, इसलिए हमें Python 3.10 में भी `Union` इस्तेमाल करना होगा। + +अगर यह type annotation में होता तो हम vertical bar इस्तेमाल कर सकते थे, जैसे: + +```Python +some_variable: PlaneItem | CarItem +``` + +लेकिन अगर हम इसे assignment `response_model=PlaneItem | CarItem` में डालते, तो हमें error मिलता, क्योंकि Python इसे type annotation के रूप में interpret करने के बजाय `PlaneItem` और `CarItem` के बीच एक **invalid operation** perform करने की कोशिश करता। + +## Models की list { #list-of-models } + +इसी तरह, आप objects की lists के responses declare कर सकते हैं। + +इसके लिए, standard Python `list` इस्तेमाल करें: + +{* ../../docs_src/extra_models/tutorial004_py310.py hl[18] *} + +## Arbitrary `dict` के साथ response { #response-with-arbitrary-dict } + +आप plain arbitrary `dict` का इस्तेमाल करके भी response declare कर सकते हैं, जिसमें Pydantic model का इस्तेमाल किए बिना केवल keys और values का type declare किया जाता है। + +यह तब उपयोगी है जब आपको valid field/attribute names (जो Pydantic model के लिए चाहिए होंगे) पहले से नहीं पता हों। + +इस मामले में, आप `dict` इस्तेमाल कर सकते हैं: + +{* ../../docs_src/extra_models/tutorial005_py310.py hl[6] *} + +## Recap { #recap } + +हर case के लिए कई Pydantic models इस्तेमाल करें और freely inherit करें। + +अगर किसी entity में अलग-अलग "states" हो सकने चाहिए, तो आपको प्रति entity एक ही data model रखने की जरूरत नहीं है। **user** "entity" एक उदाहरण है, जिसमें states में `password`, `password_hash`, या कोई password नहीं होना शामिल है। diff --git a/docs/hi/docs/tutorial/first-steps.md b/docs/hi/docs/tutorial/first-steps.md new file mode 100644 index 000000000..aa6fd4e37 --- /dev/null +++ b/docs/hi/docs/tutorial/first-steps.md @@ -0,0 +1,421 @@ +# पहले कदम { #first-steps } + +सबसे सरल FastAPI file ऐसी दिख सकती है: + +{* ../../docs_src/first_steps/tutorial001_py310.py *} + +इसे `main.py` नाम की file में copy करें। + +live server चलाएँ: + +
+ +```console +$ fastapi dev + + FastAPI Starting development server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + INFO Will watch for changes in these directories: + ['/home/user/code/awesomeapp'] + INFO Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C + to quit) + INFO Started reloader process [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. +``` + +
+ +output में, कुछ ऐसी एक line होती है: + +```hl_lines="4" +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +यह line वह URL दिखाती है जहाँ आपकी app आपकी local machine पर serve की जा रही है। + +### इसे जाँचें { #check-it } + +अपने browser में [http://127.0.0.1:8000](http://127.0.0.1:8000) खोलें। + +आपको JSON response इस तरह दिखेगा: + +```JSON +{"message": "Hello World"} +``` + +### Interactive API docs { #interactive-api-docs } + +अब [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) पर जाएँ। + +आपको automatic interactive API documentation दिखेगी ([Swagger UI](https://github.com/swagger-api/swagger-ui) द्वारा प्रदान की गई): + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) + +### वैकल्पिक API docs { #alternative-api-docs } + +और अब, [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) पर जाएँ। + +आपको वैकल्पिक automatic documentation दिखेगी ([ReDoc](https://github.com/Rebilly/ReDoc) द्वारा प्रदान की गई): + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) + +### OpenAPI { #openapi } + +**FastAPI** APIs को define करने के लिए **OpenAPI** standard का उपयोग करके आपकी पूरी API के साथ एक "schema" generate करता है। + +#### "Schema" { #schema } + +"schema" किसी चीज़ की definition या description है। वह code नहीं जो इसे implement करता है, बल्कि सिर्फ़ एक abstract description है। + +#### API "schema" { #api-schema } + +इस मामले में, [OpenAPI](https://github.com/OAI/OpenAPI-Specification) एक specification है जो बताती है कि आपकी API का schema कैसे define करना है। + +इस schema definition में आपकी API paths, उनके द्वारा लिए जा सकने वाले संभावित parameters आदि शामिल होते हैं। + +#### Data "schema" { #data-schema } + +"schema" शब्द कुछ data के आकार को भी refer कर सकता है, जैसे JSON content। + +उस मामले में, इसका मतलब JSON attributes, और उनके data types आदि होगा। + +#### OpenAPI और JSON Schema { #openapi-and-json-schema } + +OpenAPI आपकी API के लिए API schema define करता है। और उस schema में **JSON Schema**, जो JSON data schemas के लिए standard है, का उपयोग करके आपकी API द्वारा भेजे और प्राप्त किए गए data की definitions (या "schemas") शामिल होती हैं। + +#### `openapi.json` जाँचें { #check-the-openapi-json } + +अगर आप यह जानने को उत्सुक हैं कि raw OpenAPI schema कैसा दिखता है, FastAPI आपकी पूरी API के descriptions के साथ अपने आप एक JSON (schema) generate करता है। + +आप इसे सीधे यहाँ देख सकते हैं: [http://127.0.0.1:8000/openapi.json](http://127.0.0.1:8000/openapi.json)। + +यह कुछ ऐसे शुरू होने वाला JSON दिखाएगा: + +```JSON +{ + "openapi": "3.1.0", + "info": { + "title": "FastAPI", + "version": "0.1.0" + }, + "paths": { + "/items/": { + "get": { + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + + + +... +``` + +#### OpenAPI किसलिए है { #what-is-openapi-for } + +OpenAPI schema ही शामिल किए गए दो interactive documentation systems को power देता है। + +और दर्जनों विकल्प हैं, सभी OpenAPI पर आधारित। आप **FastAPI** से बनी अपनी application में इनमें से कोई भी विकल्प आसानी से जोड़ सकते हैं। + +आप इसका उपयोग उन clients के लिए अपने आप code generate करने के लिए भी कर सकते हैं जो आपकी API से communicate करते हैं। उदाहरण के लिए, frontend, mobile या IoT applications। + +### `pyproject.toml` में app `entrypoint` configure करें { #configure-the-app-entrypoint-in-pyproject-toml } + +आप `pyproject.toml` file में अपनी app कहाँ स्थित है, इसे इस तरह configure कर सकते हैं: + +```toml +[tool.fastapi] +entrypoint = "main:app" +``` + +वह `entrypoint` `fastapi` command को बताएगा कि उसे app को इस तरह import करना चाहिए: + +```python +from main import app +``` + +अगर आपका code इस तरह structured था: + +``` +. +├── backend +│   ├── main.py +│   ├── __init__.py +``` + +तो आप `entrypoint` को इस तरह set करेंगे: + +```toml +[tool.fastapi] +entrypoint = "backend.main:app" +``` + +जो इसके equivalent होगा: + +```python +from backend.main import app +``` + +### `fastapi dev` path के साथ या `--entrypoint` CLI option के साथ { #fastapi-dev-with-path-or-with-entrypoint-cli-option } + +आप `fastapi dev` command को file path भी pass कर सकते हैं, और यह उपयोग करने के लिए FastAPI app object का अनुमान लगा लेगा: + +```console +$ fastapi dev main.py +``` + +या, आप `fastapi dev` command को `--entrypoint` option भी pass कर सकते हैं: + +```console +$ fastapi dev --entrypoint main:app +``` + +लेकिन हर बार `fastapi` command call करते समय आपको सही path\entrypoint pass करना याद रखना होगा। + +इसके अलावा, दूसरे tools इसे ढूँढ नहीं पाएँगे, उदाहरण के लिए [VS Code Extension](../editor-support.md) या [FastAPI Cloud](https://fastapicloud.com), इसलिए `pyproject.toml` में `entrypoint` का उपयोग करने की सलाह दी जाती है। + +### अपनी app deploy करें (वैकल्पिक) { #deploy-your-app-optional } + +आप वैकल्पिक रूप से अपनी FastAPI app को एक single command से [FastAPI Cloud](https://fastapicloud.com) पर deploy कर सकते हैं। 🚀 + +
+ +```console +$ fastapi deploy + +Deploying to FastAPI Cloud... + +✅ Deployment successful! + +🐔 Ready the chicken! Your app is ready at https://myapp.fastapicloud.dev +``` + +
+ +CLI आपकी FastAPI application को अपने आप detect करेगा और उसे cloud पर deploy करेगा। अगर आप logged in नहीं हैं, तो authentication process पूरा करने के लिए आपका browser खुलेगा। + +बस इतना ही! अब आप उस URL पर अपनी app access कर सकते हैं। ✨ + +## Recap, चरण दर चरण { #recap-step-by-step } + +### चरण 1: `FastAPI` import करें { #step-1-import-fastapi } + +{* ../../docs_src/first_steps/tutorial001_py310.py hl[1] *} + +`FastAPI` एक Python class है जो आपकी API के लिए सारी functionality प्रदान करती है। + +/// note | तकनीकी विवरण + +`FastAPI` एक class है जो सीधे `Starlette` से inherit करती है। + +आप `FastAPI` के साथ सारी [Starlette](https://www.starlette.dev/) functionality भी उपयोग कर सकते हैं। + +/// + +### चरण 2: एक `FastAPI` "instance" बनाएँ { #step-2-create-a-fastapi-instance } + +{* ../../docs_src/first_steps/tutorial001_py310.py hl[3] *} + +यहाँ `app` variable class `FastAPI` का एक "instance" होगा। + +यह आपकी पूरी API बनाने के लिए interaction का मुख्य point होगा। + +### चरण 3: एक *path operation* बनाएँ { #step-3-create-a-path-operation } + +#### Path { #path } + +यहाँ "Path" URL के पहले `/` से शुरू होने वाले आख़िरी हिस्से को refer करता है। + +तो, ऐसे URL में: + +``` +https://example.com/items/foo +``` + +...path होगा: + +``` +/items/foo +``` + +/// note | नोट + +एक "path" को आमतौर पर "endpoint" या "route" भी कहा जाता है। + +/// + +API बनाते समय, "path" "concerns" और "resources" को अलग करने का मुख्य तरीका है। + +#### Operation { #operation } + +यहाँ "Operation" HTTP "methods" में से किसी एक को refer करता है। + +इनमें से एक: + +* `POST` +* `GET` +* `PUT` +* `DELETE` + +...और कुछ अधिक असामान्य वाले: + +* `OPTIONS` +* `HEAD` +* `PATCH` +* `TRACE` + +HTTP protocol में, आप इन "methods" में से एक (या अधिक) का उपयोग करके हर path से communicate कर सकते हैं। + +--- + +APIs बनाते समय, आप आमतौर पर कोई specific action करने के लिए इन specific HTTP methods का उपयोग करते हैं। + +आम तौर पर आप उपयोग करते हैं: + +* `POST`: data बनाने के लिए। +* `GET`: data पढ़ने के लिए। +* `PUT`: data update करने के लिए। +* `DELETE`: data delete करने के लिए। + +इसलिए, OpenAPI में, हर HTTP method को एक "operation" कहा जाता है। + +हम उन्हें भी "**operations**" कहेंगे। + +#### एक *path operation decorator* define करें { #define-a-path-operation-decorator } + +{* ../../docs_src/first_steps/tutorial001_py310.py hl[6] *} + +`@app.get("/")` **FastAPI** को बताता है कि ठीक नीचे वाला function उन requests को handle करने का प्रभारी है जो यहाँ जाती हैं: + +* path `/` +* get operation का उपयोग करते हुए + +/// note | `@decorator` जानकारी + +Python में उस `@something` syntax को "decorator" कहा जाता है। + +आप इसे किसी function के ऊपर लगाते हैं। जैसे एक सुंदर सजावटी टोपी (मुझे लगता है term वहीं से आया है)। + +एक "decorator" नीचे वाले function को लेता है और उसके साथ कुछ करता है। + +हमारे मामले में, यह decorator **FastAPI** को बताता है कि नीचे वाला function **path** `/` के साथ **operation** `get` से संबंधित है। + +यह "**path operation decorator**" है। + +/// + +आप दूसरे operations भी उपयोग कर सकते हैं: + +* `@app.post()` +* `@app.put()` +* `@app.delete()` + +और कुछ अधिक असामान्य वाले: + +* `@app.options()` +* `@app.head()` +* `@app.patch()` +* `@app.trace()` + +/// tip | सुझाव + +आप हर operation (HTTP method) को अपनी इच्छा के अनुसार उपयोग करने के लिए स्वतंत्र हैं। + +**FastAPI** कोई specific अर्थ enforce नहीं करता। + +यहाँ दी गई जानकारी guideline के रूप में प्रस्तुत की गई है, requirement के रूप में नहीं। + +उदाहरण के लिए, GraphQL का उपयोग करते समय आप आम तौर पर सभी actions केवल `POST` operations का उपयोग करके करते हैं। + +/// + +### चरण 4: **path operation function** define करें { #step-4-define-the-path-operation-function } + +यह हमारा "**path operation function**" है: + +* **path**: `/` है। +* **operation**: `get` है। +* **function**: "decorator" के नीचे वाला function है (`@app.get("/")` के नीचे)। + +{* ../../docs_src/first_steps/tutorial001_py310.py hl[7] *} + +यह एक Python function है। + +जब भी **FastAPI** को `GET` operation का उपयोग करके URL "`/`" पर कोई request मिलती है, तो यह इसे call करेगा। + +इस मामले में, यह एक `async` function है। + +--- + +आप इसे `async def` के बजाय normal function के रूप में भी define कर सकते हैं: + +{* ../../docs_src/first_steps/tutorial003_py310.py hl[7] *} + +/// note | नोट + +अगर आपको अंतर नहीं पता है, तो [Async: *"In a hurry?"*](../async.md#in-a-hurry) देखें। + +/// + +### चरण 5: content return करें { #step-5-return-the-content } + +{* ../../docs_src/first_steps/tutorial001_py310.py hl[8] *} + +आप `dict`, `list`, `str`, `int` आदि जैसे singular values return कर सकते हैं। + +आप Pydantic models भी return कर सकते हैं (इसके बारे में आप आगे और देखेंगे)। + +कई अन्य objects और models हैं जिन्हें अपने आप JSON में convert किया जाएगा (ORMs आदि सहित)। अपने पसंदीदा ones उपयोग करके देखें, बहुत संभावना है कि वे पहले से supported हों। + +### चरण 6: इसे Deploy करें { #step-6-deploy-it } + +अपनी app को **[FastAPI Cloud](https://fastapicloud.com)** पर एक command से deploy करें: `fastapi deploy`। 🎉 + +#### FastAPI Cloud के बारे में { #about-fastapi-cloud } + +**[FastAPI Cloud](https://fastapicloud.com)** को **FastAPI** के पीछे मौजूद उसी author और team ने बनाया है। + +यह कम से कम प्रयास के साथ API को **बनाने**, **deploy करने**, और **access करने** की process को streamlined करता है। + +यह FastAPI के साथ apps बनाने जैसा ही **developer experience**, उन्हें cloud पर **deploy** करने में लाता है। 🎉 + +FastAPI Cloud *FastAPI and friends* open source projects का प्राथमिक sponsor और funding provider है। ✨ + +#### दूसरे cloud providers पर deploy करें { #deploy-to-other-cloud-providers } + +FastAPI open source है और standards पर आधारित है। आप FastAPI apps को अपनी पसंद के किसी भी cloud provider पर deploy कर सकते हैं। + +FastAPI apps को उनके साथ deploy करने के लिए अपने cloud provider की guides follow करें। 🤓 + +## Recap { #recap } + +* `FastAPI` import करें। +* एक `app` instance बनाएँ। +* `@app.get("/")` जैसे decorators का उपयोग करके एक **path operation decorator** लिखें। +* एक **path operation function** define करें; उदाहरण के लिए, `def root(): ...`। +* `fastapi dev` command का उपयोग करके development server चलाएँ। +* वैकल्पिक रूप से अपनी app को `fastapi deploy` के साथ deploy करें। diff --git a/docs/hi/docs/tutorial/frontend.md b/docs/hi/docs/tutorial/frontend.md new file mode 100644 index 000000000..bc8e188fc --- /dev/null +++ b/docs/hi/docs/tutorial/frontend.md @@ -0,0 +1,139 @@ +# Frontend { #frontend } + +आप `app.frontend()` (या `router.frontend()`) के साथ static frontend apps serve कर सकते हैं। + +यह उन frontend tools के लिए उपयोगी है जो static files generate करते हैं, जैसे React with Vite, TanStack Router, Astro, Vue, Svelte, Angular, Solid, और अन्य। + +इन tools के साथ, आपके पास आमतौर पर एक step होता है जो frontend को build करता है, जैसे इस command के साथ: + +```bash +npm run build +``` + +यह आपके frontend files के साथ `./dist/` जैसी एक directory generate करेगा। + +आप उस directory को इन frontend frameworks के लिए required conventions के अनुसार serve करने के लिए `app.frontend()` का उपयोग कर सकते हैं। + +**FastAPI** पहले *path operations* की जाँच करता है। frontend files की जाँच केवल तब की जाती है जब कोई सामान्य route match नहीं हुआ हो, इसलिए आपकी API प्रभावित नहीं होगी। + +## Frontend Serve करें { #serve-a-frontend } + +अपना frontend build करने के बाद, उदाहरण के लिए `npm run build` के साथ, generate की गई files को किसी directory में रखें, उदाहरण के लिए, `dist`। + +आपकी project संरचना ऐसी दिख सकती है: + +```text +. +├── pyproject.toml +├── app +│ ├── __init__.py +│ └── main.py +└── dist + ├── index.html + └── assets + └── app.js +``` + +फिर इसे `app.frontend()` के साथ serve करें: + +{* ../../docs_src/frontend/tutorial001_py310.py hl[5] *} + +इसके साथ, `/assets/app.js` के लिए एक request `dist/assets/app.js` serve कर सकती है। + +अगर आपके पास एक **FastAPI** *path operation* भी है, तो *path operation* को प्राथमिकता मिलती है। + +## Client-Side Routing { #client-side-routing } + +कई frontend apps, जिनमें **single-page apps** (SPAs) शामिल हैं, client-side routing का उपयोग करते हैं। `/dashboard/settings` जैसा path असली file नहीं हो सकता है, लेकिन framework इसे handle करने का ध्यान रखेगा। + +इसलिए, अगर उस URL को सीधे access किया जा रहा है (app के अंदर navigate करने के बजाय), तो backend को frontend app को `index.html` से serve करना चाहिए, ताकि frontend framework फिर client-side routing को handle कर सके। + +इसके लिए, `fallback="index.html"` का उपयोग करें: + +{* ../../docs_src/frontend/tutorial002_py310.py hl[5] *} + +**FastAPI** इस fallback का उपयोग केवल उन `GET` और `HEAD` requests के लिए करता है जो browser navigation जैसी दिखती हैं। JavaScript, CSS, और images जैसी missing files अभी भी `404` लौटाती हैं। + +अन्य methods वाली requests, जैसे `POST` या `PUT`, उन paths पर जो केवल frontend fallback से match करते हैं, वे भी `404` लौटाती हैं। नियमित **FastAPI** *path operations* की priority अभी भी frontend routes से अधिक होती है। + +/// tip | सुझाव + +Default रूप से, `fallback` की value `fallback="auto"` होती है। अधिकतर मामलों में आपको `fallback` specify करने की ज़रूरत नहीं होगी। विवरण के लिए नीचे पढ़ें। + +/// + +यह वही है जो आप कई frontend apps के साथ चाहेंगे जो client-side routing का उपयोग करते हैं, उदाहरण के लिए, React with TanStack Router, Vue, Angular, SvelteKit, या Solid। + +## Custom 404 Page { #custom-404-page } + +आप missing frontend paths के लिए static `404.html` page भी serve कर सकते हैं: + +{* ../../docs_src/frontend/tutorial003_py310.py hl[5] *} + +वह response `404` का status code बनाए रखती है। + +इस मामले में, **FastAPI** missing frontend paths के लिए `index.html` serve नहीं करेगा। इसके बजाय यह `404.html` file लौटाएगा। + +/// tip | सुझाव + +Default रूप से, `fallback` की value `fallback="auto"` होती है। इसके साथ, अगर `404.html` file मिलती है, तो उसे अपने-आप fallback के रूप में उपयोग किया जाएगा। + +इसलिए, आप सामान्यतः `fallback` argument छोड़ सकते हैं। + +/// + +यह उन frontend tools के साथ उपयोगी है जो हर page के लिए static HTML files generate करते हैं, जैसे Astro। + +## Fallback Auto { #fallback-auto } + +Default रूप से, `app.frontend()` `fallback="auto"` का उपयोग करता है। + +अगर frontend directory में `404.html` file है, तो missing frontend paths उस file को status code `404` के साथ serve करते हैं। + +अन्यथा, अगर `index.html` file है, तो missing browser navigation paths `index.html` serve करते हैं, जो client-side routing वाले कई frontend apps अपेक्षा करते हैं। + +इसलिए, अधिकतर मामलों में आप `fallback` argument specify किए बिना `app.frontend("/", directory="dist")` का उपयोग कर सकते हैं। + +{* ../../docs_src/frontend/tutorial001_py310.py hl[5] *} + +## Fallback बंद करें { #disable-fallback } + +अगर आप missing frontend paths के लिए fallback file serve नहीं करना चाहते, तो `fallback=None` का उपयोग करें: + +{* ../../docs_src/frontend/tutorial005_py310.py hl[5] *} + +फिर missing frontend paths सामान्य `404` लौटाते हैं। + +## Directory जाँचें { #check-directory } + +Default रूप से, `app.frontend()` app बनाते समय जाँचता है कि directory मौजूद है। + +यह configuration errors को जल्दी पकड़ने में मदद करता है। उदाहरण के लिए, अगर frontend build output directory missing है, तो **FastAPI** startup पर error raise करेगा। + +अगर आपकी frontend files बाद में बनाई जाती हैं, उदाहरण के लिए app object बनने के बाद किसी अलग build step द्वारा, तो `check_dir=False` set करें: + +{* ../../docs_src/frontend/tutorial006_py310.py hl[5] *} + +`check_dir=False` के साथ, **FastAPI** app बनाते समय directory की जाँच नहीं करेगा। अगर configured directory किसी request को handle करते समय अभी भी missing है, तो **FastAPI** तब error raise करेगा। + +## इसे `APIRouter` के साथ उपयोग करें { #use-it-with-apirouter } + +आप frontend files को `APIRouter` में भी जोड़ सकते हैं और उसे prefix के साथ include कर सकते हैं: + +{* ../../docs_src/frontend/tutorial004_py310.py hl[6,7] *} + +इस उदाहरण में, frontend paths `/app` के अंतर्गत serve किए जाते हैं। + +App में कोई भी नियमित *path operations* अभी भी precedence लेंगे, अन्य routers में शामिल ones भी। + +## Dependencies और Middleware { #dependencies-and-middleware } + +Frontend responses सामान्य **FastAPI** application के अंदर run करती हैं, इसलिए HTTP middleware उन पर लागू होता है। + +App से, `APIRouter` से, और `include_router()` से dependencies भी frontend responses पर लागू होती हैं। यह cookie authentication या इसी तरह से frontend को protect करने के लिए उपयोगी हो सकता है। + +## केवल Static Build Output { #static-build-output-only } + +`app.frontend()` आपके frontend build द्वारा पहले से generate की गई files serve करता है। + +यह server-side rendering run नहीं करता। यह उन frontend frameworks के लिए है जो static files generate करते हैं, न कि उन frameworks के लिए जिन्हें हर request के लिए server पर dynamic rendering की ज़रूरत होती है। diff --git a/docs/hi/docs/tutorial/handling-errors.md b/docs/hi/docs/tutorial/handling-errors.md new file mode 100644 index 000000000..ee4570761 --- /dev/null +++ b/docs/hi/docs/tutorial/handling-errors.md @@ -0,0 +1,244 @@ +# Errors को हैंडल करना { #handling-errors } + +ऐसी कई स्थितियाँ होती हैं जिनमें आपको अपनी API का उपयोग कर रहे client को error report करना पड़ता है। + +यह client frontend वाला कोई browser, किसी और का code, कोई IoT device आदि हो सकता है। + +आपको client को यह बताने की ज़रूरत पड़ सकती है कि: + +* client के पास उस operation के लिए पर्याप्त privileges नहीं हैं। +* client के पास उस resource का access नहीं है। +* जिस item को client access करने की कोशिश कर रहा था, वह मौजूद नहीं है। +* आदि। + +इन मामलों में, आप सामान्यतः **400** की range (400 से 499 तक) में एक **HTTP status code** return करेंगे। + +यह 200 HTTP status codes (200 से 299 तक) जैसा ही है। वे "200" status codes का मतलब है कि request में किसी तरह "success" हुआ था। + +400 range के status codes का मतलब है कि client की तरफ़ से कोई error था। + +वे सभी **"404 Not Found"** errors (और jokes) याद हैं? + +## `HTTPException` का उपयोग करें { #use-httpexception } + +Client को errors वाली HTTP responses return करने के लिए आप `HTTPException` का उपयोग करते हैं। + +### `HTTPException` import करें { #import-httpexception } + +{* ../../docs_src/handling_errors/tutorial001_py310.py hl[1] *} + +### अपने code में `HTTPException` raise करें { #raise-an-httpexception-in-your-code } + +`HTTPException` APIs के लिए प्रासंगिक अतिरिक्त data के साथ एक सामान्य Python exception है। + +क्योंकि यह एक Python exception है, आप इसे `return` नहीं करते, आप इसे `raise` करते हैं। + +इसका यह भी मतलब है कि अगर आप किसी utility function के अंदर हैं जिसे आप अपनी *path operation function* के अंदर call कर रहे हैं, और आप उस utility function के अंदर से `HTTPException` raise करते हैं, तो यह *path operation function* में बाकी code नहीं चलाएगा, यह उस request को तुरंत समाप्त कर देगा और `HTTPException` से HTTP error client को भेज देगा। + +किसी value को return करने के बजाय exception raise करने का लाभ Dependencies और Security वाले section में अधिक स्पष्ट होगा। + +इस example में, जब client किसी ऐसे ID से item request करता है जो मौजूद नहीं है, तो `404` के status code के साथ exception raise करें: + +{* ../../docs_src/handling_errors/tutorial001_py310.py hl[11] *} + +### परिणामी response { #the-resulting-response } + +अगर client `http://example.com/items/foo` (एक `item_id` `"foo"`) request करता है, तो उस client को 200 का HTTP status code और यह JSON response मिलेगा: + +```JSON +{ + "item": "The Foo Wrestlers" +} +``` + +लेकिन अगर client `http://example.com/items/bar` (एक non-existent `item_id` `"bar"`) request करता है, तो उस client को 404 का HTTP status code ("not found" error) और यह JSON response मिलेगा: + +```JSON +{ + "detail": "Item not found" +} +``` + +/// tip | सुझाव + +`HTTPException` raise करते समय, आप `detail` parameter के रूप में ऐसी कोई भी value pass कर सकते हैं जिसे JSON में convert किया जा सकता हो, केवल `str` ही नहीं। + +आप `dict`, `list` आदि pass कर सकते हैं। + +इन्हें **FastAPI** अपने आप handle करता है और JSON में convert करता है। + +/// + +## custom headers जोड़ें { #add-custom-headers } + +कुछ स्थितियाँ ऐसी होती हैं जहाँ HTTP error में custom headers जोड़ पाना उपयोगी होता है। उदाहरण के लिए, कुछ प्रकार की security के लिए। + +आपको शायद अपने code में सीधे इसका उपयोग करने की ज़रूरत नहीं होगी। + +लेकिन अगर किसी advanced scenario में आपको इसकी ज़रूरत पड़े, तो आप custom headers जोड़ सकते हैं: + +{* ../../docs_src/handling_errors/tutorial002_py310.py hl[14] *} + +## custom exception handlers install करें { #install-custom-exception-handlers } + +आप [Starlette से वही exception utilities](https://www.starlette.dev/exceptions/) के साथ custom exception handlers जोड़ सकते हैं। + +मान लीजिए आपके पास एक custom exception `UnicornException` है जिसे आप (या कोई library जिसका आप उपयोग करते हैं) `raise` कर सकते हैं। + +और आप इस exception को FastAPI के साथ globally handle करना चाहते हैं। + +आप `@app.exception_handler()` के साथ custom exception handler जोड़ सकते हैं: + +{* ../../docs_src/handling_errors/tutorial003_py310.py hl[5:7,13:18,24] *} + +यहाँ, अगर आप `/unicorns/yolo` request करते हैं, तो *path operation* एक `UnicornException` `raise` करेगा। + +लेकिन इसे `unicorn_exception_handler` द्वारा handle किया जाएगा। + +इसलिए, आपको `418` के HTTP status code और इस JSON content के साथ एक साफ़ error मिलेगा: + +```JSON +{"message": "Oops! yolo did something. There goes a rainbow..."} +``` + +/// note | Technical Details + +आप `from starlette.requests import Request` और `from starlette.responses import JSONResponse` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी, developer की, सुविधा के लिए `starlette.responses` को `fastapi.responses` के रूप में उपलब्ध कराता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। `Request` के साथ भी यही है। + +/// + +## default exception handlers को override करें { #override-the-default-exception-handlers } + +**FastAPI** में कुछ default exception handlers होते हैं। + +ये handlers default JSON responses return करने के लिए ज़िम्मेदार होते हैं, जब आप `HTTPException` `raise` करते हैं और जब request में invalid data होता है। + +आप इन exception handlers को अपने खुद के handlers से override कर सकते हैं। + +### request validation exceptions को override करें { #override-request-validation-exceptions } + +जब किसी request में invalid data होता है, तो **FastAPI** internally एक `RequestValidationError` raise करता है। + +और इसमें इसके लिए एक default exception handler भी शामिल होता है। + +इसे override करने के लिए, `RequestValidationError` import करें और exception handler को decorate करने के लिए इसे `@app.exception_handler(RequestValidationError)` के साथ उपयोग करें। + +Exception handler को एक `Request` और exception मिलेगा। + +{* ../../docs_src/handling_errors/tutorial004_py310.py hl[2,14:19] *} + +अब, अगर आप `/items/foo` पर जाते हैं, तो default JSON error पाने के बजाय: + +```JSON +{ + "detail": [ + { + "loc": [ + "path", + "item_id" + ], + "msg": "value is not a valid integer", + "type": "type_error.integer" + } + ] +} +``` + +आपको text version मिलेगा, जिसमें होगा: + +``` +Validation errors: +Field: ('path', 'item_id'), Error: Input should be a valid integer, unable to parse string as an integer +``` + +### `HTTPException` error handler को override करें { #override-the-httpexception-error-handler } + +उसी तरह, आप `HTTPException` handler को override कर सकते हैं। + +उदाहरण के लिए, आप इन errors के लिए JSON के बजाय plain text response return करना चाह सकते हैं: + +{* ../../docs_src/handling_errors/tutorial004_py310.py hl[3:4,9:11,25] *} + +/// note | Technical Details + +आप `from starlette.responses import PlainTextResponse` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी, developer की, सुविधा के लिए `starlette.responses` को `fastapi.responses` के रूप में उपलब्ध कराता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। + +/// + +/// warning | चेतावनी + +ध्यान रखें कि `RequestValidationError` में file name और उस line की जानकारी होती है जहाँ validation error होता है, ताकि अगर आप चाहें तो relevant जानकारी के साथ उसे अपने logs में दिखा सकें। + +लेकिन इसका मतलब है कि अगर आप इसे केवल string में convert करके वह जानकारी सीधे return कर देते हैं, तो आप अपने system के बारे में थोड़ी जानकारी leak कर सकते हैं, इसलिए यहाँ code हर error को अलग-अलग extract करके दिखाता है। + +/// + +### `RequestValidationError` body का उपयोग करें { #use-the-requestvalidationerror-body } + +`RequestValidationError` में वह `body` होता है जो इसे invalid data के साथ मिला था। + +आप अपनी app develop करते समय body को log और debug करने, user को return करने आदि के लिए इसका उपयोग कर सकते हैं। + +{* ../../docs_src/handling_errors/tutorial005_py310.py hl[14] *} + +अब ऐसा invalid item भेजकर देखें: + +```JSON +{ + "title": "towel", + "size": "XL" +} +``` + +आपको एक response मिलेगा जो बताता है कि data invalid है और जिसमें received body शामिल होगा: + +```JSON hl_lines="12-15" +{ + "detail": [ + { + "loc": [ + "body", + "size" + ], + "msg": "value is not a valid integer", + "type": "type_error.integer" + } + ], + "body": { + "title": "towel", + "size": "XL" + } +} +``` + +#### FastAPI का `HTTPException` बनाम Starlette का `HTTPException` { #fastapis-httpexception-vs-starlettes-httpexception } + +**FastAPI** का अपना `HTTPException` है। + +और **FastAPI** की `HTTPException` error class, Starlette की `HTTPException` error class से inherit करती है। + +केवल अंतर यह है कि **FastAPI** का `HTTPException`, `detail` field के लिए कोई भी JSON-able data accept करता है, जबकि Starlette का `HTTPException` इसके लिए केवल strings accept करता है। + +इसलिए, आप अपने code में सामान्य रूप से **FastAPI** का `HTTPException` raise करते रह सकते हैं। + +लेकिन जब आप exception handler register करते हैं, तो आपको उसे Starlette के `HTTPException` के लिए register करना चाहिए। + +इस तरह, अगर Starlette के internal code का कोई हिस्सा, या कोई Starlette extension या plug-in, Starlette `HTTPException` raise करता है, तो आपका handler उसे catch और handle कर पाएगा। + +इस example में, एक ही code में दोनों `HTTPException`s रखने के लिए, Starlette के exceptions को `StarletteHTTPException` नाम दिया गया है: + +```Python +from starlette.exceptions import HTTPException as StarletteHTTPException +``` + +### **FastAPI** के exception handlers का फिर से उपयोग करें { #reuse-fastapis-exception-handlers } + +अगर आप **FastAPI** के उन्हीं default exception handlers के साथ exception का उपयोग करना चाहते हैं, तो आप `fastapi.exception_handlers` से default exception handlers import करके उनका फिर से उपयोग कर सकते हैं: + +{* ../../docs_src/handling_errors/tutorial006_py310.py hl[2:5,15,21] *} + +इस example में आप केवल error को बहुत expressive message के साथ print कर रहे हैं, लेकिन आप बात समझ गए। आप exception का उपयोग कर सकते हैं और फिर बस default exception handlers का फिर से उपयोग कर सकते हैं। diff --git a/docs/hi/docs/tutorial/header-param-models.md b/docs/hi/docs/tutorial/header-param-models.md new file mode 100644 index 000000000..4c15aa579 --- /dev/null +++ b/docs/hi/docs/tutorial/header-param-models.md @@ -0,0 +1,72 @@ +# Header Parameter Models { #header-parameter-models } + +अगर आपके पास संबंधित **header parameters** का एक समूह है, तो आप उन्हें declare करने के लिए एक **Pydantic model** बना सकते हैं। + +इससे आप **model को फिर से उपयोग** कर पाएंगे, **कई जगहों** पर, और साथ ही सभी parameters के लिए validations और metadata एक साथ declare कर पाएंगे। 😎 + +/// note | नोट + +यह FastAPI version `0.115.0` से समर्थित है। 🤓 + +/// + +## Pydantic Model के साथ Header Parameters { #header-parameters-with-a-pydantic-model } + +जिन **header parameters** की आपको ज़रूरत है, उन्हें एक **Pydantic model** में declare करें, और फिर parameter को `Header` के रूप में declare करें: + +{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *} + +**FastAPI** request में **headers** से **हर field** का data **extract** करेगा और आपको वह Pydantic model देगा जिसे आपने define किया है। + +## Docs देखें { #check-the-docs } + +आप `/docs` पर docs UI में required headers देख सकते हैं: + +
+ +
+ +## Extra Headers को मना करें { #forbid-extra-headers } + +कुछ विशेष use cases में (शायद बहुत आम नहीं), आप उन headers को **restrict** करना चाह सकते हैं जिन्हें आप receive करना चाहते हैं। + +आप Pydantic की model configuration का उपयोग करके किसी भी `extra` fields को `forbid` कर सकते हैं: + +{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *} + +अगर कोई client कुछ **extra headers** भेजने की कोशिश करता है, तो उन्हें एक **error** response मिलेगा। + +उदाहरण के लिए, अगर client `plumbus` के value के साथ एक `tool` header भेजने की कोशिश करता है, तो उन्हें एक **error** response मिलेगा जो बताएगा कि header parameter `tool` की अनुमति नहीं है: + +```json +{ + "detail": [ + { + "type": "extra_forbidden", + "loc": ["header", "tool"], + "msg": "Extra inputs are not permitted", + "input": "plumbus", + } + ] +} +``` + +## Convert Underscores को Disable करें { #disable-convert-underscores } + +नियमित header parameters की तरह ही, जब parameter names में underscore characters होते हैं, तो वे **स्वचालित रूप से hyphens में convert** हो जाते हैं। + +उदाहरण के लिए, अगर आपके code में header parameter `save_data` है, तो अपेक्षित HTTP header `save-data` होगा, और docs में भी वह इसी तरह दिखाई देगा। + +अगर किसी कारण से आपको इस automatic conversion को disable करना है, तो आप header parameters के लिए Pydantic models में भी ऐसा कर सकते हैं। + +{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *} + +/// warning | चेतावनी + +`convert_underscores` को `False` पर set करने से पहले, ध्यान रखें कि कुछ HTTP proxies और servers underscores वाले headers के उपयोग की अनुमति नहीं देते। + +/// + +## सारांश { #summary } + +आप **FastAPI** में **headers** declare करने के लिए **Pydantic models** का उपयोग कर सकते हैं। 😎 diff --git a/docs/hi/docs/tutorial/header-params.md b/docs/hi/docs/tutorial/header-params.md new file mode 100644 index 000000000..1b683248c --- /dev/null +++ b/docs/hi/docs/tutorial/header-params.md @@ -0,0 +1,91 @@ +# Header Parameters { #header-parameters } + +आप Header parameters को उसी तरह define कर सकते हैं जैसे आप `Query`, `Path` और `Cookie` parameters को define करते हैं। + +## `Header` import करें { #import-header } + +पहले `Header` import करें: + +{* ../../docs_src/header_params/tutorial001_an_py310.py hl[3] *} + +## `Header` parameters घोषित करें { #declare-header-parameters } + +फिर `Path`, `Query` और `Cookie` जैसी ही structure का उपयोग करके header parameters घोषित करें। + +आप default value के साथ-साथ सभी अतिरिक्त validation या annotation parameters भी define कर सकते हैं: + +{* ../../docs_src/header_params/tutorial001_an_py310.py hl[9] *} + +/// note | तकनीकी विवरण + +`Header` `Path`, `Query` और `Cookie` की एक "sister" class है। यह भी उसी common `Param` class से inherit करता है। + +लेकिन याद रखें कि जब आप `fastapi` से `Query`, `Path`, `Header`, और अन्य import करते हैं, तो वे वास्तव में functions होते हैं जो special classes return करते हैं। + +/// + +/// note | नोट + +headers घोषित करने के लिए, आपको `Header` का उपयोग करना होगा, क्योंकि अन्यथा parameters को query parameters के रूप में interpret किया जाएगा। + +/// + +## स्वचालित conversion { #automatic-conversion } + +`Header` में `Path`, `Query` और `Cookie` द्वारा दी जाने वाली functionality के ऊपर थोड़ी अतिरिक्त functionality होती है। + +अधिकांश standard headers एक "hyphen" character से अलग किए जाते हैं, जिसे "minus symbol" (`-`) भी कहा जाता है। + +लेकिन Python में `user-agent` जैसा variable invalid है। + +इसलिए, default रूप से, `Header` headers को extract और document करने के लिए parameter names के characters को underscore (`_`) से hyphen (`-`) में convert करेगा। + +साथ ही, HTTP headers case-insensitive होते हैं, इसलिए, आप उन्हें standard Python style (जिसे "snake_case" भी कहा जाता है) में declare कर सकते हैं। + +इसलिए, Python code में सामान्य रूप से जैसे आप `user_agent` का उपयोग करते हैं, वैसा ही कर सकते हैं, बजाय इसके कि आपको पहले अक्षरों को `User_Agent` की तरह capitalize करना पड़े या कुछ समान करना पड़े। + +अगर किसी कारण से आपको underscores से hyphens में automatic conversion disable करना हो, तो `Header` के parameter `convert_underscores` को `False` पर set करें: + +{* ../../docs_src/header_params/tutorial002_an_py310.py hl[10] *} + +/// warning | चेतावनी + +`convert_underscores` को `False` पर set करने से पहले, ध्यान रखें कि कुछ HTTP proxies और servers underscores वाले headers के उपयोग की अनुमति नहीं देते। + +/// + +## Duplicate headers { #duplicate-headers } + +duplicate headers receive करना संभव है। इसका मतलब है, कई values वाला वही header। + +आप type declaration में list का उपयोग करके ऐसे cases define कर सकते हैं। + +आप duplicate header से सभी values Python `list` के रूप में receive करेंगे। + +उदाहरण के लिए, `X-Token` का header declare करने के लिए जो एक से अधिक बार आ सकता है, आप लिख सकते हैं: + +{* ../../docs_src/header_params/tutorial003_an_py310.py hl[9] *} + +यदि आप उस *path operation* के साथ दो HTTP headers भेजते हुए communicate करते हैं, जैसे: + +``` +X-Token: foo +X-Token: bar +``` + +response ऐसा होगा: + +```JSON +{ + "X-Token values": [ + "bar", + "foo" + ] +} +``` + +## Recap { #recap } + +`Query`, `Path` और `Cookie` जैसे ही common pattern का उपयोग करते हुए, `Header` के साथ headers declare करें। + +और अपनी variables में underscores के बारे में चिंता न करें, **FastAPI** उन्हें convert करने का ध्यान रखेगा। diff --git a/docs/hi/docs/tutorial/index.md b/docs/hi/docs/tutorial/index.md new file mode 100644 index 000000000..f5aff090f --- /dev/null +++ b/docs/hi/docs/tutorial/index.md @@ -0,0 +1,101 @@ +# Tutorial - उपयोगकर्ता गाइड { #tutorial-user-guide } + +यह tutorial आपको step by step दिखाता है कि **FastAPI** को इसकी अधिकतर features के साथ कैसे उपयोग करें। + +हर section धीरे-धीरे पिछले section पर आधारित होता है, लेकिन इसे topics को अलग रखने के लिए संरचित किया गया है, ताकि आप अपनी खास API ज़रूरतों को हल करने के लिए सीधे किसी भी specific topic पर जा सकें। + +इसे भविष्य के reference के रूप में काम करने के लिए भी बनाया गया है, ताकि आप वापस आकर ठीक वही देख सकें जिसकी आपको ज़रूरत है। + +## code चलाएँ { #run-the-code } + +सभी code blocks को copy करके सीधे उपयोग किया जा सकता है (वे वास्तव में tested Python files हैं)। + +किसी भी example को चलाने के लिए, code को `main.py` file में copy करें, और `fastapi dev` शुरू करें: + +
+ +```console +$ fastapi dev + + FastAPI Starting development server 🚀 + + Searching for package file structure from directories + with __init__.py files + Importing from /home/user/code/awesomeapp + + module 🐍 main.py + + code Importing the FastAPI app object from the module with + the following code: + + from main import app + + app Using import string: main:app + + server Server started at http://127.0.0.1:8000 + server Documentation at http://127.0.0.1:8000/docs + + tip Running in development mode, for production use: + fastapi run + + Logs: + + INFO Will watch for changes in these directories: + ['/home/user/code/awesomeapp'] + INFO Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C + to quit) + INFO Started reloader process [383138] using WatchFiles + INFO Started server process [383153] + INFO Waiting for application startup. + INFO Application startup complete. +``` + +
+ +यह **बहुत ज़्यादा प्रोत्साहित** किया जाता है कि आप code लिखें या copy करें, उसे edit करें और locally चलाएँ। + +इसे अपने editor में उपयोग करना ही वास्तव में आपको FastAPI के लाभ दिखाता है, जैसे आपको कितना कम code लिखना पड़ता है, सभी type checks, autocompletion, आदि। + +--- + +## FastAPI install करें { #install-fastapi } + +पहला step FastAPI install करना है। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाएँ, उसे activate करें, और फिर **FastAPI install करें**: + +
+ +```console +$ pip install "fastapi[standard]" + +---> 100% +``` + +
+ +/// note | नोट + +जब आप `pip install "fastapi[standard]"` के साथ install करते हैं, तो यह कुछ default optional standard dependencies के साथ आता है, जिनमें `fastapi-cloud-cli` शामिल है, जो आपको [FastAPI Cloud](https://fastapicloud.com) पर deploy करने देता है। + +अगर आप वे optional dependencies नहीं चाहते, तो इसके बजाय आप `pip install fastapi` install कर सकते हैं। + +अगर आप standard dependencies install करना चाहते हैं लेकिन `fastapi-cloud-cli` के बिना, तो आप `pip install "fastapi[standard-no-fastapi-cloud-cli]"` के साथ install कर सकते हैं। + +/// + +/// tip | सुझाव + +FastAPI के पास [VS Code के लिए official extension](https://marketplace.visualstudio.com/items?itemName=FastAPILabs.fastapi-vscode) (और Cursor) है, जो बहुत सारी features देता है, जिनमें path operation explorer, path operation search, tests में CodeLens navigation (tests से definition पर jump करना), और FastAPI Cloud deployment और logs शामिल हैं — सब कुछ आपके editor से। + +/// + +## उन्नत उपयोगकर्ता गाइड { #advanced-user-guide } + +एक **उन्नत उपयोगकर्ता गाइड** भी है जिसे आप इस **Tutorial - उपयोगकर्ता गाइड** के बाद पढ़ सकते हैं। + +**उन्नत उपयोगकर्ता गाइड** इसी पर आधारित है, वही concepts उपयोग करता है, और आपको कुछ अतिरिक्त features सिखाता है। + +लेकिन आपको पहले **Tutorial - उपयोगकर्ता गाइड** पढ़ना चाहिए (जो आप अभी पढ़ रहे हैं)। + +इसे इस तरह design किया गया है कि आप सिर्फ **Tutorial - उपयोगकर्ता गाइड** के साथ एक complete application बना सकें, और फिर अपनी ज़रूरतों के अनुसार **उन्नत उपयोगकर्ता गाइड** के कुछ अतिरिक्त ideas का उपयोग करके उसे अलग-अलग तरीकों से extend कर सकें। diff --git a/docs/hi/docs/tutorial/metadata.md b/docs/hi/docs/tutorial/metadata.md new file mode 100644 index 000000000..ef3f817a2 --- /dev/null +++ b/docs/hi/docs/tutorial/metadata.md @@ -0,0 +1,120 @@ +# Metadata और Docs URLs { #metadata-and-docs-urls } + +आप अपनी **FastAPI** application में कई metadata configurations को customize कर सकते हैं। + +## API के लिए Metadata { #metadata-for-api } + +आप निम्नलिखित fields set कर सकते हैं, जिनका उपयोग OpenAPI specification और automatic API docs UIs में किया जाता है: + +| Parameter | Type | विवरण | +|------------|------|-------------| +| `title` | `str` | API का title। | +| `summary` | `str` | API का एक छोटा summary। OpenAPI 3.1.0, FastAPI 0.99.0 से उपलब्ध। | +| `description` | `str` | API का एक छोटा description। यह Markdown का उपयोग कर सकता है। | +| `version` | `str` | API का version। यह आपकी अपनी application का version है, OpenAPI का नहीं। उदाहरण के लिए `2.5.0`। | +| `terms_of_service` | `str` | API के Terms of Service के लिए एक URL। यदि दिया गया हो, तो यह URL होना चाहिए। | +| `contact` | `dict` | exposed API के लिए contact information। इसमें कई fields हो सकते हैं।
contact fields
ParameterTypeविवरण
namestrcontact person/organization का identifying name।
urlstrcontact information की ओर point करने वाला URL। URL के format में होना चाहिए।
emailstrcontact person/organization का email address। email address के format में होना चाहिए।
| +| `license_info` | `dict` | exposed API के लिए license information। इसमें कई fields हो सकते हैं।
license_info fields
ParameterTypeविवरण
namestrREQUIRED (यदि license_info set किया गया हो)। API के लिए उपयोग किया गया license name।
identifierstrAPI के लिए एक [SPDX](https://spdx.org/licenses/) license expression। identifier field, url field के साथ mutually exclusive है। OpenAPI 3.1.0, FastAPI 0.99.0 से उपलब्ध।
urlstrAPI के लिए उपयोग किए गए license का URL। URL के format में होना चाहिए।
| + +आप इन्हें इस तरह set कर सकते हैं: + +{* ../../docs_src/metadata/tutorial001_py310.py hl[3:16, 19:32] *} + +/// tip | सुझाव + +आप `description` field में Markdown लिख सकते हैं और यह output में render होगा। + +/// + +इस configuration के साथ, automatic API docs इस तरह दिखेंगे: + + + +## License identifier { #license-identifier } + +OpenAPI 3.1.0 और FastAPI 0.99.0 से, आप `license_info` को `url` के बजाय `identifier` के साथ भी set कर सकते हैं। + +उदाहरण के लिए: + +{* ../../docs_src/metadata/tutorial001_1_py310.py hl[31] *} + +## Tags के लिए Metadata { #metadata-for-tags } + +आप अपने path operations को group करने के लिए उपयोग किए गए अलग-अलग tags के लिए `openapi_tags` parameter के साथ अतिरिक्त metadata भी जोड़ सकते हैं। + +यह प्रत्येक tag के लिए एक dictionary वाली list लेता है। + +प्रत्येक dictionary में हो सकता है: + +* `name` (**required**): वही tag name वाला `str`, जिसे आप अपने *path operations* और `APIRouter`s में `tags` parameter में उपयोग करते हैं। +* `description`: tag के लिए short description वाला `str`। इसमें Markdown हो सकता है और यह docs UI में दिखाया जाएगा। +* `externalDocs`: external documentation का वर्णन करने वाला `dict`, जिसमें: + * `description`: external docs के लिए short description वाला `str`। + * `url` (**required**): external documentation के लिए URL वाला `str`। + +### Tags के लिए metadata बनाएँ { #create-metadata-for-tags } + +आइए इसे `users` और `items` के tags वाले एक उदाहरण में आज़माते हैं। + +अपने tags के लिए metadata बनाएँ और उसे `openapi_tags` parameter में pass करें: + +{* ../../docs_src/metadata/tutorial004_py310.py hl[3:16,18] *} + +ध्यान दें कि आप descriptions के अंदर Markdown का उपयोग कर सकते हैं, उदाहरण के लिए "login" bold (**login**) में दिखेगा और "fancy" italics (_fancy_) में दिखेगा। + +/// tip | सुझाव + +आपको अपने उपयोग किए गए सभी tags के लिए metadata जोड़ना ज़रूरी नहीं है। + +/// + +### अपने tags का उपयोग करें { #use-your-tags } + +अपने *path operations* (और `APIRouter`s) के साथ `tags` parameter का उपयोग करें, ताकि उन्हें अलग-अलग tags में assign किया जा सके: + +{* ../../docs_src/metadata/tutorial004_py310.py hl[21,26] *} + +/// note | नोट + +Tags के बारे में और पढ़ें [Path Operation Configuration](path-operation-configuration.md#tags) में। + +/// + +### Docs जाँचें { #check-the-docs } + +अब, अगर आप docs जाँचते हैं, तो वे सभी अतिरिक्त metadata दिखाएँगे: + + + +### Tags का क्रम { #order-of-tags } + +हर tag metadata dictionary का क्रम भी docs UI में दिखाए जाने वाले क्रम को define करता है। + +उदाहरण के लिए, भले ही `users` alphabetical order में `items` के बाद आता, यह उनसे पहले दिखाया जाता है, क्योंकि हमने उनकी metadata को list में पहली dictionary के रूप में जोड़ा था। + +## OpenAPI URL { #openapi-url } + +Default रूप से, OpenAPI schema `/openapi.json` पर serve किया जाता है। + +लेकिन आप इसे `openapi_url` parameter के साथ configure कर सकते हैं। + +उदाहरण के लिए, इसे `/api/v1/openapi.json` पर serve करने के लिए set करने हेतु: + +{* ../../docs_src/metadata/tutorial002_py310.py hl[3] *} + +यदि आप OpenAPI schema को पूरी तरह disable करना चाहते हैं, तो आप `openapi_url=None` set कर सकते हैं, इससे इसका उपयोग करने वाले documentation user interfaces भी disable हो जाएँगे। + +## Docs URLs { #docs-urls } + +आप शामिल किए गए दो documentation user interfaces configure कर सकते हैं: + +* **Swagger UI**: `/docs` पर serve किया जाता है। + * आप इसका URL `docs_url` parameter के साथ set कर सकते हैं। + * आप `docs_url=None` set करके इसे disable कर सकते हैं। +* **ReDoc**: `/redoc` पर serve किया जाता है। + * आप इसका URL `redoc_url` parameter के साथ set कर सकते हैं। + * आप `redoc_url=None` set करके इसे disable कर सकते हैं। + +उदाहरण के लिए, Swagger UI को `/documentation` पर serve करने के लिए set करना और ReDoc को disable करना: + +{* ../../docs_src/metadata/tutorial003_py310.py hl[3] *} diff --git a/docs/hi/docs/tutorial/middleware.md b/docs/hi/docs/tutorial/middleware.md new file mode 100644 index 000000000..0fd2be7a9 --- /dev/null +++ b/docs/hi/docs/tutorial/middleware.md @@ -0,0 +1,95 @@ +# Middleware { #middleware } + +आप **FastAPI** applications में middleware जोड़ सकते हैं। + +"middleware" एक function है जो हर **request** के साथ काम करता है, इससे पहले कि उसे किसी विशेष *path operation* द्वारा process किया जाए। और हर **response** के साथ भी, उसे लौटाने से पहले। + +* यह आपके application में आने वाली हर **request** लेता है। +* फिर यह उस **request** के साथ कुछ कर सकता है या कोई required code चला सकता है। +* फिर यह **request** को application के बाकी हिस्से द्वारा process होने के लिए आगे भेजता है (किसी *path operation* द्वारा)। +* फिर यह application द्वारा generate किया गया **response** लेता है (किसी *path operation* द्वारा)। +* यह उस **response** के साथ कुछ कर सकता है या कोई required code चला सकता है। +* फिर यह **response** लौटाता है। + +/// note | तकनीकी विवरण + +अगर आपके पास `yield` वाली dependencies हैं, तो exit code middleware के *बाद* चलेगा। + +अगर कोई background tasks थे ([Background Tasks](background-tasks.md) section में कवर किया गया है, आप इसे बाद में देखेंगे), तो वे सभी middleware के *बाद* चलेंगे। + +/// + +## Middleware बनाएं { #create-a-middleware } + +middleware बनाने के लिए आप किसी function के ऊपर decorator `@app.middleware("http")` का उपयोग करते हैं। + +middleware function को मिलता है: + +* `request`। +* एक function `call_next` जो `request` को parameter के रूप में प्राप्त करेगा। + * यह function `request` को संबंधित *path operation* तक पास करेगा। + * फिर यह संबंधित *path operation* द्वारा generate किया गया `response` लौटाता है। +* फिर आप `response` लौटाने से पहले उसे और modify कर सकते हैं। + +{* ../../docs_src/middleware/tutorial001_py310.py hl[8:9,11,14] *} + +/// tip | सुझाव + +ध्यान रखें कि custom proprietary headers को [`X-` prefix का उपयोग करके](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) जोड़ा जा सकता है। + +लेकिन अगर आपके पास custom headers हैं जिन्हें आप browser में client को दिखाना चाहते हैं, तो आपको उन्हें अपने CORS configurations ([CORS (Cross-Origin Resource Sharing)](cors.md)) में `expose_headers` parameter का उपयोग करके जोड़ना होगा, जैसा कि [Starlette के CORS docs](https://www.starlette.dev/middleware/#corsmiddleware) में documented है। + +/// + +/// note | तकनीकी विवरण + +आप `from starlette.requests import Request` भी उपयोग कर सकते हैं। + +**FastAPI** इसे आपके लिए, developer की सुविधा के रूप में provide करता है। लेकिन यह सीधे Starlette से आता है। + +/// + +### `response` से पहले और बाद में { #before-and-after-the-response } + +आप `request` के साथ चलाने के लिए code जोड़ सकते हैं, इससे पहले कि कोई *path operation* उसे प्राप्त करे। + +और `response` generate होने के बाद भी, उसे लौटाने से पहले। + +उदाहरण के लिए, आप एक custom header `X-Process-Time` जोड़ सकते हैं जिसमें seconds में वह time हो जो request को process करने और response generate करने में लगा: + +{* ../../docs_src/middleware/tutorial001_py310.py hl[10,12:13] *} + +/// tip | सुझाव + +यहाँ हम `time.time()` के बजाय [`time.perf_counter()`](https://docs.python.org/3/library/time.html#time.perf_counter) का उपयोग करते हैं क्योंकि यह इन use cases के लिए अधिक precise हो सकता है। 🤓 + +/// + +## कई middleware का execution order { #multiple-middleware-execution-order } + +जब आप `@app.middleware()` decorator या `app.add_middleware()` method का उपयोग करके कई middleware जोड़ते हैं, तो हर नया middleware application को wrap करता है, जिससे एक stack बनता है। जो middleware अंत में जोड़ा जाता है वह *outermost* होता है, और पहला *innermost* होता है। + +request path पर, *outermost* middleware पहले चलता है। + +response path पर, यह अंत में चलता है। + +उदाहरण के लिए: + +```Python +app.add_middleware(MiddlewareA) +app.add_middleware(MiddlewareB) +``` + +इससे execution order यह होता है: + +* **Request**: MiddlewareB → MiddlewareA → route + +* **Response**: route → MiddlewareA → MiddlewareB + +यह stacking behavior सुनिश्चित करता है कि middleware एक predictable और controllable order में execute हों। + +## अन्य middleware { #other-middlewares } + +आप बाद में [Advanced User Guide: Advanced Middleware](../advanced/middleware.md) में अन्य middleware के बारे में और पढ़ सकते हैं। + +आप अगले section में middleware के साथ CORS को handle करने के बारे में पढ़ेंगे। diff --git a/docs/hi/docs/tutorial/path-operation-configuration.md b/docs/hi/docs/tutorial/path-operation-configuration.md new file mode 100644 index 000000000..99c63aec9 --- /dev/null +++ b/docs/hi/docs/tutorial/path-operation-configuration.md @@ -0,0 +1,107 @@ +# Path Operation Configuration { #path-operation-configuration } + +कई parameters हैं जिन्हें आप अपने *path operation decorator* को configure करने के लिए pass कर सकते हैं। + +/// warning | चेतावनी + +ध्यान दें कि ये parameters सीधे *path operation decorator* को pass किए जाते हैं, आपके *path operation function* को नहीं। + +/// + +## Response Status Code { #response-status-code } + +आप अपनी *path operation* की response में उपयोग किए जाने वाला (HTTP) `status_code` define कर सकते हैं। + +आप सीधे `int` code pass कर सकते हैं, जैसे `404`। + +लेकिन अगर आपको याद नहीं है कि हर number code किसके लिए है, तो आप `status` में shortcut constants का उपयोग कर सकते हैं: + +{* ../../docs_src/path_operation_configuration/tutorial001_py310.py hl[1,15] *} + +वह status code response में उपयोग किया जाएगा और OpenAPI schema में जोड़ा जाएगा। + +/// note | तकनीकी विवरण + +आप `from starlette import status` भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.status` `fastapi.status` के रूप में प्रदान करता है। लेकिन यह सीधे Starlette से आता है। + +/// + +## Tags { #tags } + +आप अपनी *path operation* में tags जोड़ सकते हैं, parameter `tags` को `str` की `list` के साथ pass करें (आम तौर पर सिर्फ एक `str`): + +{* ../../docs_src/path_operation_configuration/tutorial002_py310.py hl[15,20,25] *} + +वे OpenAPI schema में जोड़े जाएंगे और automatic documentation interfaces द्वारा उपयोग किए जाएंगे: + + + +### Enums के साथ Tags { #tags-with-enums } + +अगर आपके पास एक बड़ी application है, तो आप अंत में **कई tags** जमा कर सकते हैं, और आप यह सुनिश्चित करना चाहेंगे कि related *path operations* के लिए आप हमेशा **एक ही tag** का उपयोग करें। + +इन मामलों में, tags को एक `Enum` में store करना समझदारी हो सकती है। + +**FastAPI** इसे plain strings की तरह ही support करता है: + +{* ../../docs_src/path_operation_configuration/tutorial002b_py310.py hl[1,8:10,13,18] *} + +## Summary और description { #summary-and-description } + +आप `summary` और `description` जोड़ सकते हैं: + +{* ../../docs_src/path_operation_configuration/tutorial003_py310.py hl[17:18] *} + +## Docstring से description { #description-from-docstring } + +क्योंकि descriptions आम तौर पर लंबी होती हैं और कई lines में फैलती हैं, आप *path operation* की description को function docstring में declare कर सकते हैं और **FastAPI** उसे वहीं से पढ़ेगा। + +आप docstring में [Markdown](https://en.wikipedia.org/wiki/Markdown) लिख सकते हैं, इसे सही तरीके से interpret और display किया जाएगा (docstring indentation को ध्यान में रखते हुए)। + +{* ../../docs_src/path_operation_configuration/tutorial004_py310.py hl[17:25] *} + +इसे interactive docs में उपयोग किया जाएगा: + + + +## Response description { #response-description } + +आप parameter `response_description` के साथ response description specify कर सकते हैं: + +{* ../../docs_src/path_operation_configuration/tutorial005_py310.py hl[18] *} + +/// note | नोट + +ध्यान दें कि `response_description` विशेष रूप से response को refer करता है, जबकि `description` सामान्य रूप से *path operation* को refer करता है। + +/// + +/// tip | सुझाव + +OpenAPI specify करता है कि प्रत्येक *path operation* को response description required होती है। + +इसलिए, अगर आप कोई provide नहीं करते, तो **FastAPI** अपने आप "Successful response" generate कर देगा। + +/// + + + +## एक *path operation* को Deprecated करें { #deprecate-a-path-operation } + +अगर आपको किसी *path operation* को deprecated के रूप में mark करना है, लेकिन उसे हटाना नहीं है, तो parameter `deprecated` pass करें: + +{* ../../docs_src/path_operation_configuration/tutorial006_py310.py hl[16] *} + +इसे interactive docs में स्पष्ट रूप से deprecated के रूप में mark किया जाएगा: + + + +देखें कि deprecated और non-deprecated *path operations* कैसे दिखते हैं: + + + +## Recap { #recap } + +आप *path operation decorators* को parameters pass करके अपनी *path operations* के लिए metadata आसानी से configure और add कर सकते हैं। diff --git a/docs/hi/docs/tutorial/path-params-numeric-validations.md b/docs/hi/docs/tutorial/path-params-numeric-validations.md new file mode 100644 index 000000000..59ecd7c4e --- /dev/null +++ b/docs/hi/docs/tutorial/path-params-numeric-validations.md @@ -0,0 +1,154 @@ +# Path Parameters और संख्यात्मक Validations { #path-parameters-and-numeric-validations } + +जिस तरह आप `Query` के साथ query parameters के लिए अधिक validations और metadata घोषित कर सकते हैं, उसी तरह आप `Path` के साथ path parameters के लिए उसी प्रकार की validations और metadata घोषित कर सकते हैं। + +## `Path` import करें { #import-path } + +सबसे पहले, `fastapi` से `Path` import करें, और `Annotated` import करें: + +{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[1,3] *} + +/// note | नोट + +FastAPI ने version 0.95.0 में `Annotated` के लिए support जोड़ा था (और इसकी सिफारिश करना शुरू किया था)। + +अगर आपके पास पुराना version है, तो `Annotated` का उपयोग करने की कोशिश करते समय आपको errors मिलेंगे। + +`Annotated` का उपयोग करने से पहले सुनिश्चित करें कि आप [FastAPI version को Upgrade करें](../deployment/versions.md#upgrading-the-fastapi-versions) कम से कम 0.95.1 तक। + +/// + +## Metadata घोषित करें { #declare-metadata } + +आप `Query` के लिए जैसे सभी parameters घोषित करते हैं, वैसे ही यहाँ भी कर सकते हैं। + +उदाहरण के लिए, path parameter `item_id` के लिए `title` metadata value घोषित करने के लिए आप लिख सकते हैं: + +{* ../../docs_src/path_params_numeric_validations/tutorial001_an_py310.py hl[10] *} + +/// note | नोट + +एक path parameter हमेशा required होता है क्योंकि उसे path का हिस्सा होना होता है। भले ही आपने इसे `None` के साथ घोषित किया हो या कोई default value सेट की हो, इससे कुछ भी प्रभावित नहीं होगा, यह फिर भी हमेशा required रहेगा। + +/// + +## Parameters को अपनी ज़रूरत के अनुसार क्रम दें { #order-the-parameters-as-you-need } + +/// tip | सुझाव + +यदि आप `Annotated` का उपयोग करते हैं, तो यह शायद उतना महत्वपूर्ण या ज़रूरी नहीं है। + +/// + +मान लें कि आप query parameter `q` को required `str` के रूप में घोषित करना चाहते हैं। + +और आपको उस parameter के लिए कुछ और घोषित करने की ज़रूरत नहीं है, इसलिए वास्तव में आपको `Query` का उपयोग करने की ज़रूरत नहीं है। + +लेकिन आपको फिर भी `item_id` path parameter के लिए `Path` का उपयोग करना होगा। और किसी कारण से आप `Annotated` का उपयोग नहीं करना चाहते। + +यदि आप किसी ऐसे value को, जिसके पास "default" है, ऐसे value से पहले रखते हैं जिसके पास "default" नहीं है, तो Python शिकायत करेगा। + +लेकिन आप उनका क्रम बदल सकते हैं, और बिना default वाले value (query parameter `q`) को पहले रख सकते हैं। + +**FastAPI** के लिए इससे फर्क नहीं पड़ता। यह parameters को उनके नामों, types और default declarations (`Query`, `Path`, आदि) से पहचान लेगा, इसे क्रम से कोई फर्क नहीं पड़ता। + +तो, आप अपनी function इस तरह घोषित कर सकते हैं: + +{* ../../docs_src/path_params_numeric_validations/tutorial002_py310.py hl[7] *} + +लेकिन ध्यान रखें कि यदि आप `Annotated` का उपयोग करते हैं, तो आपको यह समस्या नहीं होगी, क्योंकि आप `Query()` या `Path()` के लिए function parameter default values का उपयोग नहीं कर रहे हैं। + +{* ../../docs_src/path_params_numeric_validations/tutorial002_an_py310.py *} + +## Parameters को अपनी ज़रूरत के अनुसार क्रम दें, tricks { #order-the-parameters-as-you-need-tricks } + +/// tip | सुझाव + +यदि आप `Annotated` का उपयोग करते हैं, तो यह शायद उतना महत्वपूर्ण या ज़रूरी नहीं है। + +/// + +यहाँ एक **छोटी trick** है जो काम आ सकती है, लेकिन आपको इसकी अक्सर ज़रूरत नहीं पड़ेगी। + +यदि आप चाहते हैं कि: + +* `q` query parameter को बिना `Query` और बिना किसी default value के घोषित करें +* path parameter `item_id` को `Path` का उपयोग करके घोषित करें +* उन्हें अलग क्रम में रखें +* `Annotated` का उपयोग न करें + +...तो Python में इसके लिए एक छोटी विशेष syntax है। + +function के पहले parameter के रूप में `*` पास करें। + +Python उस `*` के साथ कुछ नहीं करेगा, लेकिन उसे पता चल जाएगा कि उसके बाद आने वाले सभी parameters को keyword arguments (key-value pairs) के रूप में call किया जाना चाहिए, जिन्हें kwargs भी कहा जाता है। भले ही उनके पास default value न हो। + +{* ../../docs_src/path_params_numeric_validations/tutorial003_py310.py hl[7] *} + +### `Annotated` के साथ बेहतर { #better-with-annotated } + +ध्यान रखें कि यदि आप `Annotated` का उपयोग करते हैं, तो चूँकि आप function parameter default values का उपयोग नहीं कर रहे हैं, आपको यह समस्या नहीं होगी, और शायद आपको `*` का उपयोग करने की ज़रूरत नहीं पड़ेगी। + +{* ../../docs_src/path_params_numeric_validations/tutorial003_an_py310.py hl[10] *} + +## Number validations: greater than or equal { #number-validations-greater-than-or-equal } + +`Query` और `Path` (और अन्य जिन्हें आप बाद में देखेंगे) के साथ आप number constraints घोषित कर सकते हैं। + +यहाँ, `ge=1` के साथ, `item_id` को `1` से "`g`reater than or `e`qual" integer number होना होगा। + +{* ../../docs_src/path_params_numeric_validations/tutorial004_an_py310.py hl[10] *} + +## Number validations: greater than और less than or equal { #number-validations-greater-than-and-less-than-or-equal } + +यही बात इन पर भी लागू होती है: + +* `gt`: `g`reater `t`han +* `le`: `l`ess than or `e`qual + +{* ../../docs_src/path_params_numeric_validations/tutorial005_an_py310.py hl[10] *} + +## Number validations: floats, greater than और less than { #number-validations-floats-greater-than-and-less-than } + +Number validations `float` values के लिए भी काम करती हैं। + +यहीं पर gt घोषित कर पाना महत्वपूर्ण हो जाता है, सिर्फ ge नहीं। क्योंकि इसके साथ आप, उदाहरण के लिए, यह require कर सकते हैं कि कोई value `0` से अधिक होनी चाहिए, भले ही वह `1` से कम हो। + +तो, `0.5` एक valid value होगा। लेकिन `0.0` या `0` नहीं होंगे। + +और यही बात lt के लिए भी है। + +{* ../../docs_src/path_params_numeric_validations/tutorial006_an_py310.py hl[13] *} + +## Recap { #recap } + +`Query`, `Path` (और अन्य जिन्हें आपने अभी तक नहीं देखा है) के साथ आप metadata और string validations उसी तरह घोषित कर सकते हैं जैसे [Query Parameters और String Validations](query-params-str-validations.md) के साथ। + +और आप numeric validations भी घोषित कर सकते हैं: + +* `gt`: `g`reater `t`han +* `ge`: `g`reater than or `e`qual +* `lt`: `l`ess `t`han +* `le`: `l`ess than or `e`qual + +/// note | नोट + +`Query`, `Path`, और अन्य classes जिन्हें आप बाद में देखेंगे, एक common `Param` class की subclasses हैं। + +वे सभी अतिरिक्त validation और metadata के लिए वही parameters साझा करती हैं जिन्हें आपने देखा है। + +/// + +/// note | तकनीकी विवरण + +जब आप `fastapi` से `Query`, `Path` और अन्य import करते हैं, तो वे वास्तव में functions होते हैं। + +जब उन्हें call किया जाता है, तो वे उसी नाम की classes के instances return करते हैं। + +तो, आप `Query` import करते हैं, जो एक function है। और जब आप इसे call करते हैं, तो यह `Query` नाम की class का एक instance return करता है। + +ये functions इसलिए हैं (classes को सीधे उपयोग करने के बजाय) ताकि आपका editor उनके types के बारे में errors mark न करे। + +इस तरह आप उन errors को ignore करने के लिए custom configurations जोड़े बिना अपने सामान्य editor और coding tools का उपयोग कर सकते हैं। + +/// diff --git a/docs/hi/docs/tutorial/path-params.md b/docs/hi/docs/tutorial/path-params.md new file mode 100644 index 000000000..602ff1212 --- /dev/null +++ b/docs/hi/docs/tutorial/path-params.md @@ -0,0 +1,251 @@ +# Path Parameters { #path-parameters } + +आप Python format strings द्वारा इस्तेमाल किए जाने वाले समान syntax के साथ path "parameters" या "variables" declare कर सकते हैं: + +{* ../../docs_src/path_params/tutorial001_py310.py hl[6:7] *} + +path parameter `item_id` की value आपके function को argument `item_id` के रूप में pass की जाएगी। + +इसलिए, अगर आप यह example run करते हैं और [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo) पर जाते हैं, तो आपको ऐसा response दिखाई देगा: + +```JSON +{"item_id":"foo"} +``` + +## Types के साथ Path parameters { #path-parameters-with-types } + +आप standard Python type annotations का उपयोग करके function में किसी path parameter का type declare कर सकते हैं: + +{* ../../docs_src/path_params/tutorial002_py310.py hl[7] *} + +इस मामले में, `item_id` को `int` declare किया गया है। + +/// tip | सुझाव + +इससे आपको अपने function के अंदर editor support मिलेगा, जिसमें error checks, completion आदि शामिल हैं। + +/// + +## Data conversion { #data-conversion } + +अगर आप यह example run करते हैं और अपने browser में [http://127.0.0.1:8000/items/3](http://127.0.0.1:8000/items/3) खोलते हैं, तो आपको ऐसा response दिखाई देगा: + +```JSON +{"item_id":3} +``` + +/// tip | सुझाव + +ध्यान दें कि आपके function ने जो value प्राप्त की (और return की) वह `3` है, Python `int` के रूप में, न कि string `"3"`। + +तो, उस type declaration के साथ, **FastAPI** आपको automatic request "parsing" देता है। + +/// + +## Data validation { #data-validation } + +लेकिन अगर आप browser में [http://127.0.0.1:8000/items/foo](http://127.0.0.1:8000/items/foo) पर जाते हैं, तो आपको ऐसा अच्छा HTTP error दिखाई देगा: + +```JSON +{ + "detail": [ + { + "type": "int_parsing", + "loc": [ + "path", + "item_id" + ], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "foo" + } + ] +} +``` + +क्योंकि path parameter `item_id` की value `"foo"` थी, जो कि `int` नहीं है। + +अगर आपने `int` के बजाय `float` दिया, तो भी वही error दिखाई देगा, जैसे: [http://127.0.0.1:8000/items/4.2](http://127.0.0.1:8000/items/4.2) + +/// tip | सुझाव + +तो, उसी Python type declaration के साथ, **FastAPI** आपको data validation देता है। + +ध्यान दें कि error यह भी स्पष्ट रूप से बताता है कि validation किस जगह pass नहीं हुआ। + +यह आपके API के साथ interact करने वाले code को develop और debug करते समय बेहद मददगार होता है। + +/// + +## Documentation { #documentation } + +और जब आप अपने browser में [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) खोलते हैं, तो आपको ऐसा automatic, interactive, API documentation दिखाई देगा: + + + +/// tip | सुझाव + +फिर से, बस उसी Python type declaration के साथ, **FastAPI** आपको automatic, interactive documentation देता है (Swagger UI को integrate करते हुए)। + +ध्यान दें कि path parameter को integer के रूप में declare किया गया है। + +/// + +## Standard-आधारित लाभ, वैकल्पिक documentation { #standards-based-benefits-alternative-documentation } + +और क्योंकि generate किया गया schema [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.1.0.md) standard से है, इसलिए कई compatible tools हैं। + +इसी वजह से, **FastAPI** स्वयं एक वैकल्पिक API documentation प्रदान करता है (ReDoc का उपयोग करते हुए), जिसे आप [http://127.0.0.1:8000/redoc](http://127.0.0.1:8000/redoc) पर access कर सकते हैं: + + + +इसी तरह, कई compatible tools हैं। इनमें कई भाषाओं के लिए code generation tools भी शामिल हैं। + +## Pydantic { #pydantic } + +सारा data validation अंदरूनी तौर पर [Pydantic](https://docs.pydantic.dev/) द्वारा किया जाता है, इसलिए आपको इसके सभी लाभ मिलते हैं। और आप जानते हैं कि आप अच्छे हाथों में हैं। + +आप `str`, `float`, `bool` और कई अन्य जटिल data types के साथ वही type declarations इस्तेमाल कर सकते हैं। + +इनमें से कई को tutorial के अगले chapters में explore किया गया है। + +## क्रम मायने रखता है { #order-matters } + +*path operations* बनाते समय, आपको ऐसी स्थितियाँ मिल सकती हैं जहाँ आपके पास एक fixed path हो। + +जैसे `/users/me`, मान लें कि यह current user के बारे में data प्राप्त करने के लिए है। + +और फिर आपके पास `/users/{user_id}` path भी हो सकता है, किसी specific user के बारे में किसी user ID से data प्राप्त करने के लिए। + +क्योंकि *path operations* का evaluation क्रम में किया जाता है, आपको यह सुनिश्चित करना होगा कि `/users/me` के लिए path, `/users/{user_id}` वाले path से पहले declare किया गया हो: + +{* ../../docs_src/path_params/tutorial003_py310.py hl[6,11] *} + +अन्यथा, `/users/{user_id}` के लिए path `/users/me` से भी match करेगा, यह "सोचते हुए" कि उसे `user_id` parameter मिल रहा है जिसकी value `"me"` है। + +इसी तरह, आप किसी path operation को फिर से define नहीं कर सकते: + +{* ../../docs_src/path_params/tutorial003b_py310.py hl[6,11] *} + +पहला वाला हमेशा इस्तेमाल किया जाएगा क्योंकि path पहले match करता है। + +## पहले से तय values { #predefined-values } + +अगर आपके पास ऐसा *path operation* है जो एक *path parameter* प्राप्त करता है, लेकिन आप चाहते हैं कि संभव valid *path parameter* values पहले से तय हों, तो आप standard Python `Enum` का उपयोग कर सकते हैं। + +### एक `Enum` class बनाएं { #create-an-enum-class } + +`Enum` import करें और एक sub-class बनाएं जो `str` और `Enum` से inherit करती हो। + +`str` से inherit करने पर API docs यह जान पाएंगे कि values का type `string` होना चाहिए और उन्हें सही तरह से render कर पाएंगे। + +फिर fixed values के साथ class attributes बनाएं, जो उपलब्ध valid values होंगी: + +{* ../../docs_src/path_params/tutorial005_py310.py hl[1,6:9] *} + +/// tip | सुझाव + +अगर आप सोच रहे हैं, "AlexNet", "ResNet", और "LeNet" बस Machine Learning models के नाम हैं। + +/// + +### एक *path parameter* declare करें { #declare-a-path-parameter } + +फिर आपके द्वारा बनाई गई enum class (`ModelName`) का उपयोग करके type annotation के साथ एक *path parameter* बनाएं: + +{* ../../docs_src/path_params/tutorial005_py310.py hl[16] *} + +### Docs देखें { #check-the-docs } + +क्योंकि *path parameter* के लिए उपलब्ध values पहले से तय हैं, interactive docs उन्हें अच्छे से दिखा सकते हैं: + + + +### Python *enumerations* के साथ काम करना { #working-with-python-enumerations } + +*path parameter* की value एक *enumeration member* होगी। + +#### *Enumeration members* की तुलना करें { #compare-enumeration-members } + +आप इसकी तुलना अपने बनाए हुए enum `ModelName` में मौजूद *enumeration member* से कर सकते हैं: + +{* ../../docs_src/path_params/tutorial005_py310.py hl[17] *} + +#### *Enumeration value* प्राप्त करें { #get-the-enumeration-value } + +आप `model_name.value` का उपयोग करके actual value (इस मामले में एक `str`) प्राप्त कर सकते हैं, या सामान्य रूप से, `your_enum_member.value`: + +{* ../../docs_src/path_params/tutorial005_py310.py hl[20] *} + +/// tip | सुझाव + +आप `ModelName.lenet.value` के साथ value `"lenet"` भी access कर सकते हैं। + +/// + +#### *Enumeration members* return करें { #return-enumeration-members } + +आप अपने *path operation* से *enum members* return कर सकते हैं, यहाँ तक कि JSON body में nested भी (जैसे एक `dict`)। + +Client को return करने से पहले उन्हें उनकी संबंधित values (इस मामले में strings) में convert कर दिया जाएगा: + +{* ../../docs_src/path_params/tutorial005_py310.py hl[18,21,23] *} + +अपने client में आपको ऐसा JSON response मिलेगा: + +```JSON +{ + "model_name": "alexnet", + "message": "Deep Learning FTW!" +} +``` + +## Paths रखने वाले Path parameters { #path-parameters-containing-paths } + +मान लें आपके पास path `/files/{file_path}` के साथ एक *path operation* है। + +लेकिन आपको `file_path` में स्वयं एक *path* रखना है, जैसे `home/johndoe/myfile.txt`। + +तो, उस file के लिए URL कुछ ऐसा होगा: `/files/home/johndoe/myfile.txt`। + +### OpenAPI support { #openapi-support } + +OpenAPI किसी *path parameter* को उसके अंदर एक *path* रखने के लिए declare करने का तरीका support नहीं करता, क्योंकि इससे ऐसे scenarios बन सकते हैं जिन्हें test और define करना कठिन हो। + +फिर भी, आप **FastAPI** में Starlette के internal tools में से एक का उपयोग करके यह कर सकते हैं। + +और docs फिर भी काम करेंगे, हालांकि ऐसा कोई documentation नहीं जोड़ेंगे जो बताए कि parameter में path होना चाहिए। + +### Path convertor { #path-convertor } + +Starlette से सीधे एक option का उपयोग करके, आप इस तरह के URL का उपयोग करते हुए एक *path* रखने वाला *path parameter* declare कर सकते हैं: + +``` +/files/{file_path:path} +``` + +इस मामले में, parameter का नाम `file_path` है, और आखिरी हिस्सा, `:path`, इसे बताता है कि parameter किसी भी *path* से match करना चाहिए। + +तो, आप इसे इसके साथ इस्तेमाल कर सकते हैं: + +{* ../../docs_src/path_params/tutorial004_py310.py hl[6] *} + +/// tip | सुझाव + +आपको parameter में `/home/johndoe/myfile.txt` रखना पड़ सकता है, leading slash (`/`) के साथ। + +उस मामले में, URL होगा: `/files//home/johndoe/myfile.txt`, `files` और `home` के बीच double slash (`//`) के साथ। + +/// + +## Recap { #recap } + +**FastAPI** के साथ, छोटे, सहज और standard Python type declarations का उपयोग करके, आपको मिलता है: + +* Editor support: error checks, autocompletion आदि। +* Data "parsing" +* Data validation +* API annotation और automatic documentation + +और आपको इन्हें केवल एक बार declare करना होता है। + +वैकल्पिक frameworks की तुलना में **FastAPI** का शायद यही मुख्य दिखने वाला लाभ है (raw performance के अलावा)। diff --git a/docs/hi/docs/tutorial/query-param-models.md b/docs/hi/docs/tutorial/query-param-models.md new file mode 100644 index 000000000..1727b7654 --- /dev/null +++ b/docs/hi/docs/tutorial/query-param-models.md @@ -0,0 +1,68 @@ +# Query Parameter Models { #query-parameter-models } + +अगर आपके पास संबंधित **query parameters** का एक समूह है, तो आप उन्हें declare करने के लिए एक **Pydantic model** बना सकते हैं। + +इससे आप **model को फिर से उपयोग** कर पाएँगे, **कई जगहों** पर, और साथ ही सभी parameters के लिए validations और metadata एक साथ declare कर पाएँगे। 😎 + +/// note | नोट + +यह FastAPI version `0.115.0` से supported है। 🤓 + +/// + +## Pydantic Model के साथ Query Parameters { #query-parameters-with-a-pydantic-model } + +जिन **query parameters** की आपको ज़रूरत है उन्हें एक **Pydantic model** में declare करें, और फिर parameter को `Query` के रूप में declare करें: + +{* ../../docs_src/query_param_models/tutorial001_an_py310.py hl[9:13,17] *} + +**FastAPI** request में मौजूद **query parameters** से **हर field** के लिए data **extract** करेगा और आपको वह Pydantic model देगा जिसे आपने define किया है। + +## Docs देखें { #check-the-docs } + +आप `/docs` पर docs UI में query parameters देख सकते हैं: + +
+ +
+ +## Extra Query Parameters को Forbid करें { #forbid-extra-query-parameters } + +कुछ विशेष use cases में (शायद बहुत आम नहीं), आप उन query parameters को **restrict** करना चाह सकते हैं जिन्हें आप receive करना चाहते हैं। + +आप किसी भी `extra` fields को `forbid` करने के लिए Pydantic की model configuration का उपयोग कर सकते हैं: + +{* ../../docs_src/query_param_models/tutorial002_an_py310.py hl[10] *} + +अगर कोई client **query parameters** में कुछ **extra** data भेजने की कोशिश करता है, तो उसे एक **error** response मिलेगा। + +उदाहरण के लिए, अगर client `plumbus` value के साथ `tool` query parameter भेजने की कोशिश करता है, जैसे: + +```http +https://example.com/items/?limit=10&tool=plumbus +``` + +उसे एक **error** response मिलेगा जो बताएगा कि query parameter `tool` allowed नहीं है: + +```json +{ + "detail": [ + { + "type": "extra_forbidden", + "loc": ["query", "tool"], + "msg": "Extra inputs are not permitted", + "input": "plumbus" + } + ] +} +``` + +## सारांश { #summary } + +आप **FastAPI** में **query parameters** declare करने के लिए **Pydantic models** का उपयोग कर सकते हैं। 😎 + +/// tip | सुझाव + +Spoiler alert: आप cookies और headers declare करने के लिए भी Pydantic models का उपयोग कर सकते हैं, लेकिन आप इसके बारे में tutorial में बाद में पढ़ेंगे। 🤫 + +/// diff --git a/docs/hi/docs/tutorial/query-params-str-validations.md b/docs/hi/docs/tutorial/query-params-str-validations.md new file mode 100644 index 000000000..05f2634a7 --- /dev/null +++ b/docs/hi/docs/tutorial/query-params-str-validations.md @@ -0,0 +1,450 @@ +# Query Parameters और String Validations { #query-parameters-and-string-validations } + +**FastAPI** आपको अपने parameters के लिए अतिरिक्त जानकारी और validation declare करने देता है। + +इस application को example के रूप में लेते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial001_py310.py hl[7] *} + +query parameter `q` का type `str | None` है, इसका मतलब है कि यह type `str` का है लेकिन `None` भी हो सकता है, और वास्तव में, default value `None` है, इसलिए FastAPI जान जाएगा कि यह required नहीं है। + +/// note | नोट + +FastAPI जान जाएगा कि `q` की value required नहीं है क्योंकि default value `= None` है। + +`str | None` होने से आपका editor आपको बेहतर support दे पाएगा और errors detect कर पाएगा। + +/// + +## अतिरिक्त validation { #additional-validation } + +हम यह enforce करने जा रहे हैं कि भले ही `q` optional हो, जब भी यह provide किया जाए, **इसकी length 50 characters से अधिक न हो**। + +### `Query` और `Annotated` import करें { #import-query-and-annotated } + +इसे हासिल करने के लिए, पहले import करें: + +* `fastapi` से `Query` +* `typing` से `Annotated` + +{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[1,3] *} + +/// note | नोट + +FastAPI ने version 0.95.0 में `Annotated` के लिए support जोड़ा (और इसकी recommendation शुरू की)। + +अगर आपके पास पुराना version है, तो `Annotated` use करने की कोशिश करने पर आपको errors मिलेंगे। + +`Annotated` use करने से पहले सुनिश्चित करें कि आप [FastAPI version Upgrade करें](../deployment/versions.md#upgrading-the-fastapi-versions) कम से कम 0.95.1 तक। + +/// + +## `q` parameter के type में `Annotated` use करें { #use-annotated-in-the-type-for-the-q-parameter } + +याद है मैंने आपको पहले बताया था कि [Python Types Intro](../python-types.md#type-hints-with-metadata-annotations) में `Annotated` का उपयोग आपके parameters में metadata जोड़ने के लिए किया जा सकता है? + +अब इसे FastAPI के साथ use करने का समय है। 🚀 + +हमारे पास यह type annotation था: + +```Python +q: str | None = None +``` + +हम इसे `Annotated` के साथ wrap करेंगे, तो यह बन जाता है: + +```Python +q: Annotated[str | None] = None +``` + +इन दोनों versions का मतलब एक ही है, `q` एक parameter है जो `str` या `None` हो सकता है, और default रूप से, यह `None` है। + +अब मज़ेदार चीज़ों पर चलते हैं। 🎉 + +## `q` parameter में `Annotated` में `Query` जोड़ें { #add-query-to-annotated-in-the-q-parameter } + +अब जब हमारे पास यह `Annotated` है जहाँ हम अधिक जानकारी रख सकते हैं (इस case में कुछ अतिरिक्त validation), `Annotated` के अंदर `Query` जोड़ें, और parameter `max_length` को `50` पर set करें: + +{* ../../docs_src/query_params_str_validations/tutorial002_an_py310.py hl[9] *} + +ध्यान दें कि default value अभी भी `None` है, इसलिए parameter अभी भी optional है। + +लेकिन अब, `Annotated` के अंदर `Query(max_length=50)` होने से, हम FastAPI को बता रहे हैं कि हम चाहते हैं कि इस value के लिए **अतिरिक्त validation** हो, हम चाहते हैं कि इसमें अधिकतम 50 characters हों। 😎 + +/// tip | टिप + +यहाँ हम `Query()` use कर रहे हैं क्योंकि यह एक **query parameter** है। बाद में हम `Path()`, `Body()`, `Header()`, और `Cookie()` जैसे अन्य देखेंगे, जो `Query()` जैसे ही arguments accept करते हैं। + +/// + +FastAPI अब: + +* data को **Validate** करेगा यह सुनिश्चित करते हुए कि max length 50 characters है +* जब data valid नहीं होगा तो client के लिए **clear error** दिखाएगा +* OpenAPI schema *path operation* में parameter को **Document** करेगा (ताकि यह **automatic docs UI** में दिखाई दे) + +## Alternative (पुराना): default value के रूप में `Query` { #alternative-old-query-as-the-default-value } + +FastAPI के पिछले versions (0.95.0 से पहले) में आपको अपने parameter की default value के रूप में `Query` use करना required था, बजाय इसे `Annotated` में रखने के, इसकी अच्छी संभावना है कि आपको आसपास ऐसा code दिखेगा, इसलिए मैं आपको इसे समझाऊंगा। + +/// tip | टिप + +नए code के लिए और जब भी संभव हो, ऊपर समझाए अनुसार `Annotated` use करें। इसके कई फायदे हैं (नीचे समझाए गए हैं) और कोई नुकसान नहीं। 🍰 + +/// + +इस तरह आप अपने function parameter की default value के रूप में `Query()` use करेंगे, parameter `max_length` को 50 पर set करते हुए: + +{* ../../docs_src/query_params_str_validations/tutorial002_py310.py hl[7] *} + +चूँकि इस case में (`Annotated` use किए बिना) हमें function में default value `None` को `Query()` से replace करना होता है, अब हमें parameter `Query(default=None)` के साथ default value set करनी होगी, यह उस default value को define करने का वही उद्देश्य पूरा करता है (कम से कम FastAPI के लिए)। + +तो: + +```Python +q: str | None = Query(default=None) +``` + +...parameter को optional बनाता है, `None` की default value के साथ, बिल्कुल इसके समान: + + +```Python +q: str | None = None +``` + +लेकिन `Query` version इसे स्पष्ट रूप से query parameter के रूप में declare करता है। + +फिर, हम `Query` को और parameters pass कर सकते हैं। इस case में, `max_length` parameter जो strings पर apply होता है: + +```Python +q: str | None = Query(default=None, max_length=50) +``` + +यह data को validate करेगा, data valid न होने पर clear error दिखाएगा, और OpenAPI schema *path operation* में parameter को document करेगा। + +### default value के रूप में या `Annotated` में `Query` { #query-as-the-default-value-or-in-annotated } + +ध्यान रखें कि `Annotated` के अंदर `Query` use करते समय आप `Query` के लिए `default` parameter use नहीं कर सकते। + +इसके बजाय, function parameter की वास्तविक default value use करें। अन्यथा, यह inconsistent होगा। + +उदाहरण के लिए, इसकी अनुमति नहीं है: + +```Python +q: Annotated[str, Query(default="rick")] = "morty" +``` + +...क्योंकि यह clear नहीं है कि default value `"rick"` होनी चाहिए या `"morty"`। + +तो, आप use करेंगे (preferably): + +```Python +q: Annotated[str, Query()] = "rick" +``` + +...या पुराने code bases में आपको मिलेगा: + +```Python +q: str = Query(default="rick") +``` + +### `Annotated` के फायदे { #advantages-of-annotated } + +function parameters में default value के बजाय **`Annotated` use करने की recommendation है**, यह कई कारणों से **बेहतर** है। 🤓 + +**function parameter** की **default** value ही **वास्तविक default** value है, यह सामान्य रूप से Python के साथ अधिक intuitive है। 😌 + +आप उसी function को FastAPI के बिना **अन्य जगहों** पर **call** कर सकते हैं, और यह **उम्मीद के अनुसार काम** करेगा। अगर कोई **required** parameter है (बिना default value के), तो आपका **editor** आपको error के साथ बता देगा, **Python** भी required parameter pass किए बिना इसे run करने पर complain करेगा। + +जब आप `Annotated` use नहीं करते और इसके बजाय **(पुराना) default value style** use करते हैं, अगर आप उस function को FastAPI के बिना **अन्य जगहों** पर call करते हैं, तो आपको function को सही से काम कराने के लिए arguments pass करना **याद रखना** होगा, अन्यथा values आपकी अपेक्षा से अलग होंगी (जैसे `str` के बजाय `QueryInfo` या कुछ similar)। और आपका editor complain नहीं करेगा, और Python भी उस function को run करते समय complain नहीं करेगा, केवल तब जब अंदर के operations error दें। + +क्योंकि `Annotated` में एक से अधिक metadata annotation हो सकते हैं, अब आप उसी function को अन्य tools के साथ भी use कर सकते हैं, जैसे [Typer](https://typer.tiangolo.com/)। 🚀 + +## और validations जोड़ें { #add-more-validations } + +आप parameter `min_length` भी जोड़ सकते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial003_an_py310.py hl[10] *} + +## regular expressions जोड़ें { #add-regular-expressions } + +आप एक regular expression `pattern` define कर सकते हैं जिससे parameter match करना चाहिए: + +{* ../../docs_src/query_params_str_validations/tutorial004_an_py310.py hl[11] *} + +यह specific regular expression pattern check करता है कि received parameter value: + +* `^`: निम्न characters से शुरू होती है, पहले कोई characters नहीं हैं। +* `fixedquery`: exact value `fixedquery` रखती है। +* `$`: वहीं समाप्त होती है, `fixedquery` के बाद कोई और characters नहीं हैं। + +अगर आप इन सभी **"regular expression"** ideas से खोया हुआ महसूस करते हैं, तो चिंता न करें। यह कई लोगों के लिए कठिन topic है। आप अभी regular expressions की जरूरत के बिना भी बहुत कुछ कर सकते हैं। + +अब आप जानते हैं कि जब भी आपको इनकी जरूरत हो, आप इन्हें **FastAPI** में use कर सकते हैं। + +## Default values { #default-values } + +बेशक, आप `None` के अलावा default values use कर सकते हैं। + +मान लीजिए कि आप `q` query parameter को `3` की `min_length` और `"fixedquery"` की default value के साथ declare करना चाहते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial005_an_py310.py hl[9] *} + +/// note | नोट + +`None` सहित किसी भी type की default value होना parameter को optional (not required) बनाता है। + +/// + +## Required parameters { #required-parameters } + +जब हमें अधिक validations या metadata declare करने की जरूरत नहीं होती, तो हम default value declare न करके ही `q` query parameter को required बना सकते हैं, जैसे: + +```Python +q: str +``` + +इसके बजाय: + +```Python +q: str | None = None +``` + +लेकिन अब हम इसे `Query` के साथ declare कर रहे हैं, उदाहरण के लिए ऐसे: + +```Python +q: Annotated[str | None, Query(min_length=3)] = None +``` + +तो, जब आपको `Query` use करते हुए किसी value को required के रूप में declare करना हो, तो आप बस default value declare न करें: + +{* ../../docs_src/query_params_str_validations/tutorial006_an_py310.py hl[9] *} + +### Required, `None` हो सकता है { #required-can-be-none } + +आप declare कर सकते हैं कि parameter `None` accept कर सकता है, लेकिन फिर भी यह required है। यह clients को value भेजने के लिए मजबूर करेगा, भले ही value `None` हो। + +ऐसा करने के लिए, आप declare कर सकते हैं कि `None` एक valid type है लेकिन बस default value declare न करें: + +{* ../../docs_src/query_params_str_validations/tutorial006c_an_py310.py hl[9] *} + +## Query parameter list / multiple values { #query-parameter-list-multiple-values } + +जब आप query parameter को स्पष्ट रूप से `Query` के साथ define करते हैं तो आप इसे values की list receive करने के लिए भी declare कर सकते हैं, या दूसरे शब्दों में, multiple values receive करने के लिए। + +उदाहरण के लिए, query parameter `q` declare करने के लिए जो URL में कई बार आ सकता है, आप लिख सकते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial011_an_py310.py hl[9] *} + +फिर, ऐसे URL के साथ: + +``` +http://localhost:8000/items/?q=foo&q=bar +``` + +आप multiple `q` *query parameters* की values (`foo` और `bar`) को अपने *path operation function* के अंदर Python `list` में, *function parameter* `q` में receive करेंगे। + +तो, उस URL का response होगा: + +```JSON +{ + "q": [ + "foo", + "bar" + ] +} +``` + +/// tip | टिप + +ऊपर के example की तरह, `list` type वाला query parameter declare करने के लिए, आपको स्पष्ट रूप से `Query` use करना होगा, अन्यथा इसे request body के रूप में interpret किया जाएगा। + +/// + +interactive API docs accordingly update होंगे, ताकि multiple values allow हो सकें: + + + +### Defaults के साथ Query parameter list / multiple values { #query-parameter-list-multiple-values-with-defaults } + +अगर कोई values provide नहीं की गई हैं, तो आप values की default `list` भी define कर सकते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial012_an_py310.py hl[9] *} + +अगर आप यहाँ जाते हैं: + +``` +http://localhost:8000/items/ +``` + +`q` का default होगा: `["foo", "bar"]` और आपका response होगा: + +```JSON +{ + "q": [ + "foo", + "bar" + ] +} +``` + +#### केवल `list` use करना { #using-just-list } + +आप `list[str]` के बजाय सीधे `list` भी use कर सकते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial013_an_py310.py hl[9] *} + +/// note | नोट + +ध्यान रखें कि इस case में, FastAPI list की contents check नहीं करेगा। + +उदाहरण के लिए, `list[int]` check (और document) करेगा कि list की contents integers हैं। लेकिन केवल `list` ऐसा नहीं करेगा। + +/// + +## अधिक metadata declare करें { #declare-more-metadata } + +आप parameter के बारे में अधिक जानकारी जोड़ सकते हैं। + +वह जानकारी generated OpenAPI में शामिल होगी और documentation user interfaces और external tools द्वारा use की जाएगी। + +/// note | नोट + +ध्यान रखें कि अलग-अलग tools में OpenAPI support के अलग-अलग levels हो सकते हैं। + +उनमें से कुछ अभी declare की गई सारी extra information नहीं दिखा सकते, हालांकि अधिकतर cases में, missing feature पहले से ही development के लिए planned है। + +/// + +आप एक `title` जोड़ सकते हैं: + +{* ../../docs_src/query_params_str_validations/tutorial007_an_py310.py hl[10] *} + +और एक `description`: + +{* ../../docs_src/query_params_str_validations/tutorial008_an_py310.py hl[14] *} + +## Alias parameters { #alias-parameters } + +कल्पना करें कि आप parameter को `item-query` बनाना चाहते हैं। + +जैसे: + +``` +http://127.0.0.1:8000/items/?item-query=foobaritems +``` + +लेकिन `item-query` valid Python variable name नहीं है। + +सबसे निकटतम `item_query` होगा। + +लेकिन आपको अभी भी यह exactly `item-query` ही चाहिए... + +तब आप एक `alias` declare कर सकते हैं, और वही alias parameter value खोजने के लिए use किया जाएगा: + +{* ../../docs_src/query_params_str_validations/tutorial009_an_py310.py hl[9] *} + +## Parameters को deprecate करना { #deprecating-parameters } + +अब मान लीजिए कि आपको यह parameter अब पसंद नहीं है। + +आपको इसे कुछ समय के लिए वहीं छोड़ना होगा क्योंकि clients इसे use कर रहे हैं, लेकिन आप चाहते हैं कि docs इसे स्पष्ट रूप से deprecated के रूप में दिखाएँ। + +फिर parameter `deprecated=True` को `Query` में pass करें: + +{* ../../docs_src/query_params_str_validations/tutorial010_an_py310.py hl[19] *} + +docs इसे इस तरह दिखाएँगे: + + + +## OpenAPI से parameters exclude करें { #exclude-parameters-from-openapi } + +generated OpenAPI schema से query parameter exclude करने के लिए (और इस प्रकार, automatic documentation systems से), `Query` के parameter `include_in_schema` को `False` पर set करें: + +{* ../../docs_src/query_params_str_validations/tutorial014_an_py310.py hl[10] *} + +## Custom Validation { #custom-validation } + +ऐसे cases हो सकते हैं जहाँ आपको कुछ **custom validation** करना पड़े जो ऊपर दिखाए गए parameters से नहीं किया जा सकता। + +ऐसे cases में, आप एक **custom validator function** use कर सकते हैं जो normal validation के बाद apply होता है (जैसे value के `str` होने की validation के बाद)। + +आप इसे `Annotated` के अंदर [Pydantic के `AfterValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-after-validator) का उपयोग करके हासिल कर सकते हैं। + +/// tip | टिप + +Pydantic में [`BeforeValidator`](https://docs.pydantic.dev/latest/concepts/validators/#field-before-validator) और अन्य भी हैं। 🤓 + +/// + +उदाहरण के लिए, यह custom validator check करता है कि item ID किसी ISBN book number के लिए `isbn-` से शुरू होती है या किसी IMDB movie URL ID के लिए `imdb-` से: + +{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py hl[5,16:19,24] *} + +/// note | नोट + +यह Pydantic version 2 या उससे ऊपर के साथ available है। 😎 + +/// + +/// tip | टिप + +अगर आपको किसी भी प्रकार की validation करनी है जिसके लिए किसी **external component** से communicate करना required है, जैसे database या कोई अन्य API, तो आपको इसके बजाय **FastAPI Dependencies** use करनी चाहिए, आप इनके बारे में बाद में सीखेंगे। + +ये custom validators उन चीज़ों के लिए हैं जिन्हें request में provide किए गए **सिर्फ** **उसी data** से check किया जा सकता है। + +/// + +### उस Code को समझें { #understand-that-code } + +महत्वपूर्ण बात बस **`Annotated` के अंदर एक function के साथ `AfterValidator` use करना** है। आप चाहें तो इस part को skip कर सकते हैं। 🤸 + +--- + +लेकिन अगर आप इस specific code example के बारे में curious हैं और अभी भी entertained हैं, तो यहाँ कुछ extra details हैं। + +#### `value.startswith()` के साथ String { #string-with-value-startswith } + +क्या आपने ध्यान दिया? `value.startswith()` use करने वाली string tuple ले सकती है, और यह tuple की हर value check करेगी: + +{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[16:19] hl[17] *} + +#### एक Random Item { #a-random-item } + +`data.items()` के साथ हमें tuples वाला एक iterable object मिलता है जिसमें हर dictionary item के लिए key और value होती है। + +हम इस iterable object को `list(data.items())` के साथ proper `list` में convert करते हैं। + +फिर `random.choice()` के साथ हम list से एक **random value** प्राप्त कर सकते हैं, तो हमें `(id, name)` वाला tuple मिलता है। यह कुछ ऐसा होगा `("imdb-tt0371724", "The Hitchhiker's Guide to the Galaxy")`। + +फिर हम tuple की **उन दो values को assign** करते हैं variables `id` और `name` को। + +तो, अगर user ने item ID provide नहीं की, तब भी उन्हें एक random suggestion receive होगा। + +...हम यह सब **एक single simple line** में करते हैं। 🤯 क्या आपको Python पसंद नहीं है? 🐍 + +{* ../../docs_src/query_params_str_validations/tutorial015_an_py310.py ln[22:30] hl[29] *} + +## Recap { #recap } + +आप अपने parameters के लिए अतिरिक्त validations और metadata declare कर सकते हैं। + +Generic validations और metadata: + +* `alias` +* `title` +* `description` +* `deprecated` + +Strings के लिए specific validations: + +* `min_length` +* `max_length` +* `pattern` + +`AfterValidator` का उपयोग करके custom validations। + +इन examples में आपने देखा कि `str` values के लिए validations कैसे declare करें। + +अगले chapters देखें ताकि आप सीख सकें कि numbers जैसे अन्य types के लिए validations कैसे declare करें। diff --git a/docs/hi/docs/tutorial/query-params.md b/docs/hi/docs/tutorial/query-params.md new file mode 100644 index 000000000..bc8573179 --- /dev/null +++ b/docs/hi/docs/tutorial/query-params.md @@ -0,0 +1,188 @@ +# Query Parameters { #query-parameters } + +जब आप ऐसे दूसरे function parameters declare करते हैं जो path parameters का हिस्सा नहीं हैं, तो उन्हें अपने-आप "query" parameters के रूप में समझा जाता है। + +{* ../../docs_src/query_params/tutorial001_py310.py hl[9] *} + +query उन key-value pairs का सेट है जो URL में `?` के बाद आते हैं, और `&` characters से अलग किए जाते हैं। + +उदाहरण के लिए, इस URL में: + +``` +http://127.0.0.1:8000/items/?skip=0&limit=10 +``` + +...query parameters हैं: + +* `skip`: `0` value के साथ +* `limit`: `10` value के साथ + +क्योंकि वे URL का हिस्सा हैं, वे "स्वाभाविक रूप से" strings होते हैं। + +लेकिन जब आप उन्हें Python types के साथ declare करते हैं (ऊपर दिए गए उदाहरण में, `int` के रूप में), तो उन्हें उस type में convert किया जाता है और उसके अनुसार validate किया जाता है। + +path parameters पर लागू होने वाली सभी वही प्रक्रियाएँ query parameters पर भी लागू होती हैं: + +* Editor support (स्पष्ट रूप से) +* Data "parsing" +* Data validation +* Automatic documentation + +## Defaults { #defaults } + +क्योंकि query parameters किसी path का fixed हिस्सा नहीं होते, वे optional हो सकते हैं और उनके default values हो सकते हैं। + +ऊपर दिए गए उदाहरण में उनके default values `skip=0` और `limit=10` हैं। + +तो, इस URL पर जाना: + +``` +http://127.0.0.1:8000/items/ +``` + +इस पर जाने जैसा ही होगा: + +``` +http://127.0.0.1:8000/items/?skip=0&limit=10 +``` + +लेकिन अगर आप, उदाहरण के लिए, इस पर जाते हैं: + +``` +http://127.0.0.1:8000/items/?skip=20 +``` + +तो आपके function में parameter values होंगी: + +* `skip=20`: क्योंकि आपने इसे URL में सेट किया है +* `limit=10`: क्योंकि वह default value था + +## Optional parameters { #optional-parameters } + +उसी तरह, आप optional query parameters declare कर सकते हैं, उनका default `None` सेट करके: + +{* ../../docs_src/query_params/tutorial002_py310.py hl[7] *} + +इस मामले में, function parameter `q` optional होगा, और default रूप से `None` होगा। + +/// tip | सुझाव + +यह भी ध्यान दें कि **FastAPI** इतना smart है कि यह पहचान लेता है कि path parameter `item_id` एक path parameter है और `q` नहीं है, इसलिए, यह एक query parameter है। + +/// + +## Query parameter type conversion { #query-parameter-type-conversion } + +आप `bool` types भी declare कर सकते हैं, और वे convert हो जाएँगे: + +{* ../../docs_src/query_params/tutorial003_py310.py hl[7] *} + +इस मामले में, अगर आप इस पर जाते हैं: + +``` +http://127.0.0.1:8000/items/foo?short=1 +``` + +या + +``` +http://127.0.0.1:8000/items/foo?short=True +``` + +या + +``` +http://127.0.0.1:8000/items/foo?short=true +``` + +या + +``` +http://127.0.0.1:8000/items/foo?short=on +``` + +या + +``` +http://127.0.0.1:8000/items/foo?short=yes +``` + +या कोई भी दूसरी case variation (uppercase, पहले अक्षर को uppercase, आदि), आपका function parameter `short` को `True` के `bool` value के साथ देखेगा। अन्यथा `False` के रूप में। + + +## कई path और query parameters { #multiple-path-and-query-parameters } + +आप एक ही समय में कई path parameters और query parameters declare कर सकते हैं, **FastAPI** जानता है कि कौन सा कौन है। + +और आपको उन्हें किसी विशेष order में declare करने की ज़रूरत नहीं है। + +उन्हें नाम से detect किया जाएगा: + +{* ../../docs_src/query_params/tutorial004_py310.py hl[6,8] *} + +## Required query parameters { #required-query-parameters } + +जब आप non-path parameters के लिए default value declare करते हैं (अभी तक, हमने केवल query parameters देखे हैं), तो वह required नहीं होता। + +अगर आप कोई specific value नहीं जोड़ना चाहते लेकिन बस उसे optional बनाना चाहते हैं, तो default को `None` के रूप में सेट करें। + +लेकिन जब आप किसी query parameter को required बनाना चाहते हैं, तो आप बस कोई default value declare न करें: + +{* ../../docs_src/query_params/tutorial005_py310.py hl[6:7] *} + +यहाँ query parameter `needy` type `str` का एक required query parameter है। + +अगर आप अपने browser में इस तरह का URL खोलते हैं: + +``` +http://127.0.0.1:8000/items/foo-item +``` + +...required parameter `needy` जोड़े बिना, तो आपको इस तरह की error दिखाई देगी: + +```JSON +{ + "detail": [ + { + "type": "missing", + "loc": [ + "query", + "needy" + ], + "msg": "Field required", + "input": null + } + ] +} +``` + +क्योंकि `needy` एक required parameter है, आपको इसे URL में सेट करना होगा: + +``` +http://127.0.0.1:8000/items/foo-item?needy=sooooneedy +``` + +...यह काम करेगा: + +```JSON +{ + "item_id": "foo-item", + "needy": "sooooneedy" +} +``` + +और निश्चित रूप से, आप कुछ parameters को required, कुछ को default value वाला, और कुछ को पूरी तरह optional define कर सकते हैं: + +{* ../../docs_src/query_params/tutorial006_py310.py hl[8] *} + +इस मामले में, 3 query parameters हैं: + +* `needy`, एक required `str`. +* `skip`, default value `0` के साथ एक `int`. +* `limit`, एक optional `int`. + +/// tip | सुझाव + +आप `Enum`s को भी उसी तरह use कर सकते हैं जैसे [Path Parameters](path-params.md#predefined-values) के साथ। + +/// diff --git a/docs/hi/docs/tutorial/request-files.md b/docs/hi/docs/tutorial/request-files.md new file mode 100644 index 000000000..4a79ca2b9 --- /dev/null +++ b/docs/hi/docs/tutorial/request-files.md @@ -0,0 +1,176 @@ +# Request Files { #request-files } + +आप client द्वारा अपलोड की जाने वाली files को `File` का उपयोग करके परिभाषित कर सकते हैं। + +/// note | नोट + +अपलोड की गई files प्राप्त करने के लिए, पहले [`python-multipart`](https://github.com/Kludex/python-multipart) install करें। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर इसे install करते हैं, उदाहरण के लिए: + +```console +$ pip install python-multipart +``` + +ऐसा इसलिए है क्योंकि अपलोड की गई files "form data" के रूप में भेजी जाती हैं। + +/// + +## `File` Import करें { #import-file } + +`fastapi` से `File` और `UploadFile` import करें: + +{* ../../docs_src/request_files/tutorial001_an_py310.py hl[3] *} + +## `File` Parameters परिभाषित करें { #define-file-parameters } + +file parameters उसी तरह बनाएं जैसे आप `Body` या `Form` के लिए बनाते हैं: + +{* ../../docs_src/request_files/tutorial001_an_py310.py hl[9] *} + +/// note | नोट + +`File` एक class है जो सीधे `Form` से inherit करती है। + +लेकिन याद रखें कि जब आप `fastapi` से `Query`, `Path`, `File` और अन्य import करते हैं, तो वे वास्तव में functions होते हैं जो विशेष classes return करते हैं। + +/// + +/// tip | सुझाव + +File bodies घोषित करने के लिए, आपको `File` का उपयोग करना होगा, क्योंकि अन्यथा parameters को query parameters या body (JSON) parameters के रूप में समझा जाएगा। + +/// + +files "form data" के रूप में अपलोड की जाएंगी। + +यदि आप अपने *path operation function* parameter का type `bytes` के रूप में घोषित करते हैं, तो **FastAPI** आपके लिए file पढ़ेगा और आपको सामग्री `bytes` के रूप में प्राप्त होगी। + +ध्यान रखें कि इसका मतलब है कि पूरी सामग्री memory में संग्रहीत होगी। यह छोटी files के लिए अच्छी तरह काम करेगा। + +लेकिन कई मामलों में आपको `UploadFile` का उपयोग करने से लाभ हो सकता है। + +## `UploadFile` के साथ File Parameters { #file-parameters-with-uploadfile } + +`UploadFile` type के साथ file parameter परिभाषित करें: + +{* ../../docs_src/request_files/tutorial001_an_py310.py hl[14] *} + +`bytes` की तुलना में `UploadFile` का उपयोग करने के कई फायदे हैं: + +* आपको parameter के default value में `File()` का उपयोग नहीं करना पड़ता। +* यह एक "spooled" file का उपयोग करता है: + * एक file जो अधिकतम size limit तक memory में संग्रहीत होती है, और इस limit को पार करने के बाद disk पर संग्रहीत होती है। +* इसका मतलब है कि यह images, videos, बड़े binaries आदि जैसी बड़ी files के लिए सारी memory का उपयोग किए बिना अच्छी तरह काम करेगा। +* आप अपलोड की गई file से metadata प्राप्त कर सकते हैं। +* इसमें [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) `async` interface है। +* यह एक वास्तविक Python [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) object expose करता है जिसे आप सीधे अन्य libraries को पास कर सकते हैं जो file-like object की अपेक्षा करती हैं। + +### `UploadFile` { #uploadfile } + +`UploadFile` में निम्नलिखित attributes होते हैं: + +* `filename`: मूल file name के साथ एक `str` जो अपलोड किया गया था (जैसे `myimage.jpg`)। +* `content_type`: content type (MIME type / media type) के साथ एक `str` (जैसे `image/jpeg`)। +* `file`: एक [`SpooledTemporaryFile`](https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile) (एक [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) object)। यह वास्तविक Python file object है जिसे आप सीधे अन्य functions या libraries को पास कर सकते हैं जो "file-like" object की अपेक्षा करती हैं। + +`UploadFile` में निम्नलिखित `async` methods होते हैं। ये सभी अंदर से संबंधित file methods को call करते हैं (internal `SpooledTemporaryFile` का उपयोग करके)। + +* `write(data)`: `data` (`str` या `bytes`) को file में लिखता है। +* `read(size)`: file के `size` (`int`) bytes/characters पढ़ता है। +* `seek(offset)`: file में byte position `offset` (`int`) पर जाता है। + * उदाहरण के लिए, `await myfile.seek(0)` file की शुरुआत पर जाएगा। + * यह विशेष रूप से तब उपयोगी है जब आप एक बार `await myfile.read()` चलाते हैं और फिर सामग्री को दोबारा पढ़ने की आवश्यकता होती है। +* `close()`: file को बंद करता है। + +क्योंकि ये सभी methods `async` methods हैं, आपको उन्हें "await" करना होगा। + +उदाहरण के लिए, एक `async` *path operation function* के अंदर आप सामग्री इस तरह प्राप्त कर सकते हैं: + +```Python +contents = await myfile.read() +``` + +यदि आप एक सामान्य `def` *path operation function* के अंदर हैं, तो आप सीधे `UploadFile.file` access कर सकते हैं, उदाहरण के लिए: + +```Python +contents = myfile.file.read() +``` + +/// note | `async` तकनीकी विवरण + +जब आप `async` methods का उपयोग करते हैं, तो **FastAPI** file methods को threadpool में चलाता है और उनके लिए await करता है। + +/// + +/// note | Starlette तकनीकी विवरण + +**FastAPI** का `UploadFile` सीधे **Starlette** के `UploadFile` से inherit करता है, लेकिन **Pydantic** और FastAPI के अन्य भागों के साथ इसे compatible बनाने के लिए कुछ आवश्यक हिस्से जोड़ता है। + +/// + +## "Form Data" क्या है { #what-is-form-data } + +HTML forms (`
`) सामान्यतः data को server पर भेजने के लिए उस data के लिए एक "special" encoding का उपयोग करते हैं, यह JSON से अलग होता है। + +**FastAPI** यह सुनिश्चित करेगा कि उस data को JSON के बजाय सही जगह से पढ़ा जाए। + +/// note | तकनीकी विवरण + +forms से data सामान्यतः "media type" `application/x-www-form-urlencoded` का उपयोग करके encoded होता है जब इसमें files शामिल नहीं होतीं। + +लेकिन जब form में files शामिल होती हैं, तो यह `multipart/form-data` के रूप में encoded होता है। यदि आप `File` का उपयोग करते हैं, तो **FastAPI** जान जाएगा कि उसे body के सही भाग से files प्राप्त करनी हैं। + +यदि आप इन encodings और form fields के बारे में अधिक पढ़ना चाहते हैं, तो [`POST` के लिए MDN web docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) पर जाएं। + +/// + +/// warning | चेतावनी + +आप एक *path operation* में कई `File` और `Form` parameters घोषित कर सकते हैं, लेकिन आप ऐसे `Body` fields भी घोषित नहीं कर सकते जिन्हें आप JSON के रूप में प्राप्त करने की अपेक्षा करते हैं, क्योंकि request में body `application/json` के बजाय `multipart/form-data` का उपयोग करके encoded होगी। + +यह **FastAPI** की limitation नहीं है, यह HTTP protocol का हिस्सा है। + +/// + +## Optional File Upload { #optional-file-upload } + +आप standard type annotations का उपयोग करके और default value `None` set करके file को optional बना सकते हैं: + +{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *} + +## अतिरिक्त Metadata के साथ `UploadFile` { #uploadfile-with-additional-metadata } + +आप `UploadFile` के साथ `File()` का भी उपयोग कर सकते हैं, उदाहरण के लिए, अतिरिक्त metadata set करने के लिए: + +{* ../../docs_src/request_files/tutorial001_03_an_py310.py hl[9,15] *} + +## Multiple File Uploads { #multiple-file-uploads } + +एक ही समय में कई files अपलोड करना संभव है। + +वे "form data" का उपयोग करके भेजे गए उसी "form field" से संबंधित होंगी। + +इसका उपयोग करने के लिए, `bytes` या `UploadFile` की list घोषित करें: + +{* ../../docs_src/request_files/tutorial002_an_py310.py hl[10,15] *} + +आपको, जैसा घोषित किया गया है, `bytes` या `UploadFile`s की एक `list` प्राप्त होगी। + +/// note | तकनीकी विवरण + +आप `from starlette.responses import HTMLResponse` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.responses` को `fastapi.responses` के रूप में प्रदान करता है। लेकिन उपलब्ध अधिकांश responses सीधे Starlette से आते हैं। + +/// + +### अतिरिक्त Metadata के साथ Multiple File Uploads { #multiple-file-uploads-with-additional-metadata } + +और पहले की तरह ही, आप अतिरिक्त parameters set करने के लिए `File()` का उपयोग कर सकते हैं, यहां तक कि `UploadFile` के लिए भी: + +{* ../../docs_src/request_files/tutorial003_an_py310.py hl[11,18:20] *} + +## Recap { #recap } + +request में अपलोड की जाने वाली files घोषित करने के लिए `File`, `bytes`, और `UploadFile` का उपयोग करें, जिन्हें form data के रूप में भेजा जाता है। diff --git a/docs/hi/docs/tutorial/request-form-models.md b/docs/hi/docs/tutorial/request-form-models.md new file mode 100644 index 000000000..98fc91ac8 --- /dev/null +++ b/docs/hi/docs/tutorial/request-form-models.md @@ -0,0 +1,78 @@ +# Form Models { #form-models } + +आप FastAPI में **form fields** declare करने के लिए **Pydantic models** का उपयोग कर सकते हैं। + +/// note | नोट + +forms का उपयोग करने के लिए, पहले [`python-multipart`](https://github.com/Kludex/python-multipart) install करें। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर इसे install करते हैं, उदाहरण के लिए: + +```console +$ pip install python-multipart +``` + +/// + +/// note | नोट + +यह FastAPI version `0.113.0` से supported है। 🤓 + +/// + +## Forms के लिए Pydantic Models { #pydantic-models-for-forms } + +आपको बस उन fields के साथ एक **Pydantic model** declare करना है जिन्हें आप **form fields** के रूप में receive करना चाहते हैं, और फिर parameter को `Form` के रूप में declare करना है: + +{* ../../docs_src/request_form_models/tutorial001_an_py310.py hl[9:11,15] *} + +**FastAPI** request में मौजूद **form data** से **हर field** के लिए data **extract** करेगा और आपको वह Pydantic model देगा जिसे आपने define किया है। + +## Docs जाँचें { #check-the-docs } + +आप इसे `/docs` पर docs UI में verify कर सकते हैं: + +
+ +
+ +## Extra Form Fields को मना करें { #forbid-extra-form-fields } + +कुछ खास use cases में (शायद बहुत आम नहीं), आप form fields को केवल उन तक **restrict** करना चाह सकते हैं जो Pydantic model में declare किए गए हैं। और किसी भी **extra** fields को **forbid** करना चाह सकते हैं। + +/// note | नोट + +यह FastAPI version `0.114.0` से supported है। 🤓 + +/// + +आप किसी भी `extra` fields को `forbid` करने के लिए Pydantic की model configuration का उपयोग कर सकते हैं: + +{* ../../docs_src/request_form_models/tutorial002_an_py310.py hl[12] *} + +अगर कोई client कुछ extra data भेजने की कोशिश करता है, तो उन्हें एक **error** response मिलेगा। + +उदाहरण के लिए, अगर client ये form fields भेजने की कोशिश करता है: + +* `username`: `Rick` +* `password`: `Portal Gun` +* `extra`: `Mr. Poopybutthole` + +तो उन्हें एक error response मिलेगा जो बताएगा कि field `extra` allowed नहीं है: + +```json +{ + "detail": [ + { + "type": "extra_forbidden", + "loc": ["body", "extra"], + "msg": "Extra inputs are not permitted", + "input": "Mr. Poopybutthole" + } + ] +} +``` + +## सारांश { #summary } + +आप FastAPI में form fields declare करने के लिए Pydantic models का उपयोग कर सकते हैं। 😎 diff --git a/docs/hi/docs/tutorial/request-forms-and-files.md b/docs/hi/docs/tutorial/request-forms-and-files.md new file mode 100644 index 000000000..c43edae0e --- /dev/null +++ b/docs/hi/docs/tutorial/request-forms-and-files.md @@ -0,0 +1,41 @@ +# Request Forms और Files { #request-forms-and-files } + +आप `File` और `Form` का उपयोग करके files और form fields को एक ही समय में define कर सकते हैं। + +/// note | नोट + +अपलोड की गई files और/या form data प्राप्त करने के लिए, पहले [`python-multipart`](https://github.com/Kludex/python-multipart) install करें। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाएँ, उसे activate करें, और फिर इसे install करें, उदाहरण के लिए: + +```console +$ pip install python-multipart +``` + +/// + +## `File` और `Form` Import करें { #import-file-and-form } + +{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[3] *} + +## `File` और `Form` parameters define करें { #define-file-and-form-parameters } + +file और form parameters उसी तरह बनाएँ जैसे आप `Body` या `Query` के लिए बनाते हैं: + +{* ../../docs_src/request_forms_and_files/tutorial001_an_py310.py hl[10:12] *} + +files और form fields, form data के रूप में अपलोड किए जाएँगे और आपको files और form fields प्राप्त होंगे। + +और आप कुछ files को `bytes` के रूप में और कुछ को `UploadFile` के रूप में declare कर सकते हैं। + +/// warning | चेतावनी + +आप एक *path operation* में कई `File` और `Form` parameters declare कर सकते हैं, लेकिन आप साथ ही ऐसे `Body` fields declare नहीं कर सकते जिन्हें आप JSON के रूप में प्राप्त करने की अपेक्षा करते हैं, क्योंकि request में body `application/json` के बजाय `multipart/form-data` का उपयोग करके encoded होगी। + +यह **FastAPI** की कोई सीमा नहीं है, यह HTTP protocol का हिस्सा है। + +/// + +## Recap { #recap } + +जब आपको एक ही request में data और files प्राप्त करने की आवश्यकता हो, तो `File` और `Form` को साथ में उपयोग करें। diff --git a/docs/hi/docs/tutorial/request-forms.md b/docs/hi/docs/tutorial/request-forms.md new file mode 100644 index 000000000..32488ec1e --- /dev/null +++ b/docs/hi/docs/tutorial/request-forms.md @@ -0,0 +1,73 @@ +# Form Data { #form-data } + +जब आपको JSON के बजाय form fields प्राप्त करने हों, तो आप `Form` का उपयोग कर सकते हैं। + +/// note | नोट + +forms का उपयोग करने के लिए, पहले [`python-multipart`](https://github.com/Kludex/python-multipart) install करें। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर इसे install करते हैं, उदाहरण के लिए: + +```console +$ pip install python-multipart +``` + +/// + +## `Form` Import करें { #import-form } + +`fastapi` से `Form` import करें: + +{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[3] *} + +## `Form` parameters परिभाषित करें { #define-form-parameters } + +form parameters उसी तरह बनाएं जैसे आप `Body` या `Query` के लिए बनाते: + +{* ../../docs_src/request_forms/tutorial001_an_py310.py hl[9] *} + +उदाहरण के लिए, OAuth2 specification का उपयोग जिन तरीकों से किया जा सकता है उनमें से एक में (जिसे "password flow" कहा जाता है) `username` और `password` को form fields के रूप में भेजना required है। + +spec के अनुसार fields के नाम बिल्कुल `username` और `password` होने चाहिए, और उन्हें JSON नहीं, बल्कि form fields के रूप में भेजा जाना चाहिए। + +`Form` के साथ आप वही configurations declare कर सकते हैं जो `Body` (और `Query`, `Path`, `Cookie`) के साथ करते हैं, जिसमें validation, examples, alias (जैसे `username` के बजाय `user-name`), आदि शामिल हैं। + +/// note | नोट + +`Form` एक class है जो सीधे `Body` से inherit करती है। + +/// + +/// tip | टिप + +form bodies declare करने के लिए, आपको स्पष्ट रूप से `Form` का उपयोग करना होगा, क्योंकि इसके बिना parameters को query parameters या body (JSON) parameters के रूप में समझा जाएगा। + +/// + +## "Form Fields" के बारे में { #about-form-fields } + +HTML forms (`
`) आमतौर पर data को server पर भेजने के लिए उस data के लिए एक "special" encoding का उपयोग करते हैं, यह JSON से अलग होता है। + +**FastAPI** यह सुनिश्चित करेगा कि उस data को JSON के बजाय सही जगह से पढ़ा जाए। + +/// note | तकनीकी विवरण + +forms से आने वाला data आमतौर पर "media type" `application/x-www-form-urlencoded` का उपयोग करके encoded होता है। + +लेकिन जब form में files शामिल होती हैं, तो इसे `multipart/form-data` के रूप में encoded किया जाता है। files को handle करने के बारे में आप अगले chapter में पढ़ेंगे। + +अगर आप इन encodings और form fields के बारे में अधिक पढ़ना चाहते हैं, तो [`POST` के लिए MDN web docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) देखें। + +/// + +/// warning | चेतावनी + +आप एक *path operation* में कई `Form` parameters declare कर सकते हैं, लेकिन आप साथ में ऐसे `Body` fields declare नहीं कर सकते जिन्हें आप JSON के रूप में प्राप्त करने की उम्मीद करते हैं, क्योंकि request में body `application/json` के बजाय `application/x-www-form-urlencoded` का उपयोग करके encoded होगी। + +यह **FastAPI** की limitation नहीं है, यह HTTP protocol का हिस्सा है। + +/// + +## Recap { #recap } + +form data input parameters declare करने के लिए `Form` का उपयोग करें। diff --git a/docs/hi/docs/tutorial/response-model.md b/docs/hi/docs/tutorial/response-model.md new file mode 100644 index 000000000..76f7ba4fc --- /dev/null +++ b/docs/hi/docs/tutorial/response-model.md @@ -0,0 +1,344 @@ +# Response Model - Return Type { #response-model-return-type } + +आप response के लिए उपयोग किए जाने वाले type को *path operation function* के **return type** को annotate करके declare कर सकते हैं। + +आप **type annotations** का उपयोग उसी तरह कर सकते हैं जैसे आप function **parameters** में input data के लिए करते हैं, आप Pydantic models, lists, dictionaries, scalar values जैसे integers, booleans, आदि का उपयोग कर सकते हैं। + +{* ../../docs_src/response_model/tutorial001_01_py310.py hl[16,21] *} + +FastAPI इस return type का उपयोग इनके लिए करेगा: + +* लौटाए गए data को **Validate** करना। + * अगर data invalid है (जैसे कि कोई field missing है), तो इसका मतलब है कि *आपके* app code में गड़बड़ी है, वह वह data return नहीं कर रहा जो उसे करना चाहिए, और यह incorrect data return करने के बजाय server error return करेगा। इस तरह आप और आपके clients सुनिश्चित हो सकते हैं कि उन्हें expected data और data shape मिलेगा। +* OpenAPI *path operation* में response के लिए एक **JSON Schema** जोड़ना। + * इसका उपयोग **automatic docs** द्वारा किया जाएगा। + * इसका उपयोग automatic client code generation tools द्वारा भी किया जाएगा। +* Pydantic का उपयोग करके लौटाए गए data को JSON में **Serialize** करना, जो **Rust** में लिखा गया है, इसलिए यह **बहुत तेज़** होगा। + +लेकिन सबसे महत्वपूर्ण: + +* यह output data को return type में defined data तक **limit और filter** करेगा। + * यह **security** के लिए विशेष रूप से महत्वपूर्ण है, हम नीचे इसका और अधिक देखेंगे। + +## `response_model` Parameter { #response-model-parameter } + +कुछ cases ऐसे होते हैं जहाँ आपको ऐसा data return करना होता है या आप ऐसा करना चाहते हैं जो type द्वारा declare किए गए data से बिल्कुल मेल नहीं खाता। + +उदाहरण के लिए, आप **dictionary return** करना या database object return करना चाह सकते हैं, लेकिन **उसे Pydantic model के रूप में declare** करना चाह सकते हैं। इस तरह Pydantic model आपके द्वारा लौटाए गए object (जैसे dictionary या database object) के लिए सभी data documentation, validation, आदि करेगा। + +अगर आपने return type annotation जोड़ा, तो tools और editors एक (सही) error के साथ शिकायत करेंगे कि आपका function ऐसा type (जैसे dict) return कर रहा है जो आपके द्वारा declare किए गए type (जैसे Pydantic model) से अलग है। + +ऐसे cases में, आप return type के बजाय *path operation decorator* parameter `response_model` का उपयोग कर सकते हैं। + +आप किसी भी *path operations* में `response_model` parameter का उपयोग कर सकते हैं: + +* `@app.get()` +* `@app.post()` +* `@app.put()` +* `@app.delete()` +* आदि। + +{* ../../docs_src/response_model/tutorial001_py310.py hl[17,22,24:27] *} + +/// note | नोट + +ध्यान दें कि `response_model` "decorator" method (`get`, `post`, आदि) का parameter है। यह आपके *path operation function* का parameter नहीं है, जैसे सभी parameters और body होते हैं। + +/// + +`response_model` वही type receive करता है जिसे आप Pydantic model field के लिए declare करेंगे, इसलिए यह Pydantic model हो सकता है, लेकिन यह, जैसे कि `List[Item]` की तरह Pydantic models की `list` भी हो सकता है। + +FastAPI इस `response_model` का उपयोग सभी data documentation, validation, आदि के लिए करेगा और output data को इसके type declaration में **convert और filter** भी करेगा। + +/// tip | सुझाव + +अगर आपके editor, mypy, आदि में strict type checks हैं, तो आप function return type को `Any` के रूप में declare कर सकते हैं। + +इस तरह आप editor को बताते हैं कि आप जानबूझकर कुछ भी return कर रहे हैं। लेकिन FastAPI फिर भी `response_model` के साथ data documentation, validation, filtering, आदि करेगा। + +/// + +### `response_model` Priority { #response-model-priority } + +अगर आप return type और `response_model` दोनों declare करते हैं, तो `response_model` को priority मिलेगी और FastAPI द्वारा इसका उपयोग किया जाएगा। + +इस तरह आप अपने functions में सही type annotations जोड़ सकते हैं, भले ही आप response model से अलग type return कर रहे हों, ताकि editor और mypy जैसे tools उनका उपयोग कर सकें। और फिर भी FastAPI `response_model` का उपयोग करके data validation, documentation, आदि कर सकता है। + +आप उस *path operation* के लिए response model बनाना disable करने के लिए `response_model=None` का भी उपयोग कर सकते हैं, आपको ऐसा तब करना पड़ सकता है जब आप उन चीज़ों के लिए type annotations जोड़ रहे हों जो valid Pydantic fields नहीं हैं, आप नीचे के sections में से एक में इसका उदाहरण देखेंगे। + +## वही input data return करें { #return-the-same-input-data } + +यहाँ हम एक `UserIn` model declare कर रहे हैं, इसमें plaintext password होगा: + +{* ../../docs_src/response_model/tutorial002_py310.py hl[7,9] *} + +/// note | नोट + +`EmailStr` का उपयोग करने के लिए, पहले [`email-validator`](https://github.com/JoshData/python-email-validator) install करें। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर इसे install करते हैं, उदाहरण के लिए: + +```console +$ pip install email-validator +``` + +या इसके साथ: + +```console +$ pip install "pydantic[email]" +``` + +/// + +और हम इस model का उपयोग अपने input को declare करने और उसी model का उपयोग अपने output को declare करने के लिए कर रहे हैं: + +{* ../../docs_src/response_model/tutorial002_py310.py hl[16] *} + +अब, जब भी कोई browser password के साथ user बना रहा होगा, API response में वही password return करेगी। + +इस case में, यह problem नहीं हो सकती, क्योंकि password भेजने वाला वही user है। + +लेकिन अगर हम उसी model का उपयोग किसी और *path operation* के लिए करते हैं, तो हम अपने user के passwords हर client को भेज सकते हैं। + +/// danger | खतरा + +कभी भी किसी user का plain password store न करें या उसे इस तरह response में न भेजें, जब तक कि आप सभी caveats नहीं जानते और यह नहीं जानते कि आप क्या कर रहे हैं। + +/// + +## Output model जोड़ें { #add-an-output-model } + +इसके बजाय हम plaintext password के साथ एक input model और उसके बिना एक output model बना सकते हैं: + +{* ../../docs_src/response_model/tutorial003_py310.py hl[9,11,16] *} + +यहाँ, भले ही हमारा *path operation function* वही input user return कर रहा है जिसमें password शामिल है: + +{* ../../docs_src/response_model/tutorial003_py310.py hl[24] *} + +...हमने `response_model` को अपना model `UserOut` declare किया है, जिसमें password शामिल नहीं है: + +{* ../../docs_src/response_model/tutorial003_py310.py hl[22] *} + +इसलिए, **FastAPI** output model में declare न किए गए सभी data को filter out करने का ध्यान रखेगा (Pydantic का उपयोग करके)। + +### `response_model` या Return Type { #response-model-or-return-type } + +इस case में, क्योंकि दोनों models अलग हैं, अगर हमने function return type को `UserOut` के रूप में annotate किया, तो editor और tools शिकायत करेंगे कि हम invalid type return कर रहे हैं, क्योंकि वे अलग classes हैं। + +इसीलिए इस उदाहरण में हमें इसे `response_model` parameter में declare करना पड़ता है। + +...लेकिन इसे कैसे overcome किया जाए, यह देखने के लिए नीचे पढ़ना जारी रखें। + +## Return Type और Data Filtering { #return-type-and-data-filtering } + +आइए पिछले उदाहरण से आगे बढ़ते हैं। हम **function को एक type के साथ annotate** करना चाहते थे, लेकिन हम function से ऐसा कुछ return कर पाना चाहते थे जिसमें वास्तव में **अधिक data** शामिल हो। + +हम चाहते हैं कि FastAPI response model का उपयोग करके data को **filter** करता रहे। ताकि भले ही function अधिक data return करे, response में केवल वही fields शामिल हों जो response model में declare किए गए हैं। + +पिछले उदाहरण में, क्योंकि classes अलग थीं, हमें `response_model` parameter का उपयोग करना पड़ा। लेकिन इसका मतलब यह भी है कि हमें function return type check करने वाले editor और tools से support नहीं मिलता। + +लेकिन अधिकतर cases में जहाँ हमें ऐसा कुछ करना होता है, हम चाहते हैं कि model बस इस उदाहरण की तरह कुछ data को **filter/remove** करे। + +और उन cases में, हम classes और inheritance का उपयोग करके function **type annotations** का लाभ उठा सकते हैं ताकि editor और tools में बेहतर support मिले, और फिर भी FastAPI **data filtering** मिल सके। + +{* ../../docs_src/response_model/tutorial003_01_py310.py hl[7:10,13:14,18] *} + +इसके साथ, हमें editors और mypy से tooling support मिलता है क्योंकि यह code types के संदर्भ में सही है, लेकिन हमें FastAPI से data filtering भी मिलती है। + +यह कैसे काम करता है? आइए इसे देखें। 🤓 + +### Type Annotations और Tooling { #type-annotations-and-tooling } + +पहले देखते हैं कि editors, mypy और अन्य tools इसे कैसे देखेंगे। + +`BaseUser` में base fields हैं। फिर `UserIn`, `BaseUser` से inherit करता है और `password` field जोड़ता है, इसलिए इसमें दोनों models के सभी fields शामिल होंगे। + +हम function return type को `BaseUser` के रूप में annotate करते हैं, लेकिन वास्तव में हम `UserIn` instance return कर रहे हैं। + +Editor, mypy, और अन्य tools इस पर शिकायत नहीं करेंगे क्योंकि typing terms में, `UserIn`, `BaseUser` का subclass है, जिसका मतलब है कि जब expected कुछ भी `BaseUser` हो, तो यह एक *valid* type है। + +### FastAPI Data Filtering { #fastapi-data-filtering } + +अब, FastAPI के लिए, यह return type देखेगा और सुनिश्चित करेगा कि आप जो return करते हैं उसमें **केवल** वही fields शामिल हों जो type में declare किए गए हैं। + +FastAPI internally Pydantic के साथ कई चीज़ें करता है ताकि यह सुनिश्चित हो सके कि class inheritance के वही rules returned data filtering के लिए उपयोग न किए जाएँ, नहीं तो आप expected से कहीं अधिक data return कर सकते हैं। + +इस तरह, आप दोनों दुनिया का best पा सकते हैं: **tooling support** के साथ type annotations और **data filtering**। + +## इसे docs में देखें { #see-it-in-the-docs } + +जब आप automatic docs देखते हैं, तो आप check कर सकते हैं कि input model और output model दोनों का अपना JSON Schema होगा: + + + +और दोनों models interactive API documentation के लिए उपयोग किए जाएँगे: + + + +## अन्य Return Type Annotations { #other-return-type-annotations } + +ऐसे cases हो सकते हैं जहाँ आप कुछ ऐसा return करते हैं जो valid Pydantic field नहीं है और आप उसे function में annotate करते हैं, केवल tooling (editor, mypy, आदि) द्वारा दिए गए support को पाने के लिए। + +### सीधे Response Return करें { #return-a-response-directly } + +सबसे common case होगा [advanced docs में बाद में समझाए अनुसार सीधे Response return करना](../advanced/response-directly.md)। + +{* ../../docs_src/response_model/tutorial003_02_py310.py hl[8,10:11] *} + +यह simple case FastAPI द्वारा automatically handle किया जाता है क्योंकि return type annotation `Response` class (या उसका subclass) है। + +और tools भी खुश होंगे क्योंकि `RedirectResponse` और `JSONResponse` दोनों `Response` के subclasses हैं, इसलिए type annotation सही है। + +### Response Subclass Annotate करें { #annotate-a-response-subclass } + +आप type annotation में `Response` के subclass का भी उपयोग कर सकते हैं: + +{* ../../docs_src/response_model/tutorial003_03_py310.py hl[8:9] *} + +यह भी काम करेगा क्योंकि `RedirectResponse`, `Response` का subclass है, और FastAPI इस simple case को automatically handle करेगा। + +### Invalid Return Type Annotations { #invalid-return-type-annotations } + +लेकिन जब आप कोई अन्य arbitrary object return करते हैं जो valid Pydantic type नहीं है (जैसे database object) और आप उसे function में उसी तरह annotate करते हैं, तो FastAPI उस type annotation से Pydantic response model बनाने की कोशिश करेगा, और fail हो जाएगा। + +ऐसा ही होगा अगर आपके पास अलग-अलग types के बीच union जैसा कुछ हो जहाँ उनमें से एक या अधिक valid Pydantic types नहीं हैं, उदाहरण के लिए यह fail होगा 💥: + +{* ../../docs_src/response_model/tutorial003_04_py310.py hl[8] *} + +...यह fail होता है क्योंकि type annotation Pydantic type नहीं है और केवल एक single `Response` class या subclass भी नहीं है, यह `Response` और `dict` के बीच union (दोनों में से कोई भी) है। + +### Response Model Disable करें { #disable-response-model } + +ऊपर दिए गए उदाहरण से आगे बढ़ते हुए, आप शायद default data validation, documentation, filtering, आदि नहीं चाहते हों जो FastAPI द्वारा किया जाता है। + +लेकिन आप शायद function में return type annotation फिर भी रखना चाहते हों ताकि editors और type checkers (जैसे mypy) जैसे tools से support मिल सके। + +इस case में, आप `response_model=None` set करके response model generation disable कर सकते हैं: + +{* ../../docs_src/response_model/tutorial003_05_py310.py hl[7] *} + +इससे FastAPI response model generation skip कर देगा और इस तरह आप अपनी जरूरत के किसी भी return type annotations का उपयोग कर सकते हैं, बिना इसके कि वह आपकी FastAPI application को प्रभावित करे। 🤓 + +## Response Model encoding parameters { #response-model-encoding-parameters } + +आपके response model में default values हो सकते हैं, जैसे: + +{* ../../docs_src/response_model/tutorial004_py310.py hl[9,11:12] *} + +* `description: Union[str, None] = None` (या Python 3.10 में `str | None = None`) का default `None` है। +* `tax: float = 10.5` का default `10.5` है। +* `tags: List[str] = []` का default खाली list है: `[]`। + +लेकिन अगर वे वास्तव में store नहीं किए गए थे तो आप उन्हें result से omit करना चाह सकते हैं। + +उदाहरण के लिए, अगर आपके पास NoSQL database में कई optional attributes वाले models हैं, लेकिन आप default values से भरे बहुत लंबे JSON responses नहीं भेजना चाहते। + +### `response_model_exclude_unset` parameter का उपयोग करें { #use-the-response-model-exclude-unset-parameter } + +आप *path operation decorator* parameter `response_model_exclude_unset=True` set कर सकते हैं: + +{* ../../docs_src/response_model/tutorial004_py310.py hl[22] *} + +और वे default values response में शामिल नहीं होंगे, केवल वास्तव में set किए गए values ही शामिल होंगे। + +तो, अगर आप ID `foo` वाले item के लिए उस *path operation* को request भेजते हैं, तो response (default values शामिल किए बिना) होगा: + +```JSON +{ + "name": "Foo", + "price": 50.2 +} +``` + +/// note | नोट + +आप इसका भी उपयोग कर सकते हैं: + +* `response_model_exclude_defaults=True` +* `response_model_exclude_none=True` + +जैसा कि `exclude_defaults` और `exclude_none` के लिए [Pydantic docs](https://docs.pydantic.dev/1.10/usage/exporting_models/#modeldict) में बताया गया है। + +/// + +#### Defaults वाले fields के लिए values वाला data { #data-with-values-for-fields-with-defaults } + +लेकिन अगर आपके data में model के default values वाले fields के लिए values हैं, जैसे ID `bar` वाला item: + +```Python hl_lines="3 5" +{ + "name": "Bar", + "description": "The bartenders", + "price": 62, + "tax": 20.2 +} +``` + +तो वे response में शामिल होंगे। + +#### Defaults जैसे ही values वाला data { #data-with-the-same-values-as-the-defaults } + +अगर data में default values जैसे ही values हैं, जैसे ID `baz` वाला item: + +```Python hl_lines="3 5-6" +{ + "name": "Baz", + "description": None, + "price": 50.2, + "tax": 10.5, + "tags": [] +} +``` + +FastAPI इतना smart है (दरअसल, Pydantic इतना smart है) कि यह समझ सके कि, भले ही `description`, `tax`, और `tags` के values defaults जैसे ही हैं, उन्हें explicitly set किया गया था (defaults से लिए जाने के बजाय)। + +इसलिए, वे JSON response में शामिल होंगे। + +/// tip | सुझाव + +ध्यान दें कि default values कुछ भी हो सकते हैं, केवल `None` नहीं। + +वे list (`[]`), `10.5` का `float`, आदि हो सकते हैं। + +/// + +### `response_model_include` और `response_model_exclude` { #response-model-include-and-response-model-exclude } + +आप *path operation decorator* parameters `response_model_include` और `response_model_exclude` का भी उपयोग कर सकते हैं। + +वे include करने के लिए attributes के नामों वाला `str` का `set` लेते हैं (बाकी को omit करते हुए) या exclude करने के लिए (बाकी को include करते हुए)। + +अगर आपके पास केवल एक Pydantic model है और आप output से कुछ data remove करना चाहते हैं, तो इसे quick shortcut के रूप में उपयोग किया जा सकता है। + +/// tip | सुझाव + +लेकिन फिर भी इन parameters के बजाय, multiple classes का उपयोग करते हुए, ऊपर दिए गए ideas का उपयोग करने की recommendation है। + +ऐसा इसलिए है क्योंकि आपके app के OpenAPI (और docs) में generated JSON Schema फिर भी complete model के लिए ही होगा, भले ही आप कुछ attributes omit करने के लिए `response_model_include` या `response_model_exclude` का उपयोग करें। + +यह `response_model_by_alias` पर भी लागू होता है जो इसी तरह काम करता है। + +/// + +{* ../../docs_src/response_model/tutorial005_py310.py hl[29,35] *} + +/// tip | सुझाव + +Syntax `{"name", "description"}` उन दो values के साथ एक `set` बनाता है। + +यह `set(["name", "description"])` के equivalent है। + +/// + +#### `set`s के बजाय `list`s का उपयोग करना { #using-lists-instead-of-sets } + +अगर आप `set` का उपयोग करना भूल जाते हैं और इसके बजाय `list` या `tuple` का उपयोग करते हैं, तो FastAPI फिर भी उसे `set` में convert कर देगा और यह सही तरह काम करेगा: + +{* ../../docs_src/response_model/tutorial006_py310.py hl[29,35] *} + +## Recap { #recap } + +Response models define करने और खासकर private data को filter out करना सुनिश्चित करने के लिए *path operation decorator* के parameter `response_model` का उपयोग करें। + +केवल explicitly set किए गए values return करने के लिए `response_model_exclude_unset` का उपयोग करें। diff --git a/docs/hi/docs/tutorial/response-status-code.md b/docs/hi/docs/tutorial/response-status-code.md new file mode 100644 index 000000000..b11bdb0ec --- /dev/null +++ b/docs/hi/docs/tutorial/response-status-code.md @@ -0,0 +1,101 @@ +# Response Status Code { #response-status-code } + +जिस तरह आप response model specify कर सकते हैं, उसी तरह आप किसी भी *path operations* में parameter `status_code` के साथ response के लिए इस्तेमाल किया जाने वाला HTTP status code भी declare कर सकते हैं: + +* `@app.get()` +* `@app.post()` +* `@app.put()` +* `@app.delete()` +* आदि। + +{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *} + +/// note | नोट + +ध्यान दें कि `status_code`, "decorator" method (`get`, `post`, आदि) का parameter है। यह आपके *path operation function* का parameter नहीं है, जैसे बाकी सभी parameters और body होते हैं। + +/// + +`status_code` parameter HTTP status code वाला एक number receive करता है। + +/// note | नोट + +`status_code` वैकल्पिक रूप से एक `IntEnum` भी receive कर सकता है, जैसे Python का [`http.HTTPStatus`](https://docs.python.org/3/library/http.html#http.HTTPStatus). + +/// + +यह: + +* response में वह status code return करेगा। +* उसे OpenAPI schema में उसी तरह document करेगा (और इसलिए, user interfaces में भी): + + + +/// note | नोट + +कुछ response codes (अगला section देखें) यह indicate करते हैं कि response में body नहीं होती। + +FastAPI यह जानता है, और ऐसे OpenAPI docs बनाएगा जो बताते हैं कि कोई response body नहीं है। + +/// + +## HTTP status codes के बारे में { #about-http-status-codes } + +/// note | नोट + +अगर आप पहले से जानते हैं कि HTTP status codes क्या होते हैं, तो अगले section पर जाएँ। + +/// + +HTTP में, आप response के हिस्से के रूप में 3 digits का एक numeric status code भेजते हैं। + +इन status codes के साथ एक associated name होता है जिससे उन्हें पहचानने में मदद मिलती है, लेकिन महत्वपूर्ण हिस्सा number होता है। + +संक्षेप में: + +* `100 - 199` "Information" के लिए होते हैं। आप इन्हें सीधे बहुत कम इस्तेमाल करते हैं। इन status codes वाले responses में body नहीं हो सकती। +* **`200 - 299`** "Successful" responses के लिए होते हैं। ये वे हैं जिन्हें आप सबसे ज़्यादा इस्तेमाल करेंगे। + * `200` default status code है, जिसका मतलब है कि सब कुछ "OK" था। + * एक और उदाहरण `201`, "Created" होगा। इसे आमतौर पर database में नया record बनाने के बाद इस्तेमाल किया जाता है। + * एक विशेष case `204`, "No Content" है। यह response तब इस्तेमाल होता है जब client को return करने के लिए कोई content नहीं होता, और इसलिए response में body नहीं होनी चाहिए। +* **`300 - 399`** "Redirection" के लिए होते हैं। इन status codes वाले responses में body हो भी सकती है और नहीं भी, सिवाय `304`, "Not Modified" के, जिसमें body नहीं होनी चाहिए। +* **`400 - 499`** "Client error" responses के लिए होते हैं। ये दूसरा type है जिसे आप शायद सबसे ज़्यादा इस्तेमाल करेंगे। + * एक उदाहरण `404` है, "Not Found" response के लिए। + * client से आने वाली generic errors के लिए, आप सिर्फ़ `400` इस्तेमाल कर सकते हैं। +* `500 - 599` server errors के लिए होते हैं। आप इन्हें लगभग कभी सीधे इस्तेमाल नहीं करते। जब आपके application code या server के किसी हिस्से में कुछ गड़बड़ होती है, तो यह अपने-आप इन status codes में से एक return करेगा। + +/// tip | सुझाव + +हर status code के बारे में और कौन-सा code किसके लिए है, यह जानने के लिए [HTTP status codes के बारे में MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) देखें। + +/// + +## नाम याद रखने का shortcut { #shortcut-to-remember-the-names } + +आइए पिछले example को फिर से देखें: + +{* ../../docs_src/response_status_code/tutorial001_py310.py hl[6] *} + +`201` "Created" के लिए status code है। + +लेकिन आपको यह याद रखने की ज़रूरत नहीं है कि इनमें से हर code का क्या मतलब है। + +आप `fastapi.status` से convenience variables इस्तेमाल कर सकते हैं। + +{* ../../docs_src/response_status_code/tutorial002_py310.py hl[1,6] *} + +वे सिर्फ़ एक सुविधा हैं, उनमें वही number होता है, लेकिन इस तरह आप उन्हें खोजने के लिए editor के autocomplete का इस्तेमाल कर सकते हैं: + + + +/// note | तकनीकी विवरण + +आप `from starlette import status` भी इस्तेमाल कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, वही `starlette.status` `fastapi.status` के रूप में provide करता है। लेकिन यह सीधे Starlette से आता है। + +/// + +## default बदलना { #changing-the-default } + +बाद में, [Advanced User Guide](../advanced/response-change-status-code.md) में, आप देखेंगे कि यहाँ declare किए जा रहे default से अलग status code कैसे return किया जाता है। diff --git a/docs/hi/docs/tutorial/schema-extra-example.md b/docs/hi/docs/tutorial/schema-extra-example.md new file mode 100644 index 000000000..dc8b29f58 --- /dev/null +++ b/docs/hi/docs/tutorial/schema-extra-example.md @@ -0,0 +1,202 @@ +# Request Example Data घोषित करें { #declare-request-example-data } + +आप उस data के examples घोषित कर सकते हैं जिसे आपका app receive कर सकता है। + +इसे करने के कई तरीके यहाँ दिए गए हैं। + +## Pydantic models में अतिरिक्त JSON Schema data { #extra-json-schema-data-in-pydantic-models } + +आप किसी Pydantic model के लिए `examples` घोषित कर सकते हैं, जिन्हें generated JSON Schema में जोड़ा जाएगा। + +{* ../../docs_src/schema_extra_example/tutorial001_py310.py hl[13:24] *} + +वह अतिरिक्त जानकारी उस model के output **JSON Schema** में जैसी है वैसी ही जोड़ी जाएगी, और API docs में उपयोग की जाएगी। + +आप `model_config` attribute का उपयोग कर सकते हैं, जो एक `dict` लेता है, जैसा कि [Pydantic के docs: Configuration](https://docs.pydantic.dev/latest/api/config/) में बताया गया है। + +आप `"json_schema_extra"` को एक `dict` के साथ set कर सकते हैं जिसमें कोई भी अतिरिक्त data हो जिसे आप generated JSON Schema में दिखाना चाहते हैं, जिसमें `examples` भी शामिल हैं। + +/// tip | सुझाव + +आप इसी technique का उपयोग JSON Schema को extend करने और अपनी custom अतिरिक्त जानकारी जोड़ने के लिए कर सकते हैं। + +उदाहरण के लिए, आप इसका उपयोग frontend user interface आदि के लिए metadata जोड़ने में कर सकते हैं। + +/// + +/// note | नोट + +OpenAPI 3.1.0 (FastAPI 0.99.0 से उपयोग किया गया) ने `examples` के लिए support जोड़ा, जो **JSON Schema** standard का हिस्सा है। + +उससे पहले, यह केवल keyword `example` को एक single example के साथ support करता था। यह अभी भी OpenAPI 3.1.0 द्वारा supported है, लेकिन deprecated है और JSON Schema standard का हिस्सा नहीं है। इसलिए आपको `example` से `examples` पर migrate करने के लिए प्रोत्साहित किया जाता है। 🤓 + +आप इस page के अंत में और पढ़ सकते हैं। + +/// + +## `Field` के अतिरिक्त arguments { #field-additional-arguments } + +Pydantic models के साथ `Field()` का उपयोग करते समय, आप अतिरिक्त `examples` भी घोषित कर सकते हैं: + +{* ../../docs_src/schema_extra_example/tutorial002_py310.py hl[2,8:11] *} + +## JSON Schema - OpenAPI में `examples` { #examples-in-json-schema-openapi } + +इनमें से किसी का भी उपयोग करते समय: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +आप अतिरिक्त जानकारी के साथ `examples` का एक group भी घोषित कर सकते हैं, जिसे **OpenAPI** के अंदर उनके **JSON Schemas** में जोड़ा जाएगा। + +### `examples` के साथ `Body` { #body-with-examples } + +यहाँ हम `Body()` में अपेक्षित data के एक example वाला `examples` pass करते हैं: + +{* ../../docs_src/schema_extra_example/tutorial003_an_py310.py hl[22:29] *} + +### docs UI में Example { #example-in-the-docs-ui } + +ऊपर दिए गए किसी भी method के साथ यह `/docs` में इस तरह दिखेगा: + + + +### कई `examples` के साथ `Body` { #body-with-multiple-examples } + +बेशक आप कई `examples` भी pass कर सकते हैं: + +{* ../../docs_src/schema_extra_example/tutorial004_an_py310.py hl[23:38] *} + +जब आप ऐसा करते हैं, तो examples उस body data के internal **JSON Schema** का हिस्सा होंगे। + +फिर भी, यह लिखते समय, Swagger UI, वह tool जो docs UI दिखाने के लिए जिम्मेदार है, **JSON Schema** में data के लिए कई examples दिखाने को support नहीं करता। लेकिन workaround के लिए नीचे पढ़ें। + +### OpenAPI-specific `examples` { #openapi-specific-examples } + +**JSON Schema** द्वारा `examples` support किए जाने से पहले से ही, OpenAPI में एक अलग field के लिए support था जिसे `examples` भी कहा जाता था। + +यह **OpenAPI-specific** `examples` OpenAPI specification में किसी अन्य section में जाता है। यह प्रत्येक JSON Schema के अंदर नहीं, बल्कि **प्रत्येक *path operation* के details** में जाता है। + +और Swagger UI ने इस विशेष `examples` field को कुछ समय से support किया है। इसलिए, आप इसका उपयोग docs UI में अलग-अलग **examples दिखाने** के लिए कर सकते हैं। + +इस OpenAPI-specific field `examples` का आकार एक `dict` है जिसमें **कई examples** होते हैं (`list` के बजाय), और प्रत्येक में अतिरिक्त जानकारी होती है जो **OpenAPI** में भी जोड़ी जाएगी। + +यह OpenAPI में मौजूद प्रत्येक JSON Schema के अंदर नहीं जाता, यह बाहर, सीधे *path operation* में जाता है। + +### `openapi_examples` Parameter का उपयोग { #using-the-openapi-examples-parameter } + +आप FastAPI में OpenAPI-specific `examples` को parameter `openapi_examples` के साथ इनके लिए घोषित कर सकते हैं: + +* `Path()` +* `Query()` +* `Header()` +* `Cookie()` +* `Body()` +* `Form()` +* `File()` + +`dict` की keys प्रत्येक example की पहचान करती हैं, और प्रत्येक value एक और `dict` होती है। + +`examples` में प्रत्येक specific example `dict` में ये हो सकते हैं: + +* `summary`: example के लिए छोटा description। +* `description`: एक लंबा description जिसमें Markdown text हो सकता है। +* `value`: यह दिखाया गया वास्तविक example है, जैसे एक `dict`। +* `externalValue`: `value` का alternative, example की ओर point करने वाला URL। हालांकि यह शायद `value` जितने tools द्वारा supported न हो। + +आप इसे इस तरह use कर सकते हैं: + +{* ../../docs_src/schema_extra_example/tutorial005_an_py310.py hl[23:49] *} + +### Docs UI में OpenAPI Examples { #openapi-examples-in-the-docs-ui } + +`Body()` में `openapi_examples` जोड़ने के साथ `/docs` इस तरह दिखेगा: + + + +## तकनीकी विवरण { #technical-details } + +/// tip | सुझाव + +यदि आप पहले से ही **FastAPI** version **0.99.0 या उससे ऊपर** का उपयोग कर रहे हैं, तो आप शायद ये details **skip** कर सकते हैं। + +ये पुराने versions के लिए अधिक relevant हैं, OpenAPI 3.1.0 उपलब्ध होने से पहले। + +आप इसे एक संक्षिप्त OpenAPI और JSON Schema **history lesson** मान सकते हैं। 🤓 + +/// + +/// warning | चेतावनी + +ये standards **JSON Schema** और **OpenAPI** के बारे में बहुत technical details हैं। + +यदि ऊपर दिए गए ideas आपके लिए पहले से ही काम कर रहे हैं, तो वह पर्याप्त हो सकता है, और शायद आपको इन details की जरूरत नहीं है, इन्हें skip करने के लिए स्वतंत्र महसूस करें। + +/// + +OpenAPI 3.1.0 से पहले, OpenAPI ने **JSON Schema** के एक पुराने और modified version का उपयोग किया। + +JSON Schema में `examples` नहीं था, इसलिए OpenAPI ने अपने स्वयं के modified version में अपना `example` field जोड़ा। + +OpenAPI ने specification के अन्य हिस्सों में भी `example` और `examples` fields जोड़े: + +* [`Parameter Object` (specification में)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameter-object) जिसका उपयोग FastAPI के इनसे किया गया: + * `Path()` + * `Query()` + * `Header()` + * `Cookie()` +* [`Request Body Object`, field `content` में, `Media Type Object` पर (specification में)](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#media-type-object) जिसका उपयोग FastAPI के इनसे किया गया: + * `Body()` + * `File()` + * `Form()` + +/// note | नोट + +यह पुराना OpenAPI-specific `examples` parameter अब FastAPI `0.103.0` से `openapi_examples` है। + +/// + +### JSON Schema का `examples` field { #json-schemas-examples-field } + +लेकिन फिर JSON Schema ने specification के एक नए version में एक [`examples`](https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9.5) field जोड़ा। + +और फिर नया OpenAPI 3.1.0 latest version (JSON Schema 2020-12) पर आधारित था, जिसमें यह नया field `examples` शामिल था। + +और अब यह नया `examples` field पुराने single (और custom) `example` field पर precedence लेता है, जो अब deprecated है। + +JSON Schema में यह नया `examples` field OpenAPI में अन्य जगहों (ऊपर वर्णित) की तरह अतिरिक्त metadata वाला dict नहीं है, यह **सिर्फ एक `list`** है। + +/// note | नोट + +OpenAPI 3.1.0 के JSON Schema के साथ इस नए सरल integration के साथ release होने के बाद भी, कुछ समय तक, Swagger UI, वह tool जो automatic docs प्रदान करता है, OpenAPI 3.1.0 को support नहीं करता था (यह version 5.0.0 से करता है 🎉)। + +इसी वजह से, 0.99.0 से पहले के FastAPI versions अभी भी OpenAPI के 3.1.0 से कम versions का उपयोग करते थे। + +/// + +### Pydantic और FastAPI `examples` { #pydantic-and-fastapi-examples } + +जब आप Pydantic model के अंदर `examples` जोड़ते हैं, `schema_extra` या `Field(examples=["something"])` का उपयोग करके, तो वह example उस Pydantic model के **JSON Schema** में जोड़ा जाता है। + +और उस Pydantic model का **JSON Schema** आपकी API के **OpenAPI** में शामिल होता है, और फिर docs UI में उपयोग किया जाता है। + +FastAPI के 0.99.0 से पहले के versions में (0.99.0 और ऊपर वाले नए OpenAPI 3.1.0 का उपयोग करते हैं), जब आप किसी भी अन्य utilities (`Query()`, `Body()`, आदि) के साथ `example` या `examples` का उपयोग करते थे, तो वे examples उस data का वर्णन करने वाले JSON Schema में नहीं जोड़े जाते थे (OpenAPI के JSON Schema के अपने version में भी नहीं), वे सीधे OpenAPI में *path operation* declaration में जोड़े जाते थे (OpenAPI के उन parts के बाहर जो JSON Schema का उपयोग करते हैं)। + +लेकिन अब जबकि FastAPI 0.99.0 और ऊपर OpenAPI 3.1.0 का उपयोग करता है, जो JSON Schema 2020-12 का उपयोग करता है, और Swagger UI 5.0.0 और ऊपर, सब कुछ अधिक consistent है और examples JSON Schema में शामिल होते हैं। + +### Swagger UI और OpenAPI-specific `examples` { #swagger-ui-and-openapi-specific-examples } + +अब, क्योंकि Swagger UI कई JSON Schema examples को support नहीं करता था (2023-08-26 तक), users के पास docs में कई examples दिखाने का कोई तरीका नहीं था। + +इसे solve करने के लिए, FastAPI `0.103.0` ने नए parameter `openapi_examples` के साथ उसी पुराने **OpenAPI-specific** `examples` field को घोषित करने के लिए **support जोड़ा**। 🤓 + +### Summary { #summary } + +मैं कहा करता था कि मुझे history उतनी पसंद नहीं है... और अब मुझे देखिए, "tech history" lessons दे रहा हूँ। 😅 + +संक्षेप में, **FastAPI 0.99.0 या उससे ऊपर upgrade करें**, और चीजें बहुत अधिक **सरल, consistent, और intuitive** हैं, और आपको ये सारे historical details जानने की जरूरत नहीं है। 😎 diff --git a/docs/hi/docs/tutorial/security/first-steps.md b/docs/hi/docs/tutorial/security/first-steps.md new file mode 100644 index 000000000..a7bf2e700 --- /dev/null +++ b/docs/hi/docs/tutorial/security/first-steps.md @@ -0,0 +1,203 @@ +# सुरक्षा - पहले कदम { #security-first-steps } + +मान लें कि आपका **backend** API किसी domain में है। + +और आपका **frontend** किसी दूसरे domain में है या उसी domain के किसी अलग path में है (या किसी mobile application में)। + +और आप चाहते हैं कि frontend, **username** और **password** का उपयोग करके backend के साथ authenticate कर सके। + +हम इसे **FastAPI** के साथ बनाने के लिए **OAuth2** का उपयोग कर सकते हैं। + +लेकिन आपको केवल वे छोटी-छोटी जानकारियाँ खोजने के लिए पूरी लंबी specification पढ़ने में समय न लगाना पड़े। + +आइए सुरक्षा संभालने के लिए **FastAPI** द्वारा दिए गए tools का उपयोग करें। + +## यह कैसा दिखता है { #how-it-looks } + +आइए पहले बस code का उपयोग करें और देखें कि यह कैसे काम करता है, और फिर हम वापस आकर समझेंगे कि क्या हो रहा है। + +## `main.py` बनाएँ { #create-main-py } + +उदाहरण को एक file `main.py` में copy करें: + +{* ../../docs_src/security/tutorial001_an_py310.py *} + +## इसे चलाएँ { #run-it } + +/// note | नोट + +[`python-multipart`](https://github.com/Kludex/python-multipart) package **FastAPI** के साथ अपने-आप install हो जाता है जब आप `pip install "fastapi[standard]"` command चलाते हैं। + +हालाँकि, अगर आप `pip install fastapi` command का उपयोग करते हैं, तो `python-multipart` package default रूप से शामिल नहीं होता। + +इसे manually install करने के लिए, सुनिश्चित करें कि आप एक [virtual environment](../../virtual-environments.md) बनाएँ, उसे activate करें, और फिर इसे इस तरह install करें: + +```console +$ pip install python-multipart +``` + +ऐसा इसलिए है क्योंकि **OAuth2**, `username` और `password` भेजने के लिए "form data" का उपयोग करता है। + +/// + +उदाहरण को इस तरह चलाएँ: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +## इसे जाँचें { #check-it } + +Interactive docs पर जाएँ: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs). + +आपको कुछ ऐसा दिखाई देगा: + + + +/// tip | Authorize बटन! + +आपके पास पहले से ही एक चमकदार नया "Authorize" बटन है। + +और आपकी *path operation* के ऊपर-दाएँ कोने में एक छोटा-सा lock है जिस पर आप click कर सकते हैं। + +/// + +और अगर आप उस पर click करते हैं, तो आपके पास `username` और `password` (और अन्य optional fields) type करने के लिए एक छोटा authorization form होगा: + + + +/// note | नोट + +आप form में क्या type करते हैं, इससे कोई फर्क नहीं पड़ता, यह अभी काम नहीं करेगा। लेकिन हम वहाँ तक पहुँचेंगे। + +/// + +यह निश्चित रूप से final users के लिए frontend नहीं है, लेकिन यह आपके पूरे API को interactively document करने के लिए एक शानदार automatic tool है। + +इसे frontend team द्वारा उपयोग किया जा सकता है (जो आप खुद भी हो सकते हैं)। + +इसे third party applications और systems द्वारा उपयोग किया जा सकता है। + +और इसे आप खुद भी उसी application को debug, check और test करने के लिए उपयोग कर सकते हैं। + +## `password` flow { #the-password-flow } + +अब थोड़ा पीछे चलते हैं और समझते हैं कि यह सब क्या है। + +`password` "flow", OAuth2 में परिभाषित उन तरीकों ("flows") में से एक है, जिनका उपयोग security और authentication संभालने के लिए किया जाता है। + +OAuth2 को इस तरह design किया गया था कि backend या API उस server से independent हो सके जो user को authenticate करता है। + +लेकिन इस case में, वही **FastAPI** application API और authentication दोनों संभालेगा। + +तो, आइए इसे उस simplified दृष्टिकोण से review करें: + +* User frontend में `username` और `password` type करता है, और `Enter` दबाता है। +* Frontend (जो user के browser में चल रहा है) उस `username` और `password` को हमारे API के एक specific URL पर भेजता है (`tokenUrl="token"` के साथ declare किया गया)। +* API उस `username` और `password` को check करता है, और एक "token" के साथ respond करता है (हमने अभी तक इनमें से कुछ भी implement नहीं किया है)। + * एक "token" बस कुछ content वाली string है जिसका उपयोग हम बाद में इस user को verify करने के लिए कर सकते हैं। + * सामान्यतः, token को कुछ समय बाद expire होने के लिए set किया जाता है। + * इसलिए, user को बाद में किसी समय फिर से log in करना होगा। + * और अगर token चोरी हो जाता है, तो risk कम होता है। यह किसी permanent key जैसा नहीं है जो हमेशा काम करेगी (अधिकांश cases में)। +* Frontend उस token को अस्थायी रूप से कहीं store करता है। +* User frontend web app के किसी दूसरे section में जाने के लिए frontend में click करता है। +* Frontend को API से कुछ और data fetch करने की आवश्यकता होती है। + * लेकिन उस specific endpoint के लिए इसे authentication चाहिए। + * इसलिए, हमारे API के साथ authenticate करने के लिए, यह `Authorization` header भेजता है जिसकी value `Bearer ` plus token होती है। + * अगर token में `foobar` है, तो `Authorization` header का content होगा: `Bearer foobar`. + +## **FastAPI** का `OAuth2PasswordBearer` { #fastapis-oauth2passwordbearer } + +**FastAPI** इन security features को implement करने के लिए abstraction के अलग-अलग levels पर कई tools देता है। + +इस उदाहरण में हम **OAuth2** का उपयोग करने जा रहे हैं, **Password** flow के साथ, **Bearer** token का उपयोग करते हुए। हम यह `OAuth2PasswordBearer` class का उपयोग करके करते हैं। + +/// note | नोट + +"bearer" token एकमात्र विकल्प नहीं है। + +लेकिन हमारे use case के लिए यह सबसे अच्छा है। + +और यह अधिकांश use cases के लिए सबसे अच्छा हो सकता है, जब तक कि आप OAuth2 expert न हों और ठीक-ठीक न जानते हों कि कोई दूसरा विकल्प आपकी आवश्यकताओं के लिए बेहतर क्यों है। + +उस case में, **FastAPI** आपको इसे बनाने के लिए tools भी देता है। + +/// + +जब हम `OAuth2PasswordBearer` class का instance बनाते हैं तो हम `tokenUrl` parameter pass करते हैं। इस parameter में वह URL होता है जिसका उपयोग client (user के browser में चल रहा frontend) token पाने के लिए `username` और `password` भेजने में करेगा। + +{* ../../docs_src/security/tutorial001_an_py310.py hl[8] *} + +/// tip | सुझाव + +यहाँ `tokenUrl="token"` एक relative URL `token` को refer करता है जिसे हमने अभी तक बनाया नहीं है। क्योंकि यह relative URL है, यह `./token` के equivalent है। + +क्योंकि हम relative URL का उपयोग कर रहे हैं, अगर आपका API `https://example.com/` पर स्थित था, तो यह `https://example.com/token` को refer करेगा। लेकिन अगर आपका API `https://example.com/api/v1/` पर स्थित था, तो यह `https://example.com/api/v1/token` को refer करेगा। + +Relative URL का उपयोग करना महत्वपूर्ण है ताकि यह सुनिश्चित हो सके कि आपका application [Proxy के पीछे](../../advanced/behind-a-proxy.md) जैसे advanced use case में भी काम करता रहे। + +/// + +यह parameter उस endpoint / *path operation* को create नहीं करता, बल्कि declare करता है कि URL `/token` वही होगा जिसका उपयोग client को token पाने के लिए करना चाहिए। उस जानकारी का उपयोग OpenAPI में किया जाता है, और फिर interactive API documentation systems में। + +हम जल्द ही वास्तविक path operation भी बनाएँगे। + +/// note | नोट + +अगर आप बहुत strict "Pythonista" हैं, तो आपको parameter name `tokenUrl` की style `token_url` के बजाय पसंद न आए। + +ऐसा इसलिए है क्योंकि यह OpenAPI spec जैसा ही name उपयोग कर रहा है। ताकि अगर आपको इनमें से किसी भी security scheme के बारे में और अधिक जाँच करनी हो, तो आप बस इसे copy और paste करके इसके बारे में और जानकारी खोज सकें। + +/// + +`oauth2_scheme` variable `OAuth2PasswordBearer` का instance है, लेकिन यह एक "callable" भी है। + +इसे इस तरह call किया जा सकता है: + +```Python +oauth2_scheme(some, parameters) +``` + +तो, इसे `Depends` के साथ उपयोग किया जा सकता है। + +### इसका उपयोग करें { #use-it } + +अब आप उस `oauth2_scheme` को `Depends` के साथ dependency में pass कर सकते हैं। + +{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *} + +यह dependency एक `str` प्रदान करेगी जिसे *path operation function* के parameter `token` को assign किया जाता है। + +**FastAPI** जान जाएगा कि यह OpenAPI schema (और automatic API docs) में "security scheme" define करने के लिए इस dependency का उपयोग कर सकता है। + +/// note | तकनीकी विवरण + +**FastAPI** जान जाएगा कि यह OpenAPI में security scheme define करने के लिए `OAuth2PasswordBearer` class (जो dependency में declare की गई है) का उपयोग कर सकता है क्योंकि यह `fastapi.security.oauth2.OAuth2` से inherit करती है, जो बदले में `fastapi.security.base.SecurityBase` से inherit करती है। + +OpenAPI (और automatic API docs) के साथ integrate होने वाली सभी security utilities `SecurityBase` से inherit करती हैं, इसी तरह **FastAPI** जान सकता है कि उन्हें OpenAPI में कैसे integrate करना है। + +/// + +## यह क्या करता है { #what-it-does } + +यह request में उस `Authorization` header को खोजेगा, check करेगा कि value `Bearer ` plus कोई token है या नहीं, और token को `str` के रूप में return करेगा। + +अगर इसे `Authorization` header नहीं दिखता, या value में `Bearer ` token नहीं है, तो यह सीधे 401 status code error (`UNAUTHORIZED`) के साथ respond करेगा। + +Error return करने के लिए आपको यह भी check करने की आवश्यकता नहीं है कि token मौजूद है या नहीं। आप निश्चिंत हो सकते हैं कि अगर आपकी function execute होती है, तो उस token में एक `str` होगा। + +आप इसे अभी interactive docs में आज़मा सकते हैं: + + + +हम अभी token की validity verify नहीं कर रहे हैं, लेकिन यह पहले से ही एक शुरुआत है। + +## Recap { #recap } + +तो, केवल 3 या 4 अतिरिक्त lines में, आपके पास पहले से ही security का कुछ primitive form है। diff --git a/docs/hi/docs/tutorial/security/get-current-user.md b/docs/hi/docs/tutorial/security/get-current-user.md new file mode 100644 index 000000000..403d6e7d5 --- /dev/null +++ b/docs/hi/docs/tutorial/security/get-current-user.md @@ -0,0 +1,105 @@ +# Current User प्राप्त करें { #get-current-user } + +पिछले अध्याय में security system (जो dependency injection system पर आधारित है) *path operation function* को `str` के रूप में एक `token` दे रहा था: + +{* ../../docs_src/security/tutorial001_an_py310.py hl[12] *} + +लेकिन वह अभी भी इतना उपयोगी नहीं है। + +आइए इसे हमें current user देने वाला बनाते हैं। + +## एक user model बनाएँ { #create-a-user-model } + +पहले, एक Pydantic user model बनाते हैं। + +जिस तरह हम bodies declare करने के लिए Pydantic का उपयोग करते हैं, उसी तरह हम इसे कहीं और भी उपयोग कर सकते हैं: + +{* ../../docs_src/security/tutorial002_an_py310.py hl[5,12:16] *} + +## एक `get_current_user` dependency बनाएँ { #create-a-get-current-user-dependency } + +आइए एक dependency `get_current_user` बनाएँ। + +याद है कि dependencies की sub-dependencies हो सकती हैं? + +`get_current_user` के पास उसी `oauth2_scheme` के साथ एक dependency होगी जिसे हमने पहले बनाया था। + +ठीक वैसे ही जैसे हम पहले सीधे *path operation* में कर रहे थे, हमारी नई dependency `get_current_user` sub-dependency `oauth2_scheme` से `str` के रूप में एक `token` प्राप्त करेगी: + +{* ../../docs_src/security/tutorial002_an_py310.py hl[25] *} + +## user प्राप्त करें { #get-the-user } + +`get_current_user` हमारे द्वारा बनाई गई एक (fake) utility function का उपयोग करेगी, जो token को `str` के रूप में लेती है और हमारा Pydantic `User` model लौटाती है: + +{* ../../docs_src/security/tutorial002_an_py310.py hl[19:22,26:27] *} + +## current user inject करें { #inject-the-current-user } + +तो अब हम *path operation* में अपने `get_current_user` के साथ वही `Depends` उपयोग कर सकते हैं: + +{* ../../docs_src/security/tutorial002_an_py310.py hl[31] *} + +ध्यान दें कि हम `current_user` का type Pydantic model `User` के रूप में declare करते हैं। + +यह function के अंदर completion और type checks में हमारी मदद करेगा। + +/// tip | टिप + +आपको याद होगा कि request bodies भी Pydantic models के साथ declare की जाती हैं। + +यहाँ **FastAPI** confuse नहीं होगा क्योंकि आप `Depends` का उपयोग कर रहे हैं। + +/// + +/// tip | टिप + +जिस तरह यह dependency system design किया गया है, वह हमें अलग-अलग dependencies (अलग-अलग "dependables") रखने देता है जो सभी एक `User` model लौटाती हैं। + +हम केवल एक dependency तक सीमित नहीं हैं जो उस type का data लौटा सकती है। + +/// + +## अन्य models { #other-models } + +अब आप *path operation functions* में सीधे current user प्राप्त कर सकते हैं और `Depends` का उपयोग करके **Dependency Injection** स्तर पर security mechanisms संभाल सकते हैं। + +और आप security requirements के लिए कोई भी model या data उपयोग कर सकते हैं (इस मामले में, Pydantic model `User`)। + +लेकिन आप किसी विशेष data model, class या type का उपयोग करने तक सीमित नहीं हैं। + +क्या आप अपने model में `id` और `email` रखना चाहते हैं और कोई `username` नहीं रखना चाहते? बिल्कुल। आप इन्हीं tools का उपयोग कर सकते हैं। + +क्या आप केवल एक `str` रखना चाहते हैं? या केवल एक `dict`? या सीधे database class model instance? सब कुछ उसी तरह काम करता है। + +असल में आपके application में login करने वाले users नहीं हैं, बल्कि robots, bots, या अन्य systems हैं, जिनके पास बस एक access token है? फिर भी, सब कुछ उसी तरह काम करता है। + +बस अपने application के लिए जिस भी प्रकार का model, जिस भी प्रकार की class, जिस भी प्रकार का database चाहिए, उसका उपयोग करें। **FastAPI** dependency injection system के साथ आपकी ज़रूरतें पूरी करता है। + +## Code size { #code-size } + +यह example verbose लग सकता है। ध्यान रखें कि हम security, data models, utility functions और *path operations* को उसी file में मिला रहे हैं। + +लेकिन यहाँ मुख्य बात है। + +security और dependency injection से जुड़ी चीज़ें एक बार लिखी जाती हैं। + +और आप इसे जितना चाहें उतना complex बना सकते हैं। फिर भी, यह केवल एक बार, एक ही जगह पर, पूरी flexibility के साथ लिखा जाता है। + +लेकिन आपके पास उसी security system का उपयोग करने वाले हजारों endpoints (*path operations*) हो सकते हैं। + +और वे सभी (या उनका कोई भी हिस्सा जिसे आप चाहें) इन dependencies या आपके द्वारा बनाई गई किसी भी अन्य dependencies को फिर से उपयोग करने का लाभ उठा सकते हैं। + +और ये सभी हजारों *path operations* सिर्फ 3 lines जितने छोटे हो सकते हैं: + +{* ../../docs_src/security/tutorial002_an_py310.py hl[30:32] *} + +## Recap { #recap } + +अब आप अपने *path operation function* में सीधे current user प्राप्त कर सकते हैं। + +हम पहले ही आधे रास्ते तक पहुँच चुके हैं। + +हमें बस user/client के लिए एक *path operation* जोड़ना है ताकि वह वास्तव में `username` और `password` भेज सके। + +वह आगे आता है। diff --git a/docs/hi/docs/tutorial/security/index.md b/docs/hi/docs/tutorial/security/index.md new file mode 100644 index 000000000..43fbd0a9b --- /dev/null +++ b/docs/hi/docs/tutorial/security/index.md @@ -0,0 +1,105 @@ +# Security { #security } + +security, authentication और authorization को handle करने के कई तरीके हैं। + +और यह सामान्यतः एक जटिल और "कठिन" विषय होता है। + +कई frameworks और systems में केवल security और authentication को handle करने में ही बहुत अधिक effort और code लग जाता है (कई मामलों में यह लिखे गए पूरे code का 50% या उससे अधिक हो सकता है)। + +**FastAPI** आपको **Security** से आसानी से, तेज़ी से, standard तरीके से निपटने में मदद करने के लिए कई tools प्रदान करता है, बिना सभी security specifications को पढ़ने और सीखने की ज़रूरत के। + +लेकिन पहले, आइए कुछ छोटे concepts देखते हैं। + +## जल्दी में हैं? { #in-a-hurry } + +अगर आपको इन terms की परवाह नहीं है और आपको बस username और password पर आधारित authentication के साथ security *अभी* जोड़नी है, तो अगले chapters पर जाएँ। + +## OAuth2 { #oauth2 } + +OAuth2 एक specification है जो authentication और authorization को handle करने के कई तरीके define करती है। + +यह काफ़ी विस्तृत specification है और कई जटिल use cases को cover करती है। + +इसमें "third party" का उपयोग करके authenticate करने के तरीके शामिल हैं। + +यही चीज़ अंदर से उन सभी systems में इस्तेमाल होती है जिनमें "login with Facebook, Google, X (Twitter), GitHub" होता है। + +### OAuth 1 { #oauth-1 } + +OAuth 1 भी था, जो OAuth2 से बहुत अलग और अधिक जटिल था, क्योंकि इसमें communication को encrypt करने के तरीके पर direct specifications शामिल थीं। + +आजकल यह बहुत लोकप्रिय या इस्तेमाल में नहीं है। + +OAuth2 यह specify नहीं करता कि communication को कैसे encrypt करना है, यह अपेक्षा करता है कि आपकी application HTTPS के साथ serve की जा रही हो। + +/// tip | सुझाव + +**deployment** वाले section में आप देखेंगे कि Traefik और Let's Encrypt का उपयोग करके HTTPS को मुफ्त में कैसे setup किया जाता है। + +/// + +## OpenID Connect { #openid-connect } + +OpenID Connect एक और specification है, जो **OAuth2** पर आधारित है। + +यह OAuth2 को बस extend करता है और कुछ ऐसी चीज़ें specify करता है जो OAuth2 में तुलनात्मक रूप से ambiguous हैं, ताकि इसे अधिक interoperable बनाया जा सके। + +उदाहरण के लिए, Google login OpenID Connect का उपयोग करता है (जो अंदर से OAuth2 का उपयोग करता है)। + +लेकिन Facebook login OpenID Connect को support नहीं करता। उसका अपना OAuth2 flavor है। + +### OpenID ("OpenID Connect" नहीं) { #openid-not-openid-connect } + +एक "OpenID" specification भी थी। उसने वही समस्या हल करने की कोशिश की जो **OpenID Connect** करता है, लेकिन वह OAuth2 पर आधारित नहीं थी। + +इसलिए, वह एक पूरा अतिरिक्त system था। + +आजकल यह बहुत लोकप्रिय या इस्तेमाल में नहीं है। + +## OpenAPI { #openapi } + +OpenAPI (पहले Swagger के नाम से जाना जाता था) APIs बनाने के लिए open specification है (अब Linux Foundation का हिस्सा)। + +**FastAPI** **OpenAPI** पर आधारित है। + +इसी वजह से कई automatic interactive documentation interfaces, code generation, आदि होना संभव होता है। + +OpenAPI कई security "schemes" define करने का तरीका देता है। + +इनका उपयोग करके, आप इन सभी standard-based tools का लाभ उठा सकते हैं, जिनमें ये interactive documentation systems भी शामिल हैं। + +OpenAPI निम्नलिखित security schemes define करता है: + +* `apiKey`: एक application-specific key जो यहाँ से आ सकती है: + * एक query parameter. + * एक header. + * एक cookie. +* `http`: standard HTTP authentication systems, जिनमें शामिल हैं: + * `bearer`: एक header `Authorization` जिसमें `Bearer ` plus एक token का value होता है। यह OAuth2 से inherited है। + * HTTP Basic authentication. + * HTTP Digest, आदि। +* `oauth2`: security handle करने के सभी OAuth2 तरीके (जिन्हें "flows" कहा जाता है)। + * इनमें से कई flows OAuth 2.0 authentication provider बनाने के लिए उपयुक्त हैं (जैसे Google, Facebook, X (Twitter), GitHub, आदि): + * `implicit` + * `clientCredentials` + * `authorizationCode` + * लेकिन एक specific "flow" है जिसे उसी application में सीधे authentication handle करने के लिए पूरी तरह इस्तेमाल किया जा सकता है: + * `password`: कुछ अगले chapters इसके examples cover करेंगे। +* `openIdConnect`: इसमें OAuth2 authentication data को automatically discover करने का तरीका होता है। + * यह automatic discovery वही है जो OpenID Connect specification में define की गई है। + +/// tip | सुझाव + +Google, Facebook, X (Twitter), GitHub, आदि जैसे अन्य authentication/authorization providers को integrate करना भी संभव और तुलनात्मक रूप से आसान है। + +सबसे जटिल समस्या उन जैसे authentication/authorization provider बनाना है, लेकिन **FastAPI** आपको इसे आसानी से करने के लिए tools देता है, और आपके लिए heavy lifting करता है। + +/// + +## **FastAPI** utilities { #fastapi-utilities } + +FastAPI इन security schemes में से प्रत्येक के लिए `fastapi.security` module में कई tools प्रदान करता है, जो इन security mechanisms का उपयोग आसान बनाते हैं। + +अगले chapters में आप देखेंगे कि **FastAPI** द्वारा प्रदान किए गए उन tools का उपयोग करके अपनी API में security कैसे जोड़ें। + +और आप यह भी देखेंगे कि यह interactive documentation system में automatically कैसे integrate हो जाता है। diff --git a/docs/hi/docs/tutorial/security/oauth2-jwt.md b/docs/hi/docs/tutorial/security/oauth2-jwt.md new file mode 100644 index 000000000..2066d8f5a --- /dev/null +++ b/docs/hi/docs/tutorial/security/oauth2-jwt.md @@ -0,0 +1,277 @@ +# Password के साथ OAuth2 (और hashing), JWT tokens के साथ Bearer { #oauth2-with-password-and-hashing-bearer-with-jwt-tokens } + +अब जब हमारे पास पूरा security flow है, तो आइए JWT tokens और secure password hashing का उपयोग करके application को वास्तव में secure बनाते हैं। + +यह code ऐसा है जिसे आप अपनी application में सच में उपयोग कर सकते हैं, password hashes को अपने database में save कर सकते हैं, आदि। + +हम पिछले chapter में जहाँ छोड़ा था, वहीं से शुरू करेंगे और उसे आगे बढ़ाएँगे। + +## JWT के बारे में { #about-jwt } + +JWT का मतलब है "JSON Web Tokens"। + +यह एक JSON object को बिना spaces वाली लंबी dense string में codify करने का standard है। यह ऐसा दिखता है: + +``` +eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c +``` + +यह encrypted नहीं है, इसलिए कोई भी contents से जानकारी वापस प्राप्त कर सकता है। + +लेकिन यह signed है। इसलिए, जब आपको कोई token मिलता है जिसे आपने issue किया था, तो आप verify कर सकते हैं कि उसे आपने ही issue किया था। + +इस तरह, आप एक token बना सकते हैं जिसकी expiration, मान लीजिए, 1 week हो। और फिर जब user अगले दिन token के साथ वापस आता है, तो आपको पता होता है कि वह user अभी भी आपके system में logged in है। + +एक week के बाद, token expired हो जाएगा और user authorized नहीं होगा और नया token पाने के लिए उसे फिर से sign in करना होगा। और अगर user (या कोई third party) expiration बदलने के लिए token को modify करने की कोशिश करे, तो आप इसे पता लगा पाएँगे, क्योंकि signatures match नहीं होंगे। + +अगर आप JWT tokens के साथ प्रयोग करना चाहते हैं और देखना चाहते हैं कि वे कैसे काम करते हैं, तो [https://jwt.io](https://jwt.io/) देखें। + +## `PyJWT` install करें { #install-pyjwt } + +Python में JWT tokens generate और verify करने के लिए हमें `PyJWT` install करना होगा। + +सुनिश्चित करें कि आप एक [virtual environment](../../virtual-environments.md) बनाएँ, उसे activate करें, और फिर `pyjwt` install करें: + +
+ +```console +$ pip install pyjwt + +---> 100% +``` + +
+ +/// note | नोट + +अगर आप RSA या ECDSA जैसे digital signature algorithms का उपयोग करने की योजना बना रहे हैं, तो आपको cryptography library dependency `pyjwt[crypto]` install करनी चाहिए। + +आप इसके बारे में [PyJWT Installation docs](https://pyjwt.readthedocs.io/en/latest/installation.html) में और पढ़ सकते हैं। + +/// + +## Password hashing { #password-hashing } + +"Hashing" का मतलब है किसी content (इस मामले में password) को bytes के sequence (बस एक string) में बदलना, जो बेकार/असमझ text जैसा दिखता है। + +जब भी आप बिल्कुल वही content (बिल्कुल वही password) pass करते हैं, आपको बिल्कुल वही gibberish मिलता है। + +लेकिन आप उस gibberish से वापस password में convert नहीं कर सकते। + +### Password hashing क्यों उपयोग करें { #why-use-password-hashing } + +अगर आपका database चोरी हो जाता है, तो चोर के पास आपके users के plaintext passwords नहीं होंगे, केवल hashes होंगे। + +इसलिए, चोर उस password को किसी दूसरे system में उपयोग करने की कोशिश नहीं कर पाएगा (क्योंकि कई users हर जगह वही password उपयोग करते हैं, यह खतरनाक होगा)। + +## `pwdlib` install करें { #install-pwdlib } + +pwdlib password hashes संभालने के लिए एक शानदार Python package है। + +यह कई secure hashing algorithms और उनके साथ काम करने के लिए utilities support करता है। + +Recommended algorithm "Argon2" है। + +सुनिश्चित करें कि आप एक [virtual environment](../../virtual-environments.md) बनाएँ, उसे activate करें, और फिर Argon2 के साथ pwdlib install करें: + +
+ +```console +$ pip install "pwdlib[argon2]" + +---> 100% +``` + +
+ +/// tip | सुझाव + +`pwdlib` के साथ, आप इसे इस तरह configure भी कर सकते हैं कि यह **Django**, **Flask** security plug-in या कई अन्य द्वारा बनाए गए passwords read कर सके। + +तो, उदाहरण के लिए, आप एक database में Django application का वही data FastAPI application के साथ share कर पाएँगे। या उसी database का उपयोग करते हुए धीरे-धीरे Django application को migrate कर पाएँगे। + +और आपके users एक ही समय में आपकी Django app या आपकी **FastAPI** app से login कर पाएँगे। + +/// + +## Passwords को hash और verify करें { #hash-and-verify-the-passwords } + +`pwdlib` से वे tools import करें जिनकी हमें ज़रूरत है। + +Recommended settings के साथ एक PasswordHash instance बनाएँ - इसका उपयोग passwords को hash और verify करने के लिए किया जाएगा। + +/// tip | सुझाव + +pwdlib bcrypt hashing algorithm को भी support करता है लेकिन legacy algorithms शामिल नहीं करता - outdated hashes के साथ काम करने के लिए passlib library का उपयोग करना recommended है। + +उदाहरण के लिए, आप इसका उपयोग किसी दूसरे system (जैसे Django) द्वारा generate किए गए passwords read और verify करने के लिए कर सकते हैं, लेकिन किसी भी नए passwords को Argon2 या Bcrypt जैसे अलग algorithm से hash कर सकते हैं। + +और एक ही समय में उन सभी के साथ compatible रह सकते हैं। + +/// + +User से आने वाले password को hash करने के लिए एक utility function बनाएँ। + +और एक और utility बनाएँ जो verify करे कि received password stored hash से match करता है या नहीं। + +और एक और utility बनाएँ जो authenticate करे और user return करे। + +{* ../../docs_src/security/tutorial004_an_py310.py hl[8,49,51,58:59,62:63,72:79] *} + +जब `authenticate_user` को ऐसे username के साथ call किया जाता है जो database में मौजूद नहीं है, तब भी हम dummy hash के against `verify_password` चलाते हैं। + +यह सुनिश्चित करता है कि username valid हो या न हो, endpoint response देने में लगभग समान समय ले, जिससे **timing attacks** रोके जा सकें जिनका उपयोग मौजूदा usernames enumerate करने के लिए किया जा सकता है। + +/// note | नोट + +अगर आप नए (fake) database `fake_users_db` को check करेंगे, तो आप देखेंगे कि hashed password अब कैसा दिखता है: `"$argon2id$v=19$m=65536,t=3,p=4$wagCPXjifgvUFBzq4hqe3w$CYaIb8sB+wtD+Vu/P4uod1+Qof8h+1g7bbDlBID48Rc"`। + +/// + +## JWT tokens संभालें { #handle-jwt-tokens } + +Installed modules import करें। + +एक random secret key बनाएँ जिसका उपयोग JWT tokens sign करने के लिए किया जाएगा। + +Secure random secret key generate करने के लिए command उपयोग करें: + +
+ +```console +$ openssl rand -hex 32 + +09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7 +``` + +
+ +और output को variable `SECRET_KEY` में copy करें (example वाला उपयोग न करें)। + +JWT token sign करने के लिए उपयोग किए गए algorithm के साथ एक variable `ALGORITHM` बनाएँ और इसे `"HS256"` पर set करें। + +Token की expiration के लिए एक variable बनाएँ। + +एक Pydantic Model define करें जिसका उपयोग response के लिए token endpoint में किया जाएगा। + +नया access token generate करने के लिए एक utility function बनाएँ। + +{* ../../docs_src/security/tutorial004_an_py310.py hl[4,7,13:15,29:31,82:90] *} + +## Dependencies update करें { #update-the-dependencies } + +`get_current_user` को update करें ताकि वह पहले जैसा ही token receive करे, लेकिन इस बार JWT tokens का उपयोग करते हुए। + +Received token को decode करें, verify करें, और current user return करें। + +अगर token invalid है, तो तुरंत HTTP error return करें। + +{* ../../docs_src/security/tutorial004_an_py310.py hl[93:110] *} + +## `/token` *path operation* update करें { #update-the-token-path-operation } + +Token की expiration time के साथ एक `timedelta` बनाएँ। + +एक वास्तविक JWT access token बनाएँ और उसे return करें। + +{* ../../docs_src/security/tutorial004_an_py310.py hl[121:136] *} + +### JWT "subject" `sub` के बारे में technical details { #technical-details-about-the-jwt-subject-sub } + +JWT specification कहता है कि token के subject के साथ एक key `sub` होती है। + +इसका उपयोग करना optional है, लेकिन यही वह जगह है जहाँ आप user की identification रखेंगे, इसलिए हम इसे यहाँ उपयोग कर रहे हैं। + +JWT का उपयोग user की पहचान करने और उन्हें आपकी API पर सीधे operations perform करने की अनुमति देने के अलावा अन्य चीज़ों के लिए भी किया जा सकता है। + +उदाहरण के लिए, आप एक "car" या एक "blog post" की पहचान कर सकते हैं। + +फिर आप उस entity के बारे में permissions जोड़ सकते हैं, जैसे "drive" (car के लिए) या "edit" (blog के लिए)। + +और फिर, आप वह JWT token किसी user (या bot) को दे सकते हैं, और वे उन actions को perform करने के लिए इसका उपयोग कर सकते हैं (car drive करना, या blog post edit करना), बिना account की ज़रूरत के, केवल उस JWT token के साथ जिसे आपकी API ने इसके लिए generate किया है। + +इन ideas का उपयोग करके, JWT का उपयोग कहीं अधिक sophisticated scenarios के लिए किया जा सकता है। + +इन cases में, उन entities में से कई की same ID हो सकती है, मान लीजिए `foo` (एक user `foo`, एक car `foo`, और एक blog post `foo`)। + +इसलिए, ID collisions से बचने के लिए, user के लिए JWT token बनाते समय, आप `sub` key के value के आगे prefix जोड़ सकते हैं, जैसे `username:`। इसलिए, इस example में, `sub` का value हो सकता था: `username:johndoe`। + +ध्यान रखने वाली महत्वपूर्ण बात यह है कि `sub` key के पास पूरी application में unique identifier होना चाहिए, और यह एक string होना चाहिए। + +## इसे check करें { #check-it } + +Server run करें और docs पर जाएँ: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)। + +आप user interface ऐसा देखेंगे: + + + +Application को पहले की तरह ही authorize करें। + +Credentials का उपयोग करते हुए: + +Username: `johndoe` +Password: `secret` + +/// tip | सुझाव + +ध्यान दें कि code में कहीं भी plaintext password "`secret`" नहीं है, हमारे पास केवल hashed version है। + +/// + + + +Endpoint `/users/me/` call करें, आपको response इस तरह मिलेगा: + +```JSON +{ + "username": "johndoe", + "email": "johndoe@example.com", + "full_name": "John Doe", + "disabled": false +} +``` + + + +अगर आप developer tools खोलते हैं, तो आप देख सकते हैं कि भेजे गए data में केवल token शामिल है, password केवल पहले request में user को authenticate करने और वह access token पाने के लिए भेजा जाता है, लेकिन उसके बाद नहीं: + + + +/// note | नोट + +`Authorization` header पर ध्यान दें, जिसका value `Bearer ` से शुरू होता है। + +/// + +## `scopes` के साथ advanced उपयोग { #advanced-usage-with-scopes } + +OAuth2 में "scopes" की धारणा है। + +आप उनका उपयोग JWT token में permissions का specific set जोड़ने के लिए कर सकते हैं। + +फिर आप यह token सीधे किसी user या third party को दे सकते हैं, ताकि वे restrictions के set के साथ आपकी API से interact कर सकें। + +आप बाद में **Advanced User Guide** में सीख सकते हैं कि उनका उपयोग कैसे करें और वे **FastAPI** में कैसे integrated हैं। + +## Recap { #recap } + +अब तक आपने जो देखा है, उससे आप OAuth2 और JWT जैसे standards का उपयोग करके एक secure **FastAPI** application setup कर सकते हैं। + +लगभग किसी भी framework में security संभालना काफ़ी जल्दी एक जटिल विषय बन जाता है। + +कई packages जो इसे बहुत सरल बनाते हैं, उन्हें data model, database, और available features के साथ कई compromises करने पड़ते हैं। और इनमें से कुछ packages जो चीज़ों को बहुत अधिक सरल बना देते हैं, उनके अंदर वास्तव में security flaws होते हैं। + +--- + +**FastAPI** किसी भी database, data model या tool के साथ कोई compromise नहीं करता। + +यह आपको उन चीज़ों को चुनने की पूरी flexibility देता है जो आपके project के लिए सबसे अच्छी हों। + +और आप सीधे कई well maintained और widely used packages जैसे `pwdlib` और `PyJWT` का उपयोग कर सकते हैं, क्योंकि **FastAPI** external packages integrate करने के लिए किसी complex mechanism की मांग नहीं करता। + +लेकिन यह आपको process को जितना संभव हो उतना सरल बनाने के लिए tools देता है, बिना flexibility, robustness, या security से compromise किए। + +और आप OAuth2 जैसे secure, standard protocols को अपेक्षाकृत simple तरीके से उपयोग और implement कर सकते हैं। + +आप **Advanced User Guide** में और सीख सकते हैं कि अधिक fine-grained permission system के लिए OAuth2 "scopes" का उपयोग कैसे करें, इन्हीं standards का पालन करते हुए। Scopes के साथ OAuth2 वह mechanism है जिसका उपयोग कई बड़े authentication providers, जैसे Facebook, Google, GitHub, Microsoft, X (Twitter), आदि करते हैं, ताकि third party applications को अपने users की ओर से उनकी APIs के साथ interact करने के लिए authorize किया जा सके। diff --git a/docs/hi/docs/tutorial/security/simple-oauth2.md b/docs/hi/docs/tutorial/security/simple-oauth2.md new file mode 100644 index 000000000..87c9a4a28 --- /dev/null +++ b/docs/hi/docs/tutorial/security/simple-oauth2.md @@ -0,0 +1,289 @@ +# Password और Bearer के साथ सरल OAuth2 { #simple-oauth2-with-password-and-bearer } + +अब पिछले अध्याय से आगे बढ़ते हैं और एक पूरा security flow बनाने के लिए छूटे हुए हिस्से जोड़ते हैं। + +## `username` और `password` प्राप्त करें { #get-the-username-and-password } + +हम `username` और `password` प्राप्त करने के लिए **FastAPI** security utilities का उपयोग करने वाले हैं। + +OAuth2 निर्दिष्ट करता है कि "password flow" (जिसका हम उपयोग कर रहे हैं) का उपयोग करते समय client/user को `username` और `password` fields को form data के रूप में भेजना होगा। + +और spec कहता है कि fields के नाम ऐसे ही होने चाहिए। इसलिए `user-name` या `email` काम नहीं करेगा। + +लेकिन चिंता न करें, frontend में आप इसे अपने अंतिम users को जैसे चाहें दिखा सकते हैं। + +और आपके database models कोई भी दूसरे नाम उपयोग कर सकते हैं जो आप चाहें। + +लेकिन login *path operation* के लिए, हमें spec के साथ compatible होने के लिए इन नामों का उपयोग करना होगा (और उदाहरण के लिए, integrated API documentation system का उपयोग कर पाने के लिए)। + +Spec यह भी बताता है कि `username` और `password` को form data के रूप में भेजा जाना चाहिए (इसलिए, यहाँ कोई JSON नहीं)। + +### `scope` { #scope } + +Spec यह भी कहता है कि client एक और form field "`scope`" भेज सकता है। + +form field का नाम `scope` है (singular में), लेकिन यह वास्तव में spaces से अलग किए गए "scopes" वाली एक लंबी string होती है। + +हर "scope" बस एक string है (बिना spaces के)। + +इनका सामान्यतः विशिष्ट security permissions घोषित करने के लिए उपयोग किया जाता है, उदाहरण के लिए: + +* `users:read` या `users:write` आम उदाहरण हैं। +* `instagram_basic` Facebook / Instagram द्वारा उपयोग किया जाता है। +* `https://www.googleapis.com/auth/drive` Google द्वारा उपयोग किया जाता है। + +/// note | नोट + +OAuth2 में "scope" बस एक string है जो किसी विशिष्ट required permission को घोषित करती है। + +इससे फर्क नहीं पड़ता कि उसमें `:` जैसे अन्य characters हैं या वह URL है। + +वे details implementation specific हैं। + +OAuth2 के लिए वे बस strings हैं। + +/// + +## `username` और `password` प्राप्त करने का Code { #code-to-get-the-username-and-password } + +अब इसे संभालने के लिए **FastAPI** द्वारा प्रदान की गई utilities का उपयोग करते हैं। + +### `OAuth2PasswordRequestForm` { #oauth2passwordrequestform } + +पहले, `OAuth2PasswordRequestForm` import करें, और `/token` के *path operation* में `Depends` के साथ इसे dependency के रूप में उपयोग करें: + +{* ../../docs_src/security/tutorial003_an_py310.py hl[4,78] *} + +`OAuth2PasswordRequestForm` एक class dependency है जो एक form body घोषित करती है जिसमें: + +* `username`। +* `password`। +* एक वैकल्पिक `scope` field, एक बड़ी string के रूप में, जो spaces से अलग की गई strings से बनी होती है। +* एक वैकल्पिक `grant_type`। + +/// tip | टिप + +OAuth2 spec वास्तव में fixed value `password` के साथ एक field `grant_type` *required* करता है, लेकिन `OAuth2PasswordRequestForm` इसे enforce नहीं करता। + +अगर आपको इसे enforce करना है, तो `OAuth2PasswordRequestForm` की जगह `OAuth2PasswordRequestFormStrict` का उपयोग करें। + +/// + +* एक वैकल्पिक `client_id` (हमारे उदाहरण के लिए हमें इसकी आवश्यकता नहीं है)। +* एक वैकल्पिक `client_secret` (हमारे उदाहरण के लिए हमें इसकी आवश्यकता नहीं है)। + +/// note | नोट + +`OAuth2PasswordRequestForm`, **FastAPI** के लिए कोई विशेष class नहीं है जैसे `OAuth2PasswordBearer` है। + +`OAuth2PasswordBearer` **FastAPI** को बताता है कि यह एक security scheme है। इसलिए इसे OpenAPI में इस तरह जोड़ा जाता है। + +लेकिन `OAuth2PasswordRequestForm` बस एक class dependency है जिसे आप खुद भी लिख सकते थे, या आप सीधे `Form` parameters घोषित कर सकते थे। + +लेकिन क्योंकि यह एक सामान्य use case है, इसे आसान बनाने के लिए **FastAPI** द्वारा सीधे प्रदान किया गया है। + +/// + +### form data का उपयोग करें { #use-the-form-data } + +/// tip | टिप + +dependency class `OAuth2PasswordRequestForm` के instance में spaces से अलग की गई लंबी string वाला attribute `scope` नहीं होगा, इसके बजाय, इसमें भेजे गए प्रत्येक scope के लिए actual strings की list वाला `scopes` attribute होगा। + +हम इस उदाहरण में `scopes` का उपयोग नहीं कर रहे हैं, लेकिन यदि आपको इसकी आवश्यकता हो तो functionality उपलब्ध है। + +/// + +अब, form field से `username` का उपयोग करके (fake) database से user data प्राप्त करें। + +यदि ऐसा कोई user नहीं है, तो हम "Incorrect username or password" कहते हुए error लौटाते हैं। + +Error के लिए, हम exception `HTTPException` का उपयोग करते हैं: + +{* ../../docs_src/security/tutorial003_an_py310.py hl[3,79:81] *} + +### password जाँचें { #check-the-password } + +इस समय हमारे पास database से user data है, लेकिन हमने password नहीं जाँचा है। + +पहले उस data को Pydantic `UserInDB` model में डालते हैं। + +आपको कभी भी plaintext passwords save नहीं करने चाहिए, इसलिए, हम (fake) password hashing system का उपयोग करेंगे। + +यदि passwords match नहीं करते, तो हम वही error लौटाते हैं। + +#### Password hashing { #password-hashing } + +"Hashing" का मतलब है: कुछ content (इस मामले में password) को bytes की sequence (बस एक string) में convert करना जो बेतरतीब दिखती है। + +जब भी आप बिल्कुल वही content (बिल्कुल वही password) पास करते हैं, तो आपको बिल्कुल वही बेतरतीब string मिलती है। + +लेकिन आप उस बेतरतीब string से वापस password में convert नहीं कर सकते। + +##### Password hashing का उपयोग क्यों करें { #why-use-password-hashing } + +अगर आपका database चोरी हो जाता है, तो चोर के पास आपके users के plaintext passwords नहीं होंगे, केवल hashes होंगे। + +इसलिए, चोर उन same passwords को किसी दूसरे system में उपयोग करने की कोशिश नहीं कर पाएगा (क्योंकि कई users हर जगह वही password उपयोग करते हैं, यह खतरनाक होगा)। + +{* ../../docs_src/security/tutorial003_an_py310.py hl[82:85] *} + +#### `**user_dict` के बारे में { #about-user-dict } + +`UserInDB(**user_dict)` का मतलब है: + +*`user_dict` की keys और values को सीधे key-value arguments के रूप में पास करें, इसके बराबर:* + +```Python +UserInDB( + username = user_dict["username"], + email = user_dict["email"], + full_name = user_dict["full_name"], + disabled = user_dict["disabled"], + hashed_password = user_dict["hashed_password"], +) +``` + +/// note | नोट + +`**user_dict` की अधिक पूरी explanation के लिए [**Extra Models** के documentation](../extra-models.md#about-user-in-model-dump) में वापस देखें। + +/// + +## token लौटाएँ { #return-the-token } + +`token` endpoint का response एक JSON object होना चाहिए। + +इसमें `token_type` होना चाहिए। हमारे मामले में, क्योंकि हम "Bearer" tokens का उपयोग कर रहे हैं, token type "`bearer`" होना चाहिए। + +और इसमें `access_token` होना चाहिए, जिसमें हमारे access token वाली एक string हो। + +इस सरल उदाहरण के लिए, हम बस पूरी तरह insecure रहेंगे और token के रूप में वही `username` लौटाएँगे। + +/// tip | टिप + +अगले अध्याय में, आप password hashing और JWT tokens के साथ एक वास्तविक secure implementation देखेंगे। + +लेकिन अभी के लिए, आइए उन specific details पर ध्यान दें जिनकी हमें आवश्यकता है। + +/// + +{* ../../docs_src/security/tutorial003_an_py310.py hl[87] *} + +/// tip | टिप + +Spec के अनुसार, आपको `access_token` और `token_type` के साथ एक JSON लौटाना चाहिए, बिल्कुल इस उदाहरण की तरह। + +यह कुछ ऐसा है जो आपको अपने code में स्वयं करना होगा, और सुनिश्चित करना होगा कि आप उन JSON keys का उपयोग करें। + +Specifications के compliant होने के लिए, यह लगभग एकमात्र चीज है जिसे आपको सही तरीके से खुद करना याद रखना होगा। + +बाकी सब **FastAPI** आपके लिए संभालता है। + +/// + +## dependencies अपडेट करें { #update-the-dependencies } + +अब हम अपनी dependencies अपडेट करने वाले हैं। + +हम `current_user` को *केवल* तब प्राप्त करना चाहते हैं जब यह user active हो। + +इसलिए, हम एक अतिरिक्त dependency `get_current_active_user` बनाते हैं जो बदले में `get_current_user` को dependency के रूप में उपयोग करती है। + +ये दोनों dependencies बस एक HTTP error लौटाएँगी यदि user मौजूद नहीं है, या inactive है। + +इसलिए, हमारे endpoint में, हमें user केवल तभी मिलेगा जब user मौजूद हो, सही तरीके से authenticated हो, और active हो: + +{* ../../docs_src/security/tutorial003_an_py310.py hl[58:66,69:74,94] *} + +/// note | नोट + +Value `Bearer` के साथ अतिरिक्त header `WWW-Authenticate`, जिसे हम यहाँ लौटा रहे हैं, spec का भी हिस्सा है। + +किसी भी HTTP (error) status code 401 "UNAUTHORIZED" को `WWW-Authenticate` header भी लौटाना चाहिए। + +Bearer tokens (हमारे मामले) में, उस header की value `Bearer` होनी चाहिए। + +आप वास्तव में उस अतिरिक्त header को छोड़ सकते हैं और फिर भी यह काम करेगा। + +लेकिन specifications के compliant होने के लिए इसे यहाँ प्रदान किया गया है। + +साथ ही, ऐसे tools हो सकते हैं जो इसकी अपेक्षा करते हैं और इसका उपयोग करते हैं (अभी या भविष्य में) और यह आपके या आपके users के लिए उपयोगी हो सकता है, अभी या भविष्य में। + +यही standards का लाभ है... + +/// + +## इसे काम करते देखें { #see-it-in-action } + +Interactive docs खोलें: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs)। + +### Authenticate करें { #authenticate } + +"Authorize" button पर click करें। + +Credentials का उपयोग करें: + +User: `johndoe` + +Password: `secret` + + + +System में authenticate होने के बाद, आप इसे ऐसे देखेंगे: + + + +### अपना user data प्राप्त करें { #get-your-own-user-data } + +अब path `/users/me` के साथ operation `GET` का उपयोग करें। + +आपको अपने user का data मिलेगा, जैसे: + +```JSON +{ + "username": "johndoe", + "email": "johndoe@example.com", + "full_name": "John Doe", + "disabled": false, + "hashed_password": "fakehashedsecret" +} +``` + + + +यदि आप lock icon पर click करके logout करते हैं, और फिर वही operation दोबारा आज़माते हैं, तो आपको HTTP 401 error मिलेगा: + +```JSON +{ + "detail": "Not authenticated" +} +``` + +### Inactive user { #inactive-user } + +अब एक inactive user के साथ प्रयास करें, इनके साथ authenticate करें: + +User: `alice` + +Password: `secret2` + +और path `/users/me` के साथ operation `GET` का उपयोग करने का प्रयास करें। + +आपको "Inactive user" error मिलेगा, जैसे: + +```JSON +{ + "detail": "Inactive user" +} +``` + +## Recap { #recap } + +अब आपके पास अपनी API के लिए `username` और `password` पर आधारित एक पूरा security system implement करने के tools हैं। + +इन tools का उपयोग करके, आप security system को किसी भी database और किसी भी user या data model के साथ compatible बना सकते हैं। + +एकमात्र detail जो missing है वह यह है कि यह अभी वास्तव में "secure" नहीं है। + +अगले अध्याय में आप देखेंगे कि secure password hashing library और JWT tokens का उपयोग कैसे करें। diff --git a/docs/hi/docs/tutorial/server-sent-events.md b/docs/hi/docs/tutorial/server-sent-events.md new file mode 100644 index 000000000..cb23938df --- /dev/null +++ b/docs/hi/docs/tutorial/server-sent-events.md @@ -0,0 +1,120 @@ +# Server-Sent Events (SSE) { #server-sent-events-sse } + +आप **Server-Sent Events** (SSE) का उपयोग करके client को data stream कर सकते हैं। + +यह [Stream JSON Lines](stream-json-lines.md) जैसा है, लेकिन `text/event-stream` format का उपयोग करता है, जिसे browsers [`EventSource` API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) के साथ natively support करते हैं। + +/// note | नोट + +FastAPI 0.135.0 में जोड़ा गया। + +/// + +## Server-Sent Events क्या हैं? { #what-are-server-sent-events } + +SSE, HTTP के माध्यम से server से client तक data stream करने के लिए एक standard है। + +हर event एक छोटा text block होता है जिसमें `data`, `event`, `id`, और `retry` जैसे "fields" होते हैं, जिन्हें खाली lines से अलग किया जाता है। + +यह ऐसा दिखता है: + +``` +data: {"name": "Portal Gun", "price": 999.99} + +data: {"name": "Plumbus", "price": 32.99} + +``` + +SSE का उपयोग आमतौर पर AI chat streaming, live notifications, logs और observability, और अन्य मामलों में किया जाता है जहाँ server client को updates push करता है। + +/// tip | सुझाव + +अगर आप binary data stream करना चाहते हैं, जैसे video या audio, तो advanced guide देखें: [Data Stream करें](../advanced/stream-data.md). + +/// + +## FastAPI के साथ SSE Stream करें { #stream-sse-with-fastapi } + +FastAPI के साथ SSE stream करने के लिए, अपनी *path operation function* में `yield` का उपयोग करें और `response_class=EventSourceResponse` set करें। + +`EventSourceResponse` को `fastapi.sse` से import करें: + +{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[4,22] *} + +हर yielded item को JSON के रूप में encode किया जाता है और SSE event के `data:` field में भेजा जाता है। + +अगर आप return type को `AsyncIterable[Item]` के रूप में declare करते हैं, तो FastAPI इसका उपयोग Pydantic के साथ data को **validate**, **document**, और **serialize** करने के लिए करेगा। + +{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[1:25] hl[10:12,23] *} + +/// tip | सुझाव + +क्योंकि Pydantic इसे **Rust** side में serialize करेगा, आपको return type declare न करने की तुलना में कहीं बेहतर **performance** मिलेगी। + +/// + +### Non-async *path operation functions* { #non-async-path-operation-functions } + +आप नियमित `def` functions (बिना `async`) का भी उपयोग कर सकते हैं, और उसी तरह `yield` का उपयोग कर सकते हैं। + +FastAPI सुनिश्चित करेगा कि यह सही तरीके से run हो ताकि यह event loop को block न करे। + +क्योंकि इस मामले में function async नहीं है, सही return type `Iterable[Item]` होगा: + +{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[28:31] hl[29] *} + +### कोई Return Type नहीं { #no-return-type } + +आप return type को छोड़ भी सकते हैं। FastAPI data को convert करने और भेजने के लिए [`jsonable_encoder`](./encoder.md) का उपयोग करेगा। + +{* ../../docs_src/server_sent_events/tutorial001_py310.py ln[34:37] hl[35] *} + +## `ServerSentEvent` { #serversentevent } + +अगर आपको `event`, `id`, `retry`, या `comment` जैसे SSE fields set करने की ज़रूरत है, तो आप plain data के बजाय `ServerSentEvent` objects yield कर सकते हैं। + +`ServerSentEvent` को `fastapi.sse` से import करें: + +{* ../../docs_src/server_sent_events/tutorial002_py310.py hl[4,26] *} + +`data` field हमेशा JSON के रूप में encode किया जाता है। आप कोई भी value pass कर सकते हैं जिसे JSON के रूप में serialize किया जा सकता हो, जिसमें Pydantic models भी शामिल हैं। + +## Raw Data { #raw-data } + +अगर आपको JSON encoding के **बिना** data भेजना है, तो `data` के बजाय `raw_data` का उपयोग करें। + +यह pre-formatted text, log lines, या `[DONE]` जैसे विशेष "sentinel" values भेजने के लिए उपयोगी है। + +{* ../../docs_src/server_sent_events/tutorial003_py310.py hl[17] *} + +/// note | नोट + +`data` और `raw_data` mutually exclusive हैं। आप हर `ServerSentEvent` पर उनमें से केवल एक ही set कर सकते हैं। + +/// + +## `Last-Event-ID` के साथ फिर से शुरू करना { #resuming-with-last-event-id } + +जब कोई browser connection drop होने के बाद reconnect करता है, तो वह अंतिम प्राप्त `id` को `Last-Event-ID` header में भेजता है। + +आप इसे header parameter के रूप में read कर सकते हैं और stream को वहाँ से resume करने के लिए उपयोग कर सकते हैं जहाँ client ने छोड़ा था: + +{* ../../docs_src/server_sent_events/tutorial004_py310.py hl[25,27,31] *} + +## POST के साथ SSE { #sse-with-post } + +SSE **किसी भी HTTP method** के साथ काम करता है, केवल `GET` के साथ नहीं। + +यह [MCP](https://modelcontextprotocol.io) जैसे protocols के लिए उपयोगी है, जो `POST` पर SSE stream करते हैं: + +{* ../../docs_src/server_sent_events/tutorial005_py310.py hl[14] *} + +## तकनीकी विवरण { #technical-details } + +FastAPI कुछ SSE best practices को out of the box implement करता है। + +* जब कोई message नहीं आया हो, तो हर 15 सेकंड में **"keep alive" `ping` comment** भेजें, ताकि कुछ proxies connection को close न कर दें, जैसा कि [HTML specification: Server-Sent Events](https://html.spec.whatwg.org/multipage/server-sent-events.html#authoring-notes) में सुझाया गया है। +* stream की **caching रोकने** के लिए `Cache-Control: no-cache` header set करें। +* Nginx जैसे कुछ proxies में **buffering रोकने** के लिए एक special header `X-Accel-Buffering: no` set करें। + +आपको इसके लिए कुछ भी करने की ज़रूरत नहीं है, यह out of the box काम करता है। 🤓 diff --git a/docs/hi/docs/tutorial/sql-databases.md b/docs/hi/docs/tutorial/sql-databases.md new file mode 100644 index 000000000..041da16e9 --- /dev/null +++ b/docs/hi/docs/tutorial/sql-databases.md @@ -0,0 +1,357 @@ +# SQL (Relational) डेटाबेस { #sql-relational-databases } + +**FastAPI** के लिए SQL (relational) डेटाबेस इस्तेमाल करना required नहीं है। लेकिन आप **कोई भी डेटाबेस** इस्तेमाल कर सकते हैं जो आप चाहें। + +यहाँ हम [SQLModel](https://sqlmodel.tiangolo.com/) का उपयोग करके एक उदाहरण देखेंगे। + +**SQLModel**, [SQLAlchemy](https://www.sqlalchemy.org/) और Pydantic के ऊपर बना है। इसे **FastAPI** के उसी लेखक ने बनाया है ताकि यह उन FastAPI applications के लिए perfect match हो जिन्हें **SQL databases** इस्तेमाल करने की जरूरत होती है। + +/// tip | टिप + +आप अपनी पसंद की कोई भी दूसरी SQL या NoSQL डेटाबेस लाइब्रेरी इस्तेमाल कर सकते हैं (कुछ मामलों में इन्हें "ORMs" कहा जाता है), FastAPI आपको कुछ भी इस्तेमाल करने के लिए मजबूर नहीं करता। 😎 + +/// + +क्योंकि SQLModel, SQLAlchemy पर आधारित है, आप SQLAlchemy द्वारा **supported कोई भी डेटाबेस** आसानी से इस्तेमाल कर सकते हैं (जिससे वे SQLModel द्वारा भी supported हो जाते हैं), जैसे: + +* PostgreSQL +* MySQL +* SQLite +* Oracle +* Microsoft SQL Server, आदि। + +इस उदाहरण में, हम **SQLite** इस्तेमाल करेंगे, क्योंकि यह एक single file का उपयोग करता है और Python में इसके लिए integrated support है। इसलिए, आप इस उदाहरण को copy कर सकते हैं और जैसा है वैसा ही run कर सकते हैं। + +बाद में, अपनी production application के लिए, आप **PostgreSQL** जैसा database server इस्तेमाल करना चाह सकते हैं। + +/// tip | टिप + +**FastAPI** और **PostgreSQL** के साथ एक official project generator है जिसमें frontend और अधिक tools शामिल हैं: [https://github.com/fastapi/full-stack-fastapi-template](https://github.com/fastapi/full-stack-fastapi-template) + +/// + +यह एक बहुत ही सरल और छोटा tutorial है, अगर आप सामान्य रूप से databases, SQL, या अधिक advanced features के बारे में सीखना चाहते हैं, तो [SQLModel docs](https://sqlmodel.tiangolo.com/) पर जाएँ। + +## `SQLModel` install करें { #install-sqlmodel } + +सबसे पहले, सुनिश्चित करें कि आप अपना [virtual environment](../virtual-environments.md) बनाएँ, उसे activate करें, और फिर `sqlmodel` install करें: + +
+ +```console +$ pip install sqlmodel +---> 100% +``` + +
+ +## Single Model के साथ App बनाएँ { #create-the-app-with-a-single-model } + +हम पहले एक single **SQLModel** model के साथ app का सबसे सरल पहला version बनाएँगे। + +बाद में हम नीचे **multiple models** के साथ security और versatility बढ़ाते हुए इसे बेहतर बनाएँगे। 🤓 + +### Models बनाएँ { #create-models } + +`SQLModel` import करें और एक डेटाबेस model बनाएँ: + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *} + +`Hero` class एक Pydantic model से बहुत मिलती-जुलती है (वास्तव में, अंदर से, यह सच में *एक Pydantic model ही है*)। + +कुछ अंतर हैं: + +* `table=True` SQLModel को बताता है कि यह एक *table model* है, इसे SQL डेटाबेस में एक **table** को represent करना चाहिए, यह सिर्फ एक *data model* नहीं है (जैसा कि कोई भी दूसरी regular Pydantic class होती)। + +* `Field(primary_key=True)` SQLModel को बताता है कि `id` SQL डेटाबेस में **primary key** है (आप SQL primary keys के बारे में SQLModel docs में अधिक जान सकते हैं)। + + **ध्यान दें:** हम primary key field के लिए `int | None` का उपयोग करते हैं ताकि Python code में हम *बिना `id` के object बना सकें* (`id=None`), यह मानते हुए कि डेटाबेस *save करते समय इसे generate करेगा*। SQLModel समझता है कि डेटाबेस `id` provide करेगा और डेटाबेस schema में *column को non-null `INTEGER` के रूप में define करता है*। विवरण के लिए [primary keys पर SQLModel docs](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/#primary-key-id) देखें। + +* `Field(index=True)` SQLModel को बताता है कि उसे इस column के लिए एक **SQL index** बनाना चाहिए, जिससे इस column द्वारा filtered data पढ़ते समय डेटाबेस में तेज़ lookups हो सकें। + + SQLModel जान जाएगा कि `str` के रूप में declared कोई चीज़ `TEXT` type का SQL column होगी (या डेटाबेस के आधार पर `VARCHAR`)। + +### Engine बनाएँ { #create-an-engine } + +SQLModel `engine` (अंदर से यह वास्तव में SQLAlchemy `engine` है) वह है जो डेटाबेस से **connections को hold** करता है। + +आपके सभी code के लिए उसी डेटाबेस से connect करने हेतु **एक single `engine` object** होगा। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *} + +`check_same_thread=False` का उपयोग FastAPI को अलग-अलग threads में वही SQLite डेटाबेस इस्तेमाल करने देता है। यह necessary है क्योंकि **एक single request** **एक से अधिक thread** का उपयोग कर सकती है (उदाहरण के लिए dependencies में)। + +चिंता न करें, code जिस तरह structured है, उससे हम सुनिश्चित करेंगे कि बाद में हम **प्रति request एक single SQLModel *session*** इस्तेमाल करें, वास्तव में `check_same_thread` यही हासिल करने की कोशिश कर रहा है। + +### Tables बनाएँ { #create-the-tables } + +फिर हम एक function जोड़ते हैं जो सभी *table models* के लिए **tables बनाने** हेतु `SQLModel.metadata.create_all(engine)` का उपयोग करता है। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *} + +### Session Dependency बनाएँ { #create-a-session-dependency } + +**`Session`** वह है जो **objects को memory में store** करता है और data में required किसी भी बदलाव का track रखता है, फिर यह डेटाबेस से communicate करने के लिए **`engine` का उपयोग करता है**। + +हम `yield` के साथ एक FastAPI **dependency** बनाएँगे जो हर request के लिए एक नया `Session` provide करेगी। यही सुनिश्चित करता है कि हम प्रति request एक single session इस्तेमाल करें। 🤓 + +फिर हम इस dependency का उपयोग करने वाले बाकी code को आसान बनाने के लिए एक `Annotated` dependency `SessionDep` बनाते हैं। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *} + +### Startup पर Database Tables बनाएँ { #create-database-tables-on-startup } + +हम application शुरू होने पर database tables बनाएँगे। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *} + +यहाँ हम application startup event पर tables बनाते हैं। + +production के लिए आप शायद एक migration script इस्तेमाल करेंगे जो आपका app शुरू करने से पहले run होती है। 🤓 + +/// tip | टिप + +SQLModel में Alembic को wrap करने वाली migration utilities होंगी, लेकिन अभी के लिए, आप सीधे [Alembic](https://alembic.sqlalchemy.org/en/latest/) इस्तेमाल कर सकते हैं। + +/// + +### Hero बनाएँ { #create-a-hero } + +क्योंकि हर SQLModel model एक Pydantic model भी है, आप इसे उसी **type annotations** में इस्तेमाल कर सकते हैं जिनमें आप Pydantic models इस्तेमाल करते। + +उदाहरण के लिए, अगर आप `Hero` type का parameter declare करते हैं, तो यह **JSON body** से read किया जाएगा। + +उसी तरह, आप इसे function के **return type** के रूप में declare कर सकते हैं, और फिर data का shape automatic API docs UI में दिखाई देगा। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *} + +यहाँ हम `SessionDep` dependency (एक `Session`) का उपयोग करके नए `Hero` को `Session` instance में add करते हैं, changes को डेटाबेस में commit करते हैं, `hero` में data refresh करते हैं, और फिर उसे return करते हैं। + +### Heroes पढ़ें { #read-heroes } + +हम `select()` का उपयोग करके डेटाबेस से `Hero`s **read** कर सकते हैं। results को paginate करने के लिए हम `limit` और `offset` include कर सकते हैं। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *} + +### एक Hero पढ़ें { #read-one-hero } + +हम एक single `Hero` **read** कर सकते हैं। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *} + +### Hero Delete करें { #delete-a-hero } + +हम एक `Hero` को **delete** भी कर सकते हैं। + +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *} + +### App Run करें { #run-the-app } + +आप app run कर सकते हैं: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +फिर `/docs` UI पर जाएँ, आप देखेंगे कि **FastAPI** API को **document** करने के लिए इन **models** का उपयोग कर रहा है, और यह data को **serialize** और **validate** करने के लिए भी इनका उपयोग करेगा। + +
+ +
+ +## Multiple Models के साथ App Update करें { #update-the-app-with-multiple-models } + +अब आइए **security** और **versatility** बढ़ाने के लिए इस app को थोड़ा **refactor** करें। + +अगर आप previous app देखें, तो UI में आप देख सकते हैं कि अभी तक यह client को बनने वाले `Hero` का `id` तय करने देता है। 😱 + +हमें ऐसा नहीं होने देना चाहिए, वे DB में पहले से assigned किसी `id` को overwrite कर सकते हैं। `id` तय करना **backend** या **database** द्वारा किया जाना चाहिए, **client द्वारा नहीं**। + +इसके अलावा, हम hero के लिए `secret_name` बनाते हैं, लेकिन अब तक, हम इसे हर जगह return कर रहे हैं, यह बहुत **secret** नहीं है... 😅 + +हम कुछ **extra models** जोड़कर इन चीज़ों को ठीक करेंगे। यहीं SQLModel चमकेगा। ✨ + +### Multiple Models बनाएँ { #create-multiple-models } + +**SQLModel** में, कोई भी model class जिसमें `table=True` है, एक **table model** है। + +और कोई भी model class जिसमें `table=True` नहीं है, एक **data model** है, ये वास्तव में सिर्फ Pydantic models हैं (कुछ छोटे extra features के साथ)। 🤓 + +SQLModel के साथ, हम सभी मामलों में सभी fields को **duplicate करने से बचने** के लिए **inheritance** का उपयोग कर सकते हैं। + +#### `HeroBase` - base class { #herobase-the-base-class } + +आइए एक `HeroBase` model से शुरू करें जिसमें सभी models द्वारा **shared fields** हों: + +* `name` +* `age` + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *} + +#### `Hero` - *table model* { #hero-the-table-model } + +फिर आइए `Hero` बनाएँ, वास्तविक *table model*, जिसमें वे **extra fields** हों जो हमेशा दूसरे models में नहीं होते: + +* `id` +* `secret_name` + +क्योंकि `Hero`, `HeroBase` से inherit करता है, इसमें `HeroBase` में declared **fields** भी हैं, इसलिए `Hero` के लिए सभी fields हैं: + +* `id` +* `name` +* `age` +* `secret_name` + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *} + +#### `HeroPublic` - public *data model* { #heropublic-the-public-data-model } + +इसके बाद, हम एक `HeroPublic` model बनाते हैं, यही वह है जो API के clients को **return** किया जाएगा। + +इसमें `HeroBase` जैसे ही fields हैं, इसलिए इसमें `secret_name` शामिल नहीं होगा। + +आख़िरकार, हमारे heroes की identity protected है! 🥷 + +यह `id: int` को फिर से declare भी करता है। ऐसा करके, हम API clients के साथ एक **contract** बना रहे हैं, ताकि वे हमेशा expect कर सकें कि `id` मौजूद होगा और `int` होगा (यह कभी `None` नहीं होगा)। + +/// tip | टिप + +return model से यह सुनिश्चित करवाना कि कोई value हमेशा available है और हमेशा `int` है (`None` नहीं), API clients के लिए बहुत उपयोगी है, वे इस certainty के साथ बहुत सरल code लिख सकते हैं। + +साथ ही, **automatically generated clients** में सरल interfaces होंगे, ताकि आपकी API से communicate करने वाले developers आपकी API के साथ काम करते समय बहुत बेहतर अनुभव पा सकें। 😎 + +/// + +`HeroPublic` में सभी fields `HeroBase` जैसे ही हैं, जिसमें `id` को `int` (`None` नहीं) के रूप में declared किया गया है: + +* `id` +* `name` +* `age` + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *} + +#### `HeroCreate` - hero बनाने के लिए *data model* { #herocreate-the-data-model-to-create-a-hero } + +अब हम एक `HeroCreate` model बनाते हैं, यही वह है जो clients से आने वाले data को **validate** करेगा। + +इसमें `HeroBase` जैसे ही fields हैं, और इसमें `secret_name` भी है। + +अब, जब clients **एक नया hero create** करेंगे, वे `secret_name` भेजेंगे, यह डेटाबेस में store होगा, लेकिन वे secret names API में clients को return नहीं किए जाएँगे। + +/// tip | टिप + +आप **passwords** को ऐसे handle करेंगे। उन्हें receive करें, लेकिन API में return न करें। + +आप passwords की values को store करने से पहले **hash** भी करेंगे, **उन्हें plain text में कभी store न करें**। + +/// + +`HeroCreate` के fields हैं: + +* `name` +* `age` +* `secret_name` + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *} + +#### `HeroUpdate` - hero update करने के लिए *data model* { #heroupdate-the-data-model-to-update-a-hero } + +app के previous version में हमारे पास **hero update करने** का तरीका नहीं था, लेकिन अब **multiple models** के साथ, हम ऐसा कर सकते हैं। 🎉 + +`HeroUpdate` *data model* थोड़ा special है, इसमें **वे सभी same fields** हैं जिनकी एक नया hero create करने के लिए जरूरत होगी, लेकिन सभी fields **optional** हैं (उन सभी की default value है)। इस तरह, जब आप hero update करते हैं, तो आप सिर्फ वे fields भेज सकते हैं जिन्हें आप update करना चाहते हैं। + +क्योंकि सभी **fields वास्तव में change होते हैं** (type में अब `None` शामिल है और अब उनकी default value `None` है), हमें उन्हें **re-declare** करना होगा। + +हमें वास्तव में `HeroBase` से inherit करने की जरूरत नहीं है क्योंकि हम सभी fields को re-declare कर रहे हैं। मैं consistency के लिए इसे inherit करता हुआ छोड़ूँगा, लेकिन यह necessary नहीं है। यह personal taste का मामला अधिक है। 🤷 + +`HeroUpdate` के fields हैं: + +* `name` +* `age` +* `secret_name` + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *} + +### `HeroCreate` के साथ create करें और `HeroPublic` return करें { #create-with-herocreate-and-return-a-heropublic } + +अब जब हमारे पास **multiple models** हैं, हम app के उन parts को update कर सकते हैं जो उनका उपयोग करते हैं। + +हम request में एक `HeroCreate` *data model* receive करते हैं, और उससे, हम एक `Hero` *table model* बनाते हैं। + +इस नए *table model* `Hero` में client द्वारा भेजे गए fields होंगे, और इसमें डेटाबेस द्वारा generated एक `id` भी होगा। + +फिर हम उसी *table model* `Hero` को function से जैसा है वैसा ही return करते हैं। लेकिन क्योंकि हम `response_model` को `HeroPublic` *data model* के साथ declare करते हैं, **FastAPI** data को validate और serialize करने के लिए `HeroPublic` का उपयोग करेगा। + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *} + +/// tip | टिप + +अब हम **return type annotation** `-> HeroPublic` के बजाय `response_model=HeroPublic` इस्तेमाल करते हैं क्योंकि जो value हम return कर रहे हैं वह वास्तव में `HeroPublic` *नहीं* है। + +अगर हमने `-> HeroPublic` declare किया होता, तो आपका editor और linter complain करते (और सही करते) कि आप `HeroPublic` के बजाय `Hero` return कर रहे हैं। + +इसे `response_model` में declare करके हम **FastAPI** को अपना काम करने के लिए कह रहे हैं, बिना type annotations और आपके editor व अन्य tools से मिलने वाली help में interfere किए। + +/// + +### `HeroPublic` के साथ Heroes पढ़ें { #read-heroes-with-heropublic } + +हम `Hero`s को **read** करने के लिए पहले जैसा ही कर सकते हैं, फिर से, हम यह सुनिश्चित करने के लिए `response_model=list[HeroPublic]` का उपयोग करते हैं कि data सही तरीके से validate और serialize हो। + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *} + +### `HeroPublic` के साथ एक Hero पढ़ें { #read-one-hero-with-heropublic } + +हम एक single hero **read** कर सकते हैं: + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *} + +### `HeroUpdate` के साथ Hero Update करें { #update-a-hero-with-heroupdate } + +हम **hero update** कर सकते हैं। इसके लिए हम HTTP `PATCH` operation इस्तेमाल करते हैं। + +और code में, हमें client द्वारा भेजे गए सभी data के साथ एक `dict` मिलता है, **सिर्फ client द्वारा भेजा गया data**, उन किसी भी values को exclude करते हुए जो सिर्फ default values होने के कारण वहाँ होतीं। ऐसा करने के लिए हम `exclude_unset=True` इस्तेमाल करते हैं। यही main trick है। 🪄 + +फिर हम `hero_data` के data के साथ `hero_db` को update करने के लिए `hero_db.sqlmodel_update(hero_data)` इस्तेमाल करते हैं। + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *} + +### फिर से Hero Delete करें { #delete-a-hero-again } + +hero को **delete करना** लगभग पहले जैसा ही रहता है। + +हम इसमें सब कुछ refactor करने की इच्छा पूरी नहीं करेंगे। 😅 + +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *} + +### App फिर से Run करें { #run-the-app-again } + +आप app फिर से run कर सकते हैं: + +
+ +```console +$ fastapi dev + +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) +``` + +
+ +अगर आप `/docs` API UI पर जाते हैं, तो आप देखेंगे कि यह अब updated है, और hero create करते समय यह client से `id` receive करने की expect नहीं करेगा, आदि। + +
+ +
+ +## Recap { #recap } + +आप SQL डेटाबेस के साथ interact करने और code को *data models* और *table models* के साथ सरल बनाने के लिए [**SQLModel**](https://sqlmodel.tiangolo.com/) का उपयोग कर सकते हैं। + +आप **SQLModel** docs में बहुत कुछ और सीख सकते हैं, वहाँ **FastAPI** के साथ SQLModel इस्तेमाल करने पर एक लंबा mini [tutorial](https://sqlmodel.tiangolo.com/tutorial/fastapi/) है। 🚀 diff --git a/docs/hi/docs/tutorial/static-files.md b/docs/hi/docs/tutorial/static-files.md new file mode 100644 index 000000000..6a41c900a --- /dev/null +++ b/docs/hi/docs/tutorial/static-files.md @@ -0,0 +1,48 @@ +# Static Files { #static-files } + +आप `StaticFiles` का उपयोग करके किसी directory से static files को अपने-आप serve कर सकते हैं। + +/// tip | सुझाव + +अगर आपको frontend host करना है, तो इसके बजाय `app.frontend()` का उपयोग करें, इसके बारे में [Frontend](frontend.md) में पढ़ें। + +`app.frontend()` अंदरूनी तौर पर `StaticFiles` का उपयोग करता है, जिसमें frontends के लिए कई अतिरिक्त फायदे होते हैं, जैसे client-side routing को handle करना। + +/// + +## `StaticFiles` का उपयोग करें { #use-staticfiles } + +* `StaticFiles` import करें। +* किसी विशिष्ट path में `StaticFiles()` instance को "Mount" करें। + +{* ../../docs_src/static_files/tutorial001_py310.py hl[2,6] *} + +/// note | तकनीकी विवरण + +आप `from starlette.staticfiles import StaticFiles` भी उपयोग कर सकते हैं। + +**FastAPI** आपकी, developer की, सुविधा के लिए वही `starlette.staticfiles` `fastapi.staticfiles` के रूप में प्रदान करता है। लेकिन यह वास्तव में सीधे Starlette से आता है। + +/// + +### "Mounting" क्या है { #what-is-mounting } + +"Mounting" का मतलब है किसी विशिष्ट path में एक पूरी "independent" application जोड़ना, जो फिर सभी sub-paths को handle करने का काम करती है। + +यह `APIRouter` का उपयोग करने से अलग है, क्योंकि mounted application पूरी तरह independent होती है। आपकी main application की OpenAPI और docs में mounted application से कुछ भी शामिल नहीं होगा, आदि। + +आप इसके बारे में [Advanced User Guide](../advanced/index.md) में और पढ़ सकते हैं। + +## विवरण { #details } + +पहला `"/static"` उस sub-path को संदर्भित करता है जिस पर यह "sub-application" "mount" की जाएगी। इसलिए, `"/static"` से शुरू होने वाला कोई भी path इसके द्वारा handle किया जाएगा। + +`directory="static"` उस directory के नाम को संदर्भित करता है जिसमें आपकी static files होती हैं। + +`name="static"` इसे एक नाम देता है, जिसे **FastAPI** द्वारा internally उपयोग किया जा सकता है। + +ये सभी parameters "`static`" से अलग हो सकते हैं, इन्हें अपनी application की जरूरतों और विशिष्ट विवरणों के अनुसार adjust करें। + +## अधिक जानकारी { #more-info } + +अधिक विवरण और options के लिए [Static Files के बारे में Starlette की docs](https://www.starlette.dev/staticfiles/) देखें। diff --git a/docs/hi/docs/tutorial/stream-json-lines.md b/docs/hi/docs/tutorial/stream-json-lines.md new file mode 100644 index 000000000..9d0645fc5 --- /dev/null +++ b/docs/hi/docs/tutorial/stream-json-lines.md @@ -0,0 +1,111 @@ +# JSON Lines को Stream करें { #stream-json-lines } + +आपके पास data की एक sequence हो सकती है जिसे आप "**stream**" में भेजना चाहें, आप इसे **JSON Lines** के साथ कर सकते हैं। + +/// note | टिप्पणी + +FastAPI 0.134.0 में जोड़ा गया। + +/// + +## Stream क्या है? { #what-is-a-stream } + +"**Streaming**" data का मतलब है कि आपका app items की पूरी sequence के तैयार होने का इंतज़ार किए बिना client को data items भेजना शुरू कर देगा। + +तो, यह पहला item भेजेगा, client उसे receive करके process करना शुरू कर देगा, और आप अभी भी अगला item produce कर रहे हो सकते हैं। + +```mermaid +sequenceDiagram + participant App + participant Client + + App->>App: Produce Item 1 + App->>Client: Send Item 1 + App->>App: Produce Item 2 + Client->>Client: Process Item 1 + App->>Client: Send Item 2 + App->>App: Produce Item 3 + Client->>Client: Process Item 2 + App->>Client: Send Item 3 + Client->>Client: Process Item 3 + Note over App: Keeps producing... + Note over Client: Keeps consuming... +``` + +यह एक infinite stream भी हो सकती है, जहाँ आप लगातार data भेजते रहते हैं। + +## JSON Lines { #json-lines } + +इन मामलों में, "**JSON Lines**" भेजना आम है, जो एक ऐसा format है जहाँ आप हर line में एक JSON object भेजते हैं। + +एक response का content type `application/jsonl` होगा (`application/json` के बजाय) और body कुछ इस तरह होगी: + +```json +{"name": "Plumbus", "description": "A multi-purpose household device."} +{"name": "Portal Gun", "description": "A portal opening device."} +{"name": "Meeseeks Box", "description": "A box that summons a Meeseeks."} +``` + +यह JSON array (Python list के equivalent) से बहुत मिलता-जुलता है, लेकिन `[]` में wrap होने और items के बीच `,` होने के बजाय, इसमें **हर line में एक JSON object** होता है, वे एक new line character से अलग होते हैं। + +/// note | टिप्पणी + +महत्वपूर्ण बात यह है कि आपका app हर line को बारी-बारी से produce कर पाएगा, जबकि client पिछली lines को consume करता रहेगा। + +/// + +/// note | तकनीकी विवरण + +क्योंकि हर JSON object एक new line से अलग होगा, उनके content में literal new line characters नहीं हो सकते, लेकिन उनमें escaped new lines (`\n`) हो सकती हैं, जो JSON standard का हिस्सा है। + +लेकिन आमतौर पर आपको इसके बारे में चिंता करने की ज़रूरत नहीं होगी, यह अपने आप हो जाता है, आगे पढ़ते रहें। 🤓 + +/// + +## उपयोग के मामले { #use-cases } + +आप इसका उपयोग **AI LLM** service से, **logs** या **telemetry** से, या अन्य प्रकार के data से data stream करने के लिए कर सकते हैं जिन्हें **JSON** items में structure किया जा सकता है। + +/// tip | सुझाव + +अगर आप binary data stream करना चाहते हैं, उदाहरण के लिए video या audio, तो advanced guide देखें: [Data Stream करें](../advanced/stream-data.md)। + +/// + +## FastAPI के साथ JSON Lines Stream करें { #stream-json-lines-with-fastapi } + +FastAPI के साथ JSON Lines stream करने के लिए, आप अपनी *path operation function* में `return` का उपयोग करने के बजाय, हर item को बारी-बारी से produce करने के लिए `yield` का उपयोग कर सकते हैं। + +{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[24] *} + +अगर हर JSON item जिसे आप वापस भेजना चाहते हैं, type `Item` (एक Pydantic model) का है और यह एक async function है, तो आप return type को `AsyncIterable[Item]` के रूप में declare कर सकते हैं: + +{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[1:24] hl[9:11,22] *} + +अगर आप return type declare करते हैं, तो FastAPI इसका उपयोग data को **validate** करने, OpenAPI में इसे **document** करने, इसे **filter** करने, और Pydantic का उपयोग करके इसे **serialize** करने के लिए करेगा। + +/// tip | सुझाव + +क्योंकि Pydantic इसे **Rust** side में serialize करेगा, आपको return type declare न करने की तुलना में बहुत अधिक **performance** मिलेगी। + +/// + +### Non-async *path operation functions* { #non-async-path-operation-functions } + +आप regular `def` functions (बिना `async` के) भी उपयोग कर सकते हैं, और उसी तरह `yield` का उपयोग कर सकते हैं। + +FastAPI यह सुनिश्चित करेगा कि यह सही तरीके से चले ताकि यह event loop को block न करे। + +क्योंकि इस मामले में function async नहीं है, सही return type `Iterable[Item]` होगा: + +{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[27:30] hl[28] *} + +### कोई Return Type नहीं { #no-return-type } + +आप return type को omit भी कर सकते हैं। FastAPI फिर data को ऐसी चीज़ में convert करने के लिए [`jsonable_encoder`](./encoder.md) का उपयोग करेगा जिसे JSON में serialize किया जा सके और फिर उसे JSON Lines के रूप में भेजेगा। + +{* ../../docs_src/stream_json_lines/tutorial001_py310.py ln[33:36] hl[34] *} + +## Server-Sent Events (SSE) { #server-sent-events-sse } + +FastAPI में Server-Sent Events (SSE) के लिए भी first-class support है, जो काफी समान हैं लेकिन कुछ extra details के साथ। आप इनके बारे में अगले chapter में जान सकते हैं: [Server-Sent Events (SSE)](server-sent-events.md)। 🤓 diff --git a/docs/hi/docs/tutorial/testing.md b/docs/hi/docs/tutorial/testing.md new file mode 100644 index 000000000..3a05e7917 --- /dev/null +++ b/docs/hi/docs/tutorial/testing.md @@ -0,0 +1,193 @@ +# Testing { #testing } + +[Starlette](https://www.starlette.dev/testclient/) की बदौलत, **FastAPI** applications की testing आसान और आनंददायक है। + +यह [HTTPX](https://www.python-httpx.org) पर आधारित है, जो बदले में Requests के आधार पर design किया गया है, इसलिए यह बहुत परिचित और intuitive है। + +इसके साथ, आप **FastAPI** के साथ सीधे [pytest](https://docs.pytest.org/) का उपयोग कर सकते हैं। + +## `TestClient` का उपयोग करना { #using-testclient } + +/// note | नोट + +`TestClient` का उपयोग करने के लिए, पहले [`httpx`](https://www.python-httpx.org) install करें। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर इसे install करते हैं, उदाहरण के लिए: + +```console +$ pip install httpx +``` + +/// + +`TestClient` import करें। + +अपनी **FastAPI** application को इसमें pass करके एक `TestClient` बनाएँ। + +ऐसी functions बनाएँ जिनका नाम `test_` से शुरू होता हो (यह एक standard `pytest` convention है)। + +`TestClient` object का उपयोग उसी तरह करें जैसे आप `httpx` के साथ करते हैं। + +जिन चीज़ों की आपको जाँच करनी है उनके लिए standard Python expressions के साथ सरल `assert` statements लिखें (फिर से, standard `pytest`)। + +{* ../../docs_src/app_testing/tutorial001_py310.py hl[2,12,15:18] *} + +/// tip | टिप + +ध्यान दें कि testing functions सामान्य `def` हैं, `async def` नहीं। + +और client को की जाने वाली calls भी सामान्य calls हैं, `await` का उपयोग नहीं करतीं। + +यह आपको बिना जटिलताओं के सीधे `pytest` का उपयोग करने देता है। + +/// + +/// note | तकनीकी विवरण + +आप `from starlette.testclient import TestClient` का भी उपयोग कर सकते हैं। + +**FastAPI** आपकी सुविधा के लिए, developer के रूप में, उसी `starlette.testclient` को `fastapi.testclient` के रूप में प्रदान करता है। लेकिन यह सीधे Starlette से आता है। + +/// + +/// tip | टिप + +अगर आप अपनी FastAPI application को requests भेजने के अलावा अपने tests में `async` functions call करना चाहते हैं (जैसे asynchronous database functions), तो advanced tutorial में [Async Tests](../advanced/async-tests.md) देखें। + +/// + +## Tests को अलग करना { #separating-tests } + +एक वास्तविक application में, आपके tests शायद किसी अलग file में होंगे। + +और आपकी **FastAPI** application भी कई files/modules आदि से मिलकर बनी हो सकती है। + +### **FastAPI** app file { #fastapi-app-file } + +मान लीजिए आपके पास [Bigger Applications](bigger-applications.md) में बताए गए अनुसार एक file structure है: + +``` +. +├── app +│   ├── __init__.py +│   └── main.py +``` + +`main.py` file में आपकी **FastAPI** app है: + + +{* ../../docs_src/app_testing/app_a_py310/main.py *} + +### Testing file { #testing-file } + +फिर आपके पास अपने tests के साथ एक file `test_main.py` हो सकती है। यह उसी Python package में हो सकती है (वही directory जिसमें `__init__.py` file है): + +``` hl_lines="5" +. +├── app +│   ├── __init__.py +│   ├── main.py +│   └── test_main.py +``` + +क्योंकि यह file उसी package में है, आप `main` module (`main.py`) से object `app` को import करने के लिए relative imports का उपयोग कर सकते हैं: + +{* ../../docs_src/app_testing/app_a_py310/test_main.py hl[3] *} + + +...और tests के लिए code पहले जैसा ही रख सकते हैं। + +## Testing: विस्तृत उदाहरण { #testing-extended-example } + +अब इस उदाहरण को आगे बढ़ाते हैं और अलग-अलग हिस्सों की testing कैसे करनी है यह देखने के लिए और विवरण जोड़ते हैं। + +### विस्तारित **FastAPI** app file { #extended-fastapi-app-file } + +आइए पहले जैसी ही file structure के साथ जारी रखें: + +``` +. +├── app +│   ├── __init__.py +│   ├── main.py +│   └── test_main.py +``` + +मान लीजिए अब आपकी **FastAPI** app वाली file `main.py` में कुछ अन्य **path operations** हैं। + +इसमें एक `GET` operation है जो error return कर सकता है। + +इसमें एक `POST` operation है जो कई errors return कर सकता है। + +दोनों *path operations* को `X-Token` header required है। + +{* ../../docs_src/app_testing/app_b_an_py310/main.py *} + +### विस्तारित testing file { #extended-testing-file } + +फिर आप विस्तारित tests के साथ `test_main.py` को update कर सकते हैं: + +{* ../../docs_src/app_testing/app_b_an_py310/test_main.py *} + + +जब भी आपको client से request में जानकारी pass करवानी हो और आपको पता न हो कि कैसे, तो आप खोज (Google) सकते हैं कि इसे `httpx` में कैसे करें, या यहाँ तक कि `requests` के साथ कैसे करें, क्योंकि HTTPX का design Requests के design पर आधारित है। + +फिर आप अपने tests में वही करते हैं। + +उदाहरण के लिए: + +* *path* या *query* parameter pass करने के लिए, इसे URL में ही जोड़ें। +* JSON body pass करने के लिए, `json` parameter में एक Python object (जैसे `dict`) pass करें। +* अगर आपको JSON के बजाय *Form Data* भेजना है, तो इसके बजाय `data` parameter का उपयोग करें। +* *headers* pass करने के लिए, `headers` parameter में एक `dict` का उपयोग करें। +* *cookies* के लिए, `cookies` parameter में एक `dict`। + +backend को data कैसे pass करें (`httpx` या `TestClient` का उपयोग करके) इसके बारे में अधिक जानकारी के लिए [HTTPX documentation](https://www.python-httpx.org) देखें। + +/// note | नोट + +ध्यान दें कि `TestClient` ऐसा data receive करता है जिसे JSON में convert किया जा सकता है, Pydantic models नहीं। + +अगर आपके test में एक Pydantic model है और आप testing के दौरान उसका data application को भेजना चाहते हैं, तो आप [JSON Compatible Encoder](encoder.md) में बताए गए `jsonable_encoder` का उपयोग कर सकते हैं। + +/// + +## इसे चलाएँ { #run-it } + +उसके बाद, आपको बस `pytest` install करना है। + +सुनिश्चित करें कि आप एक [virtual environment](../virtual-environments.md) बनाते हैं, उसे activate करते हैं, और फिर इसे install करते हैं, उदाहरण के लिए: + +
+ +```console +$ pip install pytest + +---> 100% +``` + +
+ +यह files और tests को automatically detect करेगा, उन्हें execute करेगा, और results आपको वापस report करेगा। + +Tests चलाएँ: + +
+ +```console +$ pytest + +================ test session starts ================ +platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 +rootdir: /home/user/code/superawesome-cli/app +plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1 +collected 6 items + +---> 100% + +test_main.py ...... [100%] + +================= 1 passed in 0.03s ================= +``` + +
diff --git a/docs/ja/docs/tutorial/frontend.md b/docs/ja/docs/tutorial/frontend.md index facbbc1d1..eb23d1e9b 100644 --- a/docs/ja/docs/tutorial/frontend.md +++ b/docs/ja/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ npm run build 他の router 内のものを含め、アプリ内の通常の *path operations* は引き続き優先されます。 +## 依存関係とミドルウェア { #dependencies-and-middleware } + +フロントエンドのレスポンスは通常の **FastAPI** アプリケーション内で実行されるため、HTTP ミドルウェアが適用されます。 + +アプリ、`APIRouter`、および `include_router()` からの依存関係もフロントエンドのレスポンスに適用されます。これは、cookie 認証などでフロントエンドを保護する場合に役立ちます。 + ## 静的ビルド出力のみ { #static-build-output-only } `app.frontend()` は、フロントエンドのビルドで既に生成されたファイルを配信します。 diff --git a/docs/ko/docs/tutorial/frontend.md b/docs/ko/docs/tutorial/frontend.md index 50149432c..452aed034 100644 --- a/docs/ko/docs/tutorial/frontend.md +++ b/docs/ko/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ npm run build 다른 라우터에 있는 것을 포함하여, 애플리케이션의 모든 일반 *경로 처리*가 여전히 우선합니다. +## 의존성과 미들웨어 { #dependencies-and-middleware } + +프론트엔드 응답은 일반 **FastAPI** 애플리케이션 안에서 실행되므로 HTTP 미들웨어가 적용됩니다. + +애플리케이션, `APIRouter`, `include_router()`의 의존성도 프론트엔드 응답에 적용됩니다. 이는 쿠키 인증 등으로 프론트엔드를 보호하는 데 유용할 수 있습니다. + ## 정적 빌드 출력만 사용하기 { #static-build-output-only } `app.frontend()`는 프론트엔드 빌드에서 이미 생성된 파일을 제공합니다. diff --git a/docs/pt/docs/tutorial/frontend.md b/docs/pt/docs/tutorial/frontend.md index 872c32e9e..f0d08ce98 100644 --- a/docs/pt/docs/tutorial/frontend.md +++ b/docs/pt/docs/tutorial/frontend.md @@ -1,6 +1,6 @@ # Frontend { #frontend } -Você pode servir aplicações frontend estáticas com `app.frontend()` ou `router.frontend()`. +Você pode servir aplicações frontend estáticas com `app.frontend()` (ou `router.frontend()`). Isso é útil para ferramentas de frontend que geram arquivos estáticos, como React com Vite, TanStack Router, Astro, Vue, Svelte, Angular, Solid e outras. @@ -126,6 +126,12 @@ Neste exemplo, os paths de frontend são servidos em `/app`. Quaisquer *operações de rota* regulares na aplicação ainda terão precedência, inclusive em outros routers. +## Dependências e Middleware { #dependencies-and-middleware } + +Responses de frontend são executadas dentro da aplicação **FastAPI** normal, então middlewares HTTP se aplicam a elas. + +Dependências da aplicação, de um `APIRouter` e de `include_router()` também se aplicam a responses de frontend. Isso pode ser útil para proteger um frontend com autenticação por cookie ou similar. + ## Apenas Saída de Build Estático { #static-build-output-only } `app.frontend()` serve arquivos já gerados pelo build do seu frontend. diff --git a/docs/ru/docs/tutorial/frontend.md b/docs/ru/docs/tutorial/frontend.md index 3b3e43809..b7f4dc0cb 100644 --- a/docs/ru/docs/tutorial/frontend.md +++ b/docs/ru/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ npm run build Любые обычные *операции пути* в приложении всё равно будут иметь приоритет, включая операции в других роутерах. +## Зависимости и middleware { #dependencies-and-middleware } + +HTTP-ответы фронтенда выполняются внутри обычного приложения **FastAPI**, поэтому к ним применяется HTTP middleware. + +Зависимости из приложения, из `APIRouter` и из `include_router()` также применяются к HTTP-ответам фронтенда. Это может быть полезно для защиты фронтенда с помощью аутентификации на основе cookie или похожего механизма. + ## Только статический результат сборки { #static-build-output-only } `app.frontend()` отдаёт файлы, уже сгенерированные сборкой вашего фронтенда. diff --git a/docs/tr/docs/tutorial/frontend.md b/docs/tr/docs/tutorial/frontend.md index 7918ca593..a9797dc61 100644 --- a/docs/tr/docs/tutorial/frontend.md +++ b/docs/tr/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ Bu örnekte frontend path'leri `/app` altında sunulur. Uygulamadaki herhangi bir normal *path operation*, diğer router'larda olanlar dahil, yine öncelikli olur. +## Dependencies ve Middleware { #dependencies-and-middleware } + +Frontend response'ları normal **FastAPI** uygulaması içinde çalışır, bu yüzden HTTP middleware onlara uygulanır. + +Uygulamadan, bir `APIRouter`'dan ve `include_router()`'dan gelen dependencies de frontend response'larına uygulanır. Bu, bir frontend'i cookie authentication veya benzeri bir yöntemle korumak için kullanışlı olabilir. + ## Yalnızca Statik Build Çıktısı { #static-build-output-only } `app.frontend()`, frontend build'iniz tarafından önceden oluşturulmuş dosyaları sunar. diff --git a/docs/uk/docs/tutorial/frontend.md b/docs/uk/docs/tutorial/frontend.md index c85e6693e..36703764f 100644 --- a/docs/uk/docs/tutorial/frontend.md +++ b/docs/uk/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ npm run build Будь-які звичайні *операції шляху* в застосунку все ще матимуть перевагу, включно з операціями в інших роутерах. +## Залежності та проміжне програмне забезпечення { #dependencies-and-middleware } + +Фронтенд-відповіді виконуються всередині звичайного застосунку **FastAPI**, тому HTTP-проміжне програмне забезпечення застосовується до них. + +Залежності із застосунку, з `APIRouter` і з `include_router()` також застосовуються до фронтенд-відповідей. Це може бути корисно для захисту фронтенду за допомогою автентифікації на основі кукі або подібного. + ## Лише статичний результат збірки { #static-build-output-only } `app.frontend()` обслуговує файли, вже згенеровані вашою фронтенд-збіркою. diff --git a/docs/zh-hant/docs/tutorial/frontend.md b/docs/zh-hant/docs/tutorial/frontend.md index 0c568b9f5..fbfd8b3c5 100644 --- a/docs/zh-hant/docs/tutorial/frontend.md +++ b/docs/zh-hant/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ npm run build 應用程式中的任何一般*路徑操作*仍會優先,包括其他 router 中的路徑操作。 +## 依賴項與中介軟體 { #dependencies-and-middleware } + +前端回應會在一般的 **FastAPI** 應用程式內執行,因此 HTTP middleware 會套用到它們。 + +來自 app、`APIRouter` 和 `include_router()` 的 dependencies 也會套用到前端回應。這對使用 cookie authentication 或類似方式保護前端很有用。 + ## 僅限靜態建置輸出 { #static-build-output-only } `app.frontend()` 會提供你的前端建置已經產生的檔案。 diff --git a/docs/zh/docs/tutorial/frontend.md b/docs/zh/docs/tutorial/frontend.md index 8b57bbe60..32169076e 100644 --- a/docs/zh/docs/tutorial/frontend.md +++ b/docs/zh/docs/tutorial/frontend.md @@ -126,6 +126,12 @@ npm run build 应用中的任何常规*路径操作*仍会优先,包括其他 router 中的路径操作。 +## 依赖项和中间件 { #dependencies-and-middleware } + +前端响应在普通 **FastAPI** 应用内部运行,因此 HTTP 中间件会应用于它们。 + +来自 app、`APIRouter` 和 `include_router()` 的依赖项也会应用于前端响应。这可用于通过 cookie 身份验证或类似方式保护前端。 + ## 仅限静态构建输出 { #static-build-output-only } `app.frontend()` 提供的是你的前端构建已经生成的文件。 diff --git a/fastapi/__init__.py b/fastapi/__init__.py index df55e7a6c..5177ea094 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.139.0" +__version__ = "0.139.2" from starlette import status as status diff --git a/fastapi/routing.py b/fastapi/routing.py index c442b122b..78207778a 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -7,6 +7,7 @@ import inspect import json import os import stat +import threading import types from collections.abc import ( AsyncIterator, @@ -1571,6 +1572,9 @@ class RouteContext: class _IncludedRouter(BaseRoute): original_router: "APIRouter" include_context: _RouterIncludeContext + _effective_routes_lock: Any = field( + default_factory=threading.Lock, repr=False, compare=False + ) _effective_candidates: list["_EffectiveRouteContext | _IncludedRouter"] = field( default_factory=list ) @@ -1584,44 +1588,53 @@ class _IncludedRouter(BaseRoute): routes_version = self.original_router._get_routes_version() if routes_version == self._effective_candidates_version: return self._effective_candidates - self._effective_candidates = [] - candidates = self.original_router.routes - for route in candidates: - if isinstance(route, _IncludedRouter): - child_context = self.include_context.combine(route.include_context) - child_branch = _IncludedRouter( - original_router=route.original_router, - include_context=child_context, - ) - self._effective_candidates.append(child_branch) - continue - route_context = self._build_effective_context(route) - if route_context is not None: - self._effective_candidates.append(route_context) - self._effective_candidates_version = routes_version - return self._effective_candidates + with self._effective_routes_lock: + routes_version = self.original_router._get_routes_version() + if routes_version == self._effective_candidates_version: + return self._effective_candidates + effective_candidates: list[_EffectiveRouteContext | _IncludedRouter] = [] + for route in self.original_router.routes: + if isinstance(route, _IncludedRouter): + child_context = self.include_context.combine(route.include_context) + child_branch = _IncludedRouter( + original_router=route.original_router, + include_context=child_context, + ) + effective_candidates.append(child_branch) + continue + route_context = self._build_effective_context(route) + if route_context is not None: + effective_candidates.append(route_context) + self._effective_candidates = effective_candidates + self._effective_candidates_version = routes_version + return effective_candidates def effective_low_priority_routes(self) -> list["_EffectiveRouteContext"]: routes_version = self.original_router._get_routes_version() if routes_version == self._effective_low_priority_routes_version: return self._effective_low_priority_routes - self._effective_low_priority_routes = [] - for route in self.original_router._low_priority_routes: - route_context = self._build_effective_context(route) - if route_context is not None: - self._effective_low_priority_routes.append(route_context) - for route in self.original_router.routes: - if isinstance(route, _IncludedRouter): - child_context = self.include_context.combine(route.include_context) - child_branch = _IncludedRouter( - original_router=route.original_router, - include_context=child_context, - ) - self._effective_low_priority_routes.extend( - child_branch.effective_low_priority_routes() - ) - self._effective_low_priority_routes_version = routes_version - return self._effective_low_priority_routes + with self._effective_routes_lock: + routes_version = self.original_router._get_routes_version() + if routes_version == self._effective_low_priority_routes_version: + return self._effective_low_priority_routes + effective_low_priority_routes: list[_EffectiveRouteContext] = [] + for route in self.original_router._low_priority_routes: + route_context = self._build_effective_context(route) + if route_context is not None: + effective_low_priority_routes.append(route_context) + for route in self.original_router.routes: + if isinstance(route, _IncludedRouter): + child_context = self.include_context.combine(route.include_context) + child_branch = _IncludedRouter( + original_router=route.original_router, + include_context=child_context, + ) + effective_low_priority_routes.extend( + child_branch.effective_low_priority_routes() + ) + self._effective_low_priority_routes = effective_low_priority_routes + self._effective_low_priority_routes_version = routes_version + return effective_low_priority_routes def _build_effective_context( self, route: BaseRoute @@ -1984,24 +1997,13 @@ def _iter_accept_media_types(accept: str) -> Iterator[tuple[str, float]]: def _is_frontend_navigation_request(scope: Scope) -> bool: - route_path = get_route_path(scope) - final_segment = route_path.rsplit("/", 1)[-1] - if os.path.splitext(final_segment)[1]: - return False request = Request(scope) - wildcard_accepted = False - html_rejected = False for media_type, quality in _iter_accept_media_types( request.headers.get("accept", "") ): - if media_type in {"text/html", "application/xhtml+xml"}: - if quality == 0: - html_rejected = True - else: - return True - elif media_type == "*/*" and quality != 0: - wildcard_accepted = True - return wildcard_accepted and not html_rejected + if media_type in {"text/html", "application/xhtml+xml"} and quality != 0: + return True + return False class _FrontendRoute(BaseRoute): diff --git a/scripts/contributors.py b/scripts/contributors.py deleted file mode 100644 index 5e9d72d64..000000000 --- a/scripts/contributors.py +++ /dev/null @@ -1,316 +0,0 @@ -import logging -import secrets -import subprocess -from collections import Counter -from datetime import datetime -from pathlib import Path -from typing import Any - -import httpx -import yaml -from github import Github -from pydantic import BaseModel, SecretStr -from pydantic_settings import BaseSettings - -github_graphql_url = "https://api.github.com/graphql" - - -prs_query = """ -query Q($after: String) { - repository(name: "fastapi", owner: "fastapi") { - pullRequests(first: 100, after: $after) { - edges { - cursor - node { - number - labels(first: 100) { - nodes { - name - } - } - author { - login - avatarUrl - url - } - title - createdAt - lastEditedAt - updatedAt - state - reviews(first:100) { - nodes { - author { - login - avatarUrl - url - } - state - } - } - } - } - } - } -} -""" - - -class Author(BaseModel): - login: str - avatarUrl: str - url: str - - -class LabelNode(BaseModel): - name: str - - -class Labels(BaseModel): - nodes: list[LabelNode] - - -class ReviewNode(BaseModel): - author: Author | None = None - state: str - - -class Reviews(BaseModel): - nodes: list[ReviewNode] - - -class PullRequestNode(BaseModel): - number: int - labels: Labels - author: Author | None = None - title: str - createdAt: datetime - lastEditedAt: datetime | None = None - updatedAt: datetime | None = None - state: str - reviews: Reviews - - -class PullRequestEdge(BaseModel): - cursor: str - node: PullRequestNode - - -class PullRequests(BaseModel): - edges: list[PullRequestEdge] - - -class PRsRepository(BaseModel): - pullRequests: PullRequests - - -class PRsResponseData(BaseModel): - repository: PRsRepository - - -class PRsResponse(BaseModel): - data: PRsResponseData - - -class Settings(BaseSettings): - github_token: SecretStr - github_repository: str - httpx_timeout: int = 30 - - -def get_graphql_response( - *, - settings: Settings, - query: str, - after: str | None = None, -) -> dict[str, Any]: - headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"} - variables = {"after": after} - response = httpx.post( - github_graphql_url, - headers=headers, - timeout=settings.httpx_timeout, - json={"query": query, "variables": variables, "operationName": "Q"}, - ) - if response.status_code != 200: - logging.error(f"Response was not 200, after: {after}") - logging.error(response.text) - raise RuntimeError(response.text) - data = response.json() - if "errors" in data: - logging.error(f"Errors in response, after: {after}") - logging.error(data["errors"]) - logging.error(response.text) - raise RuntimeError(response.text) - return data - - -def get_graphql_pr_edges( - *, settings: Settings, after: str | None = None -) -> list[PullRequestEdge]: - data = get_graphql_response(settings=settings, query=prs_query, after=after) - graphql_response = PRsResponse.model_validate(data) - return graphql_response.data.repository.pullRequests.edges - - -def get_pr_nodes(settings: Settings) -> list[PullRequestNode]: - pr_nodes: list[PullRequestNode] = [] - pr_edges = get_graphql_pr_edges(settings=settings) - - while pr_edges: - for edge in pr_edges: - pr_nodes.append(edge.node) - last_edge = pr_edges[-1] - pr_edges = get_graphql_pr_edges(settings=settings, after=last_edge.cursor) - return pr_nodes - - -class ContributorsResults(BaseModel): - contributors: Counter[str] - translation_reviewers: Counter[str] - translators: Counter[str] - authors: dict[str, Author] - - -def get_contributors(pr_nodes: list[PullRequestNode]) -> ContributorsResults: - contributors = Counter[str]() - translation_reviewers = Counter[str]() - translators = Counter[str]() - authors: dict[str, Author] = {} - - for pr in pr_nodes: - if pr.author: - authors[pr.author.login] = pr.author - is_lang = False - for label in pr.labels.nodes: - if label.name == "lang-all": - is_lang = True - break - for review in pr.reviews.nodes: - if review.author: - authors[review.author.login] = review.author - if is_lang: - translation_reviewers[review.author.login] += 1 - if pr.state == "MERGED" and pr.author: - if is_lang: - translators[pr.author.login] += 1 - else: - contributors[pr.author.login] += 1 - return ContributorsResults( - contributors=contributors, - translation_reviewers=translation_reviewers, - translators=translators, - authors=authors, - ) - - -def get_users_to_write( - *, - counter: Counter[str], - authors: dict[str, Author], - min_count: int = 2, -) -> dict[str, Any]: - users: dict[str, Any] = {} - for user, count in counter.most_common(): - if count >= min_count: - author = authors[user] - users[user] = { - "login": user, - "count": count, - "avatarUrl": author.avatarUrl, - "url": author.url, - } - return users - - -def update_content(*, content_path: Path, new_content: Any) -> bool: - old_content = content_path.read_text(encoding="utf-8") - - new_content = yaml.dump(new_content, sort_keys=False, width=200, allow_unicode=True) - if old_content == new_content: - logging.info(f"The content hasn't changed for {content_path}") - return False - content_path.write_text(new_content, encoding="utf-8") - logging.info(f"Updated {content_path}") - return True - - -def main() -> None: - logging.basicConfig(level=logging.INFO) - settings = Settings() # ty: ignore[missing-argument] - logging.info(f"Using config: {settings.model_dump_json()}") - g = Github(settings.github_token.get_secret_value()) - repo = g.get_repo(settings.github_repository) - - pr_nodes = get_pr_nodes(settings=settings) - contributors_results = get_contributors(pr_nodes=pr_nodes) - authors = contributors_results.authors - - top_contributors = get_users_to_write( - counter=contributors_results.contributors, - authors=authors, - ) - - top_translators = get_users_to_write( - counter=contributors_results.translators, - authors=authors, - ) - top_translations_reviewers = get_users_to_write( - counter=contributors_results.translation_reviewers, - authors=authors, - ) - - # For local development - # contributors_path = Path("../docs/en/data/contributors.yml") - contributors_path = Path("./docs/en/data/contributors.yml") - # translators_path = Path("../docs/en/data/translators.yml") - translators_path = Path("./docs/en/data/translators.yml") - # translation_reviewers_path = Path("../docs/en/data/translation_reviewers.yml") - translation_reviewers_path = Path("./docs/en/data/translation_reviewers.yml") - - updated = [ - update_content(content_path=contributors_path, new_content=top_contributors), - update_content(content_path=translators_path, new_content=top_translators), - update_content( - content_path=translation_reviewers_path, - new_content=top_translations_reviewers, - ), - ] - - if not any(updated): - logging.info("The data hasn't changed, finishing.") - return - - logging.info("Setting up GitHub Actions git user") - subprocess.run(["git", "config", "user.name", "github-actions[bot]"], check=True) - subprocess.run( - ["git", "config", "user.email", "github-actions[bot]@users.noreply.github.com"], - check=True, - ) - branch_name = f"fastapi-people-contributors-{secrets.token_hex(4)}" - logging.info(f"Creating a new branch {branch_name}") - subprocess.run(["git", "checkout", "-b", branch_name], check=True) - logging.info("Adding updated file") - subprocess.run( - [ - "git", - "add", - str(contributors_path), - str(translators_path), - str(translation_reviewers_path), - ], - check=True, - ) - logging.info("Committing updated file") - message = "👥 Update FastAPI People - Contributors and Translators" - subprocess.run(["git", "commit", "-m", message], check=True) - logging.info("Pushing branch") - subprocess.run(["git", "push", "origin", branch_name], check=True) - logging.info("Creating PR") - pr = repo.create_pull(title=message, body=message, base="master", head=branch_name) - logging.info(f"Created PR: {pr.number}") - logging.info("Finished") - - -if __name__ == "__main__": - main() diff --git a/scripts/people.py b/scripts/people.py deleted file mode 100644 index 72b591367..000000000 --- a/scripts/people.py +++ /dev/null @@ -1,483 +0,0 @@ -import logging -import secrets -import subprocess -import time -from collections import Counter -from collections.abc import Container -from datetime import datetime, timedelta, timezone -from math import ceil -from pathlib import Path -from typing import Annotated, Any - -import httpx -import yaml -from github import Github -from pydantic import BaseModel, BeforeValidator, SecretStr -from pydantic_settings import BaseSettings - -github_graphql_url = "https://api.github.com/graphql" -questions_category_id = "DIC_kwDOCZduT84B6E2a" - - -POINTS_PER_MINUTE_LIMIT = 84 # 5000 points per hour - -MINIMIZED_COMMENTS_REASONS_TO_EXCLUDE = {"abuse", "off-topic", "duplicate", "spam"} - - -class RateLimiter: - def __init__(self) -> None: - self.last_query_cost: int = 1 - self.remaining_points: int = 5000 - self.reset_at: datetime = datetime.fromtimestamp(0, timezone.utc) - self.last_request_start_time: datetime = datetime.fromtimestamp(0, timezone.utc) - self.speed_multiplier: float = 1.0 - - def __enter__(self) -> "RateLimiter": - now = datetime.now(tz=timezone.utc) - - # Handle primary rate limits - primary_limit_wait_time = 0.0 - if self.remaining_points <= self.last_query_cost: - primary_limit_wait_time = (self.reset_at - now).total_seconds() + 2 - logging.warning( - f"Approaching GitHub API rate limit, remaining points: {self.remaining_points}, " - f"reset time in {primary_limit_wait_time} seconds" - ) - - # Handle secondary rate limits - secondary_limit_wait_time = 0.0 - points_per_minute = POINTS_PER_MINUTE_LIMIT * self.speed_multiplier - interval = 60 / (points_per_minute / self.last_query_cost) - time_since_last_request = (now - self.last_request_start_time).total_seconds() - if time_since_last_request < interval: - secondary_limit_wait_time = interval - time_since_last_request - - final_wait_time = ceil(max(primary_limit_wait_time, secondary_limit_wait_time)) - logging.info(f"Sleeping for {final_wait_time} seconds to respect rate limit") - time.sleep(max(final_wait_time, 1)) - - self.last_request_start_time = datetime.now(tz=timezone.utc) - return self - - def __exit__(self, exc_type, exc_val, exc_tb) -> None: - pass - - def update_request_info(self, cost: int, remaining: int, reset_at: str) -> None: - self.last_query_cost = cost - self.remaining_points = remaining - self.reset_at = datetime.fromisoformat(reset_at.replace("Z", "+00:00")) - - -rate_limiter = RateLimiter() - - -discussions_query = """ -query Q($after: String, $category_id: ID) { - repository(name: "fastapi", owner: "fastapi") { - discussions(first: 30, after: $after, categoryId: $category_id) { - edges { - cursor - node { - number - author { - login - avatarUrl - url - } - createdAt - comments(first: 50) { - totalCount - nodes { - createdAt - author { - login - avatarUrl - url - } - isAnswer - replies(first: 10) { - totalCount - nodes { - createdAt - author { - login - avatarUrl - url - } - minimizedReason - } - } - minimizedReason - } - } - } - } - } - } - rateLimit { - cost - remaining - resetAt - } -} -""" - -LowerStr = Annotated[ - str, BeforeValidator(lambda v: v.lower() if isinstance(v, str) else v) -] - - -class Author(BaseModel): - login: str - avatarUrl: str | None = None - url: str | None = None - - -class CommentsNode(BaseModel): - createdAt: datetime - author: Author | None = None - minimizedReason: LowerStr | None = None - - -class Replies(BaseModel): - totalCount: int - nodes: list[CommentsNode] - - -class DiscussionsCommentsNode(CommentsNode): - minimizedReason: LowerStr | None = None - replies: Replies - - -class DiscussionsComments(BaseModel): - totalCount: int - nodes: list[DiscussionsCommentsNode] - - -class DiscussionsNode(BaseModel): - number: int - author: Author | None = None - title: str | None = None - createdAt: datetime - comments: DiscussionsComments - - -class DiscussionsEdge(BaseModel): - cursor: str - node: DiscussionsNode - - -class Discussions(BaseModel): - edges: list[DiscussionsEdge] - - -class DiscussionsRepository(BaseModel): - discussions: Discussions - - -class DiscussionsResponseData(BaseModel): - repository: DiscussionsRepository - - -class DiscussionsResponse(BaseModel): - data: DiscussionsResponseData - - -class Settings(BaseSettings): - github_token: SecretStr - github_repository: str - httpx_timeout: int = 30 - speed_multiplier: float = 1.0 - - -def get_graphql_response( - *, - settings: Settings, - query: str, - after: str | None = None, - category_id: str | None = None, -) -> dict[str, Any]: - headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"} - variables = {"after": after, "category_id": category_id} - response = httpx.post( - github_graphql_url, - headers=headers, - timeout=settings.httpx_timeout, - json={"query": query, "variables": variables, "operationName": "Q"}, - ) - if response.status_code != 200: - logging.error( - f"Response was not 200, after: {after}, category_id: {category_id}" - ) - logging.error(response.text) - raise RuntimeError(response.text) - data = response.json() - if "errors" in data: - logging.error(f"Errors in response, after: {after}, category_id: {category_id}") - logging.error(data["errors"]) - logging.error(response.text) - raise RuntimeError(response.text) - return data - - -def get_graphql_question_discussion_edges( - *, - settings: Settings, - after: str | None = None, -) -> list[DiscussionsEdge]: - with rate_limiter: - data = get_graphql_response( - settings=settings, - query=discussions_query, - after=after, - category_id=questions_category_id, - ) - - rate_limiter.update_request_info( - cost=data["data"]["rateLimit"]["cost"], - remaining=data["data"]["rateLimit"]["remaining"], - reset_at=data["data"]["rateLimit"]["resetAt"], - ) - graphql_response = DiscussionsResponse.model_validate(data) - return graphql_response.data.repository.discussions.edges - - -class DiscussionExpertsResults(BaseModel): - commenters: Counter[str] - last_month_commenters: Counter[str] - three_months_commenters: Counter[str] - six_months_commenters: Counter[str] - one_year_commenters: Counter[str] - authors: dict[str, Author] - - -def get_discussion_nodes(settings: Settings) -> list[DiscussionsNode]: - discussion_nodes: list[DiscussionsNode] = [] - discussion_edges = get_graphql_question_discussion_edges(settings=settings) - - while discussion_edges: - for discussion_edge in discussion_edges: - discussion_nodes.append(discussion_edge.node) - last_edge = discussion_edges[-1] - discussion_edges = get_graphql_question_discussion_edges( - settings=settings, after=last_edge.cursor - ) - return discussion_nodes - - -def get_discussions_experts( - discussion_nodes: list[DiscussionsNode], -) -> DiscussionExpertsResults: - commenters = Counter[str]() - last_month_commenters = Counter[str]() - three_months_commenters = Counter[str]() - six_months_commenters = Counter[str]() - one_year_commenters = Counter[str]() - authors: dict[str, Author] = {} - - now = datetime.now(tz=timezone.utc) - one_month_ago = now - timedelta(days=30) - three_months_ago = now - timedelta(days=90) - six_months_ago = now - timedelta(days=180) - one_year_ago = now - timedelta(days=365) - - for discussion in discussion_nodes: - discussion_author_name = None - if discussion.author: - authors[discussion.author.login] = discussion.author - discussion_author_name = discussion.author.login - discussion_commentors: dict[str, datetime] = {} - for comment in discussion.comments.nodes: - if ( - comment.minimizedReason not in MINIMIZED_COMMENTS_REASONS_TO_EXCLUDE - and comment.author - ): - authors[comment.author.login] = comment.author - if comment.author.login != discussion_author_name: - author_time = discussion_commentors.get( - comment.author.login, comment.createdAt - ) - discussion_commentors[comment.author.login] = max( - author_time, comment.createdAt - ) - for reply in comment.replies.nodes: - if ( - reply.minimizedReason not in MINIMIZED_COMMENTS_REASONS_TO_EXCLUDE - and reply.author - ): - authors[reply.author.login] = reply.author - if reply.author.login != discussion_author_name: - author_time = discussion_commentors.get( - reply.author.login, reply.createdAt - ) - discussion_commentors[reply.author.login] = max( - author_time, reply.createdAt - ) - for author_name, author_time in discussion_commentors.items(): - commenters[author_name] += 1 - if author_time > one_month_ago: - last_month_commenters[author_name] += 1 - if author_time > three_months_ago: - three_months_commenters[author_name] += 1 - if author_time > six_months_ago: - six_months_commenters[author_name] += 1 - if author_time > one_year_ago: - one_year_commenters[author_name] += 1 - discussion_experts_results = DiscussionExpertsResults( - authors=authors, - commenters=commenters, - last_month_commenters=last_month_commenters, - three_months_commenters=three_months_commenters, - six_months_commenters=six_months_commenters, - one_year_commenters=one_year_commenters, - ) - return discussion_experts_results - - -def get_top_users( - *, - counter: Counter[str], - authors: dict[str, Author], - skip_users: Container[str], - min_count: int = 2, -) -> list[dict[str, Any]]: - users: list[dict[str, Any]] = [] - for commenter, count in counter.most_common(50): - if commenter in skip_users: - continue - if count >= min_count: - author = authors[commenter] - users.append( - { - "login": commenter, - "count": count, - "avatarUrl": author.avatarUrl, - "url": author.url, - } - ) - return users - - -def get_users_to_write( - *, - counter: Counter[str], - authors: dict[str, Author], - min_count: int = 2, -) -> list[dict[str, Any]]: - users: dict[str, Any] = {} - users_list: list[dict[str, Any]] = [] - for user, count in counter.most_common(60): - if count >= min_count: - author = authors[user] - user_data = { - "login": user, - "count": count, - "avatarUrl": author.avatarUrl, - "url": author.url, - } - users[user] = user_data - users_list.append(user_data) - return users_list - - -def update_content(*, content_path: Path, new_content: Any) -> bool: - old_content = content_path.read_text(encoding="utf-8") - - new_content = yaml.dump(new_content, sort_keys=False, width=200, allow_unicode=True) - if old_content == new_content: - logging.info(f"The content hasn't changed for {content_path}") - return False - content_path.write_text(new_content, encoding="utf-8") - logging.info(f"Updated {content_path}") - return True - - -def main() -> None: - logging.basicConfig(level=logging.INFO) - settings = Settings() # ty: ignore[missing-argument] - logging.info(f"Using config: {settings.model_dump_json()}") - rate_limiter.speed_multiplier = settings.speed_multiplier - g = Github(settings.github_token.get_secret_value()) - repo = g.get_repo(settings.github_repository) - - discussion_nodes = get_discussion_nodes(settings=settings) - experts_results = get_discussions_experts(discussion_nodes=discussion_nodes) - - authors = experts_results.authors - maintainers_logins = {"tiangolo"} - maintainers = [] - for login in maintainers_logins: - user = authors[login] - maintainers.append( - { - "login": login, - "answers": experts_results.commenters[login], - "avatarUrl": user.avatarUrl, - "url": user.url, - } - ) - - experts = get_users_to_write( - counter=experts_results.commenters, - authors=authors, - ) - last_month_experts = get_users_to_write( - counter=experts_results.last_month_commenters, - authors=authors, - ) - three_months_experts = get_users_to_write( - counter=experts_results.three_months_commenters, - authors=authors, - ) - six_months_experts = get_users_to_write( - counter=experts_results.six_months_commenters, - authors=authors, - ) - one_year_experts = get_users_to_write( - counter=experts_results.one_year_commenters, - authors=authors, - ) - - people = { - "maintainers": maintainers, - "experts": experts, - "last_month_experts": last_month_experts, - "three_months_experts": three_months_experts, - "six_months_experts": six_months_experts, - "one_year_experts": one_year_experts, - } - - # For local development - # people_path = Path("../docs/en/data/people.yml") - people_path = Path("./docs/en/data/people.yml") - - updated = update_content(content_path=people_path, new_content=people) - - if not updated: - logging.info("The data hasn't changed, finishing.") - return - - logging.info("Setting up GitHub Actions git user") - subprocess.run(["git", "config", "user.name", "github-actions[bot]"], check=True) - subprocess.run( - ["git", "config", "user.email", "github-actions[bot]@users.noreply.github.com"], - check=True, - ) - branch_name = f"fastapi-people-experts-{secrets.token_hex(4)}" - logging.info(f"Creating a new branch {branch_name}") - subprocess.run(["git", "checkout", "-b", branch_name], check=True) - logging.info("Adding updated file") - subprocess.run(["git", "add", str(people_path)], check=True) - logging.info("Committing updated file") - message = "👥 Update FastAPI People - Experts" - subprocess.run(["git", "commit", "-m", message], check=True) - logging.info("Pushing branch") - subprocess.run(["git", "push", "origin", branch_name], check=True) - logging.info("Creating PR") - pr = repo.create_pull(title=message, body=message, base="master", head=branch_name) - logging.info(f"Created PR: {pr.number}") - logging.info("Finished") - - -if __name__ == "__main__": - main() diff --git a/scripts/topic_repos.py b/scripts/topic_repos.py index 94379d384..ac2991b11 100644 --- a/scripts/topic_repos.py +++ b/scripts/topic_repos.py @@ -44,7 +44,7 @@ def main() -> None: owner_html_url=repo.owner.html_url, ) ) - data = [repo.model_dump() for repo in final_repos] + data = {"repos": [repo.model_dump() for repo in final_repos]} # Local development # repos_path = Path("../docs/en/data/topic_repos.yml") diff --git a/tests/test_frontend.py b/tests/test_frontend.py index b8dd62fb8..d647037e7 100644 --- a/tests/test_frontend.py +++ b/tests/test_frontend.py @@ -32,7 +32,7 @@ def test_frontend_exact_prefix_path_serves_index(tmp_path: Path): app = FastAPI() app.frontend("/", directory=dist) - response = TestClient(app).get("/app") + response = TestClient(app).get("/app", headers={"accept": "text/html"}) assert response.status_code == 200 assert response.text == "app" @@ -617,15 +617,21 @@ def test_api_route_404_is_not_replaced_by_frontend_fallback(tmp_path: Path): assert response.json() == {"detail": "api missing"} -def test_index_fallback_for_navigation_request(tmp_path: Path): +@pytest.mark.parametrize( + ("path", "accept"), + [ + ("/dashboard/settings", "text/html"), + ("/users/jane.doe", "text/html"), + ("/v/1.2.3", "application/xhtml+xml"), + ], +) +def test_index_fallback_for_navigation_request(tmp_path: Path, path: str, accept: str): dist = tmp_path / "dist" write_file(dist / "index.html", "app shell") app = FastAPI() app.frontend("/", directory=dist, fallback="index.html") - response = TestClient(app).get( - "/dashboard/settings", headers={"accept": "text/html"} - ) + response = TestClient(app).get(path, headers={"accept": accept}) assert response.status_code == 200 assert response.text == "app shell" @@ -638,7 +644,8 @@ def test_index_fallback_parses_accept_parameters(tmp_path: Path): app.frontend("/", directory=dist, fallback="index.html") response = TestClient(app).get( - "/dashboard/settings", headers={"accept": "text/html; q=0.8"} + "/dashboard/settings", + headers={"accept": "text/html; charset=utf-8; q=0.8"}, ) assert response.status_code == 200 @@ -697,10 +704,10 @@ def test_index_fallback_respects_explicit_xhtml_rejection_with_wildcard( ("/assets/missing.css", "text/css"), ("/assets/missing.png", "image/png"), ("/api/missing", "application/json"), - ("/users/jane.doe", "text/html"), + ("/dashboard/settings", ""), ], ) -def test_index_fallback_does_not_handle_asset_like_or_non_html_requests( +def test_index_fallback_requires_explicit_html_acceptance( tmp_path: Path, path: str, accept: str ): dist = tmp_path / "dist" diff --git a/tests/test_router_include_context.py b/tests/test_router_include_context.py index cb8dc81fa..e74d79e44 100644 --- a/tests/test_router_include_context.py +++ b/tests/test_router_include_context.py @@ -1,3 +1,5 @@ +import threading +from collections.abc import Sequence from typing import Annotated, cast import pytest @@ -898,6 +900,66 @@ async def test_included_unknown_route_is_ignored_and_can_return_default_404(): assert messages[0]["status"] == 404 +def test_included_router_candidate_cache_is_thread_safe(): + router = APIRouter() + route_count = 120 + thread_count = 6 + for index in range(route_count): + + @router.get(f"/items/{index}") + def read_item(index: int = index): + return {"index": index} + + app = FastAPI() + app.include_router(router) + included_router = cast(_IncludedRouter, app.router.routes[-1]) + barrier = threading.Barrier(thread_count) + results: list[Sequence[object]] = [] + + def build_candidates() -> None: + barrier.wait() + results.append(included_router.effective_candidates()) + + threads = [threading.Thread(target=build_candidates) for _ in range(thread_count)] + for thread in threads: + thread.start() + for thread in threads: + thread.join() + + assert len(results) == thread_count + assert all(result is results[0] for result in results) + assert len(results[0]) == route_count + assert TestClient(app).get("/items/0").json() == {"index": 0} + + +def test_included_router_low_priority_cache_rechecks_version_after_lock(monkeypatch): + router = APIRouter() + app = FastAPI() + app.include_router(router) + included_router = cast(_IncludedRouter, app.router.routes[-1]) + routes_version = router._get_routes_version() + version_checked = threading.Event() + + def get_routes_version() -> int: + version_checked.set() + return routes_version + + monkeypatch.setattr(router, "_get_routes_version", get_routes_version) + result: list[Sequence[object]] = [] + + def build_low_priority_routes() -> None: + result.append(included_router.effective_low_priority_routes()) + + with included_router._effective_routes_lock: + thread = threading.Thread(target=build_low_priority_routes) + thread.start() + assert version_checked.wait(timeout=1) + included_router._effective_low_priority_routes_version = routes_version + thread.join() + + assert result == [[]] + + def test_no_prefix_include_validation_sees_effective_starlette_route_candidates(): def endpoint(request): # pragma: no cover return PlainTextResponse("ok") diff --git a/uv.lock b/uv.lock index 25cd6e8a8..1918bdb67 100644 --- a/uv.lock +++ b/uv.lock @@ -1347,15 +1347,15 @@ wheels = [ [[package]] name = "genai-prices" -version = "0.0.62" +version = "0.0.71" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx2" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/8e/ed322d1f22b57fd455749bdbe2f285d310e1c1ebe921cb3d5c0b920de648/genai_prices-0.0.62.tar.gz", hash = "sha256:baf1ffa64be0d15577878216464d6a2d04244db5fbdf78d56bde43809e7aef44", size = 67611, upload-time = "2026-05-25T18:47:16.306Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/e4/5072862613fba039da2b7c981a8649c6c6bbcb2863bd8bc81617c09ce5ee/genai_prices-0.0.71.tar.gz", hash = "sha256:de4db34ec38404f9ef383cb1ab29e204d16ccf27071af0b16d5747ee7affe36b", size = 82105, upload-time = "2026-07-10T00:38:30.491Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/35/ce64112dcc6f406b3e290dcf57a97acfa2b7d3d0391979219cb9d4a9db6d/genai_prices-0.0.62-py3-none-any.whl", hash = "sha256:5d9ab0d9e5d81e035f88bf591fb6a8dde527922786acf1ee2737358f7bbe0167", size = 70333, upload-time = "2026-05-25T18:47:17.642Z" }, + { url = "https://files.pythonhosted.org/packages/0d/98/c06c1318f6834a26268a2d4280e4183f60c5ea92152aea841feff29826ff/genai_prices-0.0.71-py3-none-any.whl", hash = "sha256:1d13111563af2b1ce43ccfacf77b7ac3216ad704c644408a56e11b181fe0d128", size = 84586, upload-time = "2026-07-10T00:38:29.252Z" }, ] [[package]] @@ -2571,100 +2571,96 @@ wheels = [ [[package]] name = "pillow" -version = "12.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, - { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, - { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, - { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, - { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, - { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, - { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, - { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, - { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, - { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, - { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, - { url = "https://files.pythonhosted.org/packages/68/e1/748f5663efe6edcfc4e74b2b93edfb9b8b99b67f21a854c3ae416500a2d9/pillow-12.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:8be29e59487a79f173507c30ddf57e733a357f67881430449bb32614075a40ab", size = 5354347, upload-time = "2026-04-01T14:42:44.255Z" }, - { url = "https://files.pythonhosted.org/packages/47/a1/d5ff69e747374c33a3b53b9f98cca7889fce1fd03d79cdc4e1bccc6c5a87/pillow-12.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71cde9a1e1551df7d34a25462fc60325e8a11a82cc2e2f54578e5e9a1e153d65", size = 4695873, upload-time = "2026-04-01T14:42:46.452Z" }, - { url = "https://files.pythonhosted.org/packages/df/21/e3fbdf54408a973c7f7f89a23b2cb97a7ef30c61ab4142af31eee6aebc88/pillow-12.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f490f9368b6fc026f021db16d7ec2fbf7d89e2edb42e8ec09d2c60505f5729c7", size = 6280168, upload-time = "2026-04-01T14:42:49.228Z" }, - { url = "https://files.pythonhosted.org/packages/d3/f1/00b7278c7dd52b17ad4329153748f87b6756ec195ff786c2bdf12518337d/pillow-12.2.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8bd7903a5f2a4545f6fd5935c90058b89d30045568985a71c79f5fd6edf9b91e", size = 8088188, upload-time = "2026-04-01T14:42:51.735Z" }, - { url = "https://files.pythonhosted.org/packages/ad/cf/220a5994ef1b10e70e85748b75649d77d506499352be135a4989c957b701/pillow-12.2.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3997232e10d2920a68d25191392e3a4487d8183039e1c74c2297f00ed1c50705", size = 6394401, upload-time = "2026-04-01T14:42:54.343Z" }, - { url = "https://files.pythonhosted.org/packages/e9/bd/e51a61b1054f09437acfbc2ff9106c30d1eb76bc1453d428399946781253/pillow-12.2.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e74473c875d78b8e9d5da2a70f7099549f9eb37ded4e2f6a463e60125bccd176", size = 7079655, upload-time = "2026-04-01T14:42:56.954Z" }, - { url = "https://files.pythonhosted.org/packages/6b/3d/45132c57d5fb4b5744567c3817026480ac7fc3ce5d4c47902bc0e7f6f853/pillow-12.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:56a3f9c60a13133a98ecff6197af34d7824de9b7b38c3654861a725c970c197b", size = 6503105, upload-time = "2026-04-01T14:42:59.847Z" }, - { url = "https://files.pythonhosted.org/packages/7d/2e/9df2fc1e82097b1df3dce58dc43286aa01068e918c07574711fcc53e6fb4/pillow-12.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e6f81de50ad6b534cab6e5aef77ff6e37722b2f5d908686f4a5c9eba17a909", size = 7203402, upload-time = "2026-04-01T14:43:02.664Z" }, - { url = "https://files.pythonhosted.org/packages/bd/2e/2941e42858ebb67e50ae741473de81c2984e6eff7b397017623c676e2e8d/pillow-12.2.0-cp311-cp311-win32.whl", hash = "sha256:8c984051042858021a54926eb597d6ee3012393ce9c181814115df4c60b9a808", size = 6378149, upload-time = "2026-04-01T14:43:05.274Z" }, - { url = "https://files.pythonhosted.org/packages/69/42/836b6f3cd7f3e5fa10a1f1a5420447c17966044c8fbf589cc0452d5502db/pillow-12.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e6b2a0c538fc200b38ff9eb6628228b77908c319a005815f2dde585a0664b60", size = 7082626, upload-time = "2026-04-01T14:43:08.557Z" }, - { url = "https://files.pythonhosted.org/packages/c2/88/549194b5d6f1f494b485e493edc6693c0a16f4ada488e5bd974ed1f42fad/pillow-12.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:9a8a34cc89c67a65ea7437ce257cea81a9dad65b29805f3ecee8c8fe8ff25ffe", size = 2463531, upload-time = "2026-04-01T14:43:10.743Z" }, - { url = "https://files.pythonhosted.org/packages/58/be/7482c8a5ebebbc6470b3eb791812fff7d5e0216c2be3827b30b8bb6603ed/pillow-12.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d192a155bbcec180f8564f693e6fd9bccff5a7af9b32e2e4bf8c9c69dbad6b5", size = 5308279, upload-time = "2026-04-01T14:43:13.246Z" }, - { url = "https://files.pythonhosted.org/packages/d8/95/0a351b9289c2b5cbde0bacd4a83ebc44023e835490a727b2a3bd60ddc0f4/pillow-12.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3f40b3c5a968281fd507d519e444c35f0ff171237f4fdde090dd60699458421", size = 4695490, upload-time = "2026-04-01T14:43:15.584Z" }, - { url = "https://files.pythonhosted.org/packages/de/af/4e8e6869cbed569d43c416fad3dc4ecb944cb5d9492defaed89ddd6fe871/pillow-12.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:03e7e372d5240cc23e9f07deca4d775c0817bffc641b01e9c3af208dbd300987", size = 6284462, upload-time = "2026-04-01T14:43:18.268Z" }, - { url = "https://files.pythonhosted.org/packages/e9/9e/c05e19657fd57841e476be1ab46c4d501bffbadbafdc31a6d665f8b737b6/pillow-12.2.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b86024e52a1b269467a802258c25521e6d742349d760728092e1bc2d135b4d76", size = 8094744, upload-time = "2026-04-01T14:43:20.716Z" }, - { url = "https://files.pythonhosted.org/packages/2b/54/1789c455ed10176066b6e7e6da1b01e50e36f94ba584dc68d9eebfe9156d/pillow-12.2.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7371b48c4fa448d20d2714c9a1f775a81155050d383333e0a6c15b1123dda005", size = 6398371, upload-time = "2026-04-01T14:43:23.443Z" }, - { url = "https://files.pythonhosted.org/packages/43/e3/fdc657359e919462369869f1c9f0e973f353f9a9ee295a39b1fea8ee1a77/pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62f5409336adb0663b7caa0da5c7d9e7bdbaae9ce761d34669420c2a801b2780", size = 7087215, upload-time = "2026-04-01T14:43:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/8b/f8/2f6825e441d5b1959d2ca5adec984210f1ec086435b0ed5f52c19b3b8a6e/pillow-12.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:01afa7cf67f74f09523699b4e88c73fb55c13346d212a59a2db1f86b0a63e8c5", size = 6509783, upload-time = "2026-04-01T14:43:29.56Z" }, - { url = "https://files.pythonhosted.org/packages/67/f9/029a27095ad20f854f9dba026b3ea6428548316e057e6fc3545409e86651/pillow-12.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc3d34d4a8fbec3e88a79b92e5465e0f9b842b628675850d860b8bd300b159f5", size = 7212112, upload-time = "2026-04-01T14:43:32.091Z" }, - { url = "https://files.pythonhosted.org/packages/be/42/025cfe05d1be22dbfdb4f264fe9de1ccda83f66e4fc3aac94748e784af04/pillow-12.2.0-cp312-cp312-win32.whl", hash = "sha256:58f62cc0f00fd29e64b29f4fd923ffdb3859c9f9e6105bfc37ba1d08994e8940", size = 6378489, upload-time = "2026-04-01T14:43:34.601Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7b/25a221d2c761c6a8ae21bfa3874988ff2583e19cf8a27bf2fee358df7942/pillow-12.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7f84204dee22a783350679a0333981df803dac21a0190d706a50475e361c93f5", size = 7084129, upload-time = "2026-04-01T14:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/10/e1/542a474affab20fd4a0f1836cb234e8493519da6b76899e30bcc5d990b8b/pillow-12.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:af73337013e0b3b46f175e79492d96845b16126ddf79c438d7ea7ff27783a414", size = 2463612, upload-time = "2026-04-01T14:43:39.421Z" }, - { url = "https://files.pythonhosted.org/packages/4a/01/53d10cf0dbad820a8db274d259a37ba50b88b24768ddccec07355382d5ad/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8297651f5b5679c19968abefd6bb84d95fe30ef712eb1b2d9b2d31ca61267f4c", size = 4100837, upload-time = "2026-04-01T14:43:41.506Z" }, - { url = "https://files.pythonhosted.org/packages/0f/98/f3a6657ecb698c937f6c76ee564882945f29b79bad496abcba0e84659ec5/pillow-12.2.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:50d8520da2a6ce0af445fa6d648c4273c3eeefbc32d7ce049f22e8b5c3daecc2", size = 4176528, upload-time = "2026-04-01T14:43:43.773Z" }, - { url = "https://files.pythonhosted.org/packages/69/bc/8986948f05e3ea490b8442ea1c1d4d990b24a7e43d8a51b2c7d8b1dced36/pillow-12.2.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:766cef22385fa1091258ad7e6216792b156dc16d8d3fa607e7545b2b72061f1c", size = 3640401, upload-time = "2026-04-01T14:43:45.87Z" }, - { url = "https://files.pythonhosted.org/packages/34/46/6c717baadcd62bc8ed51d238d521ab651eaa74838291bda1f86fe1f864c9/pillow-12.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5d2fd0fa6b5d9d1de415060363433f28da8b1526c1c129020435e186794b3795", size = 5308094, upload-time = "2026-04-01T14:43:48.438Z" }, - { url = "https://files.pythonhosted.org/packages/71/43/905a14a8b17fdb1ccb58d282454490662d2cb89a6bfec26af6d3520da5ec/pillow-12.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56b25336f502b6ed02e889f4ece894a72612fe885889a6e8c4c80239ff6e5f5f", size = 4695402, upload-time = "2026-04-01T14:43:51.292Z" }, - { url = "https://files.pythonhosted.org/packages/73/dd/42107efcb777b16fa0393317eac58f5b5cf30e8392e266e76e51cff28c3d/pillow-12.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f1c943e96e85df3d3478f7b691f229887e143f81fedab9b20205349ab04d73ed", size = 6280005, upload-time = "2026-04-01T14:43:54.242Z" }, - { url = "https://files.pythonhosted.org/packages/a8/68/b93e09e5e8549019e61acf49f65b1a8530765a7f812c77a7461bca7e4494/pillow-12.2.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:03f6fab9219220f041c74aeaa2939ff0062bd5c364ba9ce037197f4c6d498cd9", size = 8090669, upload-time = "2026-04-01T14:43:57.335Z" }, - { url = "https://files.pythonhosted.org/packages/4b/6e/3ccb54ce8ec4ddd1accd2d89004308b7b0b21c4ac3d20fa70af4760a4330/pillow-12.2.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdfebd752ec52bf5bb4e35d9c64b40826bc5b40a13df7c3cda20a2c03a0f5ed", size = 6395194, upload-time = "2026-04-01T14:43:59.864Z" }, - { url = "https://files.pythonhosted.org/packages/67/ee/21d4e8536afd1a328f01b359b4d3997b291ffd35a237c877b331c1c3b71c/pillow-12.2.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eedf4b74eda2b5a4b2b2fb4c006d6295df3bf29e459e198c90ea48e130dc75c3", size = 7082423, upload-time = "2026-04-01T14:44:02.74Z" }, - { url = "https://files.pythonhosted.org/packages/78/5f/e9f86ab0146464e8c133fe85df987ed9e77e08b29d8d35f9f9f4d6f917ba/pillow-12.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00a2865911330191c0b818c59103b58a5e697cae67042366970a6b6f1b20b7f9", size = 6505667, upload-time = "2026-04-01T14:44:05.381Z" }, - { url = "https://files.pythonhosted.org/packages/ed/1e/409007f56a2fdce61584fd3acbc2bbc259857d555196cedcadc68c015c82/pillow-12.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e1757442ed87f4912397c6d35a0db6a7b52592156014706f17658ff58bbf795", size = 7208580, upload-time = "2026-04-01T14:44:08.39Z" }, - { url = "https://files.pythonhosted.org/packages/23/c4/7349421080b12fb35414607b8871e9534546c128a11965fd4a7002ccfbee/pillow-12.2.0-cp313-cp313-win32.whl", hash = "sha256:144748b3af2d1b358d41286056d0003f47cb339b8c43a9ea42f5fea4d8c66b6e", size = 6375896, upload-time = "2026-04-01T14:44:11.197Z" }, - { url = "https://files.pythonhosted.org/packages/3f/82/8a3739a5e470b3c6cbb1d21d315800d8e16bff503d1f16b03a4ec3212786/pillow-12.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:390ede346628ccc626e5730107cde16c42d3836b89662a115a921f28440e6a3b", size = 7081266, upload-time = "2026-04-01T14:44:13.947Z" }, - { url = "https://files.pythonhosted.org/packages/c3/25/f968f618a062574294592f668218f8af564830ccebdd1fa6200f598e65c5/pillow-12.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:8023abc91fba39036dbce14a7d6535632f99c0b857807cbbbf21ecc9f4717f06", size = 2463508, upload-time = "2026-04-01T14:44:16.312Z" }, - { url = "https://files.pythonhosted.org/packages/4d/a4/b342930964e3cb4dce5038ae34b0eab4653334995336cd486c5a8c25a00c/pillow-12.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:042db20a421b9bafecc4b84a8b6e444686bd9d836c7fd24542db3e7df7baad9b", size = 5309927, upload-time = "2026-04-01T14:44:18.89Z" }, - { url = "https://files.pythonhosted.org/packages/9f/de/23198e0a65a9cf06123f5435a5d95cea62a635697f8f03d134d3f3a96151/pillow-12.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd025009355c926a84a612fecf58bb315a3f6814b17ead51a8e48d3823d9087f", size = 4698624, upload-time = "2026-04-01T14:44:21.115Z" }, - { url = "https://files.pythonhosted.org/packages/01/a6/1265e977f17d93ea37aa28aa81bad4fa597933879fac2520d24e021c8da3/pillow-12.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88ddbc66737e277852913bd1e07c150cc7bb124539f94c4e2df5344494e0a612", size = 6321252, upload-time = "2026-04-01T14:44:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/3c/83/5982eb4a285967baa70340320be9f88e57665a387e3a53a7f0db8231a0cd/pillow-12.2.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d362d1878f00c142b7e1a16e6e5e780f02be8195123f164edf7eddd911eefe7c", size = 8126550, upload-time = "2026-04-01T14:44:26.772Z" }, - { url = "https://files.pythonhosted.org/packages/4e/48/6ffc514adce69f6050d0753b1a18fd920fce8cac87620d5a31231b04bfc5/pillow-12.2.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c727a6d53cb0018aadd8018c2b938376af27914a68a492f59dfcaca650d5eea", size = 6433114, upload-time = "2026-04-01T14:44:29.615Z" }, - { url = "https://files.pythonhosted.org/packages/36/a3/f9a77144231fb8d40ee27107b4463e205fa4677e2ca2548e14da5cf18dce/pillow-12.2.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:efd8c21c98c5cc60653bcb311bef2ce0401642b7ce9d09e03a7da87c878289d4", size = 7115667, upload-time = "2026-04-01T14:44:32.773Z" }, - { url = "https://files.pythonhosted.org/packages/c1/fc/ac4ee3041e7d5a565e1c4fd72a113f03b6394cc72ab7089d27608f8aaccb/pillow-12.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f08483a632889536b8139663db60f6724bfcb443c96f1b18855860d7d5c0fd4", size = 6538966, upload-time = "2026-04-01T14:44:35.252Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a8/27fb307055087f3668f6d0a8ccb636e7431d56ed0750e07a60547b1e083e/pillow-12.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dac8d77255a37e81a2efcbd1fc05f1c15ee82200e6c240d7e127e25e365c39ea", size = 7238241, upload-time = "2026-04-01T14:44:37.875Z" }, - { url = "https://files.pythonhosted.org/packages/ad/4b/926ab182c07fccae9fcb120043464e1ff1564775ec8864f21a0ebce6ac25/pillow-12.2.0-cp313-cp313t-win32.whl", hash = "sha256:ee3120ae9dff32f121610bb08e4313be87e03efeadfc6c0d18f89127e24d0c24", size = 6379592, upload-time = "2026-04-01T14:44:40.336Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c4/f9e476451a098181b30050cc4c9a3556b64c02cf6497ea421ac047e89e4b/pillow-12.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:325ca0528c6788d2a6c3d40e3568639398137346c3d6e66bb61db96b96511c98", size = 7085542, upload-time = "2026-04-01T14:44:43.251Z" }, - { url = "https://files.pythonhosted.org/packages/00/a4/285f12aeacbe2d6dc36c407dfbbe9e96d4a80b0fb710a337f6d2ad978c75/pillow-12.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:2e5a76d03a6c6dcef67edabda7a52494afa4035021a79c8558e14af25313d453", size = 2465765, upload-time = "2026-04-01T14:44:45.996Z" }, - { url = "https://files.pythonhosted.org/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" }, - { url = "https://files.pythonhosted.org/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" }, - { url = "https://files.pythonhosted.org/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" }, - { url = "https://files.pythonhosted.org/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" }, - { url = "https://files.pythonhosted.org/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" }, - { url = "https://files.pythonhosted.org/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" }, - { url = "https://files.pythonhosted.org/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" }, - { url = "https://files.pythonhosted.org/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" }, - { url = "https://files.pythonhosted.org/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" }, - { url = "https://files.pythonhosted.org/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" }, - { url = "https://files.pythonhosted.org/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" }, - { url = "https://files.pythonhosted.org/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" }, - { url = "https://files.pythonhosted.org/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" }, - { url = "https://files.pythonhosted.org/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" }, - { url = "https://files.pythonhosted.org/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" }, - { url = "https://files.pythonhosted.org/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" }, - { url = "https://files.pythonhosted.org/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" }, - { url = "https://files.pythonhosted.org/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" }, - { url = "https://files.pythonhosted.org/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" }, - { url = "https://files.pythonhosted.org/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" }, - { url = "https://files.pythonhosted.org/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" }, - { url = "https://files.pythonhosted.org/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" }, - { url = "https://files.pythonhosted.org/packages/4e/b7/2437044fb910f499610356d1352e3423753c98e34f915252aafecc64889f/pillow-12.2.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538bd5e05efec03ae613fd89c4ce0368ecd2ba239cc25b9f9be7ed426b0af1f", size = 5273969, upload-time = "2026-04-01T14:45:55.538Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f4/8316e31de11b780f4ac08ef3654a75555e624a98db1056ecb2122d008d5a/pillow-12.2.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:394167b21da716608eac917c60aa9b969421b5dcbbe02ae7f013e7b85811c69d", size = 4659674, upload-time = "2026-04-01T14:45:58.093Z" }, - { url = "https://files.pythonhosted.org/packages/d4/37/664fca7201f8bb2aa1d20e2c3d5564a62e6ae5111741966c8319ca802361/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5d04bfa02cc2d23b497d1e90a0f927070043f6cbf303e738300532379a4b4e0f", size = 5288479, upload-time = "2026-04-01T14:46:01.141Z" }, - { url = "https://files.pythonhosted.org/packages/49/62/5b0ed78fce87346be7a5cfcfaaad91f6a1f98c26f86bdbafa2066c647ef6/pillow-12.2.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c838a5125cee37e68edec915651521191cef1e6aa336b855f495766e77a366e", size = 7032230, upload-time = "2026-04-01T14:46:03.874Z" }, - { url = "https://files.pythonhosted.org/packages/c3/28/ec0fc38107fc32536908034e990c47914c57cd7c5a3ece4d8d8f7ffd7e27/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6c9fa44005fa37a91ebfc95d081e8079757d2e904b27103f4f5fa6f0bf78c0", size = 5355404, upload-time = "2026-04-01T14:46:06.33Z" }, - { url = "https://files.pythonhosted.org/packages/5e/8b/51b0eddcfa2180d60e41f06bd6d0a62202b20b59c68f5a132e615b75aecf/pillow-12.2.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25373b66e0dd5905ed63fa3cae13c82fbddf3079f2c8bf15c6fb6a35586324c1", size = 6002215, upload-time = "2026-04-01T14:46:08.83Z" }, - { url = "https://files.pythonhosted.org/packages/bc/60/5382c03e1970de634027cee8e1b7d39776b778b81812aaf45b694dfe9e28/pillow-12.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bfa9c230d2fe991bed5318a5f119bd6780cda2915cca595393649fc118ab895e", size = 7080946, upload-time = "2026-04-01T14:46:11.734Z" }, +version = "12.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/3d/bb7fca845737cf9d7dbde16ed1843984665ff2e0a518f5db43e77ec540b9/pillow-12.3.0.tar.gz", hash = "sha256:3b8182a766685eaa002637e28b4ec8d6b18819a0c71f579bf0dbaa5830297cce", size = 47025035, upload-time = "2026-07-01T11:56:38.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/c2/669d88644cddb1485bd9534e63e8cf476c8e51cb3c3a1297677023505c0e/pillow-12.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:6c0016e7b354317c4e9e525b937ac8596c38d2d232b419529b9cd7a1cd46e39a", size = 5392418, upload-time = "2026-07-01T11:53:27.808Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ba/3762f376a2948e3036488d773a146e0ae6ecc2ca03ac20e2615bd0b2ba02/pillow-12.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bcc33feacfaefce60c12fd500a277533bdc02b10a19f7f6d348763d8140bbba7", size = 4785287, upload-time = "2026-07-01T11:53:29.761Z" }, + { url = "https://files.pythonhosted.org/packages/07/50/b5d688cc9c52d4482f3d5bcab6ce20bc2a74a85d2343841c907444a3be2c/pillow-12.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5594fc43d548a7ed94949d139aa1341b270f1863f11cfd37f5a6c8b778a6b67f", size = 6253754, upload-time = "2026-07-01T11:53:32.298Z" }, + { url = "https://files.pythonhosted.org/packages/4e/89/36f4cd76cf4baf05c50ababb976249153f18c959171c7f6ba09a6f217260/pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0606c8bf2cdefea14a43530f7657cbbb7ecf1c4222512492ef4a4434a9501ec", size = 6925605, upload-time = "2026-07-01T11:53:34.487Z" }, + { url = "https://files.pythonhosted.org/packages/eb/c0/4de58cf6633b9e3a6061ef4be6fb91fc3c90b812ece886f531e3c523d777/pillow-12.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:85f998ea1848bc6757289e739cfbdda3a04adfd58b02fc018ce54d754a5ce468", size = 6327788, upload-time = "2026-07-01T11:53:36.433Z" }, + { url = "https://files.pythonhosted.org/packages/87/3c/14d53682a19550dbbaf3b598f807d5457646c510805a44c7d7891cd1cd1a/pillow-12.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:25b9b82bb22e6e2b3cd07b39c68b7b862001226cb3dff7130d1cb914121b39ed", size = 7036288, upload-time = "2026-07-01T11:53:38.712Z" }, + { url = "https://files.pythonhosted.org/packages/38/1d/36279e3c77efe034e4cc2b0393ee74ffdb5a62391dacbf9b916154f5f0b8/pillow-12.3.0-cp310-cp310-win32.whl", hash = "sha256:37dc8f7bbb66efe481bb60defacef820c950c24713fb44962ed6aa2a50966de1", size = 6472396, upload-time = "2026-07-01T11:53:40.781Z" }, + { url = "https://files.pythonhosted.org/packages/48/7c/8fa0039574c476d7c6fa57dd7c32a130436877c6ec1e5ce1cc8ec44878c1/pillow-12.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:300557495eb45ebb8aec96c2da9c4be642fbf7cd937278b4013ba894ea8eb0eb", size = 7226887, upload-time = "2026-07-01T11:53:42.764Z" }, + { url = "https://files.pythonhosted.org/packages/fa/17/e324be141d173c1c919428066c3259f21c1b8982e564e01a4a81e96dbdcf/pillow-12.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:514435a37670e3e5e08f3945b68718b6ed329bb84367777e16f9f4dfe1e61a0f", size = 2568039, upload-time = "2026-07-01T11:53:45.372Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c8/0a78b0e02d7ac54bc03e5321c9220da52f0c2ea83b21f7c40e7f3169c502/pillow-12.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:00808c5e14ef63ac5161091d242999076604ff74b883423a11e5d7bbb38bf756", size = 5392415, upload-time = "2026-07-01T11:53:47.162Z" }, + { url = "https://files.pythonhosted.org/packages/b2/5b/a02d30018abd97ced9f5a6c63d28597694a00d066516b9c1c6de45859fc9/pillow-12.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37d6d0a00072fd2948eb22bce7e1475f34569d90c87c59f7a2ec59541b77f7a6", size = 4785266, upload-time = "2026-07-01T11:53:49.079Z" }, + { url = "https://files.pythonhosted.org/packages/c8/98/766667a4be768150a202836acd9fad19c06824ca86c4286d3cf6b274964e/pillow-12.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bcb46e2f9feff8d06323983bd83ed00c201fdcab3d74973e7072a889b3979fcd", size = 6263814, upload-time = "2026-07-01T11:53:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/3b/2d/ede717bc1144f63886c21fd349bb95860b0d1a21149ff16f2bb362b612b6/pillow-12.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23d27a3e0307ec2244cc51e7287b919aa68d097504ebe19df4e76a98a3eea5bd", size = 6934408, upload-time = "2026-07-01T11:53:53.487Z" }, + { url = "https://files.pythonhosted.org/packages/a3/48/9c58b685e69d49c31af6c8eb9012055fab7e665785165c84796e2c73ce72/pillow-12.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4f883547d4b7f0495ebe7056b0cc2aea76094e7a4abc8e933540f3271df27d9c", size = 6337160, upload-time = "2026-07-01T11:53:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/ff/fa/dc2a5c0ba6df93f67c31d34b808b7ce440b40cdbf96f0b81cde1d1e6fa93/pillow-12.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:236ff70b9312fb68943c703aa842ca6a758abfa45ac187a5e7c1452e96ef72b5", size = 7045172, upload-time = "2026-07-01T11:53:57.736Z" }, + { url = "https://files.pythonhosted.org/packages/86/a5/444817a4d4c4c2417df00513086ca196f388d8f9ef40c2e4ccd1ad1af54b/pillow-12.3.0-cp311-cp311-win32.whl", hash = "sha256:10e41f0fbf1eec8cfd234b8fe17a4caac7c9d0db4c204d3c173a8f9f6ef3232b", size = 6472232, upload-time = "2026-07-01T11:53:59.767Z" }, + { url = "https://files.pythonhosted.org/packages/63/c6/4bad1b18d132a50b27e1365e1ab163616f7a5bb56d330f66f9d1d9d4f9d4/pillow-12.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8e95e1385e4998ae9694eeaa4730ba5457ff61185b3a55e2e7bea0880aef452a", size = 7233653, upload-time = "2026-07-01T11:54:02.066Z" }, + { url = "https://files.pythonhosted.org/packages/fd/16/00f91ab7760dc842f5aad55217e80fc4a7067a0604535249bc8a2d6d9870/pillow-12.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:ebaea975e03d3141d9d3a507df75c9b3ec90fa9d2ffd07567b3a978d9d790b26", size = 2568195, upload-time = "2026-07-01T11:54:04.622Z" }, + { url = "https://files.pythonhosted.org/packages/37/bf/fb3ebff8ddcb76aac5a01389251bbbb9519922a9b520d8247c1ca864a25d/pillow-12.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ba09209fbe443b4acccebe845d8a138b89a8f4fbaeedd44953490b5315d5e965", size = 5345969, upload-time = "2026-07-01T11:54:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/d8/66/9a386a92561f402389a4fc70c18838bf6d35eb5eb5c6850b4b2dc64f5048/pillow-12.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffd0c5368496f41b0944be820fcb7a838aa6e623d250b01acf2643939c3f99d7", size = 4780323, upload-time = "2026-07-01T11:54:09.351Z" }, + { url = "https://files.pythonhosted.org/packages/25/27/ac8f99618ffd3dde21db0f4d4b1d2ab00c0880595bfd17df103f7f39fd0c/pillow-12.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9c7f76c0673154f044e9d78c8655fb4213f6ca31a836df48b40fe5d187717b9", size = 6266838, upload-time = "2026-07-01T11:54:11.71Z" }, + { url = "https://files.pythonhosted.org/packages/84/21/a35af28dcc61f37ed850a2d64c65c701321dfbf25085e469d5559360cbbf/pillow-12.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:78cb2c6865a35ab8ff8b75fd122f6033b92a62c82801110e48ddd6c936a45d91", size = 6940830, upload-time = "2026-07-01T11:54:13.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/51/8b08617af3ad95e33ce6d7dd2c99ed6c8298f7fb131636303956be022e25/pillow-12.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e491916b378fba47242221bb9ead245211b70d504f495d105d17b14a24b4907c", size = 6344383, upload-time = "2026-07-01T11:54:15.756Z" }, + { url = "https://files.pythonhosted.org/packages/1d/72/cf78ac9780bb93c28328f408973845a309d4d145041665f734572ced1b52/pillow-12.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0dd2064cbc55aaec028ef5fbb60fa47bb6c3e7918e07ff17935284b227a9d2df", size = 7052934, upload-time = "2026-07-01T11:54:17.721Z" }, + { url = "https://files.pythonhosted.org/packages/20/20/25e0f4dc178a6bc0696793720055519a0de89e7661dae886992decbd2f81/pillow-12.3.0-cp312-cp312-win32.whl", hash = "sha256:dbce0b29841537a2fa4a214c2bbf14de3587c9680caa9b4e217568472490b28f", size = 6472684, upload-time = "2026-07-01T11:54:19.839Z" }, + { url = "https://files.pythonhosted.org/packages/45/89/da2f7971a317f83d807fdd4065c0af40208e59e692cc43d315a71a0e96d1/pillow-12.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a2b55dd6b2a4c4b7d87ffa56bdb33fdc5fdb9a462173861a7bc097f17d91cb09", size = 7227137, upload-time = "2026-07-01T11:54:22.025Z" }, + { url = "https://files.pythonhosted.org/packages/de/47/4845a0a6c0dbf1db8456bd9fc791f13c5ced7ced20606d08a0aacfd25b49/pillow-12.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:331b624368d4f1d069149002f25f44bc61c8919ce8ddb3c45bdad8f6e2d89510", size = 2568267, upload-time = "2026-07-01T11:54:24.051Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/31fb64e1e7efb5a4b50cd3d92049ba89ac6e4d8d3bb6a74e15048ca3353e/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:21900ce7ba264168cd50defae43cd75d25c833ad4ad6e73ffc5596d12e25ac89", size = 4161684, upload-time = "2026-07-01T11:54:25.934Z" }, + { url = "https://files.pythonhosted.org/packages/87/b4/9805e23d2b4d77842b468513841fda254ee42f0289d25088340e4ff46e2d/pillow-12.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:4e8c2a84d977f50b9daed6eeaf3baef67d00d5d74d932288f02cb94518ee3ace", size = 4255487, upload-time = "2026-07-01T11:54:27.935Z" }, + { url = "https://files.pythonhosted.org/packages/df/39/ecf519435a200c693fe053a6ee4d835b41cf963a4dfc2551c4e637cb2a71/pillow-12.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:ae26d61dfa7a47befdc7572b521024e8745f3d809bd95ca9505a7bba9ef849ec", size = 3696433, upload-time = "2026-07-01T11:54:29.813Z" }, + { url = "https://files.pythonhosted.org/packages/42/92/2fc3ffad878ae8dd5469ec1bc8eb83b71f48e13efdf68f02709003982a32/pillow-12.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7a743ff716f746fc19a9557f60dab1600d4613255f8a7aeb3cdde4db7eb15a66", size = 5345889, upload-time = "2026-07-01T11:54:31.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/76/8803c13605b763d33d156c4678fc77f8443389c0c51c8aef707bb02015f4/pillow-12.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d69141514cc30b774ceea5e3ed3a6635c8d8a96edf664689b890f4089111fb35", size = 4780109, upload-time = "2026-07-01T11:54:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1f/01/e18aff37cb0b4aac47ac90f016d347a49aca667ef97f190b06ac2aabc928/pillow-12.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7401aebd7f581d7f83a439d87d474999317ee099218e5ad25d125290990ba65", size = 6263736, upload-time = "2026-07-01T11:54:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/f7/62/de5bdd77d935331f4f802edc11e4d82950f642caad6cb2f949837b8560e2/pillow-12.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0847a763afefb695bc912d7c131e7e0632d4edc1d8698f58ddabec8e46b8b6d3", size = 6937129, upload-time = "2026-07-01T11:54:38.216Z" }, + { url = "https://files.pythonhosted.org/packages/70/4d/105627a13300c5e0df1d174230b32fd1273062c96f7745fd552b945d1e1d/pillow-12.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:571b9fcb07b97ef3a492028fb3d2dc0993ca23a06138b0315286566d29ef718a", size = 6339562, upload-time = "2026-07-01T11:54:40.354Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1d/f13de01a553988ab895ba1c722e06cf3144d4f57656fd5b81b6d881f1179/pillow-12.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:756c768d0c9c2955feb7a56c37ea24aea2e369f8d36a88da270b6a9f19e62b5e", size = 7049439, upload-time = "2026-07-01T11:54:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/c9/f9/066794cca041b969964f779ee5fa66a9498bbf34248ac39c5d7954e4198f/pillow-12.3.0-cp313-cp313-win32.whl", hash = "sha256:a876864214e136f0eb367788dbd7df045f4806801518e2cfe9e13229cfe06d8f", size = 6473287, upload-time = "2026-07-01T11:54:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/7a58e61d62be561da3a356fe2384d4059a6345fc130e23ef1c36a5b81d24/pillow-12.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1cca606cd25738df4ed873d5ad46bbdb3d83b5cbca291f6b4ff13a4df6b0bbe8", size = 7239691, upload-time = "2026-07-01T11:54:47.141Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b0/c4ed4f0ef8f8fa5ee8351537db6650bb8189f7e118842978dd6589065692/pillow-12.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:b629de27fda84b42cde7edef0d85f13b958b47f6e9bbcbba9b673c562a89bd8b", size = 2568185, upload-time = "2026-07-01T11:54:49.137Z" }, + { url = "https://files.pythonhosted.org/packages/dc/01/001f65b68192f0228cc1dbbc8d2530ab5d58b61037ba0587f946fea607cd/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:9cf95fe4d0f84c82d282745d9bb08ad9f926efa00be4697e767b814ce40d4330", size = 4161736, upload-time = "2026-07-01T11:54:51.156Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/0219746d0fd16fc8a84498e79452375be3797d3ce4044596ce565164b84f/pillow-12.3.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:8728f216dcdb6e6d555cf971cb34076139ad74b31fc2c14da4fafc741c5f6217", size = 4255435, upload-time = "2026-07-01T11:54:53.414Z" }, + { url = "https://files.pythonhosted.org/packages/c8/02/8d0bc62ef0302318c46ff2a512822d2610e81c7aa46c9b3abe6cbaca5ad0/pillow-12.3.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a45650e8ce7fafffd731db8550230db6b0d306d181a90b67d3e6bca2f1990930", size = 3696262, upload-time = "2026-07-01T11:54:55.739Z" }, + { url = "https://files.pythonhosted.org/packages/85/e2/73c77d218410b14f5f2d565e8a998d5317b7b9c75368d29985139f7a46f0/pillow-12.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ba54cfebe86920a559a7c4d6b9050791c20513650a1952ebe3368c7dc70306f8", size = 5350344, upload-time = "2026-07-01T11:54:57.657Z" }, + { url = "https://files.pythonhosted.org/packages/c7/da/32c752228ae345f489e3a42499d817b6c3996da7e8a3bc7a04fc806b243b/pillow-12.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e158cb00350dc278f3b91551101aa7d12415a66ebf2c91d8d5ac14e56ddd3ad0", size = 4780131, upload-time = "2026-07-01T11:54:59.713Z" }, + { url = "https://files.pythonhosted.org/packages/b1/9d/8b2c807dbef61a5197c047afe99823787eb66f63daf9fb2432f91d6f0462/pillow-12.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9aeb04d6aef139de265b29683e119b638208f88cf73cdd1658aa07221165321", size = 6263757, upload-time = "2026-07-01T11:55:01.778Z" }, + { url = "https://files.pythonhosted.org/packages/5c/44/c85361f65dbe00eea8576ee467c768d25129989efb76e94f205e9ca9bb46/pillow-12.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:251bf95b67017e27b13d82f5b326234ca62d70f9cf4c2b9032de2358a3b12c7b", size = 6936962, upload-time = "2026-07-01T11:55:03.93Z" }, + { url = "https://files.pythonhosted.org/packages/18/7e/e483414b35800b86b6f08dbbc7803fb5cd52c4d6f897f47d53ea2c7e6f65/pillow-12.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fe3cca2e4e8a592be0f269a1ca4835c25199d9f3ce815c8491048f785b0a0198", size = 6339171, upload-time = "2026-07-01T11:55:05.989Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f4/68c491844841ede6bed70189546b3ee9731cf9f2cbad396faff5e1ccba45/pillow-12.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:23aceaa007d6172b02c277f0cd359c79492bbb14f7072b4ede9fbcaf20648130", size = 7048116, upload-time = "2026-07-01T11:55:08.131Z" }, + { url = "https://files.pythonhosted.org/packages/a3/34/77f3f793fed8efc7d243f21b33c5a3f0d1c97ee70346d3db855587e155ff/pillow-12.3.0-cp314-cp314-win32.whl", hash = "sha256:af8d94b0db561cf68b88a267c5c44b49e134f525d0dc2cb7ed413a66bc23559a", size = 6467209, upload-time = "2026-07-01T11:55:10.408Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e0/492879f69d94f91f60fc8cd05ba03650e9520afebb2fb7aa12777d7c7f38/pillow-12.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:fdafc9cce40277e0f7a0feabce0ee50dd2fa1800f3b38015e51296b5e814048d", size = 7237707, upload-time = "2026-07-01T11:55:12.745Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ac/6b11f2875f1c2ac040d84e1bbf9cf22a88038f901ca1037898b280b38365/pillow-12.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:e91206ee562682b51b98ef4b26a6ef48fd84e15fd4c4bc5ec768eb641d206838", size = 2565995, upload-time = "2026-07-01T11:55:14.736Z" }, + { url = "https://files.pythonhosted.org/packages/52/69/c2208e56af9bfc1913afb24020297a691eb1d4ef688474c8a04913f65e04/pillow-12.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:164b31cd1a0490ab6efae01aa5df49da7061be0af1b30e035b6e9a1bfe34ee6e", size = 5352503, upload-time = "2026-07-01T11:55:17.076Z" }, + { url = "https://files.pythonhosted.org/packages/07/70/e5686d753e898a45d778ff1718dba8516ead6ab6b95d85fc8c4b70650cf2/pillow-12.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5afb51d599ea772b8365ae807ae557f18bccfe46ab261fd1c2a9ed700fc6eb17", size = 4782956, upload-time = "2026-07-01T11:55:19.448Z" }, + { url = "https://files.pythonhosted.org/packages/d5/37/25c6692f06927ee973ff18c8d9ee98ad0b4d84ee67a09610c2dd1447958e/pillow-12.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3edce1d53195db527e0191f84b71d02022de0540bf43a16ed734ed7537b07385", size = 6322855, upload-time = "2026-07-01T11:55:21.613Z" }, + { url = "https://files.pythonhosted.org/packages/cc/91/420637fcb8f1bc11029e403b4538e6694744428d8246118e45719f944556/pillow-12.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf16ba1b4d0b6b7c8e534936632270cf70eb00dbe09005bc345b2677b726855c", size = 6989642, upload-time = "2026-07-01T11:55:24.006Z" }, + { url = "https://files.pythonhosted.org/packages/10/08/b94d7811281ccf0d143a1cf768d1c49e1e54af63e7b708ab2ee3eb87face/pillow-12.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:24870b09b224f7ae3c39ed07d10e819d06f8720bc551847b1d623832b5b0e28d", size = 6391281, upload-time = "2026-07-01T11:55:26.252Z" }, + { url = "https://files.pythonhosted.org/packages/d2/87/24233f785f55474dc02ce3e739c5528a77e3a862e9333d1dd7a25cc31f70/pillow-12.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:30f2aa603c41533cc25c05acd0da21636e84a315768feb631c937177db558931", size = 7096716, upload-time = "2026-07-01T11:55:28.318Z" }, + { url = "https://files.pythonhosted.org/packages/23/26/fcb2f6e37175b04f53570b59937867e2b80ee1685e744023153028fc14f9/pillow-12.3.0-cp314-cp314t-win32.whl", hash = "sha256:4b0a7fe987b14c31ebda6083f74f22b561fd3739bc0ac51e019622e3d72668c7", size = 6474125, upload-time = "2026-07-01T11:55:30.956Z" }, + { url = "https://files.pythonhosted.org/packages/90/de/3634abee5f1c9e13c56787b7d5517b0ba8d6de51700b95578cf338349c9f/pillow-12.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:962864dc93511324d51ddbb5b9f8731bf71675b93ca612a07441896f4688fb8c", size = 7242939, upload-time = "2026-07-01T11:55:34.044Z" }, + { url = "https://files.pythonhosted.org/packages/ce/2a/fd13f8eb24de5714a6eb444a3d67e2842c6c576e159a43793adf23051351/pillow-12.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0740a512dc522224c77d9aa5a8d70d8b7d73fb91f2c21125d8d025d3b8990e45", size = 2567506, upload-time = "2026-07-01T11:55:35.988Z" }, + { url = "https://files.pythonhosted.org/packages/5d/dc/8fdce34ec725a33c81c6ba122b904d6b9024e50ea9ac7bede62fab54506c/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0feb2e9d6ad6c9e3c06effe9d00f3f1e618a6643273576b016f591e9315a7139", size = 4162063, upload-time = "2026-07-01T11:55:37.941Z" }, + { url = "https://files.pythonhosted.org/packages/76/66/2044b9a63d3b84ff048228dfcb7cd9bf0df983e8470971bf7d4c57b693de/pillow-12.3.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:9e881fca225083806662a5c43d627d215f258ff43c890f831966c7d7ba9c7402", size = 4255549, upload-time = "2026-07-01T11:55:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/1f67e6f4ece6b582ee4b539decbcc9f848dc245a93ed8cd7338bafef72f1/pillow-12.3.0-cp315-cp315-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:4998562bf62a445225f22e07c896bb04b35b1b1f2eb6d760584c9c51d7a5f78c", size = 3696331, upload-time = "2026-07-01T11:55:41.98Z" }, + { url = "https://files.pythonhosted.org/packages/12/40/d306fc2c8e4d45d7f175c77edca7063be7b86fe7fe6e68f4353bf71d808c/pillow-12.3.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:dc624f6bc473dacdf7ef7eb8678d0d08edf15cd94fad6ae5c7d6cc67a4e4902f", size = 5350370, upload-time = "2026-07-01T11:55:44.028Z" }, + { url = "https://files.pythonhosted.org/packages/dd/44/668fb1437e8ce420f62d6106eb66e44a5971602a4d794615bdf79315d82d/pillow-12.3.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:71d6097b330eea8fd15097780c8e89cb1a8ce7838669f48c5bacd6f663dd4701", size = 4780147, upload-time = "2026-07-01T11:55:46.073Z" }, + { url = "https://files.pythonhosted.org/packages/0c/08/93fa2e70e30a2d81547e481b6ee2bb9522117221fb1e0ce4b5df70967677/pillow-12.3.0-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:28ce87c5ab450a9dd970b52e5aca5fe63ed432d18a2eaddd1979a00a1ba24ace", size = 6273659, upload-time = "2026-07-01T11:55:48.264Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6d/043e96ff814fc31a33077e4cba86082167db520c93632afdf2042febbb0c/pillow-12.3.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b02afb9b97f65fbca5f31db6a2a3ba21aa93030225f150fa3f249717e938fb4", size = 6947439, upload-time = "2026-07-01T11:55:50.503Z" }, + { url = "https://files.pythonhosted.org/packages/af/92/ba71d2ee2ac0edf3fa33bd9d5ee9ee080da70b1766f3ca3934f9938ddac9/pillow-12.3.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1182d52bc2d5e5d7d0949503aa7e36d12f42205dc287e4883f407b1988820d39", size = 6353577, upload-time = "2026-07-01T11:55:52.697Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ce/e63064e2122923ff687c8ad792d0d736a7b3920a56a46982e81a7fdd25d6/pillow-12.3.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e795b7eb908249c4e43c7c99fac7c2c75dab0c43566e37db472a355f63693d71", size = 7060394, upload-time = "2026-07-01T11:55:55.149Z" }, + { url = "https://files.pythonhosted.org/packages/54/76/a09cc3ccc8d773a7283d34c38bec1708f9e3cc932093cbc4c5e71ac4060b/pillow-12.3.0-cp315-cp315-win32.whl", hash = "sha256:57b3d78c95ba9059768b10e28b813002261d3f3dfc55cc48b0c988f625175827", size = 6467375, upload-time = "2026-07-01T11:55:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/3e/03/1846c49ba3b1d5550392a4bbd06d6fb4578e1cd91a803198b5c90f5f7d53/pillow-12.3.0-cp315-cp315-win_amd64.whl", hash = "sha256:fa4ecea169a355be7a3ade2c783e2ed12f0e40d2c5621cda8b3297faf7fbb9f5", size = 7237048, upload-time = "2026-07-01T11:55:59.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/bb/89f35dcc79610423f9f195504d7def7f0d1416a711541b42867e25fe3412/pillow-12.3.0-cp315-cp315-win_arm64.whl", hash = "sha256:877c3f311ff35410f690861c4409e7ccbf0cd2f878e50628a28e5a0bb689e658", size = 2566006, upload-time = "2026-07-01T11:56:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/30/88/707027ba09942dfa2c28759b5c222d769290a41c6d20ea60ec250801941f/pillow-12.3.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:e9871b1ffbfa9656b60aeee92ed5136a5742696006fa322b29ea3d8da0ecc9cf", size = 5352509, upload-time = "2026-07-01T11:56:04.2Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6d/00352fa25332c2569cd387851f568cc5a4b75a9adbfb37ac4fbce4c02eec/pillow-12.3.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:53aa02d20d10c3d814d536aa4e5ac9b84ca0ff5a88377963b085ad6822f93e64", size = 4783167, upload-time = "2026-07-01T11:56:06.631Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/9e049dfa21af7c22427275720e2490267ba8138120add5c4c574deb69782/pillow-12.3.0-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:446c34dcc4324b084a53b705127dc15717b22c5e140ae0a3c38349d4efec071e", size = 6329237, upload-time = "2026-07-01T11:56:08.868Z" }, + { url = "https://files.pythonhosted.org/packages/36/16/cf6eeaae8d0fce8dd390a33437cf68c5d5bd73834a2bc6e2f14efda0ab45/pillow-12.3.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf1845d02ad822a369a49f2bb9345b1614744267682e7a03527dc3bf6eea1777", size = 6997047, upload-time = "2026-07-01T11:56:11.379Z" }, + { url = "https://files.pythonhosted.org/packages/1e/69/dbf769bdd55f48bf5733cac28edc6364ffaa072ec9ba336266e4fe66be55/pillow-12.3.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:186941b6aef820ad110fb01fb06eb925374dc3a21b17e37ec9a53b250c6fe2d1", size = 6400440, upload-time = "2026-07-01T11:56:13.908Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e1/ffc9cfc2eea0d178da8018e18e959301ad9d6bc9f3edb7181e748a474b97/pillow-12.3.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:f13c32a3abd6079a66d9526e18dad9b6d280384d49d7c54040cd57b6424041d9", size = 7105895, upload-time = "2026-07-01T11:56:16.575Z" }, + { url = "https://files.pythonhosted.org/packages/18/f0/a5595c1e8c3ae44b9828cb2f0fa8155e5095ef04d6327b8f61cf44a3df85/pillow-12.3.0-cp315-cp315t-win32.whl", hash = "sha256:1657923d2d45afb66526e5b933e5b3052e6bdea196c90d3abb2424e18c77dae8", size = 6474384, upload-time = "2026-07-01T11:56:18.855Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/62bcd9f844984c5938d3b05264a61d797a29d3e0812341a8204af70bbdee/pillow-12.3.0-cp315-cp315t-win_amd64.whl", hash = "sha256:8cd2f7bdda092d99c9fc2fb7391354f306d01443d22785d0cbfafa2e2c8bb418", size = 7243537, upload-time = "2026-07-01T11:56:21.214Z" }, + { url = "https://files.pythonhosted.org/packages/3d/68/1f3066acedf37673694a7141381d8f811ae97f30d34413d236abe7d489f1/pillow-12.3.0-cp315-cp315t-win_arm64.whl", hash = "sha256:06ff022112bc9cbf83b60f8e028d94ad87b60621706487e65f673de61610ab59", size = 2567491, upload-time = "2026-07-01T11:56:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/75/18/2e8b40223153ccbc60df07f9e8928dc0c76202aa4e55ae9f53962b6510d6/pillow-12.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b3c777e849237620b022f7f297dd67705f9f5cf1685f09f02e46f93e92725468", size = 5302510, upload-time = "2026-07-01T11:56:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/46/3e/51fabf59d5ab801ceab709453d3ab6b180083496579549de4c45ced6528a/pillow-12.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b343699e8308bdc51978310e1c959c584e7869cc8c40780058c87da7781a1e94", size = 4736058, upload-time = "2026-07-01T11:56:28.041Z" }, + { url = "https://files.pythonhosted.org/packages/bf/20/22fe9384b7949e25fb1293bcfc84fb82590ff4ea6b37c95b24d26d793d86/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbd139c8447d25dd750ab79ee274cc5e1fe80fc56340ab10b18a195e1b6eca3e", size = 5237776, upload-time = "2026-07-01T11:56:30.263Z" }, + { url = "https://files.pythonhosted.org/packages/08/14/f6ba68107680ffa74b39985f3f30884e41318fbc4250caa423c79b4788bb/pillow-12.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e7e480451b9fa137494bccd3a7d69adbe8ac65a87d97be61e11f1b1050a5bac3", size = 5860358, upload-time = "2026-07-01T11:56:32.68Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0169bc772ec491108b62f644f8ecf1fe5d8ae5ebafde2ee2142210166903/pillow-12.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:04f01d28a6aaff387bf842a13be313df23ba0597a44f1a976c9feb3c6ff4711a", size = 7231786, upload-time = "2026-07-01T11:56:35.046Z" }, ] [[package]] @@ -2706,26 +2702,26 @@ wheels = [ [[package]] name = "prek" -version = "0.4.5" +version = "0.4.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/65/23866f43521d31173879aa74bb3a2df50ab7f3f74cdb4eaa31b8f446c7ca/prek-0.4.5.tar.gz", hash = "sha256:2be7bcf839de19a0144ed5a5aadf73bc5899cf6823bb1c58cf1d45ae389c201a", size = 482566, upload-time = "2026-06-15T11:36:48.299Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/46/e436a6eb9fdb4d3fd08d0ab7fdba19fe03a9e994ec810de57869b853bd8e/prek-0.4.8.tar.gz", hash = "sha256:d15d8bef72ab7b02c7dc01458ac9e05b3131534492b5ce9bb11c4f6f636fa868", size = 494570, upload-time = "2026-07-04T12:05:10.941Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/cb/a9eedf9a35ca6ec72f12af2b4392d7f757bb24863b7b7af4523f939cf3fa/prek-0.4.5-py3-none-linux_armv6l.whl", hash = "sha256:f7517774c72b001573520dc7111156779fd3e5b4452c11f09ff53c71a067e835", size = 5618105, upload-time = "2026-06-15T11:36:21.998Z" }, - { url = "https://files.pythonhosted.org/packages/30/a7/c96c06f17db7da0a57be2be4c229aa00b525bca8001c9c765663b339cbb7/prek-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aca9fa995536036a0171bcf7a4db96dc0a14f480054eda1d7d1c2e7739650993", size = 5972998, upload-time = "2026-06-15T11:36:41.12Z" }, - { url = "https://files.pythonhosted.org/packages/28/f1/721695355cdaa44be6f091e3a77fb9c72ed60289520f78b2f8c9a7197bdd/prek-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:66877ff21ae9d548f0f7e56fab8e65f1500a74a810e7749188c3f35a4a1b911b", size = 5525098, upload-time = "2026-06-15T11:36:30.127Z" }, - { url = "https://files.pythonhosted.org/packages/9b/1b/a334e1bb5361b49adf52b5ac7b6532018940f9f0f253437e8f43c3c1f7f3/prek-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:50697089a86a78d16f087c1912a2f3bc2bea82319a220fac52cc8e3ec9fc0426", size = 5793732, upload-time = "2026-06-15T11:36:35.745Z" }, - { url = "https://files.pythonhosted.org/packages/28/8c/aff94d276e91207a87cedff7cfefdd4aca20444137cca77bf53fffebe77a/prek-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:590427a42a3c1e5064487a0dc91167ae0c8a52168e77f574758ef9b138fcfd61", size = 5521719, upload-time = "2026-06-15T11:36:39.383Z" }, - { url = "https://files.pythonhosted.org/packages/4f/73/cfb0c5c909442050a8357e26233f7e511ba8e0d2f4b0bdc460065d62beb6/prek-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fd98b986767dafdb6b4305b563ee5a3a8f13bd3c78b98d708626815ea9f147f", size = 5922623, upload-time = "2026-06-15T11:36:18.063Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ad/ff9d26551ba80d190bd08c6341176a5d56d4e6de9c2ebf077793d4adbb78/prek-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fccd11613ae92619d1ecda0ab3359ceebeb38898909ec84a8d383733d12158cc", size = 6722071, upload-time = "2026-06-15T11:36:43.086Z" }, - { url = "https://files.pythonhosted.org/packages/d3/43/11d1dfd66c919953fe89ae2fdedd4f413ee923883043816d35982177bb75/prek-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14109d37b33e5529db41a3539d4f8f72d295f6eeddede3964994d898b8cec05c", size = 6176454, upload-time = "2026-06-15T11:36:33.803Z" }, - { url = "https://files.pythonhosted.org/packages/d7/d4/9749f25c2e0ee5225f812457b888acef301e0ccce64bebcda2ac1d04abee/prek-0.4.5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:40d262418105b2ede9836593a1927fc927cc8093c432e998640964102196996e", size = 5791133, upload-time = "2026-06-15T11:36:23.891Z" }, - { url = "https://files.pythonhosted.org/packages/c7/72/5e0344bab1eacf813a5b1b082cb4c6253930096166dad51c1cccee0a4f83/prek-0.4.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a586d14c3b852fdee1c3dcd0b9cb0915db9f9d054334b854fd9470bf68edf129", size = 5658098, upload-time = "2026-06-15T11:36:44.862Z" }, - { url = "https://files.pythonhosted.org/packages/be/a5/1f406e0362dd0f18ba09a562d50d7c04a70ac05d350b1ab6fba36ca3e9f0/prek-0.4.5-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:a8ed0d28f3e7790e4402a9324c386509066df6e67cc587f7406f9a245b97b7e8", size = 5498634, upload-time = "2026-06-15T11:36:31.828Z" }, - { url = "https://files.pythonhosted.org/packages/c7/df/b0cbf0fa527330188390b7b6c8d279cd5e509923262d0a6c5cc44bbdf103/prek-0.4.5-py3-none-musllinux_1_1_i686.whl", hash = "sha256:86f76bd3d2ecf6fd9034d75c62ff4c786eb11d0dd0a1f79bbb4343b023e12769", size = 5784840, upload-time = "2026-06-15T11:36:37.481Z" }, - { url = "https://files.pythonhosted.org/packages/9d/d7/977ee3c622c906677dd94187a00392ce2dd76035486b3a3b1b5a5267dd34/prek-0.4.5-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:e491a1a4641d91d8b03dcce5588397e76d2a5b432c9b0a6c70475972b4512ab4", size = 6300384, upload-time = "2026-06-15T11:36:27.602Z" }, - { url = "https://files.pythonhosted.org/packages/79/fa/43b1d761381dc1c7eeb8f2235c66e902970d4b2bff2dec0f02836c085769/prek-0.4.5-py3-none-win32.whl", hash = "sha256:7546989b2403c96137bd79d19ebfe21facb87266cefe819db2458c3b9b23f350", size = 5287935, upload-time = "2026-06-15T11:36:20.293Z" }, - { url = "https://files.pythonhosted.org/packages/f5/fe/59b5eb3124f5a4cc255a93857b9ab42402635b273f157e91de23bfa40e8f/prek-0.4.5-py3-none-win_amd64.whl", hash = "sha256:8b2ac9227504371d97338215b344184cb0b31ca94113515a3a90c509c6c5a707", size = 5682560, upload-time = "2026-06-15T11:36:25.865Z" }, - { url = "https://files.pythonhosted.org/packages/97/0e/589ff0eab9034909b1ec8654ee03483797305fb743b3554ce6140d82da9d/prek-0.4.5-py3-none-win_arm64.whl", hash = "sha256:646a86a1a082dbd99fed96314b1064f5644bb34c1f4037a63547a18e2160fb86", size = 5509019, upload-time = "2026-06-15T11:36:46.595Z" }, + { url = "https://files.pythonhosted.org/packages/5c/78/b4149c8913ced2e42debb49e261c4788a1ce431e84226921c2e1a7ea8545/prek-0.4.8-py3-none-linux_armv6l.whl", hash = "sha256:1f8f8cdc65836b571824c965daebb81b449f7e4a43894c58621f5708d5a185ed", size = 5668955, upload-time = "2026-07-04T12:04:41.588Z" }, + { url = "https://files.pythonhosted.org/packages/76/5f/7f54a0087b6b2f1751aeb41266d9c15e66fd0055492814798ab818cd0414/prek-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:bce1798e96d9e3a6e6abf435da7107e81452f69edb3ca7c6f90a457355ea46e2", size = 6030947, upload-time = "2026-07-04T12:04:43.8Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d6/f2829fc3902920c36b764a386fa303e71a8219dac25cb3827c575e84199a/prek-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ab3a52db17254d701c3cebb7eea58c8230aa7c1959aacfd5b5f25de18edb15d1", size = 5572593, upload-time = "2026-07-04T12:04:45.763Z" }, + { url = "https://files.pythonhosted.org/packages/74/8c/c5589955bcd5e3e33b67d8bc3110818cecac82a38fd6bc8b5dfdc5de421c/prek-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:b3fcfd620523bbc3f51a21d7cd63449f659b9e2cf3582de12dd5949e23227b8f", size = 5847150, upload-time = "2026-07-04T12:04:47.419Z" }, + { url = "https://files.pythonhosted.org/packages/2d/9d/1f2dc91bdb79d2c4714b27eac9477a51490fba5b4731330dbbebc76bd345/prek-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42e65bc8425e9d7f1691a13ca1da2e07807d1ba76c35740833354b945131689e", size = 5573738, upload-time = "2026-07-04T12:04:49.125Z" }, + { url = "https://files.pythonhosted.org/packages/81/29/69a7b58e16ecbc5f3989bf4b028018d11a82dcdd320b93d6588d72f32aa7/prek-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f578492a8e0c9bc6b4bf6dfbba8716f647d4cd0769bf10ad6cf336e3096fd392", size = 5981054, upload-time = "2026-07-04T12:04:50.842Z" }, + { url = "https://files.pythonhosted.org/packages/63/cc/9b9850a60c22ed18c7755ebd2d72c6eefb37fac58149d09f6adc4691c2cf/prek-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4335f9d5beb123a3884a7fe34f57c9f0828f4fbb7666beab4298833459b104f", size = 6751350, upload-time = "2026-07-04T12:04:52.529Z" }, + { url = "https://files.pythonhosted.org/packages/01/e5/c425aa7272b430630119e6757def3a2007555ba8cbeb2630e0448e7a8b7f/prek-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18a8747df9c602e052881d3efb14dd7f7d62a59bd7277ae5171c9e7661d59d84", size = 6243881, upload-time = "2026-07-04T12:04:54.703Z" }, + { url = "https://files.pythonhosted.org/packages/1c/da/accd3ad07fd2891d3c2777eb42435439fdf11982c51d60f087c0b6b6e102/prek-0.4.8-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:4db639db481d5f854eff9b3d2108889e613b8c15868bcf6bdd777c7cee577436", size = 5848846, upload-time = "2026-07-04T12:04:56.402Z" }, + { url = "https://files.pythonhosted.org/packages/15/00/3477704635249f21f5f98ce444cd7690c2aa9dc8d146a045db88ef2cd8c5/prek-0.4.8-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c3890a6f92316d2cf44eb50584e8d2b23a596dd70487022e61186a71a2ac0900", size = 5713942, upload-time = "2026-07-04T12:04:58.311Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e6/3ca4fabaebeadc976d9a92d1d9130674265355ea3b728418bad61583b097/prek-0.4.8-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:fc7e15c24c591a37c6ffce5b25a021b16c299ac2649f183d812b67d665cd6551", size = 5554725, upload-time = "2026-07-04T12:04:59.96Z" }, + { url = "https://files.pythonhosted.org/packages/a5/46/2ab6aaaeff0cedb8955b2e4032071c8712382bdd423bb849718c3720180d/prek-0.4.8-py3-none-musllinux_1_1_i686.whl", hash = "sha256:36fe721704ff0c7624c1167639e23a5fe658bfd38c314f487219c9afd1eeb733", size = 5838595, upload-time = "2026-07-04T12:05:01.861Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8b/91398f2b6cd1629d5d8ca8c85b08eca500814a374313b0193f4aaf6ab6c4/prek-0.4.8-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:162e544abc394a8124f3a4ad68efee116bad09440e679dbd1675177335c2a432", size = 6357222, upload-time = "2026-07-04T12:05:03.845Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2a/ce5cbfaad36866134a21754640a05ecdba641fcd7ad15aa74cf3443f34f6/prek-0.4.8-py3-none-win32.whl", hash = "sha256:2602e46c8c5da7dfa69f60fcf88c2b57132ac623f49fb08bfb3094298c5f07e3", size = 5354388, upload-time = "2026-07-04T12:05:05.587Z" }, + { url = "https://files.pythonhosted.org/packages/df/03/3bc908bc5f7e430315553e47dfa055f19923a3888f9afe4da19f244b5cbf/prek-0.4.8-py3-none-win_amd64.whl", hash = "sha256:7cb22da60bee41b89c4978c0bea7126a3c0ccc003dae6748cf29b53947815edc", size = 5748221, upload-time = "2026-07-04T12:05:07.559Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a7/4295e6d5f5028171dfeb115ad38ab76bf3fe0c8df91b70d73c79aa760a94/prek-0.4.8-py3-none-win_arm64.whl", hash = "sha256:da70057f577b15d4bd121bf9dd29ee205fd4b4d75a0cafba062e84d7e8b4378b", size = 5574425, upload-time = "2026-07-04T12:05:09.595Z" }, ] [[package]] @@ -2874,19 +2870,19 @@ email = [ [[package]] name = "pydantic-ai" -version = "2.1.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic-ai-slim", extra = ["anthropic", "cli", "evals", "google", "logfire", "mcp", "openai", "retries", "web"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/14/2e/e393160043ee999c501c95f27dcb6439eec3092eca7d75b94f06165905a8/pydantic_ai-2.1.0.tar.gz", hash = "sha256:6279657f7640df3765a9faab9d9fa25b33c58caf20d67c6189dd5469dbbbb782", size = 18451, upload-time = "2026-06-29T09:51:16.374Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/6b/6fa7e71ad5c0ccd37ec8987a1a9d42e3ce6d8d3212ef7f3e0d6326647bd4/pydantic_ai-2.5.0.tar.gz", hash = "sha256:06d48d9b432a71096851910a29b98fcc88a746d3d0241c7db283737037998661", size = 18532, upload-time = "2026-07-04T00:47:38.869Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c7/dc/7cfa843db74fefdba995a2e21fe602e725e2ba77b0d7bc402bc62c1bf2d7/pydantic_ai-2.1.0-py3-none-any.whl", hash = "sha256:001272126e78f982805b1a4ac7e7805da10c88c4f28f9e4b905e923cfc1192d5", size = 7700, upload-time = "2026-06-29T09:51:07.83Z" }, + { url = "https://files.pythonhosted.org/packages/cd/aa/c1f0d8cd78c0653936ef842210ca96e6849085d58c3e93520f4e68563ef8/pydantic_ai-2.5.0-py3-none-any.whl", hash = "sha256:309f85896b80ff58f9baffd4f28a2fb6485e07c9e20a6c1f31cf81a18910c153", size = 7717, upload-time = "2026-07-04T00:47:29.504Z" }, ] [[package]] name = "pydantic-ai-slim" -version = "2.1.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, @@ -2898,9 +2894,9 @@ dependencies = [ { name = "pydantic-graph" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ca/d1/78fd15c9c68b95ac0bec2d2afe22feb3b2f46e4b8e3a6dd1cead61cde434/pydantic_ai_slim-2.1.0.tar.gz", hash = "sha256:f79dca2429dbb9d2e32a0e2c613cfb9b9d6f0dc81d42cff8a5c81ede8ca0a6c7", size = 738698, upload-time = "2026-06-29T09:51:18.634Z" } +sdist = { url = "https://files.pythonhosted.org/packages/22/8b/360c7dc4d28eb267fd8a872f269400f3ad4521dee2f2ce771a9479bc0260/pydantic_ai_slim-2.5.0.tar.gz", hash = "sha256:cec48e93ac45a3e5c20e3f7f27469fc7267b9f99b5b0433331465dc25a0d01e3", size = 769832, upload-time = "2026-07-04T00:47:40.759Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/c7/b531cf65a1f8b221ab39739a16fe08f7b6a56fd24db875b9c33010320c9c/pydantic_ai_slim-2.1.0-py3-none-any.whl", hash = "sha256:2afda56459606226113ab433ee659aac7ff29bb66feefa716c67144bd82e5ce2", size = 910109, upload-time = "2026-06-29T09:51:11.016Z" }, + { url = "https://files.pythonhosted.org/packages/21/11/2d5f17149ebb9b02718ed041a522ae907981ccca4949024598f5db0dd0d6/pydantic_ai_slim-2.5.0-py3-none-any.whl", hash = "sha256:8ad3a3c8cb0faee7b4017da7e5fff40017225f54fc700b52682ce19867d23e09", size = 946225, upload-time = "2026-07-04T00:47:32.528Z" }, ] [package.optional-dependencies] @@ -3057,7 +3053,7 @@ wheels = [ [[package]] name = "pydantic-evals" -version = "2.1.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -3067,9 +3063,9 @@ dependencies = [ { name = "pyyaml" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7b/d6/d2a315322052fa849cb1008c18ac43688e06e1059573cd424cfd20d49be5/pydantic_evals-2.1.0.tar.gz", hash = "sha256:58e72ceba61ba673f074b58f057413dc8d3393720767c89969681d2e4f4067a4", size = 76016, upload-time = "2026-06-29T09:51:19.798Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/6a/62cc359ea7d92d31b630c94e6934a751ef8c3ecaa2044dac6437cd184b15/pydantic_evals-2.5.0.tar.gz", hash = "sha256:e49345856a30ce95a1683d7984cf5268a057cba938194eacc476559dd6241dcc", size = 84832, upload-time = "2026-07-04T00:47:42.163Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/7b/59f9d4f865e2f518179a3a42986e4e8785b70c98ec180c8da10fd78c02a6/pydantic_evals-2.1.0-py3-none-any.whl", hash = "sha256:6c9fc370808d883c20477ce69a6d6bc0b9c5a8d3dab66be5dd85d960a3f88d23", size = 90590, upload-time = "2026-06-29T09:51:12.816Z" }, + { url = "https://files.pythonhosted.org/packages/2a/86/8e6bf22e671079bcb537b4dd674c85b2612648f0c84c42300a1e53d6f629/pydantic_evals-2.5.0-py3-none-any.whl", hash = "sha256:4e06b54c2f4afd3cf9d6de8893624dd79bb544330148366fe8205768dc53e09a", size = 100158, upload-time = "2026-07-04T00:47:34.979Z" }, ] [[package]] @@ -3087,7 +3083,7 @@ wheels = [ [[package]] name = "pydantic-graph" -version = "2.1.0" +version = "2.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -3095,9 +3091,9 @@ dependencies = [ { name = "pydantic" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/39/20/018532c826aba3c29ffc37bda46c20f3db4584bb555a8c7dc2769866267d/pydantic_graph-2.1.0.tar.gz", hash = "sha256:36ed6af24543421fb628fee593ccf5553a286c1dd1677018f5064a4e44c4daba", size = 43052, upload-time = "2026-06-29T09:51:20.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/51/dcd78a4ccfb7c0e68a3c1c57100797f8674dcd24b33665670307d4912f61/pydantic_graph-2.5.0.tar.gz", hash = "sha256:b41ca881108a0584366966001badcd4792d4b19330a1f2c527512c3f1739aab5", size = 43905, upload-time = "2026-07-04T00:47:43.249Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/a8/7f32bdeda6cff28652bba1c21ab2155c81d1733abd4b8bfef46a26e8d3fe/pydantic_graph-2.1.0-py3-none-any.whl", hash = "sha256:bae1e99829abf590a8693442ba5693ace38d2d93a80ae48be55c610a503e3709", size = 50772, upload-time = "2026-06-29T09:51:14.161Z" }, + { url = "https://files.pythonhosted.org/packages/dd/9a/823b4b617acc392326d56c62ca736fc494f1ec9c4e7b748817a14fcc63e0/pydantic_graph-2.5.0-py3-none-any.whl", hash = "sha256:bee68a560cb946ddc1f9d5d11c2d1bebe21b1b1ab352ac1c5d80e16cde5a6b67", size = 51646, upload-time = "2026-07-04T00:47:36.549Z" }, ] [[package]] @@ -4161,7 +4157,7 @@ wheels = [ [[package]] name = "strawberry-graphql" -version = "0.320.0" +version = "0.320.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cross-web" }, @@ -4170,9 +4166,9 @@ dependencies = [ { name = "python-dateutil" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/44/38/95a72342ba94570a35b8cc7db6a6789378f10b69be1d77e0fb022f5079e0/strawberry_graphql-0.320.0.tar.gz", hash = "sha256:c5ee8abf7f4db00d3515e537578a5c094a4a9f3b732a959d9020c8788d3ac7c7", size = 229231, upload-time = "2026-06-27T23:11:43.262Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/87/ac45b0ed6aedf32c13322810ce131d542f1b26c2360e7bf2830078e4d3cd/strawberry_graphql-0.320.1.tar.gz", hash = "sha256:ef244499dd23d964b27cb096609d182dcc9eba3f4d000c95b3505f119cf9e9d3", size = 229165, upload-time = "2026-07-02T10:58:12.904Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/db/99/f8e8919a73ae466f9740e63115f010a02fac3f2b69c02ea60c9c85d42c1a/strawberry_graphql-0.320.0-py3-none-any.whl", hash = "sha256:8ea27f43f73a1accbfd1599b37234086d26504cf60213ebb521528319ccf2309", size = 332321, upload-time = "2026-06-27T23:11:41.045Z" }, + { url = "https://files.pythonhosted.org/packages/6b/b0/f84325fc10ac26b51b0e827035ee63faddd337ab0641469046faf8e2823e/strawberry_graphql-0.320.1-py3-none-any.whl", hash = "sha256:782deb6378412f943feac573274068073b4f9e25ec7caf323136db9277cec20e", size = 332207, upload-time = "2026-07-02T10:58:11.319Z" }, ] [[package]] @@ -4370,27 +4366,27 @@ wheels = [ [[package]] name = "ty" -version = "0.0.55" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/08/48/f687c8d268e3581f2f104d1f2ac5944d5b5e841b3695c613b3f263e5bbf7/ty-0.0.55.tar.gz", hash = "sha256:88ca87073825a79a8327c550efcc86cec94344890244c5946f84c9e44a969f31", size = 6040230, upload-time = "2026-06-27T00:27:29.385Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/a3/1a90ba7e5a61c6d09adb92346ddba97668095fc257b577af433e5ac4f404/ty-0.0.55-py3-none-linux_armv6l.whl", hash = "sha256:31e83eef512d066542fe990fe1a3b814423abd1616376c54e48af7045b3e1749", size = 11677249, upload-time = "2026-06-27T00:26:52.18Z" }, - { url = "https://files.pythonhosted.org/packages/82/3a/669f9aa478c38243e213a2684db1502086026cfadc15bb1b29b7cbde030d/ty-0.0.55-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ab4bca857950608fea73e269e2da369d43e6467131de85160d68e2fa466fa248", size = 11444180, upload-time = "2026-06-27T00:26:54.576Z" }, - { url = "https://files.pythonhosted.org/packages/15/a4/6a4b2507a53ce6530c66c5b4fe0d58551eb1748ffa9e0696c32fdd55bbd4/ty-0.0.55-py3-none-macosx_11_0_arm64.whl", hash = "sha256:55032bfd31bf2c5355ee81bdc6407b144a1cc7ee41e5681dd1368e4cef2ba327", size = 10963134, upload-time = "2026-06-27T00:26:57.348Z" }, - { url = "https://files.pythonhosted.org/packages/ce/ae/a3b1a0f1cc83b7d258662cb98aa80a720c2e671d0e8fa0d17a4d5d057a7a/ty-0.0.55-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef1e049f69ce65b3c269af67624607f435e1c32319786c1e453ef9611502f295", size = 11493517, upload-time = "2026-06-27T00:26:59.26Z" }, - { url = "https://files.pythonhosted.org/packages/0d/9f/311ce39065a979ef40a9b847f685c8e02464e53adf1671e081eea90640ca/ty-0.0.55-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:631409975c681d5a280fc5a99b7b32e9e801f33be7567c6b42ec331362f59d7d", size = 11460590, upload-time = "2026-06-27T00:27:01.425Z" }, - { url = "https://files.pythonhosted.org/packages/cd/8f/3bf29aa77bd78aae48275153135a2052fa7d3ccdf1ecabeb99c8773abd66/ty-0.0.55-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e08cb0436e68b9351555ae8f2697138c9009b4d5b4ae4272232988b2a431a98f", size = 12098430, upload-time = "2026-06-27T00:27:03.596Z" }, - { url = "https://files.pythonhosted.org/packages/bc/6e/e88411a88240b94640bba06fb6d0d92b247fbeef47ee2bc71f39e58c2558/ty-0.0.55-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16c215ad9f823829409b94ee188cfaa4563f6e1384f6ce3fecb1db75f6c7cf7c", size = 12673086, upload-time = "2026-06-27T00:27:05.589Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7e/8f1762fb7f9245a68ba5ae338d73c59403ce57554e5d311b8bb55027b0ec/ty-0.0.55-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b510eb8f4032baf11b7aee2f1d53babc3b4ca03939b9cdcf6a9d15761d575188", size = 12242559, upload-time = "2026-06-27T00:27:07.714Z" }, - { url = "https://files.pythonhosted.org/packages/72/1f/143657daf2670d977dac83435f1fe03d4843efb798d8e1e75950e541aadd/ty-0.0.55-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ddc05e7959709c3b9b83aa627128a80446865e3c1a4882638dcff6d776dc34a", size = 12021409, upload-time = "2026-06-27T00:27:09.881Z" }, - { url = "https://files.pythonhosted.org/packages/6d/30/69487c439dd1fad3a4a3d96f0a472193de297eaba6fc4b8ea687ce434ac2/ty-0.0.55-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:636e8e5078787b8c6916c94e1406719f10189a4ca6b37b813a5922ce5857a8c7", size = 12303807, upload-time = "2026-06-27T00:27:11.986Z" }, - { url = "https://files.pythonhosted.org/packages/e8/ca/cd88b6493dafc7db077f5e17c0438eb3af6e2d6d08f616dbb52a8ddfd567/ty-0.0.55-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ef7d6deaacb73fec603666b5471f1dc5a5699aa84e11a6d4d644dd07ca72121e", size = 11441263, upload-time = "2026-06-27T00:27:14.087Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fe/66b6915671653ab739f71e4f1b0528e69da64429b7ebf3840c625b6e43f2/ty-0.0.55-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9aeea0fe5875d3cf37faf0e44d0fdf9669335467749741b8fc0103916fb5cd32", size = 11484584, upload-time = "2026-06-27T00:27:16.311Z" }, - { url = "https://files.pythonhosted.org/packages/4a/4f/7a9c0bbac8b899e9f6c0ec110c6612f52e4db35f6bb17ddc0ef60384fa3e/ty-0.0.55-py3-none-musllinux_1_2_i686.whl", hash = "sha256:0b699c01310dbd2705a07c97c5f4aaeedef61bd9adeea2e7c46aed32401d3576", size = 11759309, upload-time = "2026-06-27T00:27:18.471Z" }, - { url = "https://files.pythonhosted.org/packages/ca/de/b6f8b1b69aa631b5716ef3f985c3b56de0e46c2499cc00d30c402b41f714/ty-0.0.55-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:32cbeba543e46de2a983ec6d525d8b56514f7422bd1e1b57c44ccf7bfa72c38a", size = 12128755, upload-time = "2026-06-27T00:27:20.55Z" }, - { url = "https://files.pythonhosted.org/packages/7d/90/a912531e51ee7e076b42972479290fa687c0f5e747b7e773f3033164acaa/ty-0.0.55-py3-none-win32.whl", hash = "sha256:52b968e24eb4f7a5c3bd251db1f99f60dd385890356d38fc619d84f1b423446a", size = 11117501, upload-time = "2026-06-27T00:27:22.714Z" }, - { url = "https://files.pythonhosted.org/packages/4c/7a/99d59843bf8908a7f9f4d13fda107dbad07b7faa28ecd7860eacf363fb1c/ty-0.0.55-py3-none-win_amd64.whl", hash = "sha256:bf39cbfdc0add44d94bd3fff1f53c351418d134b6a66b87efdb7876d7b7a2224", size = 12150106, upload-time = "2026-06-27T00:27:24.881Z" }, - { url = "https://files.pythonhosted.org/packages/b3/44/20987505cedf2a865b08482f0eabc181fd9599b062964057ec8a128a4296/ty-0.0.55-py3-none-win_arm64.whl", hash = "sha256:f7f3700a9a060e8f1af11e4fb63fafcaf272b041781f4ccdfda2b3b5c6c1e439", size = 11560157, upload-time = "2026-06-27T00:27:27.332Z" }, +version = "0.0.56" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/07/fb29aea5235b0aa8ecfc4d1cc6ddf9fba8b863d67d96c6d345694d644c43/ty-0.0.56.tar.gz", hash = "sha256:84d114dc3796361c0fc72945016eabd74d46b9ee64f198cb0e485719704681e5", size = 6050123, upload-time = "2026-07-01T16:44:56.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/48/bce79e7ca5c1cc529d3e0d37ddd1121aea4b68a4f749974ad1cc77161871/ty-0.0.56-py3-none-linux_armv6l.whl", hash = "sha256:186d4a53e15747c947e1ec3d7eec8e345d8e40a1ca10e634c585db52497e87dd", size = 11643066, upload-time = "2026-07-01T16:44:18.374Z" }, + { url = "https://files.pythonhosted.org/packages/80/d1/22555d8a1d719661f10050f3865d877bbf497da908961c75fe22217dd18a/ty-0.0.56-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:aae1a980fd9535da0469b7ba2b2e1b54a907743a5e0f442dd57eee9f5bfd034c", size = 11407487, upload-time = "2026-07-01T16:44:20.956Z" }, + { url = "https://files.pythonhosted.org/packages/cf/2d/b3b7a74ce8bc59ef48843ad80179bb0d9598bbd6cfc0d11d519bdf6b1352/ty-0.0.56-py3-none-macosx_11_0_arm64.whl", hash = "sha256:afd3058c0a6c5f241e814734f133008c93ee805f61c9cf4ce7412b8822b5d9ad", size = 10962270, upload-time = "2026-07-01T16:44:22.959Z" }, + { url = "https://files.pythonhosted.org/packages/64/ac/6c2fd7de0304a8a7218a756af74f7e62a5e8540fdb175e0a869e51042345/ty-0.0.56-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:058b52f7a823ac13aae3cae30809dd6b5145794b64d8478f9ef38c75d79b4483", size = 11471406, upload-time = "2026-07-01T16:44:25.327Z" }, + { url = "https://files.pythonhosted.org/packages/50/b6/11d861156861c03c7726b74558f9a0e0092661aff83a4fda1279df28c425/ty-0.0.56-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2c66e00c1522add1f2bbdd2e45828c953b35c306b7bef03ec9169c75a63699a0", size = 11445612, upload-time = "2026-07-01T16:44:27.531Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ba/09df108582090f3c0770ec4bc8675affed60248f6793a78d909be16211d9/ty-0.0.56-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40903d71c669a30691b5a5d5728056c7877a1bd6be4f233a38883a8b28cf34d7", size = 12093889, upload-time = "2026-07-01T16:44:29.548Z" }, + { url = "https://files.pythonhosted.org/packages/d7/f7/dbb4b4ccb69cd64c209ae55b1ab788ace8222c2bc1f6845be9e7cbedbf25/ty-0.0.56-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63fe3947fe0c46c69a7d950e6832ee70a9ec17321fefbff3d2e3c20baf9e5bd0", size = 12666337, upload-time = "2026-07-01T16:44:31.586Z" }, + { url = "https://files.pythonhosted.org/packages/86/e9/73f903fe4a3d9ea02f26f57c1eb07e3b1029ec92b0e8c2364718893440e3/ty-0.0.56-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71a0c1a72f9854532e710e119b6871ffe4542c8a65146f1f65dcd78fecd885b4", size = 12280247, upload-time = "2026-07-01T16:44:33.637Z" }, + { url = "https://files.pythonhosted.org/packages/d6/90/cebd222495832f1a00dcd321ba25f3cab804221a4991b992c2178bec68ee/ty-0.0.56-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70d1665596494e24d8ebd198438872b5a56ec3cae5f2bcf6c673be797acc4e3c", size = 11991107, upload-time = "2026-07-01T16:44:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/b7/07/8f7337a07250f42d975cdb6decf47fc5b421e6c7da5e3e7be1e85f63a7e5/ty-0.0.56-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:778f99e51558afc1dbbe48ee38ab6aae7b31390ed8c1a1ef1499b295e9f1e82f", size = 12298970, upload-time = "2026-07-01T16:44:38.243Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b9/a52cd59034a48f5f18c6b155cc2cc36861d874b6d0af204b12c898024c3d/ty-0.0.56-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:867bc5708e0066bb4ff6c7db524bd5deea2676c62bfe71d3303138b3be850af0", size = 11425683, upload-time = "2026-07-01T16:44:40.473Z" }, + { url = "https://files.pythonhosted.org/packages/1d/2e/48e42d33357d52eefb695c0c3fcfc96879b73668a7447d1d1e0ad774fedc/ty-0.0.56-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6012f4189c928edb330a37deb9930f982380bd4aa7c4b8e0428eec9651c7551", size = 11469258, upload-time = "2026-07-01T16:44:42.513Z" }, + { url = "https://files.pythonhosted.org/packages/d5/01/ad1b4138be1e3fa97863af3925aa2134f17a593240c35dc38c3429fb5ad1/ty-0.0.56-py3-none-musllinux_1_2_i686.whl", hash = "sha256:8ee83de1a7ff4cc32837ec06134ce391d441bc5b35ecd8d3cfe053f120f3e4c1", size = 11758736, upload-time = "2026-07-01T16:44:44.567Z" }, + { url = "https://files.pythonhosted.org/packages/09/34/9d81967ff240eaa57e9249728ef7b7790747cf6d3c9a98ec86b2cfdcc8ee/ty-0.0.56-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:62619b3b0e2c6248ef30d3f0e2f2217ae9893040585be07f32324242f197cd6f", size = 12100242, upload-time = "2026-07-01T16:44:46.584Z" }, + { url = "https://files.pythonhosted.org/packages/c3/36/f51d4666d2de6cf33c1f3a1fcc4bb6b70b197dd6ceaa491eef71d78fe8e8/ty-0.0.56-py3-none-win32.whl", hash = "sha256:b30687bb5cd9729d34c889a289edf32770388d9bb05243e534e723fb45e0381b", size = 11093759, upload-time = "2026-07-01T16:44:49.171Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b4/8fb5d4acfa4afb152245b20fa263069a7547bd1f8e4bfca4eda280c897d7/ty-0.0.56-py3-none-win_amd64.whl", hash = "sha256:ad4c8c47b6f4e3f9ed3fc0b1a5d650088d229e17dd8f63c1826d6bbe94cc3235", size = 12100327, upload-time = "2026-07-01T16:44:51.26Z" }, + { url = "https://files.pythonhosted.org/packages/b8/fc/6a183e71edde90d0c35c2303f23f7a45b6891d1a2c45daf7b8f869831e19/ty-0.0.56-py3-none-win_arm64.whl", hash = "sha256:57538f273d444a5f1293fa7860e967178afe3917611fc5eff16b64e1204fe0d6", size = 11538780, upload-time = "2026-07-01T16:44:53.8Z" }, ] [[package]] @@ -4410,11 +4406,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] @@ -4802,7 +4798,7 @@ wheels = [ [[package]] name = "zensical" -version = "0.0.46" +version = "0.0.47" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -4814,20 +4810,20 @@ dependencies = [ { name = "pyyaml" }, { name = "tomli" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/57/c7bbb71f943e1e0ba5ce460f4930ec836ead7286969e7fd742f7a6c049ab/zensical-0.0.46.tar.gz", hash = "sha256:3ec21f4fb1e78cd7c0d6b07ae336b04770e27ba020dabc457b2790e5d34f1978", size = 3973968, upload-time = "2026-06-21T18:52:40.368Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4c/bd/bbc499ee35ac9ec5459dbfec7bb7231556689e97eaa13a5eddbe1f0443b5/zensical-0.0.46-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d91af81ab058c8693dfd75f2f77b4c73bcba4125681d1d276f38624291820bd2", size = 12796482, upload-time = "2026-06-21T18:52:07.369Z" }, - { url = "https://files.pythonhosted.org/packages/88/1b/7acc273184d59b8e894d15ebe3cf1c5e81b3a822fde1792ea3e33be37a2e/zensical-0.0.46-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:d9221264a9a87409900a47e29985607b0c9245dacb89077e87c8e16e31edc167", size = 12660030, upload-time = "2026-06-21T18:52:10.186Z" }, - { url = "https://files.pythonhosted.org/packages/80/df/bd0a68de98a19fc6050c58be11f36d05ea72a213b6a7ff7395d33c793747/zensical-0.0.46-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec43018d5343ca2e1d71aa352eeddd560fef504effd03025840a5a783abefa4f", size = 13057130, upload-time = "2026-06-21T18:52:12.911Z" }, - { url = "https://files.pythonhosted.org/packages/f4/db/e27635f5787a42245f900e658340698a6654e165d466f9a3b640efced2cd/zensical-0.0.46-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26e98fb8ab7ab50cdd20a73e2c7d4d9aae0b46cf2d8691e6bb22f9c261b8a60a", size = 13022345, upload-time = "2026-06-21T18:52:15.84Z" }, - { url = "https://files.pythonhosted.org/packages/e7/9d/6ce2ba11c97154870b458a8dae4637ade93b7097912f0102f5ea7fe8cf5b/zensical-0.0.46-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46fe578f26963f8ee89567983e62737b6fadc9197d4742e1020b522e092d7baa", size = 13377445, upload-time = "2026-06-21T18:52:18.538Z" }, - { url = "https://files.pythonhosted.org/packages/68/06/9930d43cd9d2f899b648d63491007c1b4f9716cf118b0c98e867b933069c/zensical-0.0.46-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aef03fa186a5589148e10b62610500989c6b075a2c08e1554233adbf91b2a3dc", size = 13086749, upload-time = "2026-06-21T18:52:21.452Z" }, - { url = "https://files.pythonhosted.org/packages/c4/ed/2342cf860fbb02314938b0d1f1b02344935801b04d185ff3151ef1812898/zensical-0.0.46-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:bc7446cdf97a8dea390f20ed2bd6b030cddc1bd36a8ce113ea3efef6fa61c573", size = 13231120, upload-time = "2026-06-21T18:52:24.171Z" }, - { url = "https://files.pythonhosted.org/packages/de/b0/d2ece02f63cd767fcf10fd7608dc8e0a995f87dc5261209b1dbc296fd57b/zensical-0.0.46-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:bbee37801f1ed500f158dc0992c569282950f780ae353c37fe6969f99983d701", size = 13295035, upload-time = "2026-06-21T18:52:26.942Z" }, - { url = "https://files.pythonhosted.org/packages/4b/b2/cb0048a612e63e615399fc507472a557d1c5b7c2f74065c5bf11998fd597/zensical-0.0.46-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:9487c147c9cceb50c04d0ad70b024821a6eab1629dafd70ab6d1e86ec841e623", size = 13437191, upload-time = "2026-06-21T18:52:29.69Z" }, - { url = "https://files.pythonhosted.org/packages/91/16/515f81db8055b109a510063be481e60a657c4fad1a883680b2ee4aa9a424/zensical-0.0.46-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f42a4683c762f026878d19ede4bcf7bfbb84dbecb5ad923949abb77806ed88a5", size = 13369382, upload-time = "2026-06-21T18:52:32.521Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5c/da54ee65b642eb7d88dd4a3db35845d0765915638e05d5d434a10b42f1c3/zensical-0.0.46-cp310-abi3-win32.whl", hash = "sha256:85f018f2a7ee76a83915c87ddb12b58cf343fd6154081d33ac95b6751b011dd7", size = 12354298, upload-time = "2026-06-21T18:52:34.976Z" }, - { url = "https://files.pythonhosted.org/packages/73/26/fc7ef081acbdada8436825221cb728ee84a81d4d78a7bb79aa58bd150d31/zensical-0.0.46-cp310-abi3-win_amd64.whl", hash = "sha256:1543a693a160de60e86ca589592401b584670e7e12c5ae30e3c2ba76786f7ec3", size = 12599687, upload-time = "2026-06-21T18:52:37.913Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/d7/99/53171a5211359a95b0d6caad9a1c960b002a4bf82eacdd3cdbde225db25f/zensical-0.0.47.tar.gz", hash = "sha256:324f783b22cd0deed0d0f3b69e28d5380b47238a1ef0f25913b88014af2bade2", size = 3976587, upload-time = "2026-07-05T15:51:25.488Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/7c/5848aad6a5b566f5a3cebc77ab787793daec622b79295de243da7b8efae6/zensical-0.0.47-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:4162fb8b62f38e6d9b75688c1fb87e18a1cffef5eee51d2c18b79334b548e973", size = 12789282, upload-time = "2026-07-05T15:50:45.118Z" }, + { url = "https://files.pythonhosted.org/packages/ce/08/c2a056ae22eba7063985cc89554ab8e54efb11af8e26f6d606e5bd300ae8/zensical-0.0.47-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:cdc2d84f38da809a28402eda5f2b6dbb150e14427f296c5b521e0dfc6a2e8a39", size = 12661193, upload-time = "2026-07-05T15:50:49.138Z" }, + { url = "https://files.pythonhosted.org/packages/dc/2b/30dc3262e99bc72e9a6529487fbfca58cf4f81cebfac59ef8e8d757d90eb/zensical-0.0.47-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97ed2b21aba5f788fc39d1597d00938d602b4d2724ed599a1dab7548fe4f0025", size = 13044347, upload-time = "2026-07-05T15:50:52.429Z" }, + { url = "https://files.pythonhosted.org/packages/4d/80/296ff21b686cda47c4a3f94c35bf62472291501c34e4f06dfd107cb237da/zensical-0.0.47-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6f6b2de477c45284201e92301f997415f45f78493bd20f479ea9a1c86bbbbcca", size = 13006494, upload-time = "2026-07-05T15:50:55.376Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4e/5aeba02363db89c625cd41c76d2a6e40b07d6a75d869d970ace7accd2bfa/zensical-0.0.47-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ee29ff819372eaab02ca0f14ac82e804332d898c57c56ffd4d89674f3e5ff71", size = 13379253, upload-time = "2026-07-05T15:50:58.743Z" }, + { url = "https://files.pythonhosted.org/packages/fd/dc/e72a1449544aeffa3dbe878c6340bcec98ba4eebabe7a592414064b0ae8d/zensical-0.0.47-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd94937c48a2e42b5b65b32c5075849937f23cebccaf250d249efa27266e0be", size = 13103356, upload-time = "2026-07-05T15:51:02.247Z" }, + { url = "https://files.pythonhosted.org/packages/48/eb/7fb956915cc59ee9f2ddf36ec5b16e8e858823217611e1d766816a661651/zensical-0.0.47-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4702605b991bece11494a9bb318d5ba7229f00e5adcc9e6010acd58e9719686b", size = 13220897, upload-time = "2026-07-05T15:51:05.919Z" }, + { url = "https://files.pythonhosted.org/packages/57/6b/944954d60bf1d61de2a7fa2732d0fb2d5deee64567db6088cdd2b5f76973/zensical-0.0.47-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:77f08ffcc3da9ca2f972330e501927aa7e8e445bfa7b758107edc645acda266e", size = 13280476, upload-time = "2026-07-05T15:51:09.352Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/4aece9b99eb5809b04fc6577fc50e877c998917293dfd58b5b229616b92b/zensical-0.0.47-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:319cf370ecc6d87da69c935c5acd6e1959dd48954766e9f954319bdb0bec5d36", size = 13439392, upload-time = "2026-07-05T15:51:12.564Z" }, + { url = "https://files.pythonhosted.org/packages/41/10/5cbffe680605b4fa7c0d6326f07a6adc86854f8b9d61bb1b29bb482d40eb/zensical-0.0.47-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:59656bf604a8b03eede4ce1a847640bab1129ab86dec2e39e5bd4b808b1802d2", size = 13386534, upload-time = "2026-07-05T15:51:15.892Z" }, + { url = "https://files.pythonhosted.org/packages/d5/06/79c7726b0388da40a96cc01113d872d639da1d8a5f96b5265e46e8472ceb/zensical-0.0.47-cp310-abi3-win32.whl", hash = "sha256:81a13a8bacadedada4847eed4aaa4a3f4ef0e5b78dbb2a2022bc6fb7e7dc9464", size = 12356060, upload-time = "2026-07-05T15:51:19.167Z" }, + { url = "https://files.pythonhosted.org/packages/f9/7a/7daaeadfdf7dc2941c8b931f9c6c83396d1da45634cec1a28f8d85a57708/zensical-0.0.47-cp310-abi3-win_amd64.whl", hash = "sha256:944a309be69b11daa8bba46c61fb74f32a98b637f3e7c11135e7cc2a5eccbe32", size = 12601932, upload-time = "2026-07-05T15:51:22.485Z" }, ] [[package]]