Compare commits
1 Commits
main
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1fe31a31ac |
24
.github/workflows/build-docs.yml
vendored
24
.github/workflows/build-docs.yml
vendored
@ -7,11 +7,6 @@ on:
|
|||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
- synchronize
|
- synchronize
|
||||||
|
|
||||||
env:
|
|
||||||
UV_SYSTEM_PYTHON: 1
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
changes:
|
changes:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -57,19 +52,17 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
- name: Setup uv
|
- uses: actions/cache@v4
|
||||||
uses: astral-sh/setup-uv@v3
|
id: cache
|
||||||
with:
|
with:
|
||||||
version: "0.4.15"
|
path: ${{ env.pythonLocation }}
|
||||||
enable-cache: true
|
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt', 'requirements-docs-insiders.txt', 'requirements-docs-tests.txt') }}-v02
|
||||||
cache-dependency-glob: |
|
|
||||||
requirements**.txt
|
|
||||||
pyproject.toml
|
|
||||||
- name: Install docs extras
|
- name: Install docs extras
|
||||||
run: uv pip install -r requirements-docs.txt
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: pip install -r requirements-docs.txt
|
||||||
- name: Install Material for MkDocs Insiders
|
- name: Install Material for MkDocs Insiders
|
||||||
if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' )
|
if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' ) && steps.cache.outputs.cache-hit != 'true'
|
||||||
run: uv pip install -r requirements-docs-insiders.txt
|
run: pip install -r requirements-docs-insiders.txt
|
||||||
env:
|
env:
|
||||||
TOKEN: ${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}
|
TOKEN: ${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
@ -84,7 +77,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: docs-site
|
name: docs-site
|
||||||
path: ./site/**
|
path: ./site/**
|
||||||
include-hidden-files: true
|
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/alls-green#why
|
# https://github.com/marketplace/actions/alls-green#why
|
||||||
docs-all-green: # This job does nothing and is only used for the branch protection
|
docs-all-green: # This job does nothing and is only used for the branch protection
|
||||||
|
29
.github/workflows/deploy-docs.yml
vendored
29
.github/workflows/deploy-docs.yml
vendored
@ -12,9 +12,6 @@ permissions:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
statuses: write
|
statuses: write
|
||||||
|
|
||||||
env:
|
|
||||||
UV_SYSTEM_PYTHON: 1
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy-docs:
|
deploy-docs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -28,16 +25,14 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
- name: Setup uv
|
- uses: actions/cache@v4
|
||||||
uses: astral-sh/setup-uv@v3
|
id: cache
|
||||||
with:
|
with:
|
||||||
version: "0.4.15"
|
path: ${{ env.pythonLocation }}
|
||||||
enable-cache: true
|
key: ${{ runner.os }}-python-github-actions-${{ env.pythonLocation }}-${{ hashFiles('requirements-github-actions.txt') }}-v01
|
||||||
cache-dependency-glob: |
|
|
||||||
requirements**.txt
|
|
||||||
pyproject.toml
|
|
||||||
- name: Install GitHub Actions dependencies
|
- name: Install GitHub Actions dependencies
|
||||||
run: uv pip install -r requirements-github-actions.txt
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: pip install -r requirements-github-actions.txt
|
||||||
- name: Deploy Docs Status Pending
|
- name: Deploy Docs Status Pending
|
||||||
run: python ./scripts/deploy_docs_status.py
|
run: python ./scripts/deploy_docs_status.py
|
||||||
env:
|
env:
|
||||||
@ -60,19 +55,19 @@ jobs:
|
|||||||
# hashFiles returns an empty string if there are no files
|
# hashFiles returns an empty string if there are no files
|
||||||
if: hashFiles('./site/*')
|
if: hashFiles('./site/*')
|
||||||
id: deploy
|
id: deploy
|
||||||
env:
|
uses: cloudflare/pages-action@v1
|
||||||
PROJECT_NAME: sqlmodel
|
|
||||||
BRANCH: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'main' && 'main' ) || ( github.event.workflow_run.head_sha ) }}
|
|
||||||
uses: cloudflare/wrangler-action@v3
|
|
||||||
with:
|
with:
|
||||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
command: pages deploy ./site --project-name=${{ env.PROJECT_NAME }} --branch=${{ env.BRANCH }}
|
projectName: sqlmodel
|
||||||
|
directory: './site'
|
||||||
|
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
branch: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'main' && 'main' ) || ( github.event.workflow_run.head_sha ) }}
|
||||||
- name: Comment Deploy
|
- name: Comment Deploy
|
||||||
run: python ./scripts/deploy_docs_status.py
|
run: python ./scripts/deploy_docs_status.py
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }}
|
DEPLOY_URL: ${{ steps.deploy.outputs.url }}
|
||||||
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}
|
COMMIT_SHA: ${{ github.event.workflow_run.head_sha }}
|
||||||
RUN_ID: ${{ github.run_id }}
|
RUN_ID: ${{ github.run_id }}
|
||||||
IS_DONE: "true"
|
IS_DONE: "true"
|
||||||
|
13
.github/workflows/issue-manager.yml
vendored
13
.github/workflows/issue-manager.yml
vendored
@ -2,7 +2,7 @@ name: Issue Manager
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "13 18 * * *"
|
- cron: "11 4 * * *"
|
||||||
issue_comment:
|
issue_comment:
|
||||||
types:
|
types:
|
||||||
- created
|
- created
|
||||||
@ -16,7 +16,6 @@ on:
|
|||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
issue-manager:
|
issue-manager:
|
||||||
@ -27,7 +26,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||||
run: echo "$GITHUB_CONTEXT"
|
run: echo "$GITHUB_CONTEXT"
|
||||||
- uses: tiangolo/issue-manager@0.5.1
|
- uses: tiangolo/issue-manager@0.5.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
config: >
|
config: >
|
||||||
@ -36,12 +35,8 @@ jobs:
|
|||||||
"delay": 864000,
|
"delay": 864000,
|
||||||
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs."
|
"message": "Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs."
|
||||||
},
|
},
|
||||||
"waiting": {
|
"changes-requested": {
|
||||||
"delay": 2628000,
|
"delay": 2628000,
|
||||||
"message": "As this PR has been waiting for the original user for a while but seems to be inactive, it's now going to be closed. But if there's anyone interested, feel free to create a new PR."
|
"message": "As this PR had requested changes to be applied but has been inactive for a while, it's now going to be closed. But if there's anyone interested, feel free to create a new PR."
|
||||||
},
|
|
||||||
"invalid": {
|
|
||||||
"delay": 0,
|
|
||||||
"message": "This was marked as invalid and will be closed now. If this is an error, please provide additional details."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
.github/workflows/labeler.yml
vendored
2
.github/workflows/labeler.yml
vendored
@ -17,8 +17,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v5
|
- uses: actions/labeler@v5
|
||||||
if: ${{ github.event.action != 'labeled' && github.event.action != 'unlabeled' }}
|
|
||||||
- run: echo "Done adding labels"
|
|
||||||
# Run this after labeler applied labels
|
# Run this after labeler applied labels
|
||||||
check-labels:
|
check-labels:
|
||||||
needs:
|
needs:
|
||||||
|
18
.github/workflows/smokeshow.yml
vendored
18
.github/workflows/smokeshow.yml
vendored
@ -8,33 +8,25 @@ on:
|
|||||||
permissions:
|
permissions:
|
||||||
statuses: write
|
statuses: write
|
||||||
|
|
||||||
env:
|
|
||||||
UV_SYSTEM_PYTHON: 1
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
smokeshow:
|
smokeshow:
|
||||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
- name: Setup uv
|
|
||||||
uses: astral-sh/setup-uv@v3
|
- run: pip install smokeshow
|
||||||
with:
|
|
||||||
version: "0.4.15"
|
|
||||||
enable-cache: true
|
|
||||||
cache-dependency-glob: |
|
|
||||||
requirements**.txt
|
|
||||||
pyproject.toml
|
|
||||||
- run: uv pip install -r requirements-github-actions.txt
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage-html
|
name: coverage-html
|
||||||
path: htmlcov
|
path: htmlcov
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
run-id: ${{ github.event.workflow_run.id }}
|
||||||
|
|
||||||
- run: smokeshow upload htmlcov
|
- run: smokeshow upload htmlcov
|
||||||
env:
|
env:
|
||||||
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
|
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
|
||||||
|
46
.github/workflows/test.yml
vendored
46
.github/workflows/test.yml
vendored
@ -18,9 +18,6 @@ on:
|
|||||||
# cron every week on monday
|
# cron every week on monday
|
||||||
- cron: "0 0 * * 1"
|
- cron: "0 0 * * 1"
|
||||||
|
|
||||||
env:
|
|
||||||
UV_SYSTEM_PYTHON: 1
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -37,34 +34,33 @@ jobs:
|
|||||||
- pydantic-v1
|
- pydantic-v1
|
||||||
- pydantic-v2
|
- pydantic-v2
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Setup uv
|
|
||||||
uses: astral-sh/setup-uv@v3
|
|
||||||
with:
|
|
||||||
version: "0.4.15"
|
|
||||||
enable-cache: true
|
|
||||||
cache-dependency-glob: |
|
|
||||||
requirements**.txt
|
|
||||||
pyproject.toml
|
|
||||||
# Allow debugging with tmate
|
# Allow debugging with tmate
|
||||||
- name: Setup tmate session
|
- name: Setup tmate session
|
||||||
uses: mxschmitt/action-tmate@v3
|
uses: mxschmitt/action-tmate@v3
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
|
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
|
||||||
with:
|
with:
|
||||||
limit-access-to-actor: true
|
limit-access-to-actor: true
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
id: cache
|
||||||
|
with:
|
||||||
|
path: ${{ env.pythonLocation }}
|
||||||
|
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-v01
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: uv pip install -r requirements-tests.txt
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
|
run: pip install -r requirements-tests.txt
|
||||||
- name: Install Pydantic v1
|
- name: Install Pydantic v1
|
||||||
if: matrix.pydantic-version == 'pydantic-v1'
|
if: matrix.pydantic-version == 'pydantic-v1'
|
||||||
run: uv pip install --upgrade "pydantic>=1.10.0,<2.0.0"
|
run: pip install --upgrade "pydantic>=1.10.0,<2.0.0"
|
||||||
- name: Install Pydantic v2
|
- name: Install Pydantic v2
|
||||||
if: matrix.pydantic-version == 'pydantic-v2'
|
if: matrix.pydantic-version == 'pydantic-v2'
|
||||||
run: uv pip install --upgrade "pydantic>=2.0.2,<3.0.0" "typing-extensions==4.6.1"
|
run: pip install --upgrade "pydantic>=2.0.2,<3.0.0" "typing-extensions==4.6.1"
|
||||||
- name: Lint
|
- name: Lint
|
||||||
# Do not run on Python 3.7 as mypy behaves differently
|
# Do not run on Python 3.7 as mypy behaves differently
|
||||||
if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2'
|
if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2'
|
||||||
@ -73,50 +69,44 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
run: bash scripts/test.sh
|
run: bash scripts/test.sh
|
||||||
env:
|
env:
|
||||||
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-${{ matrix.pydantic-version }}
|
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
|
||||||
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
|
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
|
||||||
- name: Store coverage files
|
- name: Store coverage files
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage-${{ matrix.python-version }}-${{ matrix.pydantic-version }}
|
name: coverage-${{ matrix.python-version }}-${{ matrix.pydantic-version }}
|
||||||
path: coverage
|
path: coverage
|
||||||
include-hidden-files: true
|
|
||||||
|
|
||||||
coverage-combine:
|
coverage-combine:
|
||||||
needs:
|
needs:
|
||||||
- test
|
- test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.12'
|
python-version: '3.12'
|
||||||
- name: Setup uv
|
|
||||||
uses: astral-sh/setup-uv@v3
|
|
||||||
with:
|
|
||||||
version: "0.4.15"
|
|
||||||
enable-cache: true
|
|
||||||
cache-dependency-glob: |
|
|
||||||
requirements**.txt
|
|
||||||
pyproject.toml
|
|
||||||
- name: Get coverage files
|
- name: Get coverage files
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
pattern: coverage-*
|
pattern: coverage-*
|
||||||
path: coverage
|
path: coverage
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
- name: Install Dependencies
|
|
||||||
run: uv pip install -r requirements-tests.txt
|
- run: pip install coverage[toml]
|
||||||
|
|
||||||
- run: ls -la coverage
|
- run: ls -la coverage
|
||||||
- run: coverage combine coverage
|
- run: coverage combine coverage
|
||||||
- run: coverage report
|
- run: coverage report
|
||||||
- run: coverage html --title "Coverage for ${{ github.sha }}"
|
- run: coverage html --title "Coverage for ${{ github.sha }}"
|
||||||
|
|
||||||
- name: Store coverage HTML
|
- name: Store coverage HTML
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: coverage-html
|
name: coverage-html
|
||||||
path: htmlcov
|
path: htmlcov
|
||||||
include-hidden-files: true
|
|
||||||
|
|
||||||
# https://github.com/marketplace/actions/alls-green#why
|
# https://github.com/marketplace/actions/alls-green#why
|
||||||
alls-green: # This job does nothing and is only used for the branch protection
|
alls-green: # This job does nothing and is only used for the branch protection
|
||||||
|
@ -14,7 +14,7 @@ repos:
|
|||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.6.5
|
rev: v0.6.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args:
|
args:
|
||||||
|
@ -80,7 +80,45 @@ We don't call `uuid.uuid4()` ourselves in the code (we don't put the parenthesis
|
|||||||
|
|
||||||
This means that the UUID will be generated in the Python code, **before sending the data to the database**.
|
This means that the UUID will be generated in the Python code, **before sending the data to the database**.
|
||||||
|
|
||||||
{* ./docs_src/advanced/uuid/tutorial001_py310.py ln[1:10] hl[1,7] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="1 7"
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:1-10]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="1 8"
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001.py[ln:1-11]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
Pydantic has support for <a href="https://docs.pydantic.dev/latest/api/standard_library_types/#uuid" class="external-link" target="_blank">`UUID` types</a>.
|
Pydantic has support for <a href="https://docs.pydantic.dev/latest/api/standard_library_types/#uuid" class="external-link" target="_blank">`UUID` types</a>.
|
||||||
|
|
||||||
@ -94,7 +132,49 @@ As `uuid.uuid4` will be called when creating the model instance, even before sen
|
|||||||
|
|
||||||
And that **same ID (a UUID)** will be saved in the database.
|
And that **same ID (a UUID)** will be saved in the database.
|
||||||
|
|
||||||
{* ./docs_src/advanced/uuid/tutorial001_py310.py ln[23:34] hl[25,27,29,34] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="5 7 9 14"
|
||||||
|
# Code above omitted 👆
|
||||||
|
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:23-34]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="5 7 9 14"
|
||||||
|
# Code above omitted 👆
|
||||||
|
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001.py[ln:24-35]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
### Select a Hero
|
### Select a Hero
|
||||||
|
|
||||||
@ -102,7 +182,49 @@ We can do the same operations we could do with other fields.
|
|||||||
|
|
||||||
For example we can **select a hero by ID**:
|
For example we can **select a hero by ID**:
|
||||||
|
|
||||||
{* ./docs_src/advanced/uuid/tutorial001_py310.py ln[37:54] hl[49] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="15"
|
||||||
|
# Code above omitted 👆
|
||||||
|
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:37-54]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="15"
|
||||||
|
# Code above omitted 👆
|
||||||
|
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001.py[ln:38-55]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
/// tip
|
/// tip
|
||||||
|
|
||||||
@ -116,7 +238,49 @@ SQLModel (actually SQLAlchemy) will take care of making it work. ✨
|
|||||||
|
|
||||||
We could also select by ID with `session.get()`:
|
We could also select by ID with `session.get()`:
|
||||||
|
|
||||||
{* ./docs_src/advanced/uuid/tutorial002_py310.py ln[37:53] hl[49] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="15"
|
||||||
|
# Code above omitted 👆
|
||||||
|
|
||||||
|
{!./docs_src/advanced/uuid/tutorial002_py310.py[ln:37-54]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="15"
|
||||||
|
# Code above omitted 👆
|
||||||
|
|
||||||
|
{!./docs_src/advanced/uuid/tutorial002.py[ln:38-55]!}
|
||||||
|
|
||||||
|
# Code below omitted 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial002_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/advanced/uuid/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
The same way as with other fields, we could update, delete, etc. 🚀
|
The same way as with other fields, we could update, delete, etc. 🚀
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ There are many databases of many types.
|
|||||||
|
|
||||||
A database could be a single file called `heroes.db`, managed with code in a very efficient way. An example would be SQLite, more about that on a bit.
|
A database could be a single file called `heroes.db`, managed with code in a very efficient way. An example would be SQLite, more about that on a bit.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### A server database
|
### A server database
|
||||||
|
|
||||||
@ -80,11 +80,11 @@ In this case, your code would talk to this server application instead of reading
|
|||||||
|
|
||||||
The database could be located in a different server/machine:
|
The database could be located in a different server/machine:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Or the database could be located in the same server/machine:
|
Or the database could be located in the same server/machine:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The most important aspect of these types of databases is that **your code doesn't read or modify** the files containing the data directly.
|
The most important aspect of these types of databases is that **your code doesn't read or modify** the files containing the data directly.
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ In some cases, the database could even be a group of server applications running
|
|||||||
|
|
||||||
In this case, your code would talk to one or more of these server applications running on different machines.
|
In this case, your code would talk to one or more of these server applications running on different machines.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Most of the databases that work as server applications also support multiple servers in one way or another.
|
Most of the databases that work as server applications also support multiple servers in one way or another.
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ For example, the table for the teams has the ID `1` for the team `Preventers` an
|
|||||||
|
|
||||||
As these **primary key** IDs can uniquely identify each row on the table for teams, we can now go to the table for heroes and refer to those IDs in the table for teams.
|
As these **primary key** IDs can uniquely identify each row on the table for teams, we can now go to the table for heroes and refer to those IDs in the table for teams.
|
||||||
|
|
||||||

|
<img alt="table relationships" src="/img/databases/relationships.svg">
|
||||||
|
|
||||||
So, in the table for heroes, we use the `team_id` column to define a relationship to the *foreign* table for teams. Each value in the `team_id` column on the table with heroes will be the same value as the `id` column of one row in the table with teams.
|
So, in the table for heroes, we use the `team_id` column to define a relationship to the *foreign* table for teams. Each value in the `team_id` column on the table with heroes will be the same value as the `id` column of one row in the table with teams.
|
||||||
|
|
||||||
|
@ -236,7 +236,8 @@ database.execute(
|
|||||||
).all()
|
).all()
|
||||||
```
|
```
|
||||||
|
|
||||||
{class="shadow"}
|
<img class="shadow" src="/img/db-to-code/autocompletion01.png">
|
||||||
|
|
||||||
|
|
||||||
## ORMs and SQL
|
## ORMs and SQL
|
||||||
|
|
||||||
@ -279,7 +280,7 @@ For example this **Relation** or table:
|
|||||||
|
|
||||||
* **Mapper**: this comes from Math, when there's something that can convert from some set of things to another, that's called a "**mapping function**". That's where the **Mapper** comes from.
|
* **Mapper**: this comes from Math, when there's something that can convert from some set of things to another, that's called a "**mapping function**". That's where the **Mapper** comes from.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
We could also write a **mapping function** in Python that converts from the *set of lowercase letters* to the *set of uppercase letters*, like this:
|
We could also write a **mapping function** in Python that converts from the *set of lowercase letters* to the *set of uppercase letters*, like this:
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@ As **SQLModel** is built on top of <a href="https://www.sqlalchemy.org/" class="
|
|||||||
|
|
||||||
## Install DB Browser for SQLite
|
## Install DB Browser for SQLite
|
||||||
|
|
||||||
Remember that [SQLite is a simple database in a single file](databases.md#a-single-file-database){.internal-link target=_blank}?
|
Remember that [SQLite is a simple database in a single file](../databases.md#a-single-file-database){.internal-link target=_blank}?
|
||||||
|
|
||||||
For most of the tutorial I'll use SQLite for the examples.
|
For most of the tutorial I'll use SQLite for the examples.
|
||||||
|
|
||||||
Python has integrated support for SQLite, it is a single file read and processed from Python. And it doesn't need an [External Database Server](databases.md#a-server-database){.internal-link target=_blank}, so it will be perfect for learning.
|
Python has integrated support for SQLite, it is a single file read and processed from Python. And it doesn't need an [External Database Server](../databases.md#a-server-database){.internal-link target=_blank}, so it will be perfect for learning.
|
||||||
|
|
||||||
In fact, SQLite is perfectly capable of handling quite big applications. At some point you might want to migrate to a server-based database like <a href="https://www.postgresql.org/" class="external-link" target="_blank">PostgreSQL</a> (which is also free). But for now we'll stick to SQLite.
|
In fact, SQLite is perfectly capable of handling quite big applications. At some point you might want to migrate to a server-based database like <a href="https://www.postgresql.org/" class="external-link" target="_blank">PostgreSQL</a> (which is also free). But for now we'll stick to SQLite.
|
||||||
|
|
||||||
|
@ -2,43 +2,6 @@
|
|||||||
|
|
||||||
## Latest Changes
|
## Latest Changes
|
||||||
|
|
||||||
### Refactors
|
|
||||||
|
|
||||||
* 🚨 Fix types for new Pydantic. PR [#1131](https://github.com/fastapi/sqlmodel/pull/1131) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
|
|
||||||
### Docs
|
|
||||||
|
|
||||||
* ✏️ Fix typo in the release notes of v0.0.22. PR [#1195](https://github.com/fastapi/sqlmodel/pull/1195) by [@PipeKnight](https://github.com/PipeKnight).
|
|
||||||
* 📝 Update includes for `docs/advanced/uuid.md`. PR [#1151](https://github.com/fastapi/sqlmodel/pull/1151) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 📝 Update includes for `docs/tutorial/create-db-and-table.md`. PR [#1149](https://github.com/fastapi/sqlmodel/pull/1149) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 📝 Fix internal links in docs. PR [#1148](https://github.com/fastapi/sqlmodel/pull/1148) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* ✏️ Fix typo in documentation. PR [#1106](https://github.com/fastapi/sqlmodel/pull/1106) by [@Solipsistmonkey](https://github.com/Solipsistmonkey).
|
|
||||||
* 📝 Remove highlights in `indexes.md` . PR [#1100](https://github.com/fastapi/sqlmodel/pull/1100) by [@alejsdev](https://github.com/alejsdev).
|
|
||||||
|
|
||||||
### Internal
|
|
||||||
|
|
||||||
* ⬆️ Upgrade markdown-include-variants to version 0.0.3. PR [#1152](https://github.com/fastapi/sqlmodel/pull/1152) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Update issue manager workflow. PR [#1137](https://github.com/fastapi/sqlmodel/pull/1137) by [@alejsdev](https://github.com/alejsdev).
|
|
||||||
* 👷 Fix smokeshow, checkout files on CI. PR [#1136](https://github.com/fastapi/sqlmodel/pull/1136) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Use uv in CI. PR [#1135](https://github.com/fastapi/sqlmodel/pull/1135) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* ➕ Add docs dependency markdown-include-variants. PR [#1129](https://github.com/fastapi/sqlmodel/pull/1129) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 🔨 Update script to standardize format. PR [#1130](https://github.com/fastapi/sqlmodel/pull/1130) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Update `labeler.yml`. PR [#1128](https://github.com/fastapi/sqlmodel/pull/1128) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Update worfkow deploy-docs-notify URL. PR [#1126](https://github.com/fastapi/sqlmodel/pull/1126) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Upgrade Cloudflare GitHub Action. PR [#1124](https://github.com/fastapi/sqlmodel/pull/1124) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1097](https://github.com/fastapi/sqlmodel/pull/1097) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
|
||||||
* ⬆ Bump tiangolo/issue-manager from 0.5.0 to 0.5.1. PR [#1107](https://github.com/fastapi/sqlmodel/pull/1107) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
|
||||||
* 👷 Update `issue-manager.yml`. PR [#1103](https://github.com/fastapi/sqlmodel/pull/1103) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Fix coverage processing in CI, one name per matrix run. PR [#1104](https://github.com/fastapi/sqlmodel/pull/1104) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 💚 Set `include-hidden-files` to `True` when using the `upload-artifact` GH action. PR [#1098](https://github.com/fastapi/sqlmodel/pull/1098) by [@svlandeg](https://github.com/svlandeg).
|
|
||||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1088](https://github.com/fastapi/sqlmodel/pull/1088) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
|
||||||
|
|
||||||
## 0.0.22
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
|
|
||||||
* 🐛 Fix support for types with `Optional[Annotated[x, f()]]`, e.g. `id: Optional[pydantic.UUID4]`. PR [#1093](https://github.com/fastapi/sqlmodel/pull/1093) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
|
|
||||||
* ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#1085](https://github.com/fastapi/sqlmodel/pull/1085) by [@tiangolo](https://github.com/tiangolo).
|
* ✏️ Fix a typo in `docs/virtual-environments.md`. PR [#1085](https://github.com/fastapi/sqlmodel/pull/1085) by [@tiangolo](https://github.com/tiangolo).
|
||||||
@ -48,7 +11,6 @@
|
|||||||
|
|
||||||
### Internal
|
### Internal
|
||||||
|
|
||||||
* ✅ Refactor test_enums to make them independent of previous imports. PR [#1095](https://github.com/fastapi/sqlmodel/pull/1095) by [@tiangolo](https://github.com/tiangolo).
|
|
||||||
* 👷 Update `latest-changes` GitHub Action. PR [#1087](https://github.com/fastapi/sqlmodel/pull/1087) by [@tiangolo](https://github.com/tiangolo).
|
* 👷 Update `latest-changes` GitHub Action. PR [#1087](https://github.com/fastapi/sqlmodel/pull/1087) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1028](https://github.com/fastapi/sqlmodel/pull/1028) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#1028](https://github.com/fastapi/sqlmodel/pull/1028) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||||
* ⬆ Bump ruff from 0.4.7 to 0.6.2. PR [#1081](https://github.com/fastapi/sqlmodel/pull/1081) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
* ⬆ Bump ruff from 0.4.7 to 0.6.2. PR [#1081](https://github.com/fastapi/sqlmodel/pull/1081) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
@ -69,7 +31,7 @@
|
|||||||
* 💄 Add dark-mode logo. PR [#1061](https://github.com/tiangolo/sqlmodel/pull/1061) by [@tiangolo](https://github.com/tiangolo).
|
* 💄 Add dark-mode logo. PR [#1061](https://github.com/tiangolo/sqlmodel/pull/1061) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 🔨 Update docs.py script to enable dirty reload conditionally. PR [#1060](https://github.com/tiangolo/sqlmodel/pull/1060) by [@tiangolo](https://github.com/tiangolo).
|
* 🔨 Update docs.py script to enable dirty reload conditionally. PR [#1060](https://github.com/tiangolo/sqlmodel/pull/1060) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 🔧 Update MkDocs previews. PR [#1058](https://github.com/tiangolo/sqlmodel/pull/1058) by [@tiangolo](https://github.com/tiangolo).
|
* 🔧 Update MkDocs previews. PR [#1058](https://github.com/tiangolo/sqlmodel/pull/1058) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 💄 Update Termynal line-height. PR [#1057](https://github.com/tiangolo/sqlmodel/pull/1057) by [@tiangolo](https://github.com/tiangolo).
|
* 💄 Update Termynal line-height. PR [#1057](https://github.com/tiangolo/sqlmodel/pull/1057) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 👷 Upgrade build docs configs. PR [#1047](https://github.com/tiangolo/sqlmodel/pull/1047) by [@tiangolo](https://github.com/tiangolo).
|
* 👷 Upgrade build docs configs. PR [#1047](https://github.com/tiangolo/sqlmodel/pull/1047) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 👷 Add alls-green for test-redistribute. PR [#1055](https://github.com/tiangolo/sqlmodel/pull/1055) by [@tiangolo](https://github.com/tiangolo).
|
* 👷 Add alls-green for test-redistribute. PR [#1055](https://github.com/tiangolo/sqlmodel/pull/1055) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 👷 Update docs-previews to handle no docs changes. PR [#1056](https://github.com/tiangolo/sqlmodel/pull/1056) by [@tiangolo](https://github.com/tiangolo).
|
* 👷 Update docs-previews to handle no docs changes. PR [#1056](https://github.com/tiangolo/sqlmodel/pull/1056) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
@ -41,7 +41,45 @@ That's why this package is called `SQLModel`. Because it's mainly used to create
|
|||||||
|
|
||||||
For that, we will import `SQLModel` (plus other things we will also use) and create a class `Hero` that inherits from `SQLModel` and represents the **table model** for our heroes:
|
For that, we will import `SQLModel` (plus other things we will also use) and create a class `Hero` that inherits from `SQLModel` and represents the **table model** for our heroes:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:8] hl[1,4] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="1 4"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="3 6"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
This class `Hero` **represents the table** for our heroes. And each instance we create later will **represent a row** in the table.
|
This class `Hero` **represents the table** for our heroes. And each instance we create later will **represent a row** in the table.
|
||||||
|
|
||||||
@ -63,7 +101,45 @@ The name of each of these variables will be the name of the column in the table.
|
|||||||
|
|
||||||
And the type of each of them will also be the type of table column:
|
And the type of each of them will also be the type of table column:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:8] hl[1,5:8] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="1 5-8"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="1 3 7-10"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
Let's now see with more detail these field/column declarations.
|
Let's now see with more detail these field/column declarations.
|
||||||
|
|
||||||
@ -77,7 +153,45 @@ That is the standard way to declare that something "could be an `int` or `None`"
|
|||||||
|
|
||||||
And we also set the default value of `age` to `None`.
|
And we also set the default value of `age` to `None`.
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:8] hl[8] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="8"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="1 10"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
/// tip
|
/// tip
|
||||||
|
|
||||||
@ -107,7 +221,45 @@ So, we need to mark `id` as the **primary key**.
|
|||||||
|
|
||||||
To do that, we use the special `Field` function from `sqlmodel` and set the argument `primary_key=True`:
|
To do that, we use the special `Field` function from `sqlmodel` and set the argument `primary_key=True`:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:8] hl[1,5] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="1 5"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="3 7"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
That way, we tell **SQLModel** that this `id` field/column is the primary key of the table.
|
That way, we tell **SQLModel** that this `id` field/column is the primary key of the table.
|
||||||
|
|
||||||
@ -150,7 +302,45 @@ If you have a server database (for example PostgreSQL or MySQL), the **engine**
|
|||||||
|
|
||||||
Creating the **engine** is very simple, just call `create_engine()` with a URL for the database to use:
|
Creating the **engine** is very simple, just call `create_engine()` with a URL for the database to use:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:16] hl[1,14] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="1 14"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-16]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="3 16"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-18]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
You should normally have a single **engine** object for your whole application and re-use it everywhere.
|
You should normally have a single **engine** object for your whole application and re-use it everywhere.
|
||||||
|
|
||||||
@ -174,7 +364,45 @@ SQLite supports a special database that lives all *in memory*. Hence, it's very
|
|||||||
|
|
||||||
* `sqlite://`
|
* `sqlite://`
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:16] hl[11:12,14] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="11-12 14"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-16]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="13-14 16"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-18]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
You can read a lot more about all the databases supported by **SQLAlchemy** (and that way supported by **SQLModel**) in the <a href="https://docs.sqlalchemy.org/en/14/core/engines.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
|
You can read a lot more about all the databases supported by **SQLAlchemy** (and that way supported by **SQLModel**) in the <a href="https://docs.sqlalchemy.org/en/14/core/engines.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
|
||||||
|
|
||||||
@ -186,7 +414,45 @@ It will make the engine print all the SQL statements it executes, which can help
|
|||||||
|
|
||||||
It is particularly useful for **learning** and **debugging**:
|
It is particularly useful for **learning** and **debugging**:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:16] hl[14] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="14"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-16]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="16"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-18]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
But in production, you would probably want to remove `echo=True`:
|
But in production, you would probably want to remove `echo=True`:
|
||||||
|
|
||||||
@ -212,7 +478,21 @@ And SQLModel's version of `create_engine()` is type annotated internally, so you
|
|||||||
|
|
||||||
Now everything is in place to finally create the database and table:
|
Now everything is in place to finally create the database and table:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py hl[16] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="16"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="18"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
/// tip
|
/// tip
|
||||||
|
|
||||||
@ -323,7 +603,25 @@ Let's run the program to see it all working.
|
|||||||
|
|
||||||
Put the code it in a file `app.py` if you haven't already.
|
Put the code it in a file `app.py` if you haven't already.
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py *}
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
/// tip
|
/// tip
|
||||||
|
|
||||||
@ -428,7 +726,45 @@ In this example it's just the `SQLModel.metadata.create_all(engine)`.
|
|||||||
|
|
||||||
Let's put it in a function `create_db_and_tables()`:
|
Let's put it in a function `create_db_and_tables()`:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial002_py310.py ln[1:18] hl[17:18] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="17-18"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial002_py310.py[ln:1-18]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="19-20"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial002.py[ln:1-20]!}
|
||||||
|
|
||||||
|
# More code here later 👇
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
/// details | 👀 Full file preview
|
||||||
|
|
||||||
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial002_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
If `SQLModel.metadata.create_all(engine)` was not in a function and we tried to import something from this module (from this file) in another, it would try to create the database and table **every time** we executed that other file that imported this module.
|
If `SQLModel.metadata.create_all(engine)` was not in a function and we tried to import something from this module (from this file) in another, it would try to create the database and table **every time** we executed that other file that imported this module.
|
||||||
|
|
||||||
@ -458,7 +794,21 @@ The word **script** often implies that the code could be run independently and e
|
|||||||
|
|
||||||
For that we can use the special variable `__name__` in an `if` block:
|
For that we can use the special variable `__name__` in an `if` block:
|
||||||
|
|
||||||
{* ./docs_src/tutorial/create_db_and_table/tutorial002_py310.py hl[21:22] *}
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
|
```Python hl_lines="21-22"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial002_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
|
```Python hl_lines="23-24"
|
||||||
|
{!./docs_src/tutorial/create_db_and_table/tutorial002.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
### About `__name__ == "__main__"`
|
### About `__name__ == "__main__"`
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ Fine, in that case, you can **sneak peek** the final code to create indexes here
|
|||||||
|
|
||||||
//// tab | Python 3.10+
|
//// tab | Python 3.10+
|
||||||
|
|
||||||
```Python
|
```Python hl_lines="8 10"
|
||||||
{!./docs_src/tutorial/indexes/tutorial002_py310.py!}
|
{!./docs_src/tutorial/indexes/tutorial002_py310.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ Fine, in that case, you can **sneak peek** the final code to create indexes here
|
|||||||
|
|
||||||
//// tab | Python 3.7+
|
//// tab | Python 3.7+
|
||||||
|
|
||||||
```Python
|
```Python hl_lines="8 10"
|
||||||
{!./docs_src/tutorial/indexes/tutorial002.py!}
|
{!./docs_src/tutorial/indexes/tutorial002.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -710,4 +710,4 @@ Hero: None
|
|||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
As querying the SQL database for a single row is a common operation, you now have several tools to do it in a short and simple way. 🎉
|
As querying the SQL database for a single row is a common operation, you know have several tools to do it in a short and simple way. 🎉
|
||||||
|
@ -184,7 +184,6 @@ markdown_extensions:
|
|||||||
|
|
||||||
# Other extensions
|
# Other extensions
|
||||||
mdx_include:
|
mdx_include:
|
||||||
markdown_include_variants:
|
|
||||||
|
|
||||||
extra:
|
extra:
|
||||||
analytics:
|
analytics:
|
||||||
|
@ -16,4 +16,3 @@ cairosvg==2.7.1
|
|||||||
# For griffe, it formats with black
|
# For griffe, it formats with black
|
||||||
typer == 0.12.3
|
typer == 0.12.3
|
||||||
mkdocs-macros-plugin==1.0.5
|
mkdocs-macros-plugin==1.0.5
|
||||||
markdown-include-variants==0.0.3
|
|
||||||
|
@ -2,4 +2,3 @@ PyGithub>=2.3.0,<3.0.0
|
|||||||
pydantic>=2.5.3,<3.0.0
|
pydantic>=2.5.3,<3.0.0
|
||||||
pydantic-settings>=2.1.0,<3.0.0
|
pydantic-settings>=2.1.0,<3.0.0
|
||||||
httpx>=0.27.0,<0.28.0
|
httpx>=0.27.0,<0.28.0
|
||||||
smokeshow
|
|
||||||
|
@ -6,7 +6,7 @@ mypy ==1.4.1
|
|||||||
ruff ==0.6.2
|
ruff ==0.6.2
|
||||||
# For FastAPI tests
|
# For FastAPI tests
|
||||||
fastapi >=0.103.2
|
fastapi >=0.103.2
|
||||||
httpx ==0.24.1
|
httpx ==0.27.2
|
||||||
# TODO: upgrade when deprecating Python 3.7
|
# TODO: upgrade when deprecating Python 3.7
|
||||||
dirty-equals ==0.6.0
|
dirty-equals ==0.6.0
|
||||||
jinja2 ==3.1.4
|
jinja2 ==3.1.4
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/sh -e
|
||||||
|
|
||||||
set -e
|
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
ruff check sqlmodel tests docs_src scripts --fix
|
ruff check sqlmodel tests docs_src scripts --fix
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
__version__ = "0.0.22"
|
__version__ = "0.0.21"
|
||||||
|
|
||||||
# Re-export from SQLAlchemy
|
# Re-export from SQLAlchemy
|
||||||
from sqlalchemy.engine import create_engine as create_engine
|
from sqlalchemy.engine import create_engine as create_engine
|
||||||
|
@ -21,7 +21,7 @@ from typing import (
|
|||||||
from pydantic import VERSION as P_VERSION
|
from pydantic import VERSION as P_VERSION
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from pydantic.fields import FieldInfo
|
from pydantic.fields import FieldInfo
|
||||||
from typing_extensions import Annotated, get_args, get_origin
|
from typing_extensions import get_args, get_origin
|
||||||
|
|
||||||
# Reassign variable to make it reexported for mypy
|
# Reassign variable to make it reexported for mypy
|
||||||
PYDANTIC_VERSION = P_VERSION
|
PYDANTIC_VERSION = P_VERSION
|
||||||
@ -177,17 +177,16 @@ if IS_PYDANTIC_V2:
|
|||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_sa_type_from_type_annotation(annotation: Any) -> Any:
|
def get_type_from_field(field: Any) -> Any:
|
||||||
|
type_: Any = field.annotation
|
||||||
# Resolve Optional fields
|
# Resolve Optional fields
|
||||||
if annotation is None:
|
if type_ is None:
|
||||||
raise ValueError("Missing field type")
|
raise ValueError("Missing field type")
|
||||||
origin = get_origin(annotation)
|
origin = get_origin(type_)
|
||||||
if origin is None:
|
if origin is None:
|
||||||
return annotation
|
return type_
|
||||||
elif origin is Annotated:
|
|
||||||
return get_sa_type_from_type_annotation(get_args(annotation)[0])
|
|
||||||
if _is_union_type(origin):
|
if _is_union_type(origin):
|
||||||
bases = get_args(annotation)
|
bases = get_args(type_)
|
||||||
if len(bases) > 2:
|
if len(bases) > 2:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
||||||
@ -198,14 +197,9 @@ if IS_PYDANTIC_V2:
|
|||||||
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
"Cannot have a (non-optional) union as a SQLAlchemy field"
|
||||||
)
|
)
|
||||||
# Optional unions are allowed
|
# Optional unions are allowed
|
||||||
use_type = bases[0] if bases[0] is not NoneType else bases[1]
|
return bases[0] if bases[0] is not NoneType else bases[1]
|
||||||
return get_sa_type_from_type_annotation(use_type)
|
|
||||||
return origin
|
return origin
|
||||||
|
|
||||||
def get_sa_type_from_field(field: Any) -> Any:
|
|
||||||
type_: Any = field.annotation
|
|
||||||
return get_sa_type_from_type_annotation(type_)
|
|
||||||
|
|
||||||
def get_field_metadata(field: Any) -> Any:
|
def get_field_metadata(field: Any) -> Any:
|
||||||
for meta in field.metadata:
|
for meta in field.metadata:
|
||||||
if isinstance(meta, (PydanticMetadata, MaxLen)):
|
if isinstance(meta, (PydanticMetadata, MaxLen)):
|
||||||
@ -450,7 +444,7 @@ else:
|
|||||||
)
|
)
|
||||||
return field.allow_none # type: ignore[no-any-return, attr-defined]
|
return field.allow_none # type: ignore[no-any-return, attr-defined]
|
||||||
|
|
||||||
def get_sa_type_from_field(field: Any) -> Any:
|
def get_type_from_field(field: Any) -> Any:
|
||||||
if isinstance(field.type_, type) and field.shape == SHAPE_SINGLETON:
|
if isinstance(field.type_, type) and field.shape == SHAPE_SINGLETON:
|
||||||
return field.type_
|
return field.type_
|
||||||
raise ValueError(f"The field {field.name} has no matching SQLAlchemy type")
|
raise ValueError(f"The field {field.name} has no matching SQLAlchemy type")
|
||||||
|
@ -52,7 +52,7 @@ from sqlalchemy.orm.decl_api import DeclarativeMeta
|
|||||||
from sqlalchemy.orm.instrumentation import is_instrumented
|
from sqlalchemy.orm.instrumentation import is_instrumented
|
||||||
from sqlalchemy.sql.schema import MetaData
|
from sqlalchemy.sql.schema import MetaData
|
||||||
from sqlalchemy.sql.sqltypes import LargeBinary, Time, Uuid
|
from sqlalchemy.sql.sqltypes import LargeBinary, Time, Uuid
|
||||||
from typing_extensions import Literal, TypeAlias, deprecated, get_origin
|
from typing_extensions import Literal, deprecated, get_origin
|
||||||
|
|
||||||
from ._compat import ( # type: ignore[attr-defined]
|
from ._compat import ( # type: ignore[attr-defined]
|
||||||
IS_PYDANTIC_V2,
|
IS_PYDANTIC_V2,
|
||||||
@ -71,7 +71,7 @@ from ._compat import ( # type: ignore[attr-defined]
|
|||||||
get_field_metadata,
|
get_field_metadata,
|
||||||
get_model_fields,
|
get_model_fields,
|
||||||
get_relationship_to,
|
get_relationship_to,
|
||||||
get_sa_type_from_field,
|
get_type_from_field,
|
||||||
init_pydantic_private_attrs,
|
init_pydantic_private_attrs,
|
||||||
is_field_noneable,
|
is_field_noneable,
|
||||||
is_table_model_class,
|
is_table_model_class,
|
||||||
@ -90,12 +90,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
NoArgAnyCallable = Callable[[], Any]
|
NoArgAnyCallable = Callable[[], Any]
|
||||||
IncEx: TypeAlias = Union[
|
IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any], None]
|
||||||
Set[int],
|
|
||||||
Set[str],
|
|
||||||
Mapping[int, Union["IncEx", Literal[True]]],
|
|
||||||
Mapping[str, Union["IncEx", Literal[True]]],
|
|
||||||
]
|
|
||||||
OnDeleteType = Literal["CASCADE", "SET NULL", "RESTRICT"]
|
OnDeleteType = Literal["CASCADE", "SET NULL", "RESTRICT"]
|
||||||
|
|
||||||
|
|
||||||
@ -654,7 +649,7 @@ def get_sqlalchemy_type(field: Any) -> Any:
|
|||||||
if sa_type is not Undefined:
|
if sa_type is not Undefined:
|
||||||
return sa_type
|
return sa_type
|
||||||
|
|
||||||
type_ = get_sa_type_from_field(field)
|
type_ = get_type_from_field(field)
|
||||||
metadata = get_field_metadata(field)
|
metadata = get_field_metadata(field)
|
||||||
|
|
||||||
# Check enums first as an enum can also be a str, needed by Pydantic/FastAPI
|
# Check enums first as an enum can also be a str, needed by Pydantic/FastAPI
|
||||||
@ -863,8 +858,8 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
mode: Union[Literal["json", "python"], str] = "python",
|
mode: Union[Literal["json", "python"], str] = "python",
|
||||||
include: Union[IncEx, None] = None,
|
include: IncEx = None,
|
||||||
exclude: Union[IncEx, None] = None,
|
exclude: IncEx = None,
|
||||||
context: Union[Dict[str, Any], None] = None,
|
context: Union[Dict[str, Any], None] = None,
|
||||||
by_alias: bool = False,
|
by_alias: bool = False,
|
||||||
exclude_unset: bool = False,
|
exclude_unset: bool = False,
|
||||||
@ -913,8 +908,8 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry
|
|||||||
def dict(
|
def dict(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
include: Union[IncEx, None] = None,
|
include: IncEx = None,
|
||||||
exclude: Union[IncEx, None] = None,
|
exclude: IncEx = None,
|
||||||
by_alias: bool = False,
|
by_alias: bool = False,
|
||||||
exclude_unset: bool = False,
|
exclude_unset: bool = False,
|
||||||
exclude_defaults: bool = False,
|
exclude_defaults: bool = False,
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
import uuid
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from sqlmodel import Field, Session, SQLModel, create_engine, select
|
|
||||||
|
|
||||||
from tests.conftest import needs_pydanticv2
|
|
||||||
|
|
||||||
|
|
||||||
@needs_pydanticv2
|
|
||||||
def test_annotated_optional_types(clear_sqlmodel) -> None:
|
|
||||||
from pydantic import UUID4
|
|
||||||
|
|
||||||
class Hero(SQLModel, table=True):
|
|
||||||
# Pydantic UUID4 is: Annotated[UUID, UuidVersion(4)]
|
|
||||||
id: Optional[UUID4] = Field(default_factory=uuid.uuid4, primary_key=True)
|
|
||||||
|
|
||||||
engine = create_engine("sqlite:///:memory:")
|
|
||||||
SQLModel.metadata.create_all(engine)
|
|
||||||
with Session(engine) as db:
|
|
||||||
hero = Hero()
|
|
||||||
db.add(hero)
|
|
||||||
db.commit()
|
|
||||||
statement = select(Hero)
|
|
||||||
result = db.exec(statement).all()
|
|
||||||
assert len(result) == 1
|
|
||||||
assert isinstance(hero.id, uuid.UUID)
|
|
@ -1,11 +1,10 @@
|
|||||||
import importlib
|
import enum
|
||||||
|
import uuid
|
||||||
|
|
||||||
import pytest
|
|
||||||
from sqlalchemy import create_mock_engine
|
from sqlalchemy import create_mock_engine
|
||||||
from sqlalchemy.sql.type_api import TypeEngine
|
from sqlalchemy.sql.type_api import TypeEngine
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
from . import test_enums_models
|
|
||||||
from .conftest import needs_pydanticv1, needs_pydanticv2
|
from .conftest import needs_pydanticv1, needs_pydanticv2
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -17,6 +16,30 @@ Associated issues:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MyEnum1(str, enum.Enum):
|
||||||
|
A = "A"
|
||||||
|
B = "B"
|
||||||
|
|
||||||
|
|
||||||
|
class MyEnum2(str, enum.Enum):
|
||||||
|
C = "C"
|
||||||
|
D = "D"
|
||||||
|
|
||||||
|
|
||||||
|
class BaseModel(SQLModel):
|
||||||
|
id: uuid.UUID = Field(primary_key=True)
|
||||||
|
enum_field: MyEnum2
|
||||||
|
|
||||||
|
|
||||||
|
class FlatModel(SQLModel, table=True):
|
||||||
|
id: uuid.UUID = Field(primary_key=True)
|
||||||
|
enum_field: MyEnum1
|
||||||
|
|
||||||
|
|
||||||
|
class InheritModel(BaseModel, table=True):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def pg_dump(sql: TypeEngine, *args, **kwargs):
|
def pg_dump(sql: TypeEngine, *args, **kwargs):
|
||||||
dialect = sql.compile(dialect=postgres_engine.dialect)
|
dialect = sql.compile(dialect=postgres_engine.dialect)
|
||||||
sql_str = str(dialect).rstrip()
|
sql_str = str(dialect).rstrip()
|
||||||
@ -35,9 +58,7 @@ postgres_engine = create_mock_engine("postgresql://", pg_dump)
|
|||||||
sqlite_engine = create_mock_engine("sqlite://", sqlite_dump)
|
sqlite_engine = create_mock_engine("sqlite://", sqlite_dump)
|
||||||
|
|
||||||
|
|
||||||
def test_postgres_ddl_sql(clear_sqlmodel, capsys: pytest.CaptureFixture[str]):
|
def test_postgres_ddl_sql(capsys):
|
||||||
assert test_enums_models, "Ensure the models are imported and registered"
|
|
||||||
importlib.reload(test_enums_models)
|
|
||||||
SQLModel.metadata.create_all(bind=postgres_engine, checkfirst=False)
|
SQLModel.metadata.create_all(bind=postgres_engine, checkfirst=False)
|
||||||
|
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
@ -45,19 +66,17 @@ def test_postgres_ddl_sql(clear_sqlmodel, capsys: pytest.CaptureFixture[str]):
|
|||||||
assert "CREATE TYPE myenum2 AS ENUM ('C', 'D');" in captured.out
|
assert "CREATE TYPE myenum2 AS ENUM ('C', 'D');" in captured.out
|
||||||
|
|
||||||
|
|
||||||
def test_sqlite_ddl_sql(clear_sqlmodel, capsys: pytest.CaptureFixture[str]):
|
def test_sqlite_ddl_sql(capsys):
|
||||||
assert test_enums_models, "Ensure the models are imported and registered"
|
|
||||||
importlib.reload(test_enums_models)
|
|
||||||
SQLModel.metadata.create_all(bind=sqlite_engine, checkfirst=False)
|
SQLModel.metadata.create_all(bind=sqlite_engine, checkfirst=False)
|
||||||
|
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "enum_field VARCHAR(1) NOT NULL" in captured.out, captured
|
assert "enum_field VARCHAR(1) NOT NULL" in captured.out
|
||||||
assert "CREATE TYPE" not in captured.out
|
assert "CREATE TYPE" not in captured.out
|
||||||
|
|
||||||
|
|
||||||
@needs_pydanticv1
|
@needs_pydanticv1
|
||||||
def test_json_schema_flat_model_pydantic_v1():
|
def test_json_schema_flat_model_pydantic_v1():
|
||||||
assert test_enums_models.FlatModel.schema() == {
|
assert FlatModel.schema() == {
|
||||||
"title": "FlatModel",
|
"title": "FlatModel",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -78,7 +97,7 @@ def test_json_schema_flat_model_pydantic_v1():
|
|||||||
|
|
||||||
@needs_pydanticv1
|
@needs_pydanticv1
|
||||||
def test_json_schema_inherit_model_pydantic_v1():
|
def test_json_schema_inherit_model_pydantic_v1():
|
||||||
assert test_enums_models.InheritModel.schema() == {
|
assert InheritModel.schema() == {
|
||||||
"title": "InheritModel",
|
"title": "InheritModel",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -99,7 +118,7 @@ def test_json_schema_inherit_model_pydantic_v1():
|
|||||||
|
|
||||||
@needs_pydanticv2
|
@needs_pydanticv2
|
||||||
def test_json_schema_flat_model_pydantic_v2():
|
def test_json_schema_flat_model_pydantic_v2():
|
||||||
assert test_enums_models.FlatModel.model_json_schema() == {
|
assert FlatModel.model_json_schema() == {
|
||||||
"title": "FlatModel",
|
"title": "FlatModel",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -115,7 +134,7 @@ def test_json_schema_flat_model_pydantic_v2():
|
|||||||
|
|
||||||
@needs_pydanticv2
|
@needs_pydanticv2
|
||||||
def test_json_schema_inherit_model_pydantic_v2():
|
def test_json_schema_inherit_model_pydantic_v2():
|
||||||
assert test_enums_models.InheritModel.model_json_schema() == {
|
assert InheritModel.model_json_schema() == {
|
||||||
"title": "InheritModel",
|
"title": "InheritModel",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
import enum
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from sqlmodel import Field, SQLModel
|
|
||||||
|
|
||||||
|
|
||||||
class MyEnum1(str, enum.Enum):
|
|
||||||
A = "A"
|
|
||||||
B = "B"
|
|
||||||
|
|
||||||
|
|
||||||
class MyEnum2(str, enum.Enum):
|
|
||||||
C = "C"
|
|
||||||
D = "D"
|
|
||||||
|
|
||||||
|
|
||||||
class BaseModel(SQLModel):
|
|
||||||
id: uuid.UUID = Field(primary_key=True)
|
|
||||||
enum_field: MyEnum2
|
|
||||||
|
|
||||||
|
|
||||||
class FlatModel(SQLModel, table=True):
|
|
||||||
id: uuid.UUID = Field(primary_key=True)
|
|
||||||
enum_field: MyEnum1
|
|
||||||
|
|
||||||
|
|
||||||
class InheritModel(BaseModel, table=True):
|
|
||||||
pass
|
|
Loading…
x
Reference in New Issue
Block a user