Merge branch 'main' into main
@ -1,5 +1,3 @@
|
|||||||
name: Question or Problem
|
|
||||||
description: Ask a question or ask about a problem
|
|
||||||
labels: [question]
|
labels: [question]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
@ -8,29 +6,29 @@ body:
|
|||||||
Thanks for your interest in SQLModel! 🚀
|
Thanks for your interest in SQLModel! 🚀
|
||||||
|
|
||||||
Please follow these instructions, fill every question, and do every step. 🙏
|
Please follow these instructions, fill every question, and do every step. 🙏
|
||||||
|
|
||||||
I'm asking this because answering questions and solving problems in GitHub issues is what consumes most of the time.
|
I'm asking this because answering questions and solving problems in GitHub is what consumes most of the time.
|
||||||
|
|
||||||
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling issues.
|
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling questions.
|
||||||
|
|
||||||
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
|
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
|
||||||
|
|
||||||
If more SQLModel users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
If more SQLModel users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
||||||
|
|
||||||
By asking questions in a structured way (following this) it will be much easier to help you.
|
By asking questions in a structured way (following this) it will be much easier to help you.
|
||||||
|
|
||||||
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
|
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
|
||||||
|
|
||||||
As there are too many issues with questions, I'll have to close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
|
As there are too many questions, I'll have to discard and close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
id: checks
|
id: checks
|
||||||
attributes:
|
attributes:
|
||||||
label: First Check
|
label: First Check
|
||||||
description: Please confirm and check all the following options.
|
description: Please confirm and check all the following options.
|
||||||
options:
|
options:
|
||||||
- label: I added a very descriptive title to this issue.
|
- label: I added a very descriptive title here.
|
||||||
required: true
|
required: true
|
||||||
- label: I used the GitHub search to find a similar issue and didn't find it.
|
- label: I used the GitHub search to find a similar question and didn't find it.
|
||||||
required: true
|
required: true
|
||||||
- label: I searched the SQLModel documentation, with the integrated search.
|
- label: I searched the SQLModel documentation, with the integrated search.
|
||||||
required: true
|
required: true
|
||||||
@ -48,10 +46,10 @@ body:
|
|||||||
label: Commit to Help
|
label: Commit to Help
|
||||||
description: |
|
description: |
|
||||||
After submitting this, I commit to one of:
|
After submitting this, I commit to one of:
|
||||||
|
|
||||||
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
||||||
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
||||||
* Implement a Pull Request for a confirmed bug.
|
* Review one Pull Request by downloading the code and following all the review process](https://sqlmodel.tiangolo.com/help/#review-pull-requests).
|
||||||
|
|
||||||
options:
|
options:
|
||||||
- label: I commit to help with one of those options 👆
|
- label: I commit to help with one of those options 👆
|
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -2,3 +2,12 @@ blank_issues_enabled: false
|
|||||||
contact_links:
|
contact_links:
|
||||||
- name: Security Contact
|
- name: Security Contact
|
||||||
about: Please report security vulnerabilities to security@tiangolo.com
|
about: Please report security vulnerabilities to security@tiangolo.com
|
||||||
|
- name: Question or Problem
|
||||||
|
about: Ask a question or ask about a problem in GitHub Discussions.
|
||||||
|
url: https://github.com/tiangolo/sqlmodel/discussions/categories/questions
|
||||||
|
- name: Feature Request
|
||||||
|
about: To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already.
|
||||||
|
url: https://github.com/tiangolo/sqlmodel/discussions/categories/questions
|
||||||
|
- name: Show and tell
|
||||||
|
about: Show what you built with SQLModel or to be used with SQLModel.
|
||||||
|
url: https://github.com/tiangolo/sqlmodel/discussions/categories/show-and-tell
|
||||||
|
214
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@ -1,214 +0,0 @@
|
|||||||
name: Feature Request
|
|
||||||
description: Suggest an idea or ask for a feature that you would like to have in SQLModel
|
|
||||||
labels: [enhancement]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for your interest in SQLModel! 🚀
|
|
||||||
|
|
||||||
Please follow these instructions, fill every question, and do every step. 🙏
|
|
||||||
|
|
||||||
I'm asking this because answering questions and solving problems in GitHub issues is what consumes most of the time.
|
|
||||||
|
|
||||||
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling issues.
|
|
||||||
|
|
||||||
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
|
|
||||||
|
|
||||||
If more SQLModel users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
|
|
||||||
|
|
||||||
By asking questions in a structured way (following this) it will be much easier to help you.
|
|
||||||
|
|
||||||
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
|
|
||||||
|
|
||||||
As there are too many issues with questions, I'll have to close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
|
|
||||||
- type: checkboxes
|
|
||||||
id: checks
|
|
||||||
attributes:
|
|
||||||
label: First Check
|
|
||||||
description: Please confirm and check all the following options.
|
|
||||||
options:
|
|
||||||
- label: I added a very descriptive title to this issue.
|
|
||||||
required: true
|
|
||||||
- label: I used the GitHub search to find a similar issue and didn't find it.
|
|
||||||
required: true
|
|
||||||
- label: I searched the SQLModel documentation, with the integrated search.
|
|
||||||
required: true
|
|
||||||
- label: I already searched in Google "How to X in SQLModel" and didn't find any information.
|
|
||||||
required: true
|
|
||||||
- label: I already read and followed all the tutorial in the docs and didn't find an answer.
|
|
||||||
required: true
|
|
||||||
- label: I already checked if it is not related to SQLModel but to [Pydantic](https://github.com/samuelcolvin/pydantic).
|
|
||||||
required: true
|
|
||||||
- label: I already checked if it is not related to SQLModel but to [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy).
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
id: help
|
|
||||||
attributes:
|
|
||||||
label: Commit to Help
|
|
||||||
description: |
|
|
||||||
After submitting this, I commit to one of:
|
|
||||||
|
|
||||||
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
|
|
||||||
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
|
|
||||||
* Implement a Pull Request for a confirmed bug.
|
|
||||||
|
|
||||||
options:
|
|
||||||
- label: I commit to help with one of those options 👆
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: example
|
|
||||||
attributes:
|
|
||||||
label: Example Code
|
|
||||||
description: |
|
|
||||||
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
|
|
||||||
|
|
||||||
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
|
|
||||||
|
|
||||||
placeholder: |
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from sqlmodel import Field, Session, SQLModel, create_engine
|
|
||||||
|
|
||||||
|
|
||||||
class Hero(SQLModel, table=True):
|
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
|
||||||
name: str
|
|
||||||
secret_name: str
|
|
||||||
age: Optional[int] = None
|
|
||||||
|
|
||||||
|
|
||||||
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
|
||||||
|
|
||||||
engine = create_engine("sqlite:///database.db")
|
|
||||||
|
|
||||||
|
|
||||||
SQLModel.metadata.create_all(engine)
|
|
||||||
|
|
||||||
with Session(engine) as session:
|
|
||||||
session.add(hero_1)
|
|
||||||
session.commit()
|
|
||||||
session.refresh(hero_1)
|
|
||||||
print(hero_1)
|
|
||||||
render: python
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
What is your feature request?
|
|
||||||
|
|
||||||
Write a short description telling me what you are trying to solve and what you are currently doing.
|
|
||||||
placeholder: |
|
|
||||||
* Create a Hero model.
|
|
||||||
* Create a Hero instance.
|
|
||||||
* Save it to a SQLite database.
|
|
||||||
* I would like it to also automatically send me an email with all the SQL code executed.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: wanted-solution
|
|
||||||
attributes:
|
|
||||||
label: Wanted Solution
|
|
||||||
description: |
|
|
||||||
Tell me what's the solution you would like.
|
|
||||||
placeholder: |
|
|
||||||
I would like it to have a `send_email` configuration that defaults to `False`, and can be set to `True` to send me an email.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: wanted-code
|
|
||||||
attributes:
|
|
||||||
label: Wanted Code
|
|
||||||
description: Show me an example of how you would want the code to look like.
|
|
||||||
placeholder: |
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from sqlmodel import Field, Session, SQLModel, create_engine
|
|
||||||
|
|
||||||
|
|
||||||
class Hero(SQLModel, table=True, send_email=True):
|
|
||||||
id: Optional[int] = Field(default=None, primary_key=True)
|
|
||||||
name: str
|
|
||||||
secret_name: str
|
|
||||||
age: Optional[int] = None
|
|
||||||
|
|
||||||
|
|
||||||
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
|
|
||||||
|
|
||||||
engine = create_engine("sqlite:///database.db")
|
|
||||||
|
|
||||||
|
|
||||||
SQLModel.metadata.create_all(engine)
|
|
||||||
|
|
||||||
with Session(engine) as session:
|
|
||||||
session.add(hero_1)
|
|
||||||
session.commit()
|
|
||||||
session.refresh(hero_1)
|
|
||||||
print(hero_1)
|
|
||||||
|
|
||||||
|
|
||||||
render: python
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: alternatives
|
|
||||||
attributes:
|
|
||||||
label: Alternatives
|
|
||||||
description: |
|
|
||||||
Tell me about alternatives you've considered.
|
|
||||||
placeholder: |
|
|
||||||
To hire someone to look at the logs, write the SQL in paper, and then send me a letter by post with it.
|
|
||||||
- type: dropdown
|
|
||||||
id: os
|
|
||||||
attributes:
|
|
||||||
label: Operating System
|
|
||||||
description: What operating system are you on?
|
|
||||||
multiple: true
|
|
||||||
options:
|
|
||||||
- Linux
|
|
||||||
- Windows
|
|
||||||
- macOS
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: os-details
|
|
||||||
attributes:
|
|
||||||
label: Operating System Details
|
|
||||||
description: You can add more details about your operating system here, in particular if you chose "Other".
|
|
||||||
- type: input
|
|
||||||
id: sqlmodel-version
|
|
||||||
attributes:
|
|
||||||
label: SQLModel Version
|
|
||||||
description: |
|
|
||||||
What SQLModel version are you using?
|
|
||||||
|
|
||||||
You can find the SQLModel version with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python -c "import sqlmodel; print(sqlmodel.__version__)"
|
|
||||||
```
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: python-version
|
|
||||||
attributes:
|
|
||||||
label: Python Version
|
|
||||||
description: |
|
|
||||||
What Python version are you using?
|
|
||||||
|
|
||||||
You can find the Python version with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python --version
|
|
||||||
```
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: context
|
|
||||||
attributes:
|
|
||||||
label: Additional Context
|
|
||||||
description: Add any additional context information or screenshots you think are useful.
|
|
22
.github/ISSUE_TEMPLATE/privileged.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: Privileged
|
||||||
|
description: You are @tiangolo or he asked you directly to create an issue here. If not, check the other options. 👇
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for your interest in SQLModel! 🚀
|
||||||
|
|
||||||
|
If you are not @tiangolo or he didn't ask you directly to create an issue here, please start the conversation in a [Question in GitHub Discussions](https://github.com/tiangolo/sqlmodel/discussions/categories/questions) instead.
|
||||||
|
- type: checkboxes
|
||||||
|
id: privileged
|
||||||
|
attributes:
|
||||||
|
label: Privileged issue
|
||||||
|
description: Confirm that you are allowed to create an issue here.
|
||||||
|
options:
|
||||||
|
- label: I'm @tiangolo or he asked me directly to create an issue here.
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: content
|
||||||
|
attributes:
|
||||||
|
label: Issue Content
|
||||||
|
description: Add the content of the issue here.
|
@ -48,9 +48,7 @@ if __name__ == "__main__":
|
|||||||
use_pr = pr
|
use_pr = pr
|
||||||
break
|
break
|
||||||
if not use_pr:
|
if not use_pr:
|
||||||
logging.error(
|
logging.error(f"No PR found for hash: {event.workflow_run.head_commit.id}")
|
||||||
f"No PR found for hash: {event.workflow_run.head_commit.id}"
|
|
||||||
)
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
github_headers = {
|
github_headers = {
|
||||||
"Authorization": f"token {settings.input_token.get_secret_value()}"
|
"Authorization": f"token {settings.input_token.get_secret_value()}"
|
||||||
|
7
.github/actions/watch-previews/Dockerfile
vendored
@ -1,7 +0,0 @@
|
|||||||
FROM python:3.7
|
|
||||||
|
|
||||||
RUN pip install httpx PyGithub "pydantic==1.5.1"
|
|
||||||
|
|
||||||
COPY ./app /app
|
|
||||||
|
|
||||||
CMD ["python", "/app/main.py"]
|
|
10
.github/actions/watch-previews/action.yml
vendored
@ -1,10 +0,0 @@
|
|||||||
name: Watch docs previews in PRs
|
|
||||||
description: Check PRs and trigger new docs deploys
|
|
||||||
author: "Sebastián Ramírez <tiangolo@gmail.com>"
|
|
||||||
inputs:
|
|
||||||
token:
|
|
||||||
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
|
|
||||||
required: true
|
|
||||||
runs:
|
|
||||||
using: docker
|
|
||||||
image: Dockerfile
|
|
102
.github/actions/watch-previews/app/main.py
vendored
@ -1,102 +0,0 @@
|
|||||||
import logging
|
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
from github import Github
|
|
||||||
from github.NamedUser import NamedUser
|
|
||||||
from pydantic import BaseModel, BaseSettings, SecretStr
|
|
||||||
|
|
||||||
github_api = "https://api.github.com"
|
|
||||||
netlify_api = "https://api.netlify.com"
|
|
||||||
main_branch = "main"
|
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
|
||||||
input_token: SecretStr
|
|
||||||
github_repository: str
|
|
||||||
github_event_path: Path
|
|
||||||
github_event_name: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Artifact(BaseModel):
|
|
||||||
id: int
|
|
||||||
node_id: str
|
|
||||||
name: str
|
|
||||||
size_in_bytes: int
|
|
||||||
url: str
|
|
||||||
archive_download_url: str
|
|
||||||
expired: bool
|
|
||||||
created_at: datetime
|
|
||||||
updated_at: datetime
|
|
||||||
|
|
||||||
|
|
||||||
class ArtifactResponse(BaseModel):
|
|
||||||
total_count: int
|
|
||||||
artifacts: List[Artifact]
|
|
||||||
|
|
||||||
|
|
||||||
def get_message(commit: str) -> str:
|
|
||||||
return f"Docs preview for commit {commit} at"
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
settings = Settings()
|
|
||||||
logging.info(f"Using config: {settings.json()}")
|
|
||||||
g = Github(settings.input_token.get_secret_value())
|
|
||||||
repo = g.get_repo(settings.github_repository)
|
|
||||||
owner: NamedUser = repo.owner
|
|
||||||
headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
|
|
||||||
prs = list(repo.get_pulls(state="open"))
|
|
||||||
response = httpx.get(
|
|
||||||
f"{github_api}/repos/{settings.github_repository}/actions/artifacts",
|
|
||||||
headers=headers,
|
|
||||||
)
|
|
||||||
data = response.json()
|
|
||||||
artifacts_response = ArtifactResponse.parse_obj(data)
|
|
||||||
for pr in prs:
|
|
||||||
logging.info("-----")
|
|
||||||
logging.info(f"Processing PR #{pr.number}: {pr.title}")
|
|
||||||
pr_comments = list(pr.get_issue_comments())
|
|
||||||
pr_commits = list(pr.get_commits())
|
|
||||||
last_commit = pr_commits[0]
|
|
||||||
for pr_commit in pr_commits:
|
|
||||||
if pr_commit.commit.author.date > last_commit.commit.author.date:
|
|
||||||
last_commit = pr_commit
|
|
||||||
commit = last_commit.commit.sha
|
|
||||||
logging.info(f"Last commit: {commit}")
|
|
||||||
message = get_message(commit)
|
|
||||||
notified = False
|
|
||||||
for pr_comment in pr_comments:
|
|
||||||
if message in pr_comment.body:
|
|
||||||
notified = True
|
|
||||||
logging.info(f"Docs preview was notified: {notified}")
|
|
||||||
if not notified:
|
|
||||||
artifact_name = f"docs-zip-{commit}"
|
|
||||||
use_artifact: Optional[Artifact] = None
|
|
||||||
for artifact in artifacts_response.artifacts:
|
|
||||||
if artifact.name == artifact_name:
|
|
||||||
use_artifact = artifact
|
|
||||||
break
|
|
||||||
if not use_artifact:
|
|
||||||
logging.info("Artifact not available")
|
|
||||||
else:
|
|
||||||
logging.info(f"Existing artifact: {use_artifact.name}")
|
|
||||||
response = httpx.post(
|
|
||||||
f"{github_api}/repos/{settings.github_repository}/actions/workflows/preview-docs.yml/dispatches",
|
|
||||||
headers=headers,
|
|
||||||
json={
|
|
||||||
"ref": main_branch,
|
|
||||||
"inputs": {
|
|
||||||
"pr": f"{pr.number}",
|
|
||||||
"name": artifact_name,
|
|
||||||
"commit": commit,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
logging.info(
|
|
||||||
f"Trigger sent, response status: {response.status_code} - content: {response.content}"
|
|
||||||
)
|
|
||||||
logging.info("Finished")
|
|
95
.github/workflows/build-docs.yml
vendored
@ -4,78 +4,91 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize]
|
types:
|
||||||
workflow_dispatch:
|
- opened
|
||||||
inputs:
|
- synchronize
|
||||||
debug_enabled:
|
|
||||||
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
|
||||||
required: false
|
|
||||||
default: false
|
|
||||||
jobs:
|
jobs:
|
||||||
|
changes:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Required permissions
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
# Set job outputs to values from filter step
|
||||||
|
outputs:
|
||||||
|
docs: ${{ steps.filter.outputs.docs }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
# For pull requests it's not necessary to checkout the code but for the main branch it is
|
||||||
|
- uses: dorny/paths-filter@v2
|
||||||
|
id: filter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
docs:
|
||||||
|
- README.md
|
||||||
|
- docs/**
|
||||||
|
- docs_src/**
|
||||||
|
- pyproject.toml
|
||||||
|
- mkdocs.yml
|
||||||
|
- mkdocs.insiders.yml
|
||||||
|
|
||||||
build-docs:
|
build-docs:
|
||||||
runs-on: ubuntu-20.04
|
needs:
|
||||||
|
- changes
|
||||||
|
if: ${{ needs.changes.outputs.docs == 'true' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Dump GitHub context
|
- name: Dump GitHub context
|
||||||
env:
|
env:
|
||||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||||
run: echo "$GITHUB_CONTEXT"
|
run: echo "$GITHUB_CONTEXT"
|
||||||
- uses: actions/checkout@v3.1.0
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.7"
|
python-version: "3.11"
|
||||||
# Allow debugging with tmate
|
|
||||||
- name: Setup tmate session
|
|
||||||
uses: mxschmitt/action-tmate@v3
|
|
||||||
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
|
|
||||||
with:
|
|
||||||
limit-access-to-actor: true
|
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: ${{ env.pythonLocation }}
|
path: ${{ env.pythonLocation }}
|
||||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-docs
|
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-v01
|
||||||
- name: Install poetry
|
- name: Install Poetry
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
# TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
|
|
||||||
# once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
|
|
||||||
# Ref: https://github.com/python-poetry/poetry-core/pull/188
|
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
|
python -m pip install "poetry"
|
||||||
python -m pip install "poetry==1.2.0a2"
|
python -m poetry self add poetry-version-plugin
|
||||||
python -m poetry plugin add poetry-version-plugin
|
|
||||||
- name: Configure poetry
|
- name: Configure poetry
|
||||||
run: python -m poetry config virtualenvs.create false
|
run: python -m poetry config virtualenvs.create false
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
run: python -m poetry install
|
run: python -m poetry install
|
||||||
- name: Install Material for MkDocs Insiders
|
- name: Install Material for MkDocs Insiders
|
||||||
if: github.event.pull_request.head.repo.fork == false && steps.cache.outputs.cache-hit != 'true'
|
if: ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false ) && steps.cache.outputs.cache-hit != 'true'
|
||||||
run: python -m poetry run pip install git+https://${{ secrets.ACTIONS_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
|
run: python -m poetry run pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
|
||||||
- uses: actions/cache@v3
|
- uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
key: mkdocs-cards-${{ github.ref }}
|
key: mkdocs-cards-${{ github.ref }}
|
||||||
path: .cache
|
path: .cache
|
||||||
- name: Build Docs
|
- name: Build Docs
|
||||||
if: github.event.pull_request.head.repo.fork == true
|
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true
|
||||||
run: python -m poetry run mkdocs build
|
run: python -m poetry run mkdocs build
|
||||||
- name: Build Docs with Insiders
|
- name: Build Docs with Insiders
|
||||||
if: github.event.pull_request.head.repo.fork == false
|
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
|
||||||
run: python -m poetry run mkdocs build --config-file mkdocs.insiders.yml
|
run: python -m poetry run mkdocs build --config-file mkdocs.insiders.yml
|
||||||
- name: Zip docs
|
|
||||||
run: python -m poetry run bash ./scripts/zip-docs.sh
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: docs-zip
|
name: docs-site
|
||||||
path: ./site/docs.zip
|
path: ./site/**
|
||||||
- name: Deploy to Netlify
|
|
||||||
uses: nwtgck/actions-netlify@v1.1.5
|
# https://github.com/marketplace/actions/alls-green#why
|
||||||
|
docs-all-green: # This job does nothing and is only used for the branch protection
|
||||||
|
if: always()
|
||||||
|
needs:
|
||||||
|
- build-docs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Decide whether the needed jobs succeeded or failed
|
||||||
|
uses: re-actors/alls-green@release/v1
|
||||||
with:
|
with:
|
||||||
publish-dir: './site'
|
jobs: ${{ toJSON(needs) }}
|
||||||
production-branch: main
|
allowed-skips: build-docs
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
enable-commit-comment: false
|
|
||||||
env:
|
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
||||||
|
48
.github/workflows/deploy-docs.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
name: Deploy Docs
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- Build Docs
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-docs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Dump GitHub context
|
||||||
|
env:
|
||||||
|
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||||
|
run: echo "$GITHUB_CONTEXT"
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Clean site
|
||||||
|
run: |
|
||||||
|
rm -rf ./site
|
||||||
|
mkdir ./site
|
||||||
|
- name: Download Artifact Docs
|
||||||
|
id: download
|
||||||
|
uses: dawidd6/action-download-artifact@v2.28.0
|
||||||
|
with:
|
||||||
|
if_no_artifact_found: ignore
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
workflow: build-docs.yml
|
||||||
|
run_id: ${{ github.event.workflow_run.id }}
|
||||||
|
name: docs-site
|
||||||
|
path: ./site/
|
||||||
|
- name: Deploy to Cloudflare Pages
|
||||||
|
if: steps.download.outputs.found_artifact == 'true'
|
||||||
|
id: deploy
|
||||||
|
uses: cloudflare/pages-action@v1
|
||||||
|
with:
|
||||||
|
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
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
|
||||||
|
if: steps.deploy.outputs.url != ''
|
||||||
|
uses: ./.github/actions/comment-docs-preview-in-pr
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
deploy_url: "${{ steps.deploy.outputs.url }}"
|
10
.github/workflows/latest-changes.yml
vendored
@ -12,22 +12,22 @@ on:
|
|||||||
description: PR number
|
description: PR number
|
||||||
required: true
|
required: true
|
||||||
debug_enabled:
|
debug_enabled:
|
||||||
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: 'false'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
latest-changes:
|
latest-changes:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.1.0
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# To allow latest-changes to commit to the main branch
|
# To allow latest-changes to commit to the main branch
|
||||||
token: ${{ secrets.ACTIONS_TOKEN }}
|
token: ${{ secrets.SQLMODEL_LATEST_CHANGES }}
|
||||||
# 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 }}
|
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: docker://tiangolo/latest-changes:0.0.3
|
- uses: docker://tiangolo/latest-changes:0.0.3
|
||||||
|
46
.github/workflows/preview-docs.yml
vendored
@ -1,46 +0,0 @@
|
|||||||
name: Preview Docs
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows:
|
|
||||||
- Build Docs
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
preview-docs:
|
|
||||||
runs-on: ubuntu-20.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3.1.0
|
|
||||||
- name: Clean site
|
|
||||||
run: |
|
|
||||||
rm -rf ./site
|
|
||||||
mkdir ./site
|
|
||||||
- name: Download Artifact Docs
|
|
||||||
uses: dawidd6/action-download-artifact@v2.24.2
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
workflow: build-docs.yml
|
|
||||||
run_id: ${{ github.event.workflow_run.id }}
|
|
||||||
name: docs-zip
|
|
||||||
path: ./site/
|
|
||||||
- name: Unzip docs
|
|
||||||
run: |
|
|
||||||
cd ./site
|
|
||||||
unzip docs.zip
|
|
||||||
rm -f docs.zip
|
|
||||||
- name: Deploy to Netlify
|
|
||||||
id: netlify
|
|
||||||
uses: nwtgck/actions-netlify@v1.1.5
|
|
||||||
with:
|
|
||||||
publish-dir: './site'
|
|
||||||
production-deploy: false
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
enable-commit-comment: false
|
|
||||||
env:
|
|
||||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
||||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
||||||
- name: Comment Deploy
|
|
||||||
uses: ./.github/actions/comment-docs-preview-in-pr
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
deploy_url: "${{ steps.netlify.outputs.deploy-url }}"
|
|
20
.github/workflows/publish.yml
vendored
@ -7,15 +7,15 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
debug_enabled:
|
debug_enabled:
|
||||||
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: 'false'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.1.0
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
@ -23,24 +23,20 @@ jobs:
|
|||||||
# 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 }}
|
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@v3
|
- uses: actions/cache@v3
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: ${{ env.pythonLocation }}
|
path: ${{ env.pythonLocation }}
|
||||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root
|
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-v2
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
# TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
|
|
||||||
# once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
|
|
||||||
# Ref: https://github.com/python-poetry/poetry-core/pull/188
|
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
|
python -m pip install "poetry"
|
||||||
python -m pip install "poetry==1.2.0a2"
|
python -m poetry self add poetry-version-plugin
|
||||||
python -m poetry plugin add poetry-version-plugin
|
|
||||||
- name: Configure poetry
|
- name: Configure poetry
|
||||||
run: python -m poetry config virtualenvs.create false
|
run: python -m poetry config virtualenvs.create false
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
|
2
.github/workflows/smokeshow.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
|||||||
|
|
||||||
- run: pip install smokeshow
|
- run: pip install smokeshow
|
||||||
|
|
||||||
- uses: dawidd6/action-download-artifact@v2.24.2
|
- uses: dawidd6/action-download-artifact@v2.28.0
|
||||||
with:
|
with:
|
||||||
workflow: test.yml
|
workflow: test.yml
|
||||||
commit: ${{ github.event.workflow_run.head_sha }}
|
commit: ${{ github.event.workflow_run.head_sha }}
|
||||||
|
48
.github/workflows/test.yml
vendored
@ -5,24 +5,30 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize]
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
debug_enabled:
|
debug_enabled:
|
||||||
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: 'false'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.6.15", "3.7", "3.8", "3.9", "3.10"]
|
python-version:
|
||||||
|
- "3.7"
|
||||||
|
- "3.8"
|
||||||
|
- "3.9"
|
||||||
|
- "3.10"
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3.1.0
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
@ -30,31 +36,26 @@ jobs:
|
|||||||
# 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 }}
|
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@v3
|
- uses: actions/cache@v3
|
||||||
id: cache
|
id: cache
|
||||||
with:
|
with:
|
||||||
path: ${{ env.pythonLocation }}
|
path: ${{ env.pythonLocation }}
|
||||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root
|
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-v2
|
||||||
- name: Install poetry
|
- name: Install poetry
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
# TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
|
|
||||||
# once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
|
|
||||||
# Ref: https://github.com/python-poetry/poetry-core/pull/188
|
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
|
python -m pip install "poetry"
|
||||||
python -m pip install "poetry==1.2.0a2"
|
python -m poetry self add poetry-version-plugin
|
||||||
python -m poetry plugin add poetry-version-plugin
|
|
||||||
- name: Configure poetry
|
- name: Configure poetry
|
||||||
run: python -m poetry config virtualenvs.create false
|
run: python -m poetry config virtualenvs.create false
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
run: python -m poetry install
|
run: python -m poetry install
|
||||||
- name: Lint
|
- name: Lint
|
||||||
if: ${{ matrix.python-version != '3.6.15' }}
|
|
||||||
run: python -m poetry run bash scripts/lint.sh
|
run: python -m poetry run bash scripts/lint.sh
|
||||||
- run: mkdir coverage
|
- run: mkdir coverage
|
||||||
- name: Test
|
- name: Test
|
||||||
@ -68,11 +69,12 @@ jobs:
|
|||||||
name: coverage
|
name: coverage
|
||||||
path: coverage
|
path: coverage
|
||||||
coverage-combine:
|
coverage-combine:
|
||||||
needs: [test]
|
needs:
|
||||||
|
- test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
@ -96,3 +98,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: coverage-html
|
name: coverage-html
|
||||||
path: htmlcov
|
path: htmlcov
|
||||||
|
|
||||||
|
# https://github.com/marketplace/actions/alls-green#why
|
||||||
|
alls-green: # This job does nothing and is only used for the branch protection
|
||||||
|
if: always()
|
||||||
|
needs:
|
||||||
|
- coverage-combine
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Decide whether the needed jobs succeeded or failed
|
||||||
|
uses: re-actors/alls-green@release/v1
|
||||||
|
with:
|
||||||
|
jobs: ${{ toJSON(needs) }}
|
||||||
|
25
.pre-commit-config.yaml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
default_language_version:
|
||||||
|
python: python3.10
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.5.0
|
||||||
|
hooks:
|
||||||
|
- id: check-added-large-files
|
||||||
|
- id: check-toml
|
||||||
|
- id: check-yaml
|
||||||
|
args:
|
||||||
|
- --unsafe
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||||
|
rev: v0.1.2
|
||||||
|
hooks:
|
||||||
|
- id: ruff
|
||||||
|
args:
|
||||||
|
- --fix
|
||||||
|
- id: ruff-format
|
||||||
|
ci:
|
||||||
|
autofix_commit_msg: 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
|
||||||
|
autoupdate_commit_msg: ⬆ [pre-commit.ci] pre-commit autoupdate
|
24
CITATION.cff
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# This CITATION.cff file was generated with cffinit.
|
||||||
|
# Visit https://bit.ly/cffinit to generate yours today!
|
||||||
|
|
||||||
|
cff-version: 1.2.0
|
||||||
|
title: SQLModel
|
||||||
|
message: >-
|
||||||
|
If you use this software, please cite it using the
|
||||||
|
metadata from this file.
|
||||||
|
type: software
|
||||||
|
authors:
|
||||||
|
- given-names: Sebastián
|
||||||
|
family-names: Ramírez
|
||||||
|
email: tiangolo@gmail.com
|
||||||
|
identifiers:
|
||||||
|
repository-code: 'https://github.com/tiangolo/sqlmodel'
|
||||||
|
url: 'https://sqlmodel.tiangolo.com'
|
||||||
|
abstract: >-
|
||||||
|
SQLModel, SQL databases in Python, designed for
|
||||||
|
simplicity, compatibility, and robustness.
|
||||||
|
keywords:
|
||||||
|
- fastapi
|
||||||
|
- pydantic
|
||||||
|
- sqlalchemy
|
||||||
|
license: MIT
|
@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
|
A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python</a>.
|
||||||
|
|
||||||
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
|
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
|
||||||
|
|
||||||
|
@ -6,10 +6,6 @@ First, you might want to see the basic ways to [help SQLModel and get help](help
|
|||||||
|
|
||||||
If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
|
If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
|
||||||
|
|
||||||
### Python
|
|
||||||
|
|
||||||
SQLModel supports Python 3.6 and above, but for development you should have at least **Python 3.7**.
|
|
||||||
|
|
||||||
### Poetry
|
### Poetry
|
||||||
|
|
||||||
**SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project.
|
**SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project.
|
||||||
@ -116,7 +112,7 @@ There is a script that you can run locally to test all the code and generate cov
|
|||||||
<div class="termy">
|
<div class="termy">
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ bash scripts/test-cov-html.sh
|
$ bash scripts/test.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,7 +111,7 @@ DROP TABLE hero;
|
|||||||
|
|
||||||
That is how you tell the database in SQL to delete the entire table `hero`.
|
That is how you tell the database in SQL to delete the entire table `hero`.
|
||||||
|
|
||||||
<a href="http://www.nooooooooooooooo.com/" class="external-link" target="_blank">Nooooo!</a> We lost all the data in the `hero` table! 💥😱
|
<a href="https://theuselessweb.site/nooooooooooooooo/" class="external-link" target="_blank">Nooooo!</a> We lost all the data in the `hero` table! 💥😱
|
||||||
|
|
||||||
### SQL Sanitization
|
### SQL Sanitization
|
||||||
|
|
||||||
@ -305,4 +305,4 @@ You will see **your own code** a lot more than the internal table names, so it's
|
|||||||
So, to keep things consistent, I'll keep using the same table names that **SQLModel** would have generated.
|
So, to keep things consistent, I'll keep using the same table names that **SQLModel** would have generated.
|
||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
You can also override the table name. You can read about it in the Advanced User Guide.
|
You can also override the table name. You can read about it in the Advanced User Guide.
|
||||||
|
@ -12,7 +12,7 @@ Nevertheless, SQLModel is completely **independent** of FastAPI and can be used
|
|||||||
|
|
||||||
## Just Modern Python
|
## Just Modern Python
|
||||||
|
|
||||||
It's all based on standard <abbr title="Python currently supported versions, 3.6 and above.">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
|
It's all based on standard <abbr title="Currently supported versions of Python">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
|
||||||
|
|
||||||
If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">Python types intro</a>.
|
If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">Python types intro</a>.
|
||||||
|
|
||||||
|
150
docs/help.md
@ -58,26 +58,125 @@ You can:
|
|||||||
|
|
||||||
I love to hear about how **SQLModel** is being used, what you have liked in it, in which project/company are you using it, etc.
|
I love to hear about how **SQLModel** is being used, what you have liked in it, in which project/company are you using it, etc.
|
||||||
|
|
||||||
## Help others with issues in GitHub
|
## Help others with questions in GitHub
|
||||||
|
|
||||||
You can see <a href="https://github.com/tiangolo/sqlmodel/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. 🤓
|
You can try and help others with their questions in:
|
||||||
|
|
||||||
|
* <a href="https://github.com/tiangolo/sqlmodel/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered" class="external-link" target="_blank">GitHub Discussions</a>
|
||||||
|
* <a href="https://github.com/tiangolo/sqlmodel/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+" class="external-link" target="_blank">GitHub Issues</a>
|
||||||
|
|
||||||
|
In many cases you might already know the answer for those questions. 🤓
|
||||||
|
|
||||||
|
Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗
|
||||||
|
|
||||||
|
The idea is for the **SQLModel** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Here's how to help others with questions (in discussions or issues):
|
||||||
|
|
||||||
|
### Understand the question
|
||||||
|
|
||||||
|
* Check if you can understand what is the **purpose** and use case of the person asking.
|
||||||
|
|
||||||
|
* Then check if the question (the vast majority are questions) is **clear**.
|
||||||
|
|
||||||
|
* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**.
|
||||||
|
|
||||||
|
* If you can't understand the question, ask for more **details**.
|
||||||
|
|
||||||
|
### Reproduce the problem
|
||||||
|
|
||||||
|
For most of the cases and most of the questions there's something related to the person's **original code**.
|
||||||
|
|
||||||
|
In many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**.
|
||||||
|
|
||||||
|
* You can ask them to provide a <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">minimal, reproducible, example</a>, that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better.
|
||||||
|
|
||||||
|
* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just have in mind that this might take a lot of time and it might be better to ask them to clarify the problem first.
|
||||||
|
|
||||||
|
### Suggest solutions
|
||||||
|
|
||||||
|
* After being able to understand the question, you can give them a possible **answer**.
|
||||||
|
|
||||||
|
* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do.
|
||||||
|
|
||||||
|
### Ask to close
|
||||||
|
|
||||||
|
If they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸
|
||||||
|
|
||||||
|
* Now, if that solved their problem, you can ask them to:
|
||||||
|
|
||||||
|
* In GitHub Discussions: mark the comment as the **answer**.
|
||||||
|
* In GitHub Issues: **close** the issue**.
|
||||||
|
|
||||||
## Watch the GitHub repository
|
## Watch the GitHub repository
|
||||||
|
|
||||||
You can "watch" SQLModel in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/sqlmodel" class="external-link" target="_blank">https://github.com/tiangolo/sqlmodel</a>. 👀
|
You can "watch" SQLModel in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/sqlmodel" class="external-link" target="_blank">https://github.com/tiangolo/sqlmodel</a>. 👀
|
||||||
|
|
||||||
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue.
|
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, or discussions, or PRs, etc.
|
||||||
|
|
||||||
Then you can try and help them solve those issues.
|
Then you can try and help them solve those questions.
|
||||||
|
|
||||||
## Create issues
|
## Ask Questions
|
||||||
|
|
||||||
You can <a href="https://github.com/tiangolo/sqlmodel/issues/new/choose" class="external-link" target="_blank">create a new issue</a> in the GitHub repository, for example to:
|
You can <a href="https://github.com/tiangolo/sqlmodel/discussions/new?category=questions" class="external-link" target="_blank">create a new question</a> in the GitHub repository, for example to:
|
||||||
|
|
||||||
* Ask a **question** or ask about a **problem**.
|
* Ask a **question** or ask about a **problem**.
|
||||||
* Suggest a new **feature**.
|
* Suggest a new **feature**.
|
||||||
|
|
||||||
**Note**: if you create an issue, then I'm going to ask you to also help others. 😉
|
**Note**: if you do it, then I'm going to ask you to also help others. 😉
|
||||||
|
|
||||||
|
## Review Pull Requests
|
||||||
|
|
||||||
|
You can help me review pull requests from others.
|
||||||
|
|
||||||
|
Again, please try your best to be kind. 🤗
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Here's what to have in mind and how to review a pull request:
|
||||||
|
|
||||||
|
### Understand the problem
|
||||||
|
|
||||||
|
* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in a GitHub Discussion or issue.
|
||||||
|
|
||||||
|
* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that.
|
||||||
|
|
||||||
|
### Don't worry about style
|
||||||
|
|
||||||
|
* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually.
|
||||||
|
|
||||||
|
* Also don't worry about style rules, there are already automatized tools checking that.
|
||||||
|
|
||||||
|
And if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes.
|
||||||
|
|
||||||
|
### Check the code
|
||||||
|
|
||||||
|
* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem.
|
||||||
|
|
||||||
|
* Then **comment** saying that you did that, that's how I will know you really checked it.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
Unfortunately, I can't simply trust PRs that just have several approvals.
|
||||||
|
|
||||||
|
Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
|
||||||
|
|
||||||
|
So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
|
||||||
|
|
||||||
|
* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
* Help me check that the PR has **tests**.
|
||||||
|
|
||||||
|
* Check that the tests **fail** before the PR. 🚨
|
||||||
|
|
||||||
|
* Then check that the tests **pass** after the PR. ✅
|
||||||
|
|
||||||
|
* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that.
|
||||||
|
|
||||||
|
* Then also comment what you tried, that way I'll know that you checked it. 🤓
|
||||||
|
|
||||||
## Create a Pull Request
|
## Create a Pull Request
|
||||||
|
|
||||||
@ -86,7 +185,44 @@ You can [contribute](contributing.md){.internal-link target=_blank} to the sourc
|
|||||||
* To fix a typo you found on the documentation.
|
* To fix a typo you found on the documentation.
|
||||||
* To propose new documentation sections.
|
* To propose new documentation sections.
|
||||||
* To fix an existing issue/bug.
|
* To fix an existing issue/bug.
|
||||||
|
* Make sure to add tests.
|
||||||
* To add a new feature.
|
* To add a new feature.
|
||||||
|
* Make sure to add tests.
|
||||||
|
* Make sure to add documentation if it's relevant.
|
||||||
|
|
||||||
|
## Help Maintain SQLModel
|
||||||
|
|
||||||
|
Help me maintain **SQLModel**! 🤓
|
||||||
|
|
||||||
|
There's a lot of work to do, and for most of it, **YOU** can do it.
|
||||||
|
|
||||||
|
The main tasks that you can do right now are:
|
||||||
|
|
||||||
|
* [Help others with questions in GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (see the section above).
|
||||||
|
* [Review Pull Requests](#review-pull-requests){.internal-link target=_blank} (see the section above).
|
||||||
|
|
||||||
|
Those two tasks are what **consume time the most**. That's the main work of maintaining SQLModel.
|
||||||
|
|
||||||
|
If you can help me with that, **you are helping me maintain SQLModel** and making sure it keeps **advancing faster and better**. 🚀
|
||||||
|
|
||||||
|
## Join the chat
|
||||||
|
|
||||||
|
Join the 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">FastAPI and Friends Discord chat server</a> 👥 and hang out with others in the community. There's a `#sqlmodel` channel.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
For questions, ask them in <a href="https://github.com/tiangolo/sqlmodel/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, there's a much better chance you will receive help there.
|
||||||
|
|
||||||
|
Use the chat only for other general conversations.
|
||||||
|
|
||||||
|
### Don't use the chat for questions
|
||||||
|
|
||||||
|
Have in mind that as chats allow more "free conversation", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers.
|
||||||
|
|
||||||
|
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. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat. 😅
|
||||||
|
|
||||||
|
Conversations in the chat are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation.
|
||||||
|
|
||||||
|
On the other side, there are thousands of users in the chat, so there's a high chance you'll find someone to talk to there, almost all the time. 😄
|
||||||
|
|
||||||
## Sponsor the author
|
## Sponsor the author
|
||||||
|
|
||||||
|
@ -90,4 +90,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@ -202,4 +202,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@ -148,4 +148,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
@ -81,4 +81,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -34,4 +34,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
@ -61,4 +61,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
@ -94,4 +94,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
@ -94,4 +94,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -94,4 +94,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@ -97,4 +97,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -94,4 +94,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@ -97,4 +97,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -97,4 +97,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
@ -100,4 +100,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -89,4 +89,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@ -217,4 +217,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
@ -130,4 +130,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
@ -130,4 +130,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
@ -130,4 +130,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
@ -38,4 +38,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
@ -49,4 +49,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
@ -139,4 +139,4 @@
|
|||||||
</root>
|
</root>
|
||||||
</mxGraphModel>
|
</mxGraphModel>
|
||||||
</diagram>
|
</diagram>
|
||||||
</mxfile>
|
</mxfile>
|
||||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
@ -50,7 +50,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
|
|||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
|
A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python</a>.
|
||||||
|
|
||||||
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
|
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ Successfully installed sqlmodel
|
|||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com" target="_blank">SQLModel documentation</a>.
|
For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com/databases/" target="_blank">SQLModel documentation</a>.
|
||||||
|
|
||||||
Here's a quick example. ✨
|
Here's a quick example. ✨
|
||||||
|
|
||||||
|
@ -72,14 +72,14 @@ class Termynal {
|
|||||||
* Initialise the widget, get lines, clear container and start animation.
|
* Initialise the widget, get lines, clear container and start animation.
|
||||||
*/
|
*/
|
||||||
init() {
|
init() {
|
||||||
/**
|
/**
|
||||||
* Calculates width and height of Termynal container.
|
* Calculates width and height of Termynal container.
|
||||||
* If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.
|
* If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.
|
||||||
*/
|
*/
|
||||||
const containerStyle = getComputedStyle(this.container);
|
const containerStyle = getComputedStyle(this.container);
|
||||||
this.container.style.width = containerStyle.width !== '0px' ?
|
this.container.style.width = containerStyle.width !== '0px' ?
|
||||||
containerStyle.width : undefined;
|
containerStyle.width : undefined;
|
||||||
this.container.style.minHeight = containerStyle.height !== '0px' ?
|
this.container.style.minHeight = containerStyle.height !== '0px' ?
|
||||||
containerStyle.height : undefined;
|
containerStyle.height : undefined;
|
||||||
|
|
||||||
this.container.setAttribute('data-termynal', '');
|
this.container.setAttribute('data-termynal', '');
|
||||||
@ -138,7 +138,7 @@ class Termynal {
|
|||||||
restart.innerHTML = "restart ↻"
|
restart.innerHTML = "restart ↻"
|
||||||
return restart
|
return restart
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFinish() {
|
generateFinish() {
|
||||||
const finish = document.createElement('a')
|
const finish = document.createElement('a')
|
||||||
finish.onclick = (e) => {
|
finish.onclick = (e) => {
|
||||||
@ -215,7 +215,7 @@ class Termynal {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts line data objects into line elements.
|
* Converts line data objects into line elements.
|
||||||
*
|
*
|
||||||
* @param {Object[]} lineData - Dynamically loaded lines.
|
* @param {Object[]} lineData - Dynamically loaded lines.
|
||||||
* @param {Object} line - Line data object.
|
* @param {Object} line - Line data object.
|
||||||
* @returns {Element[]} - Array of line elements.
|
* @returns {Element[]} - Array of line elements.
|
||||||
@ -231,7 +231,7 @@ class Termynal {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function for generating attributes string.
|
* Helper function for generating attributes string.
|
||||||
*
|
*
|
||||||
* @param {Object} line - Line data object.
|
* @param {Object} line - Line data object.
|
||||||
* @returns {string} - String of attributes.
|
* @returns {string} - String of attributes.
|
||||||
*/
|
*/
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<qa-bot
|
<qa-bot
|
||||||
server="https://tiangolo-sqlmodel.docsqa.jina.ai"
|
server="https://tiangolo-sqlmodel.docsqa.jina.ai"
|
||||||
theme="infer"
|
theme="infer"
|
||||||
title="SQLModel Bot"
|
title="SQLModel Bot"
|
||||||
description="SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
|
description="SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
|
||||||
style="font-size: 0.8rem"
|
style="font-size: 0.8rem"
|
||||||
@ -28,4 +28,4 @@
|
|||||||
</dl>
|
</dl>
|
||||||
</template>
|
</template>
|
||||||
</qa-bot>
|
</qa-bot>
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
@ -2,13 +2,81 @@
|
|||||||
|
|
||||||
## Latest Changes
|
## Latest Changes
|
||||||
|
|
||||||
* 👷 Refactor CI artifact upload/download for docs previews. PR [#514](https://github.com/tiangolo/sqlmodel/pull/514) by [@tiangolo](https://github.com/tiangolo).
|
* 🎨 Update inline source examples, hide `#` in annotations (from MkDocs Material). PR [#677](https://github.com/tiangolo/sqlmodel/pull/677) by [@Matthieu-LAURENT39](https://github.com/Matthieu-LAURENT39).
|
||||||
|
* ✨ Do not allow invalid combinations of field parameters for columns and relationships, `sa_column` excludes `sa_column_args`, `primary_key`, `nullable`, etc.. PR [#681](https://github.com/tiangolo/sqlmodel/pull/681) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
## 0.0.10
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* ✨ Add support for all `Field` parameters from Pydantic `1.9.0` and above, make Pydantic `1.9.0` the minimum required version. PR [#440](https://github.com/tiangolo/sqlmodel/pull/440) by [@daniil-berg](https://github.com/daniil-berg).
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
* 🔧 Adopt Ruff for formatting. PR [#679](https://github.com/tiangolo/sqlmodel/pull/679) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
|
||||||
|
## 0.0.9
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
* 🗑️ Deprecate Python 3.6 and upgrade Poetry and Poetry Version Plugin. PR [#627](https://github.com/tiangolo/sqlmodel/pull/627) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* ✨ Raise a more clear error when a type is not valid. PR [#425](https://github.com/tiangolo/sqlmodel/pull/425) by [@ddanier](https://github.com/ddanier).
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* 🐛 Fix `AsyncSession` type annotations for `exec()`. PR [#58](https://github.com/tiangolo/sqlmodel/pull/58) by [@Bobronium](https://github.com/Bobronium).
|
||||||
|
* 🐛 Fix allowing using a `ForeignKey` directly, remove repeated column construction from `SQLModelMetaclass.__init__` and upgrade minimum SQLAlchemy to `>=1.4.36`. PR [#443](https://github.com/tiangolo/sqlmodel/pull/443) by [@daniil-berg](https://github.com/daniil-berg).
|
||||||
|
* 🐛 Fix enum type checks ordering in `get_sqlalchemy_type`. PR [#669](https://github.com/tiangolo/sqlmodel/pull/669) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 🐛 Fix SQLAlchemy version 1.4.36 breaks SQLModel relationships (#315). PR [#461](https://github.com/tiangolo/sqlmodel/pull/461) by [@byrman](https://github.com/byrman).
|
||||||
|
|
||||||
|
### Upgrades
|
||||||
|
|
||||||
|
* ⬆️ Upgrade support for SQLAlchemy 1.4.49, update tests. PR [#519](https://github.com/tiangolo/sqlmodel/pull/519) by [@sandrotosi](https://github.com/sandrotosi).
|
||||||
|
* ⬆ Raise SQLAlchemy version requirement to at least `1.4.29` (related to #434). PR [#439](https://github.com/tiangolo/sqlmodel/pull/439) by [@daniil-berg](https://github.com/daniil-berg).
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
* 📝 Clarify description of in-memory SQLite database in `docs/tutorial/create-db-and-table.md`. PR [#601](https://github.com/tiangolo/sqlmodel/pull/601) by [@SimonCW](https://github.com/SimonCW).
|
||||||
|
* 📝 Tweak wording in `docs/tutorial/fastapi/multiple-models.md`. PR [#674](https://github.com/tiangolo/sqlmodel/pull/674) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* ✏️ Fix contributing instructions to run tests, update script name. PR [#634](https://github.com/tiangolo/sqlmodel/pull/634) by [@PookieBuns](https://github.com/PookieBuns).
|
||||||
|
* 📝 Update link to docs for intro to databases. PR [#593](https://github.com/tiangolo/sqlmodel/pull/593) by [@abenezerBelachew](https://github.com/abenezerBelachew).
|
||||||
|
* 📝 Update docs, use `offset` in example with `limit` and `where`. PR [#273](https://github.com/tiangolo/sqlmodel/pull/273) by [@jbmchuck](https://github.com/jbmchuck).
|
||||||
|
* 📝 Fix docs for Pydantic's fields using `le` (`lte` is invalid, use `le` ). PR [#207](https://github.com/tiangolo/sqlmodel/pull/207) by [@jrycw](https://github.com/jrycw).
|
||||||
|
* 📝 Update outdated link in `docs/db-to-code.md`. PR [#649](https://github.com/tiangolo/sqlmodel/pull/649) by [@MatveyF](https://github.com/MatveyF).
|
||||||
|
* ✏️ Fix typos found with codespell. PR [#520](https://github.com/tiangolo/sqlmodel/pull/520) by [@kianmeng](https://github.com/kianmeng).
|
||||||
|
* 📝 Fix typos (duplication) in main page. PR [#631](https://github.com/tiangolo/sqlmodel/pull/631) by [@Mr-DRP](https://github.com/Mr-DRP).
|
||||||
|
* 📝 Update release notes, add second author to PR. PR [#429](https://github.com/tiangolo/sqlmodel/pull/429) by [@br-follow](https://github.com/br-follow).
|
||||||
|
* 📝 Update instructions about how to make a foreign key required in `docs/tutorial/relationship-attributes/define-relationships-attributes.md`. PR [#474](https://github.com/tiangolo/sqlmodel/pull/474) by [@jalvaradosegura](https://github.com/jalvaradosegura).
|
||||||
|
* 📝 Update help SQLModel docs. PR [#548](https://github.com/tiangolo/sqlmodel/pull/548) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* ✏️ Fix typo in internal function name `get_sqlachemy_type()`. PR [#496](https://github.com/tiangolo/sqlmodel/pull/496) by [@cmarqu](https://github.com/cmarqu).
|
* ✏️ Fix typo in internal function name `get_sqlachemy_type()`. PR [#496](https://github.com/tiangolo/sqlmodel/pull/496) by [@cmarqu](https://github.com/cmarqu).
|
||||||
* ⬆ Bump actions/cache from 2 to 3. PR [#497](https://github.com/tiangolo/sqlmodel/pull/497) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
|
||||||
* ✏️ Fix typo in docs. PR [#446](https://github.com/tiangolo/sqlmodel/pull/446) by [@davidbrochart](https://github.com/davidbrochart).
|
* ✏️ Fix typo in docs. PR [#446](https://github.com/tiangolo/sqlmodel/pull/446) by [@davidbrochart](https://github.com/davidbrochart).
|
||||||
* ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.2. PR [#493](https://github.com/tiangolo/sqlmodel/pull/493) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
|
||||||
* ✏️ Fix typo in `docs/tutorial/create-db-and-table.md`. PR [#477](https://github.com/tiangolo/sqlmodel/pull/477) by [@FluffyDietEngine](https://github.com/FluffyDietEngine).
|
* ✏️ Fix typo in `docs/tutorial/create-db-and-table.md`. PR [#477](https://github.com/tiangolo/sqlmodel/pull/477) by [@FluffyDietEngine](https://github.com/FluffyDietEngine).
|
||||||
* ✏️ Fix small typos in docs. PR [#481](https://github.com/tiangolo/sqlmodel/pull/481) by [@micuffaro](https://github.com/micuffaro).
|
* ✏️ Fix small typos in docs. PR [#481](https://github.com/tiangolo/sqlmodel/pull/481) by [@micuffaro](https://github.com/micuffaro).
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#672](https://github.com/tiangolo/sqlmodel/pull/672) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
|
||||||
|
* ⬆ Bump dawidd6/action-download-artifact from 2.24.2 to 2.28.0. PR [#660](https://github.com/tiangolo/sqlmodel/pull/660) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
|
* ✅ Refactor OpenAPI FastAPI tests to simplify updating them later, this moves things around without changes. PR [#671](https://github.com/tiangolo/sqlmodel/pull/671) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* ⬆ Bump actions/checkout from 3 to 4. PR [#670](https://github.com/tiangolo/sqlmodel/pull/670) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
|
* 🔧 Update mypy config, use `strict = true` instead of manual configs. PR [#428](https://github.com/tiangolo/sqlmodel/pull/428) by [@michaeloliverx](https://github.com/michaeloliverx).
|
||||||
|
* ⬆️ Upgrade MkDocs Material. PR [#668](https://github.com/tiangolo/sqlmodel/pull/668) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 🎨 Update docs format and references with pre-commit and Ruff. PR [#667](https://github.com/tiangolo/sqlmodel/pull/667) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 🎨 Run pre-commit on all files and autoformat. PR [#666](https://github.com/tiangolo/sqlmodel/pull/666) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 👷 Move to Ruff and add pre-commit. PR [#661](https://github.com/tiangolo/sqlmodel/pull/661) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 🛠️ Add `CITATION.cff` file for academic citations. PR [#13](https://github.com/tiangolo/sqlmodel/pull/13) by [@sugatoray](https://github.com/sugatoray).
|
||||||
|
* 👷 Update docs deployments to Cloudflare. PR [#630](https://github.com/tiangolo/sqlmodel/pull/630) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 👷♂️ Upgrade CI for docs. PR [#628](https://github.com/tiangolo/sqlmodel/pull/628) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 👷 Update CI debug mode with Tmate. PR [#629](https://github.com/tiangolo/sqlmodel/pull/629) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 👷 Update latest changes token. PR [#616](https://github.com/tiangolo/sqlmodel/pull/616) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* ⬆️ Upgrade analytics. PR [#558](https://github.com/tiangolo/sqlmodel/pull/558) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 🔧 Update new issue chooser to point to GitHub Discussions. PR [#546](https://github.com/tiangolo/sqlmodel/pull/546) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 🔧 Add template for GitHub Discussion questions and update issues template. PR [#544](https://github.com/tiangolo/sqlmodel/pull/544) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* 👷 Refactor CI artifact upload/download for docs previews. PR [#514](https://github.com/tiangolo/sqlmodel/pull/514) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
* ⬆ Bump actions/cache from 2 to 3. PR [#497](https://github.com/tiangolo/sqlmodel/pull/497) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
|
* ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.2. PR [#493](https://github.com/tiangolo/sqlmodel/pull/493) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
* 🔧 Update Smokeshow coverage threshold. PR [#487](https://github.com/tiangolo/sqlmodel/pull/487) by [@tiangolo](https://github.com/tiangolo).
|
* 🔧 Update Smokeshow coverage threshold. PR [#487](https://github.com/tiangolo/sqlmodel/pull/487) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* 👷 Move from Codecov to Smokeshow. PR [#486](https://github.com/tiangolo/sqlmodel/pull/486) by [@tiangolo](https://github.com/tiangolo).
|
* 👷 Move from Codecov to Smokeshow. PR [#486](https://github.com/tiangolo/sqlmodel/pull/486) by [@tiangolo](https://github.com/tiangolo).
|
||||||
* ⬆ Bump actions/setup-python from 2 to 4. PR [#411](https://github.com/tiangolo/sqlmodel/pull/411) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
* ⬆ Bump actions/setup-python from 2 to 4. PR [#411](https://github.com/tiangolo/sqlmodel/pull/411) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||||
@ -28,7 +96,7 @@
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* 🐛 Fix auto detecting and setting `nullable`, allowing overrides in field. PR [#423](https://github.com/tiangolo/sqlmodel/pull/423) by [@JonasKs](https://github.com/JonasKs).
|
* 🐛 Fix auto detecting and setting `nullable`, allowing overrides in field. PR [#423](https://github.com/tiangolo/sqlmodel/pull/423) by [@JonasKs](https://github.com/JonasKs) and [@br-follow](https://github.com/br-follow).
|
||||||
* ♻️ Update `expresion.py`, sync from Jinja2 template, implement `inherit_cache` to solve errors like: `SAWarning: Class SelectOfScalar will not make use of SQL compilation caching`. PR [#422](https://github.com/tiangolo/sqlmodel/pull/422) by [@tiangolo](https://github.com/tiangolo).
|
* ♻️ Update `expresion.py`, sync from Jinja2 template, implement `inherit_cache` to solve errors like: `SAWarning: Class SelectOfScalar will not make use of SQL compilation caching`. PR [#422](https://github.com/tiangolo/sqlmodel/pull/422) by [@tiangolo](https://github.com/tiangolo).
|
||||||
|
|
||||||
### Docs
|
### Docs
|
||||||
|
@ -36,7 +36,7 @@ When we create a new `Hero` instance, we don't set the `id`:
|
|||||||
|
|
||||||
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-26]!}
|
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-26]!}
|
||||||
|
|
||||||
# Code below ommitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -125,7 +125,7 @@ We can verify by creating a session using a `with` block and adding the objects.
|
|||||||
|
|
||||||
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-41]!}
|
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-41]!}
|
||||||
|
|
||||||
# Code below ommitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -198,9 +198,9 @@ INFO Engine COMMIT
|
|||||||
|
|
||||||
// And now our prints
|
// And now our prints
|
||||||
After committing the session
|
After committing the session
|
||||||
Hero 1:
|
Hero 1:
|
||||||
Hero 2:
|
Hero 2:
|
||||||
Hero 3:
|
Hero 3:
|
||||||
|
|
||||||
// What is happening here? 😱
|
// What is happening here? 😱
|
||||||
```
|
```
|
||||||
@ -238,7 +238,7 @@ To confirm and understand how this **automatic expiration and refresh** of data
|
|||||||
|
|
||||||
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-58]!}
|
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-58]!}
|
||||||
|
|
||||||
# Code below ommitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -271,21 +271,21 @@ Let's see how it works:
|
|||||||
```console
|
```console
|
||||||
$ python app.py
|
$ python app.py
|
||||||
|
|
||||||
// Output above ommitted 👆
|
// Output above omitted 👆
|
||||||
|
|
||||||
// After committing, the objects are expired and have no values
|
// After committing, the objects are expired and have no values
|
||||||
After committing the session
|
After committing the session
|
||||||
Hero 1:
|
Hero 1:
|
||||||
Hero 2:
|
Hero 2:
|
||||||
Hero 3:
|
Hero 3:
|
||||||
|
|
||||||
// Now we will access an attribute like the ID, this is the first print
|
// Now we will access an attribute like the ID, this is the first print
|
||||||
After committing the session, show IDs
|
After committing the session, show IDs
|
||||||
|
|
||||||
// Notice that before printing the first ID, the Session makes the Engine go to the database to refresh the data 🤓
|
// Notice that before printing the first ID, the Session makes the Engine go to the database to refresh the data 🤓
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00017s] (1,)
|
INFO Engine [generated in 0.00017s] (1,)
|
||||||
|
|
||||||
@ -293,8 +293,8 @@ INFO Engine [generated in 0.00017s] (1,)
|
|||||||
Hero 1 ID: 1
|
Hero 1 ID: 1
|
||||||
|
|
||||||
// Before the next print, refresh the data for the second object
|
// Before the next print, refresh the data for the second object
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.001245s ago] (2,)
|
INFO Engine [cached since 0.001245s ago] (2,)
|
||||||
|
|
||||||
@ -302,8 +302,8 @@ INFO Engine [cached since 0.001245s ago] (2,)
|
|||||||
Hero 2 ID: 2
|
Hero 2 ID: 2
|
||||||
|
|
||||||
// Before the third print, refresh its data
|
// Before the third print, refresh its data
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.002215s ago] (3,)
|
INFO Engine [cached since 0.002215s ago] (3,)
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ You can do that too with `session.refresh(object)`:
|
|||||||
|
|
||||||
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-67]!}
|
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-67]!}
|
||||||
|
|
||||||
# Code below ommitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -362,23 +362,23 @@ Here's how the output would look like:
|
|||||||
```console
|
```console
|
||||||
$ python app.py
|
$ python app.py
|
||||||
|
|
||||||
// Output above ommitted 👆
|
// Output above omitted 👆
|
||||||
|
|
||||||
// The first refresh
|
// The first refresh
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00024s] (1,)
|
INFO Engine [generated in 0.00024s] (1,)
|
||||||
|
|
||||||
// The second refresh
|
// The second refresh
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.001487s ago] (2,)
|
INFO Engine [cached since 0.001487s ago] (2,)
|
||||||
|
|
||||||
// The third refresh
|
// The third refresh
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.002377s ago] (3,)
|
INFO Engine [cached since 0.002377s ago] (3,)
|
||||||
|
|
||||||
@ -427,7 +427,7 @@ And the output shows again the same data:
|
|||||||
```console
|
```console
|
||||||
$ python app.py
|
$ python app.py
|
||||||
|
|
||||||
// Output above ommitted 👆
|
// Output above omitted 👆
|
||||||
|
|
||||||
// By finishing the with block, the Session is closed, including a rollback of any pending transaction that could have been there and was not committed
|
// By finishing the with block, the Session is closed, including a rollback of any pending transaction that could have been there and was not committed
|
||||||
INFO Engine ROLLBACK
|
INFO Engine ROLLBACK
|
||||||
@ -468,12 +468,12 @@ INFO Engine PRAGMA main.table_info("hero")
|
|||||||
INFO Engine [raw sql] ()
|
INFO Engine [raw sql] ()
|
||||||
INFO Engine PRAGMA temp.table_info("hero")
|
INFO Engine PRAGMA temp.table_info("hero")
|
||||||
INFO Engine [raw sql] ()
|
INFO Engine [raw sql] ()
|
||||||
INFO Engine
|
INFO Engine
|
||||||
CREATE TABLE hero (
|
CREATE TABLE hero (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
secret_name VARCHAR NOT NULL,
|
secret_name VARCHAR NOT NULL,
|
||||||
age INTEGER,
|
age INTEGER,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -497,23 +497,23 @@ INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
|
|||||||
INFO Engine [cached since 0.001483s ago] ('Rusty-Man', 'Tommy Sharp', 48)
|
INFO Engine [cached since 0.001483s ago] ('Rusty-Man', 'Tommy Sharp', 48)
|
||||||
INFO Engine COMMIT
|
INFO Engine COMMIT
|
||||||
After committing the session
|
After committing the session
|
||||||
Hero 1:
|
Hero 1:
|
||||||
Hero 2:
|
Hero 2:
|
||||||
Hero 3:
|
Hero 3:
|
||||||
After committing the session, show IDs
|
After committing the session, show IDs
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00029s] (1,)
|
INFO Engine [generated in 0.00029s] (1,)
|
||||||
Hero 1 ID: 1
|
Hero 1 ID: 1
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.002132s ago] (2,)
|
INFO Engine [cached since 0.002132s ago] (2,)
|
||||||
Hero 2 ID: 2
|
Hero 2 ID: 2
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.003367s ago] (3,)
|
INFO Engine [cached since 0.003367s ago] (3,)
|
||||||
Hero 3 ID: 3
|
Hero 3 ID: 3
|
||||||
@ -521,16 +521,16 @@ After committing the session, show names
|
|||||||
Hero 1 name: Deadpond
|
Hero 1 name: Deadpond
|
||||||
Hero 2 name: Spider-Boy
|
Hero 2 name: Spider-Boy
|
||||||
Hero 3 name: Rusty-Man
|
Hero 3 name: Rusty-Man
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00025s] (1,)
|
INFO Engine [generated in 0.00025s] (1,)
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.001583s ago] (2,)
|
INFO Engine [cached since 0.001583s ago] (2,)
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.002722s ago] (3,)
|
INFO Engine [cached since 0.002722s ago] (3,)
|
||||||
After refreshing the heroes
|
After refreshing the heroes
|
||||||
|
@ -168,7 +168,7 @@ Let's assume that now the file structure is:
|
|||||||
|
|
||||||
### Circular Imports and Type Annotations
|
### Circular Imports and Type Annotations
|
||||||
|
|
||||||
The problem with circular imports is that Python can't resolve them at <abbr title="While it is executing the program, as oposed to the code as just text in a file stored on disk.">*runtime*</abbr>.
|
The problem with circular imports is that Python can't resolve them at <abbr title="While it is executing the program, as opposed to the code as just text in a file stored on disk.">*runtime*</abbr>.
|
||||||
|
|
||||||
But when using Python **type annotations** it's very common to need to declare the type of some variables with classes imported from other files.
|
But when using Python **type annotations** it's very common to need to declare the type of some variables with classes imported from other files.
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ As the `Hero` class model now has a field (column, attribute) `team_id`, we can
|
|||||||
|
|
||||||
We haven't committed this hero to the database yet, but there are already a couple of things to pay **attention** to.
|
We haven't committed this hero to the database yet, but there are already a couple of things to pay **attention** to.
|
||||||
|
|
||||||
If the database already had some teams, we wouldn't even know **what is the ID** that is going to be automatically assigned to each team by the database, for example, we couldn't just guess `1` or `2`.
|
If the database already had some teams, we wouldn't even know **what is the ID** that is going to be automatically assigned to each team by the database, for example, we couldn't just guess `1` or `2`.
|
||||||
|
|
||||||
But once the team is created and committed to the database, we can access the object's `id` field to get that ID.
|
But once the team is created and committed to the database, we can access the object's `id` field to get that ID.
|
||||||
|
|
||||||
@ -171,8 +171,8 @@ That line alone would generate an output of:
|
|||||||
|
|
||||||
```
|
```
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [generated in 0.00025s] (2,)
|
INFO Engine [generated in 0.00025s] (2,)
|
||||||
```
|
```
|
||||||
@ -199,8 +199,8 @@ Let's now create two more heroes:
|
|||||||
When creating `hero_rusty_man`, we are accessing `team_preventers.id`, so that will also trigger a refresh of its data, generating an output of:
|
When creating `hero_rusty_man`, we are accessing `team_preventers.id`, so that will also trigger a refresh of its data, generating an output of:
|
||||||
|
|
||||||
```
|
```
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [cached since 0.001795s ago] (1,)
|
INFO Engine [cached since 0.001795s ago] (1,)
|
||||||
```
|
```
|
||||||
@ -256,18 +256,18 @@ $ python app.py
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// Refresh the first hero
|
// Refresh the first hero
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00021s] (1,)
|
INFO Engine [generated in 0.00021s] (1,)
|
||||||
// Refresh the second hero
|
// Refresh the second hero
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.001575s ago] (2,)
|
INFO Engine [cached since 0.001575s ago] (2,)
|
||||||
// Refresh the third hero
|
// Refresh the third hero
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.002518s ago] (3,)
|
INFO Engine [cached since 0.002518s ago] (3,)
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ This is the same model we have been using up to now, we are just adding the new
|
|||||||
|
|
||||||
Most of that should look familiar:
|
Most of that should look familiar:
|
||||||
|
|
||||||
The column will be named `team_id`. It will be an integer, and it could be `NULL` in the database (or `None` in Python), becase there could be some heroes that don't belong to any team.
|
The column will be named `team_id`. It will be an integer, and it could be `NULL` in the database (or `None` in Python), because there could be some heroes that don't belong to any team.
|
||||||
|
|
||||||
We add a default of `None` to the `Field()` so we don't have to explicitly pass `team_id=None` when creating a hero.
|
We add a default of `None` to the `Field()` so we don't have to explicitly pass `team_id=None` when creating a hero.
|
||||||
|
|
||||||
@ -191,24 +191,24 @@ INFO Engine PRAGMA temp.table_info("hero")
|
|||||||
INFO Engine [raw sql] ()
|
INFO Engine [raw sql] ()
|
||||||
|
|
||||||
// Create the tables
|
// Create the tables
|
||||||
INFO Engine
|
INFO Engine
|
||||||
CREATE TABLE team (
|
CREATE TABLE team (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
headquarters VARCHAR NOT NULL,
|
headquarters VARCHAR NOT NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
INFO Engine [no key 0.00010s] ()
|
INFO Engine [no key 0.00010s] ()
|
||||||
INFO Engine
|
INFO Engine
|
||||||
CREATE TABLE hero (
|
CREATE TABLE hero (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
secret_name VARCHAR NOT NULL,
|
secret_name VARCHAR NOT NULL,
|
||||||
age INTEGER,
|
age INTEGER,
|
||||||
team_id INTEGER,
|
team_id INTEGER,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
FOREIGN KEY(team_id) REFERENCES team (id)
|
FOREIGN KEY(team_id) REFERENCES team (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -229,9 +229,9 @@ So, the first SQL could also be written as:
|
|||||||
|
|
||||||
```SQL
|
```SQL
|
||||||
CREATE TABLE team (
|
CREATE TABLE team (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
headquarters TEXT NOT NULL,
|
headquarters TEXT NOT NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
@ -240,12 +240,12 @@ And the second table could be written as:
|
|||||||
|
|
||||||
```SQL hl_lines="8"
|
```SQL hl_lines="8"
|
||||||
CREATE TABLE hero (
|
CREATE TABLE hero (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
secret_name TEXT NOT NULL,
|
secret_name TEXT NOT NULL,
|
||||||
age INTEGER,
|
age INTEGER,
|
||||||
team_id INTEGER,
|
team_id INTEGER,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
FOREIGN KEY(team_id) REFERENCES team (id)
|
FOREIGN KEY(team_id) REFERENCES team (id)
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
@ -203,8 +203,8 @@ $ python app.py
|
|||||||
// Previous output omitted 😉
|
// Previous output omitted 😉
|
||||||
|
|
||||||
// Get the heroes with their teams
|
// Get the heroes with their teams
|
||||||
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
||||||
FROM hero, team
|
FROM hero, team
|
||||||
WHERE hero.team_id = team.id
|
WHERE hero.team_id = team.id
|
||||||
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine [no key 0.00015s] ()
|
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine [no key 0.00015s] ()
|
||||||
|
|
||||||
@ -323,7 +323,7 @@ $ python app.py
|
|||||||
// Previous output omitted 😉
|
// Previous output omitted 😉
|
||||||
|
|
||||||
// Select using a JOIN with automatic ON
|
// Select using a JOIN with automatic ON
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
||||||
FROM hero JOIN team ON team.id = hero.team_id
|
FROM hero JOIN team ON team.id = hero.team_id
|
||||||
INFO Engine [no key 0.00032s] ()
|
INFO Engine [no key 0.00032s] ()
|
||||||
|
|
||||||
@ -458,7 +458,7 @@ $ python app.py
|
|||||||
// Previous output omitted 😉
|
// Previous output omitted 😉
|
||||||
|
|
||||||
// SELECT using LEFT OUTER JOIN
|
// SELECT using LEFT OUTER JOIN
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
||||||
FROM hero LEFT OUTER JOIN team ON team.id = hero.team_id
|
FROM hero LEFT OUTER JOIN team ON team.id = hero.team_id
|
||||||
|
|
||||||
INFO Engine [no key 0.00051s] ()
|
INFO Engine [no key 0.00051s] ()
|
||||||
@ -522,9 +522,9 @@ If we run that, it would output:
|
|||||||
$ python app.py
|
$ python app.py
|
||||||
|
|
||||||
// Select only the hero data
|
// Select only the hero data
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
||||||
// But still join with the team table
|
// But still join with the team table
|
||||||
FROM hero JOIN team ON team.id = hero.team_id
|
FROM hero JOIN team ON team.id = hero.team_id
|
||||||
// And filter with WHERE to get only the Preventers
|
// And filter with WHERE to get only the Preventers
|
||||||
WHERE team.name = ?
|
WHERE team.name = ?
|
||||||
INFO Engine [no key 0.00066s] ('Preventers',)
|
INFO Engine [no key 0.00066s] ('Preventers',)
|
||||||
@ -564,9 +564,9 @@ And if we run that, it will output:
|
|||||||
$ python app.py
|
$ python app.py
|
||||||
|
|
||||||
// Select the hero and the team data
|
// Select the hero and the team data
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
|
||||||
// Join the hero with the team table
|
// Join the hero with the team table
|
||||||
FROM hero JOIN team ON team.id = hero.team_id
|
FROM hero JOIN team ON team.id = hero.team_id
|
||||||
// Filter with WHERE to get only Preventers
|
// Filter with WHERE to get only Preventers
|
||||||
WHERE team.name = ?
|
WHERE team.name = ?
|
||||||
INFO Engine [no key 0.00018s] ('Preventers',)
|
INFO Engine [no key 0.00018s] ('Preventers',)
|
||||||
|
@ -56,7 +56,7 @@ We can simply set the `team_id` to `None`, and now it doesn't have a connection
|
|||||||
# Code above omitted 👆
|
# Code above omitted 👆
|
||||||
|
|
||||||
{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:31-32]!}
|
{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:31-32]!}
|
||||||
|
|
||||||
# Previous code here omitted 👈
|
# Previous code here omitted 👈
|
||||||
|
|
||||||
{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:68-72]!}
|
{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:68-72]!}
|
||||||
@ -94,8 +94,8 @@ INFO Engine COMMIT
|
|||||||
// Automatically start a new transaction
|
// Automatically start a new transaction
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
// Refresh the hero
|
// Refresh the hero
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.1661s ago] (3,)
|
INFO Engine [cached since 0.1661s ago] (3,)
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ Doing it is just like updating any other field:
|
|||||||
# Code above omitted 👆
|
# Code above omitted 👆
|
||||||
|
|
||||||
{!./docs_src/tutorial/connect/update/tutorial001.py[ln:31-32]!}
|
{!./docs_src/tutorial/connect/update/tutorial001.py[ln:31-32]!}
|
||||||
|
|
||||||
# Previous code here omitted 👈
|
# Previous code here omitted 👈
|
||||||
|
|
||||||
{!./docs_src/tutorial/connect/update/tutorial001.py[ln:62-66]!}
|
{!./docs_src/tutorial/connect/update/tutorial001.py[ln:62-66]!}
|
||||||
@ -94,8 +94,8 @@ INFO Engine COMMIT
|
|||||||
// Automatically start a new transaction
|
// Automatically start a new transaction
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
// Refresh the hero data
|
// Refresh the hero data
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.08837s ago] (3,)
|
INFO Engine [cached since 0.08837s ago] (3,)
|
||||||
|
|
||||||
|
@ -164,6 +164,6 @@ Of course, you can also go and take a full SQL course or read a book about SQL,
|
|||||||
|
|
||||||
We saw how to interact with SQLite databases in files using **DB Browser for SQLite** in a visual user interface.
|
We saw how to interact with SQLite databases in files using **DB Browser for SQLite** in a visual user interface.
|
||||||
|
|
||||||
We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database is looking correclty, to debug, etc.
|
We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database is looking correctly, to debug, etc.
|
||||||
|
|
||||||
In the next chapters we will start using **SQLModel** to interact with the database, and we will continue to use **DB Browser for SQLite** at the same time to look at the database underneath. 🔍
|
In the next chapters we will start using **SQLModel** to interact with the database, and we will continue to use **DB Browser for SQLite** at the same time to look at the database underneath. 🔍
|
||||||
|
@ -220,7 +220,7 @@ Each supported database has it's own URL type. For example, for **SQLite** it is
|
|||||||
* `sqlite:///databases/local/application.db`
|
* `sqlite:///databases/local/application.db`
|
||||||
* `sqlite:///db.sqlite`
|
* `sqlite:///db.sqlite`
|
||||||
|
|
||||||
For SQLAlchemy, there's also a special one, which is a database all *in memory*, this means that it is deleted after the program terminates, and it's also very fast:
|
SQLite supports a special database that lives all *in memory*. Hence, it's very fast, but be careful, the database gets deleted after the program terminates. You can specify this in-memory database by using just two slash characters (`//`) and no file name:
|
||||||
|
|
||||||
* `sqlite://`
|
* `sqlite://`
|
||||||
|
|
||||||
@ -422,15 +422,15 @@ INFO Engine PRAGMA main.table_info("hero")
|
|||||||
INFO Engine [raw sql] ()
|
INFO Engine [raw sql] ()
|
||||||
INFO Engine PRAGMA temp.table_info("hero")
|
INFO Engine PRAGMA temp.table_info("hero")
|
||||||
INFO Engine [raw sql] ()
|
INFO Engine [raw sql] ()
|
||||||
INFO Engine
|
INFO Engine
|
||||||
|
|
||||||
// Finally, the glorious SQL to create the table ✨
|
// Finally, the glorious SQL to create the table ✨
|
||||||
|
|
||||||
CREATE TABLE hero (
|
CREATE TABLE hero (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
secret_name VARCHAR NOT NULL,
|
secret_name VARCHAR NOT NULL,
|
||||||
age INTEGER,
|
age INTEGER,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -108,8 +108,8 @@ $ python app.py
|
|||||||
|
|
||||||
// The SELECT with WHERE
|
// The SELECT with WHERE
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00011s] ('Spider-Youngster',)
|
INFO Engine [no key 0.00011s] ('Spider-Youngster',)
|
||||||
|
|
||||||
@ -272,8 +272,8 @@ $ python app.py
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// SQL to search for the hero
|
// SQL to search for the hero
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00013s] ('Spider-Youngster',)
|
INFO Engine [no key 0.00013s] ('Spider-Youngster',)
|
||||||
```
|
```
|
||||||
|
@ -42,7 +42,7 @@ We want to allow clients to set different `offset` and `limit` values.
|
|||||||
|
|
||||||
But we don't want them to be able to set a `limit` of something like `9999`, that's over `9000`! 😱
|
But we don't want them to be able to set a `limit` of something like `9999`, that's over `9000`! 😱
|
||||||
|
|
||||||
So, to prevent it, we add additional validation to the `limit` query parameter, declaring that it has to be **l**ess **t**han or **e**qual to `100` with `lte=100`.
|
So, to prevent it, we add additional validation to the `limit` query parameter, declaring that it has to be **l**ess than or **e**qual to `100` with `le=100`.
|
||||||
|
|
||||||
This way, a client can decide to take fewer heroes if they want, but not more.
|
This way, a client can decide to take fewer heroes if they want, but not more.
|
||||||
|
|
||||||
|
@ -53,11 +53,11 @@ Here's the weird thing, the `id` currently seems also "optional". 🤔
|
|||||||
|
|
||||||
This is because in our **SQLModel** class we declare the `id` with `Optional[int]`, because it could be `None` in memory until we save it in the database and we finally get the actual ID.
|
This is because in our **SQLModel** class we declare the `id` with `Optional[int]`, because it could be `None` in memory until we save it in the database and we finally get the actual ID.
|
||||||
|
|
||||||
But in the responses, we would always send a model from the database, and it would **always have an ID**. So the `id` in the responses could be declared as required too.
|
But in the responses, we always send a model from the database, so it **always has an ID**. So the `id` in the responses can be declared as required.
|
||||||
|
|
||||||
This would mean that our application is making the compromise with the clients that if it sends a hero, it would for sure have an `id` with a value, it would not be `None`.
|
This means that our application is making the promise to the clients that if it sends a hero, it will for sure have an `id` with a value, it will not be `None`.
|
||||||
|
|
||||||
### Why Is it Important to Compromise with the Responses
|
### Why Is it Important to Have a Contract for Responses
|
||||||
|
|
||||||
The ultimate goal of an API is for some **clients to use it**.
|
The ultimate goal of an API is for some **clients to use it**.
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, s
|
|||||||
|
|
||||||
# Code here omitted 👈
|
# Code here omitted 👈
|
||||||
|
|
||||||
{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:159-164]!}
|
{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:158-163]!}
|
||||||
|
|
||||||
# Code below omitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
@ -234,7 +234,7 @@ In the case of the hero, this tells FastAPI to extract the `team` too. And in th
|
|||||||
|
|
||||||
# Code here omitted 👈
|
# Code here omitted 👈
|
||||||
|
|
||||||
{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:168-173]!}
|
{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:167-172]!}
|
||||||
|
|
||||||
# Code below omitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
@ -177,7 +177,7 @@ And then we remove the previous `with` block with the old **session**.
|
|||||||
|
|
||||||
# Code here omitted 👈
|
# Code here omitted 👈
|
||||||
|
|
||||||
{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:55-107]!}
|
{!./docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py[ln:55-106]!}
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -92,7 +92,7 @@ These are equivalent and very similar to the **path operations** for the **heroe
|
|||||||
```Python hl_lines="3-9 12-20 23-28 31-47 50-57"
|
```Python hl_lines="3-9 12-20 23-28 31-47 50-57"
|
||||||
# Code above omitted 👆
|
# Code above omitted 👆
|
||||||
|
|
||||||
{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:139-193]!}
|
{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:138-192]!}
|
||||||
|
|
||||||
# Code below omitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
@ -82,7 +82,7 @@ But now, we need to deal with a bit of logistics and details we are not paying a
|
|||||||
|
|
||||||
This test looks fine, but there's a problem.
|
This test looks fine, but there's a problem.
|
||||||
|
|
||||||
If we run it, it will use the same **production database** that we are using to store our very important **heroes**, and we will end up adding unnecesary data to it, or even worse, in future tests we could end up removing production data.
|
If we run it, it will use the same **production database** that we are using to store our very important **heroes**, and we will end up adding unnecessary data to it, or even worse, in future tests we could end up removing production data.
|
||||||
|
|
||||||
So, we should use an independent **testing database**, just for the tests.
|
So, we should use an independent **testing database**, just for the tests.
|
||||||
|
|
||||||
|
@ -64,15 +64,13 @@ $ cd sqlmodel-tutorial
|
|||||||
|
|
||||||
Make sure you have an officially supported version of Python.
|
Make sure you have an officially supported version of Python.
|
||||||
|
|
||||||
Currently it is **Python 3.6** and above (Python 3.5 was already deprecated).
|
|
||||||
|
|
||||||
You can check which version you have with:
|
You can check which version you have with:
|
||||||
|
|
||||||
<div class="termy">
|
<div class="termy">
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ python3 --version
|
$ python3 --version
|
||||||
Python 3.6.9
|
Python 3.11
|
||||||
```
|
```
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -84,8 +82,6 @@ You might want to try with the specific versions, for example with:
|
|||||||
* `python3.10`
|
* `python3.10`
|
||||||
* `python3.9`
|
* `python3.9`
|
||||||
* `python3.8`
|
* `python3.8`
|
||||||
* `python3.7`
|
|
||||||
* `python3.6`
|
|
||||||
|
|
||||||
The code would look like this:
|
The code would look like this:
|
||||||
|
|
||||||
@ -136,10 +132,10 @@ Here are the commands you could use:
|
|||||||
<div class="termy">
|
<div class="termy">
|
||||||
|
|
||||||
```console
|
```console
|
||||||
// Remember that you might need to use python3.9 or similar 💡
|
// Remember that you might need to use python3.9 or similar 💡
|
||||||
// Create the virtual environment using the module "venv"
|
// Create the virtual environment using the module "venv"
|
||||||
$ python3 -m venv env
|
$ python3 -m venv env
|
||||||
// ...here it creates the virtual enviroment in the directory "env"
|
// ...here it creates the virtual environment in the directory "env"
|
||||||
// Activate the virtual environment
|
// Activate the virtual environment
|
||||||
$ source ./env/bin/activate
|
$ source ./env/bin/activate
|
||||||
// Verify that the virtual environment is active
|
// Verify that the virtual environment is active
|
||||||
@ -161,7 +157,7 @@ Here are the commands you could use:
|
|||||||
```console
|
```console
|
||||||
// Create the virtual environment using the module "venv"
|
// Create the virtual environment using the module "venv"
|
||||||
# >$ python3 -m venv env
|
# >$ python3 -m venv env
|
||||||
// ...here it creates the virtual enviroment in the directory "env"
|
// ...here it creates the virtual environment in the directory "env"
|
||||||
// Activate the virtual environment
|
// Activate the virtual environment
|
||||||
# >$ .\env\Scripts\Activate.ps1
|
# >$ .\env\Scripts\Activate.ps1
|
||||||
// Verify that the virtual environment is active
|
// Verify that the virtual environment is active
|
||||||
|
@ -342,10 +342,10 @@ $ python app.py
|
|||||||
|
|
||||||
// Create the table
|
// Create the table
|
||||||
CREATE TABLE hero (
|
CREATE TABLE hero (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
secret_name VARCHAR NOT NULL,
|
secret_name VARCHAR NOT NULL,
|
||||||
age INTEGER,
|
age INTEGER,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -353,8 +353,8 @@ CREATE TABLE hero (
|
|||||||
CREATE INDEX ix_hero_name ON hero (name)
|
CREATE INDEX ix_hero_name ON hero (name)
|
||||||
|
|
||||||
// The SELECT with WHERE looks the same
|
// The SELECT with WHERE looks the same
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00014s] ('Deadpond',)
|
INFO Engine [no key 0.00014s] ('Deadpond',)
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ The first step is to import the `Session` class:
|
|||||||
```Python hl_lines="3"
|
```Python hl_lines="3"
|
||||||
{!./docs_src/tutorial/insert/tutorial001.py[ln:1-3]!}
|
{!./docs_src/tutorial/insert/tutorial001.py[ln:1-3]!}
|
||||||
|
|
||||||
# Code below ommitted 👇
|
# Code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -93,7 +93,7 @@ $ python app.py
|
|||||||
// Previous output omitted 🙈
|
// Previous output omitted 🙈
|
||||||
|
|
||||||
// Select with LIMIT
|
// Select with LIMIT
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
INFO Engine [no key 0.00014s] (3, 0)
|
INFO Engine [no key 0.00014s] (3, 0)
|
||||||
@ -165,7 +165,7 @@ $python app.py
|
|||||||
// Previous output omitted 🙈
|
// Previous output omitted 🙈
|
||||||
|
|
||||||
// Select with LIMIT and OFFSET
|
// Select with LIMIT and OFFSET
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
INFO Engine [no key 0.00020s] (3, 3)
|
INFO Engine [no key 0.00020s] (3, 3)
|
||||||
@ -221,7 +221,7 @@ $ python app.py
|
|||||||
// Previous output omitted 🙈
|
// Previous output omitted 🙈
|
||||||
|
|
||||||
// Select last batch with LIMIT and OFFSET
|
// Select last batch with LIMIT and OFFSET
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
INFO Engine [no key 0.00038s] (3, 6)
|
INFO Engine [no key 0.00038s] (3, 6)
|
||||||
@ -241,7 +241,7 @@ You probably noticed the new SQL keywords `LIMIT` and `OFFSET`.
|
|||||||
You can use them in SQL, at the end of the other parts:
|
You can use them in SQL, at the end of the other parts:
|
||||||
|
|
||||||
```SQL
|
```SQL
|
||||||
SELECT id, name, secret_name, age
|
SELECT id, name, secret_name, age
|
||||||
FROM hero
|
FROM hero
|
||||||
LIMIT 3 OFFSET 6
|
LIMIT 3 OFFSET 6
|
||||||
```
|
```
|
||||||
@ -271,11 +271,11 @@ Of course, you can also combine `.limit()` and `.offset()` with `.where()` and o
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Run the Program with Limit and Where on the Command Line
|
## Run the Program with Limit, Offset, and Where on the Command Line
|
||||||
|
|
||||||
If we run it on the command line, it will find all the heroes in the database with an age above 32. That would normally be 4 heroes.
|
If we run it on the command line, it will find all the heroes in the database with an age above 32. That would normally be 4 heroes.
|
||||||
|
|
||||||
But we are limiting the results to only get the first 3:
|
But we are starting to include after an offset of 1 (so we don't count the first one), and we are limiting the results to only get the first 2 after that:
|
||||||
|
|
||||||
<div class="termy">
|
<div class="termy">
|
||||||
|
|
||||||
@ -284,18 +284,17 @@ $ python app.py
|
|||||||
|
|
||||||
// Previous output omitted 🙈
|
// Previous output omitted 🙈
|
||||||
|
|
||||||
// Select with WHERE and LIMIT
|
// Select with WHERE and LIMIT and OFFSET
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.age > ?
|
WHERE hero.age > ?
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
INFO Engine [no key 0.00022s] (32, 3, 0)
|
INFO Engine [no key 0.00022s] (32, 2, 1)
|
||||||
|
|
||||||
// Print the heroes received, only 3
|
// Print the heroes received, only 2
|
||||||
[
|
[
|
||||||
Hero(age=35, secret_name='Trevor Challa', id=5, name='Black Lion'),
|
Hero(age=36, id=6, name='Dr. Weird', secret_name='Steve Weird'),
|
||||||
Hero(age=36, secret_name='Steve Weird', id=6, name='Dr. Weird'),
|
Hero(age=48, id=3, name='Rusty-Man', secret_name='Tommy Sharp')
|
||||||
Hero(age=48, secret_name='Tommy Sharp', id=3, name='Rusty-Man')
|
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -122,16 +122,16 @@ INFO Engine COMMIT
|
|||||||
// Automatically start a new transaction
|
// Automatically start a new transaction
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
// Refresh the data
|
// Refresh the data
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00019s] (1,)
|
INFO Engine [generated in 0.00019s] (1,)
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.001959s ago] (2,)
|
INFO Engine [cached since 0.001959s ago] (2,)
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.003215s ago] (3,)
|
INFO Engine [cached since 0.003215s ago] (3,)
|
||||||
|
|
||||||
@ -139,8 +139,8 @@ INFO Engine [cached since 0.003215s ago] (3,)
|
|||||||
Deadpond: name='Deadpond' age=None id=1 secret_name='Dive Wilson'
|
Deadpond: name='Deadpond' age=None id=1 secret_name='Dive Wilson'
|
||||||
|
|
||||||
// Accessing the .team attribute triggers a refresh
|
// Accessing the .team attribute triggers a refresh
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team, heroteamlink
|
FROM team, heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
||||||
INFO Engine [generated in 0.00025s] (1,)
|
INFO Engine [generated in 0.00025s] (1,)
|
||||||
|
|
||||||
@ -151,8 +151,8 @@ Deadpond teams: [Team(id=1, name='Z-Force', headquarters='Sister Margaret’s Ba
|
|||||||
Rusty-Man: name='Rusty-Man' age=48 id=2 secret_name='Tommy Sharp'
|
Rusty-Man: name='Rusty-Man' age=48 id=2 secret_name='Tommy Sharp'
|
||||||
|
|
||||||
// Accessing the .team attribute triggers a refresh
|
// Accessing the .team attribute triggers a refresh
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team, heroteamlink
|
FROM team, heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.001716s ago] (2,)
|
INFO Engine [cached since 0.001716s ago] (2,)
|
||||||
|
|
||||||
@ -163,8 +163,8 @@ Rusty-Man Teams: [Team(id=2, name='Preventers', headquarters='Sharp Tower')]
|
|||||||
Spider-Boy: name='Spider-Boy' age=None id=3 secret_name='Pedro Parqueador'
|
Spider-Boy: name='Spider-Boy' age=None id=3 secret_name='Pedro Parqueador'
|
||||||
|
|
||||||
// Accessing the .team attribute triggers a refresh
|
// Accessing the .team attribute triggers a refresh
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team, heroteamlink
|
FROM team, heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.002739s ago] (3,)
|
INFO Engine [cached since 0.002739s ago] (3,)
|
||||||
|
|
||||||
@ -179,4 +179,4 @@ INFO Engine ROLLBACK
|
|||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
After setting up the model link, using it with **relationship attributes** is fairly straighforward, just Python objects. ✨
|
After setting up the model link, using it with **relationship attributes** is fairly straightforward, just Python objects. ✨
|
||||||
|
@ -151,35 +151,35 @@ $ python app.py
|
|||||||
|
|
||||||
// Boilerplate omitted 😉
|
// Boilerplate omitted 😉
|
||||||
|
|
||||||
INFO Engine
|
INFO Engine
|
||||||
CREATE TABLE team (
|
CREATE TABLE team (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
headquarters VARCHAR NOT NULL,
|
headquarters VARCHAR NOT NULL,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
INFO Engine [no key 0.00033s] ()
|
INFO Engine [no key 0.00033s] ()
|
||||||
INFO Engine
|
INFO Engine
|
||||||
CREATE TABLE hero (
|
CREATE TABLE hero (
|
||||||
id INTEGER,
|
id INTEGER,
|
||||||
name VARCHAR NOT NULL,
|
name VARCHAR NOT NULL,
|
||||||
secret_name VARCHAR NOT NULL,
|
secret_name VARCHAR NOT NULL,
|
||||||
age INTEGER,
|
age INTEGER,
|
||||||
PRIMARY KEY (id)
|
PRIMARY KEY (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
INFO Engine [no key 0.00016s] ()
|
INFO Engine [no key 0.00016s] ()
|
||||||
INFO Engine
|
INFO Engine
|
||||||
|
|
||||||
// Our shinny new link table ✨
|
// Our shinny new link table ✨
|
||||||
CREATE TABLE heroteamlink (
|
CREATE TABLE heroteamlink (
|
||||||
team_id INTEGER,
|
team_id INTEGER,
|
||||||
hero_id INTEGER,
|
hero_id INTEGER,
|
||||||
PRIMARY KEY (team_id, hero_id),
|
PRIMARY KEY (team_id, hero_id),
|
||||||
FOREIGN KEY(team_id) REFERENCES team (id),
|
FOREIGN KEY(team_id) REFERENCES team (id),
|
||||||
FOREIGN KEY(hero_id) REFERENCES hero (id)
|
FOREIGN KEY(hero_id) REFERENCES hero (id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -165,16 +165,16 @@ INFO Engine COMMIT
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// Automatically fetch the data on attribute access
|
// Automatically fetch the data on attribute access
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [generated in 0.00028s] (1,)
|
INFO Engine [generated in 0.00028s] (1,)
|
||||||
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
||||||
FROM heroteamlink
|
FROM heroteamlink
|
||||||
WHERE ? = heroteamlink.team_id
|
WHERE ? = heroteamlink.team_id
|
||||||
INFO Engine [generated in 0.00026s] (1,)
|
INFO Engine [generated in 0.00026s] (1,)
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00024s] (1,)
|
INFO Engine [generated in 0.00024s] (1,)
|
||||||
|
|
||||||
@ -182,12 +182,12 @@ INFO Engine [generated in 0.00024s] (1,)
|
|||||||
Z-Force hero: name='Deadpond' age=None id=1 secret_name='Dive Wilson' is training: False
|
Z-Force hero: name='Deadpond' age=None id=1 secret_name='Dive Wilson' is training: False
|
||||||
|
|
||||||
// Automatically fetch the data on attribute access
|
// Automatically fetch the data on attribute access
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [cached since 0.008822s ago] (2,)
|
INFO Engine [cached since 0.008822s ago] (2,)
|
||||||
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
||||||
FROM heroteamlink
|
FROM heroteamlink
|
||||||
WHERE ? = heroteamlink.team_id
|
WHERE ? = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.005778s ago] (2,)
|
INFO Engine [cached since 0.005778s ago] (2,)
|
||||||
|
|
||||||
@ -195,8 +195,8 @@ INFO Engine [cached since 0.005778s ago] (2,)
|
|||||||
Preventers hero: name='Deadpond' age=None id=1 secret_name='Dive Wilson' is training: True
|
Preventers hero: name='Deadpond' age=None id=1 secret_name='Dive Wilson' is training: True
|
||||||
|
|
||||||
// Automatically fetch the data on attribute access
|
// Automatically fetch the data on attribute access
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.004196s ago] (2,)
|
INFO Engine [cached since 0.004196s ago] (2,)
|
||||||
|
|
||||||
@ -204,8 +204,8 @@ INFO Engine [cached since 0.004196s ago] (2,)
|
|||||||
Preventers hero: name='Spider-Boy' age=None id=2 secret_name='Pedro Parqueador' is training: True
|
Preventers hero: name='Spider-Boy' age=None id=2 secret_name='Pedro Parqueador' is training: True
|
||||||
|
|
||||||
// Automatically fetch the data on attribute access
|
// Automatically fetch the data on attribute access
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.006005s ago] (3,)
|
INFO Engine [cached since 0.006005s ago] (3,)
|
||||||
|
|
||||||
@ -253,14 +253,14 @@ $ python app.py
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// Select the hero
|
// Select the hero
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00014s] ('Spider-Boy',)
|
INFO Engine [no key 0.00014s] ('Spider-Boy',)
|
||||||
|
|
||||||
// Select the team
|
// Select the team
|
||||||
INFO Engine SELECT team.id, team.name, team.headquarters
|
INFO Engine SELECT team.id, team.name, team.headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.name = ?
|
WHERE team.name = ?
|
||||||
INFO Engine [no key 0.00012s] ('Z-Force',)
|
INFO Engine [no key 0.00012s] ('Z-Force',)
|
||||||
|
|
||||||
@ -269,18 +269,18 @@ INFO Engine INSERT INTO heroteamlink (team_id, hero_id, is_training) VALUES (?,
|
|||||||
INFO Engine [generated in 0.00023s] (1, 2, 1)
|
INFO Engine [generated in 0.00023s] (1, 2, 1)
|
||||||
|
|
||||||
// Automatically refresh the data on attribute access
|
// Automatically refresh the data on attribute access
|
||||||
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
||||||
FROM heroteamlink
|
FROM heroteamlink
|
||||||
WHERE ? = heroteamlink.team_id
|
WHERE ? = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.01514s ago] (1,)
|
INFO Engine [cached since 0.01514s ago] (1,)
|
||||||
INFO Engine COMMIT
|
INFO Engine COMMIT
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.08953s ago] (2,)
|
INFO Engine [cached since 0.08953s ago] (2,)
|
||||||
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
||||||
FROM heroteamlink
|
FROM heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id
|
WHERE ? = heroteamlink.hero_id
|
||||||
INFO Engine [generated in 0.00018s] (2,)
|
INFO Engine [generated in 0.00018s] (2,)
|
||||||
|
|
||||||
@ -291,18 +291,18 @@ Updated Spider-Boy's Teams: [
|
|||||||
]
|
]
|
||||||
|
|
||||||
// Automatically refresh team data on attribute access
|
// Automatically refresh team data on attribute access
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [cached since 0.1084s ago] (1,)
|
INFO Engine [cached since 0.1084s ago] (1,)
|
||||||
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
||||||
FROM heroteamlink
|
FROM heroteamlink
|
||||||
WHERE ? = heroteamlink.team_id
|
WHERE ? = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.1054s ago] (1,)
|
INFO Engine [cached since 0.1054s ago] (1,)
|
||||||
|
|
||||||
// Print team hero links
|
// Print team hero links
|
||||||
Z-Force heroes: [
|
Z-Force heroes: [
|
||||||
HeroTeamLink(team_id=1, is_training=False, hero_id=1),
|
HeroTeamLink(team_id=1, is_training=False, hero_id=1),
|
||||||
HeroTeamLink(team_id=1, is_training=True, hero_id=2)
|
HeroTeamLink(team_id=1, is_training=True, hero_id=2)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -350,8 +350,8 @@ $ python app.py
|
|||||||
// Previous output omitted 🙈
|
// Previous output omitted 🙈
|
||||||
|
|
||||||
// Automatically fetch team data on attribute access
|
// Automatically fetch team data on attribute access
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [generated in 0.00015s] (2,)
|
INFO Engine [generated in 0.00015s] (2,)
|
||||||
|
|
||||||
@ -366,16 +366,16 @@ INFO Engine COMMIT
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// Automatically fetch data on attribute access
|
// Automatically fetch data on attribute access
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.2004s ago] (2,)
|
INFO Engine [cached since 0.2004s ago] (2,)
|
||||||
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
INFO Engine SELECT heroteamlink.team_id AS heroteamlink_team_id, heroteamlink.hero_id AS heroteamlink_hero_id, heroteamlink.is_training AS heroteamlink_is_training
|
||||||
FROM heroteamlink
|
FROM heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id
|
WHERE ? = heroteamlink.hero_id
|
||||||
INFO Engine [cached since 0.1005s ago] (2,)
|
INFO Engine [cached since 0.1005s ago] (2,)
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [cached since 0.09707s ago] (2,)
|
INFO Engine [cached since 0.09707s ago] (2,)
|
||||||
|
|
||||||
@ -383,8 +383,8 @@ INFO Engine [cached since 0.09707s ago] (2,)
|
|||||||
Spider-Boy team: headquarters='Sharp Tower' id=2 name='Preventers' is training: False
|
Spider-Boy team: headquarters='Sharp Tower' id=2 name='Preventers' is training: False
|
||||||
|
|
||||||
// Automatically fetch data on attribute access
|
// Automatically fetch data on attribute access
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [cached since 0.2097s ago] (1,)
|
INFO Engine [cached since 0.2097s ago] (1,)
|
||||||
|
|
||||||
|
@ -115,12 +115,12 @@ INFO Engine COMMIT
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// Automatically refresh the data while accessing the attribute .teams
|
// Automatically refresh the data while accessing the attribute .teams
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00044s] (3,)
|
INFO Engine [generated in 0.00044s] (3,)
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team, heroteamlink
|
FROM team, heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.1648s ago] (3,)
|
INFO Engine [cached since 0.1648s ago] (3,)
|
||||||
|
|
||||||
@ -131,8 +131,8 @@ Updated Spider-Boy's Teams: [
|
|||||||
]
|
]
|
||||||
|
|
||||||
// Automatically refresh the data while accessing the attribute .heores
|
// Automatically refresh the data while accessing the attribute .heores
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero, heroteamlink
|
FROM hero, heroteamlink
|
||||||
WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id
|
WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id
|
||||||
INFO Engine [cached since 0.1499s ago] (1,)
|
INFO Engine [cached since 0.1499s ago] (1,)
|
||||||
|
|
||||||
@ -208,12 +208,12 @@ INFO Engine COMMIT
|
|||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
|
|
||||||
// Automatically refresh the data while accessing the attribute .heroes
|
// Automatically refresh the data while accessing the attribute .heroes
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team
|
FROM team
|
||||||
WHERE team.id = ?
|
WHERE team.id = ?
|
||||||
INFO Engine [generated in 0.00029s] (1,)
|
INFO Engine [generated in 0.00029s] (1,)
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero, heroteamlink
|
FROM hero, heroteamlink
|
||||||
WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id
|
WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id
|
||||||
INFO Engine [cached since 0.5625s ago] (1,)
|
INFO Engine [cached since 0.5625s ago] (1,)
|
||||||
|
|
||||||
@ -223,12 +223,12 @@ Reverted Z-Force's heroes: [
|
|||||||
]
|
]
|
||||||
|
|
||||||
// Automatically refresh the data while accessing the attribute .teams
|
// Automatically refresh the data while accessing the attribute .teams
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [cached since 0.4209s ago] (3,)
|
INFO Engine [cached since 0.4209s ago] (3,)
|
||||||
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
|
||||||
FROM team, heroteamlink
|
FROM team, heroteamlink
|
||||||
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
WHERE ? = heroteamlink.hero_id AND team.id = heroteamlink.team_id
|
||||||
INFO Engine [cached since 0.5842s ago] (3,)
|
INFO Engine [cached since 0.5842s ago] (3,)
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ Let's see the utilities to read a single row.
|
|||||||
|
|
||||||
## Continue From Previous Code
|
## Continue From Previous Code
|
||||||
|
|
||||||
We'll continue with the same examples we have been using in the previous chapters to create and select data and we'll keep udpating them.
|
We'll continue with the same examples we have been using in the previous chapters to create and select data and we'll keep updating them.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>👀 Full file preview</summary>
|
<summary>👀 Full file preview</summary>
|
||||||
@ -86,8 +86,8 @@ $ python app.py
|
|||||||
// Some boilerplate output omitted 😉
|
// Some boilerplate output omitted 😉
|
||||||
|
|
||||||
// The SELECT with WHERE
|
// The SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.age <= ?
|
WHERE hero.age <= ?
|
||||||
INFO Engine [no key 0.00021s] (35,)
|
INFO Engine [no key 0.00021s] (35,)
|
||||||
|
|
||||||
@ -132,8 +132,8 @@ $ python app.py
|
|||||||
// Some boilerplate output omitted 😉
|
// Some boilerplate output omitted 😉
|
||||||
|
|
||||||
// The SELECT with WHERE
|
// The SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.age <= ?
|
WHERE hero.age <= ?
|
||||||
INFO Engine [no key 0.00021s] (35,)
|
INFO Engine [no key 0.00021s] (35,)
|
||||||
|
|
||||||
@ -180,8 +180,8 @@ $ python app.py
|
|||||||
// Some boilerplate output omitted 😉
|
// Some boilerplate output omitted 😉
|
||||||
|
|
||||||
// The SELECT with WHERE
|
// The SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00015s] ('Deadpond',)
|
INFO Engine [no key 0.00015s] ('Deadpond',)
|
||||||
|
|
||||||
@ -203,8 +203,8 @@ $ python app.py
|
|||||||
// Some boilerplate output omitted 😉
|
// Some boilerplate output omitted 😉
|
||||||
|
|
||||||
// The SELECT with WHERE
|
// The SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00015s] ('Deadpond',)
|
INFO Engine [no key 0.00015s] ('Deadpond',)
|
||||||
|
|
||||||
@ -274,8 +274,8 @@ $ python app.py
|
|||||||
// Some boilerplate output omitted 😉
|
// Some boilerplate output omitted 😉
|
||||||
|
|
||||||
// SELECT with WHERE
|
// SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.age < ?
|
WHERE hero.age < ?
|
||||||
INFO Engine [no key 0.00014s] (25,)
|
INFO Engine [no key 0.00014s] (25,)
|
||||||
|
|
||||||
@ -370,8 +370,8 @@ $ python app.py
|
|||||||
// Some boilerplate output omitted 😉
|
// Some boilerplate output omitted 😉
|
||||||
|
|
||||||
// SELECT with WHERE
|
// SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00021s] (1,)
|
INFO Engine [generated in 0.00021s] (1,)
|
||||||
|
|
||||||
@ -413,8 +413,8 @@ $ python app.py
|
|||||||
|
|
||||||
// SELECT with WHERE
|
// SELECT with WHERE
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00024s] (9001,)
|
INFO Engine [generated in 0.00024s] (9001,)
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ Now let's update **Spider-Boy**, removing him from the team by setting `hero_spi
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
The first important thing is, we *haven't commited* the hero yet, so accessing the list of heroes would not trigger an automatic refresh.
|
The first important thing is, we *haven't committed* the hero yet, so accessing the list of heroes would not trigger an automatic refresh.
|
||||||
|
|
||||||
But in our code, in this exact point in time, we already said that **Spider-Boy** is no longer part of the **Preventers**. 🔥
|
But in our code, in this exact point in time, we already said that **Spider-Boy** is no longer part of the **Preventers**. 🔥
|
||||||
|
|
||||||
@ -144,10 +144,10 @@ But now, what happens when we print the `preventers_team.heroes`?
|
|||||||
|
|
||||||
``` hl_lines="3"
|
``` hl_lines="3"
|
||||||
Preventers Team Heroes again: [
|
Preventers Team Heroes again: [
|
||||||
Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
|
Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
|
||||||
Hero(name='Spider-Boy', age=None, id=3, secret_name='Pedro Parqueador', team_id=2, team=None),
|
Hero(name='Spider-Boy', age=None, id=3, secret_name='Pedro Parqueador', team_id=2, team=None),
|
||||||
Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
|
Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
|
||||||
Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
|
Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
|
||||||
Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
|
Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -182,15 +182,15 @@ Now, if we commit it and print again:
|
|||||||
When we access `preventers_team.heroes` after the `commit`, that triggers a refresh, so we get the latest list, without **Spider-Boy**, so that's fine again:
|
When we access `preventers_team.heroes` after the `commit`, that triggers a refresh, so we get the latest list, without **Spider-Boy**, so that's fine again:
|
||||||
|
|
||||||
```
|
```
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age, hero.team_id AS hero_team_id
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age, hero.team_id AS hero_team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE ? = hero.team_id
|
WHERE ? = hero.team_id
|
||||||
2021-08-13 11:15:24,658 INFO sqlalchemy.engine.Engine [cached since 0.1924s ago] (2,)
|
2021-08-13 11:15:24,658 INFO sqlalchemy.engine.Engine [cached since 0.1924s ago] (2,)
|
||||||
|
|
||||||
Preventers Team Heroes after commit: [
|
Preventers Team Heroes after commit: [
|
||||||
Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
|
Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
|
||||||
Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
|
Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
|
||||||
Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
|
Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
|
||||||
Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
|
Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
@ -260,9 +260,9 @@ That second print would output:
|
|||||||
|
|
||||||
```
|
```
|
||||||
Preventers Team Heroes again: [
|
Preventers Team Heroes again: [
|
||||||
Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
|
Hero(name='Rusty-Man', age=48, id=2, secret_name='Tommy Sharp', team_id=2),
|
||||||
Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
|
Hero(name='Tarantula', age=32, id=6, secret_name='Natalia Roman-on', team_id=2),
|
||||||
Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
|
Hero(name='Dr. Weird', age=36, id=7, secret_name='Steve Weird', team_id=2),
|
||||||
Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
|
Hero(name='Captain North America', age=93, id=8, secret_name='Esteban Rogelios', team_id=2)
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -115,9 +115,7 @@ This means that this attribute could be `None`, or it could be a full `Team` obj
|
|||||||
|
|
||||||
This is because the related **`team_id` could also be `None`** (or `NULL` in the database).
|
This is because the related **`team_id` could also be `None`** (or `NULL` in the database).
|
||||||
|
|
||||||
If it was required for a `Hero` instance to belong to a `Team`, then the `team_id` would be `int` instead of `Optional[int]`.
|
If it was required for a `Hero` instance to belong to a `Team`, then the `team_id` would be `int` instead of `Optional[int]`, its `Field` would be `Field(foreign_key="team.id")` instead of `Field(default=None, foreign_key="team.id")` and the `team` attribute would be a `Team` instead of `Optional[Team]`.
|
||||||
|
|
||||||
And the `team` attribute would be a `Team` instead of `Optional[Team]`.
|
|
||||||
|
|
||||||
## Relationship Attributes With Lists
|
## Relationship Attributes With Lists
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ With what we have learned **up to now**, we could use a `select()` statement, th
|
|||||||
|
|
||||||
## Get Relationship Team - New Way
|
## Get Relationship Team - New Way
|
||||||
|
|
||||||
But now that we have the **relationship attributes**, we can just access them, and **SQLModel** (actually SQLAlchemy) will go and fetch the correspoinding data from the database, and make it available in the attribute. ✨
|
But now that we have the **relationship attributes**, we can just access them, and **SQLModel** (actually SQLAlchemy) will go and fetch the corresponding data from the database, and make it available in the attribute. ✨
|
||||||
|
|
||||||
So, the highlighted block above, has the same results as the block below:
|
So, the highlighted block above, has the same results as the block below:
|
||||||
|
|
||||||
@ -111,8 +111,8 @@ That would print a list with all the heroes in the Preventers team:
|
|||||||
$ python app.py
|
$ python app.py
|
||||||
|
|
||||||
// Automatically fetch the heroes
|
// Automatically fetch the heroes
|
||||||
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age, hero.team_id AS hero_team_id
|
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age, hero.team_id AS hero_team_id
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE ? = hero.team_id
|
WHERE ? = hero.team_id
|
||||||
INFO Engine [cached since 0.8774s ago] (2,)
|
INFO Engine [cached since 0.8774s ago] (2,)
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ First we have to import `select` from `sqlmodel` at the top of the file:
|
|||||||
```Python hl_lines="3"
|
```Python hl_lines="3"
|
||||||
{!./docs_src/tutorial/select/tutorial001.py[ln:1-3]!}
|
{!./docs_src/tutorial/select/tutorial001.py[ln:1-3]!}
|
||||||
|
|
||||||
# More code below ommitted 👇
|
# More code below omitted 👇
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -274,7 +274,7 @@ This `session.exec(statement)` will generate this output:
|
|||||||
|
|
||||||
```
|
```
|
||||||
INFO Engine BEGIN (implicit)
|
INFO Engine BEGIN (implicit)
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
INFO Engine [no key 0.00032s] ()
|
INFO Engine [no key 0.00032s] ()
|
||||||
```
|
```
|
||||||
@ -455,7 +455,7 @@ In this chapter we are touching some of them.
|
|||||||
|
|
||||||
### SQLModel's `select`
|
### SQLModel's `select`
|
||||||
|
|
||||||
When importing from `sqlmodel` the `select()` function, you are using **SQLModel**'s version of `select`.
|
When importing from `sqlmodel` the `select()` function, you are using **SQLModel**'s version of `select`.
|
||||||
|
|
||||||
SQLAchemy also has it's own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally.
|
SQLAchemy also has it's own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally.
|
||||||
|
|
||||||
@ -472,7 +472,7 @@ SQLAlchemy's own `Session` has a method `session.execute()`. It doesn't have a `
|
|||||||
|
|
||||||
If you see SQLAlchemy tutorials, they will always use `session.execute()`.
|
If you see SQLAlchemy tutorials, they will always use `session.execute()`.
|
||||||
|
|
||||||
**SQLModel**'s own `Session` inherits directly from SQLAlchemy's `Session`, and adds this additonal method `session.exec()`. Underneath, it uses the same `session.execute()`.
|
**SQLModel**'s own `Session` inherits directly from SQLAlchemy's `Session`, and adds this additional method `session.exec()`. Underneath, it uses the same `session.execute()`.
|
||||||
|
|
||||||
But `session.exec()` does several **tricks** combined with the tricks in `session()` to give you the **best editor support**, with **autocompletion** and **inline errors** everywhere, even after getting data from a select. ✨
|
But `session.exec()` does several **tricks** combined with the tricks in `session()` to give you the **best editor support**, with **autocompletion** and **inline errors** everywhere, even after getting data from a select. ✨
|
||||||
|
|
||||||
|
@ -132,8 +132,8 @@ $ python app.py
|
|||||||
// Some boilerplate and previous output omitted 😉
|
// Some boilerplate and previous output omitted 😉
|
||||||
|
|
||||||
// The SELECT with WHERE
|
// The SELECT with WHERE
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.name = ?
|
WHERE hero.name = ?
|
||||||
INFO Engine [no key 0.00017s] ('Spider-Boy',)
|
INFO Engine [no key 0.00017s] ('Spider-Boy',)
|
||||||
|
|
||||||
@ -275,8 +275,8 @@ $ python app.py
|
|||||||
// Previous output omitted 🙈
|
// Previous output omitted 🙈
|
||||||
|
|
||||||
// The SQL to SELECT the fresh hero data
|
// The SQL to SELECT the fresh hero data
|
||||||
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||||||
FROM hero
|
FROM hero
|
||||||
WHERE hero.id = ?
|
WHERE hero.id = ?
|
||||||
INFO Engine [generated in 0.00018s] (2,)
|
INFO Engine [generated in 0.00018s] (2,)
|
||||||
```
|
```
|
||||||
|